标签分类
技术文章
当前位置:主页 > 计算机编程 > java > Java职场中到底啥是分布式系统开发经验

Java应聘中到底啥是分布式系统开发经验

  • 发布时间:
  • 作者:码农之家原创
  • 点击:140

Java职场中到底啥是分布式系统开发经验

这篇文章主要知识点是关于java分布式系统,java分布式系统开发,java分布式系统开发经验,Java职场中到底啥是分布式系统开发经验,Java使用Redisson分布式锁实现原理 的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

Scala与Clojure函数式编程模式:Java虚拟机高效编程
  • 类型:Java大小:34 MB格式:PDF出版:人民邮电出版社作者:贝维拉夸林
立即下载
代码不朽:编写可维护软件的10大要则(Java版)
  • 类型:Java大小:25.1 MB格式:PDF出版:电子工业出版社作者:Joost
立即下载

更多相关的学习资源可以参阅 程序设计电子书移动开发电子书Java视频Java电子书、等栏目。

前言现在有很多Java技术方向的同学在找工作的时候,肯定都会去招聘网站上找职位投递简历。但是在很多职位JD上往往会有这样的一个要求:熟悉分布式系统理论、设计和开发,具备复杂分布式系统构建经验。之前不少同学后台留言问过我:这个分布式系统的设计和开发经验,到底指的是什么?那么这篇文章就给大家来解释一下这个问题。

1、从单块系统说起

要说分布式系统是什么东西,那么就得先从单块系统开始说起。

很多同学应该都知道,如果你在一些中小型的传统软件公司里工作,那么很有可能现在在做的系统是如下这个样子。所有的代码都在一个工程里,最多可能就是通过maven等构件工具拆分了一下代码工程模块,不同的模块可以放在不同的工程代码里。

在部署的时候,可能就是直接在线上的几台机器里直接放到里面的tomcat下来运行。

然后在web服务器前面可能会有一层负载均衡服务器,比如用nginx或者是其他的负载均衡设备。

很多流量很小的企业内部系统,比如OA、CRM、财务等系统,甚至可能就直接在一台机器的tomcat下部署一下。

然后直接配置一下域名解析,就可以让这个系统的可能几十个,或者几百个用户通过访问域名来使用这个软件了。

至于说系统的依赖大概来说很可能只有一个,那就是MySQL、Oracle等关系型数据库,可能会在某台机器上专门部署一个数据库,让应用系统来使用。大家看看下面的图,体会一下这种单体架构。

Java同学找工作最懵圈的问题:到底啥是分布式系统开发经验?(推荐)

这种系统在很多中小型公司里现在还是比较多的,就是典型的单块系统,所有代码在一个工程,部署在一个tomcat里即可,这里包含了系统所有的功能。

你哪怕就部署一台机器,这个系统也可以运行,只不过为了所谓的“高可用”,可能一般会部署两台机器,前面加一层负载均衡设备,这样其中一个机器挂了,另外一个机器上还有一个系统可以用。

2、团队越来越大,业务越来越复杂

其实上面说的那种单块系统,如果是一个10人以内的小团队大家一起维护和开发一个用户数量不多,请求量不大的系统,也是没问题的,还挺方便的,对吧。

你搞一个代码仓库,然后就一份代码,每个人都在自己本地写代码,最后把代码合并一下,做做测试,然后就直接部署基于Tomcat来就可以了。

但是问题就在于说,如果你的团队超过了10个人,比如有20个人,甚至几十个人,上百个人要一起协作开发这个系统,然后里面的业务逻辑特别多,可能功能模块多达几百个。这个时候就麻烦了,你要是还用那种单块系统的模式,那肯定是很痛苦的。

 因为几十个人维护一个单块系统,大家在一个工程里写代码,大量的冲突以及代码合并都会让人崩溃

而且部署的时候会有各种冲突,比如某个功能模块要上线了,但是他必须得把整个单块系统所有的功能都回归测试一遍才敢上线。

 因为大家的代码都在一个工程里,都是耦合在一起的,你修改了代码,必须全部测试一遍才能保证系统正常。

所以说这个时候,就必须想办法把系统改造成分布式系统了。

 3、分布式出现:庞大系统分而治之

这个时候就可以尝试把一个大的系统拆分为很多小的系统,甚至很多小的服务,然后几个人组成一个小组就专门维护其中一个小系统,或者每个人维护一个小服务。

简单来说,就是分而治之,这样每个人可以专注维护自己的代码。然后不同的小系统自己开发、测试和上线,都不会跟别人耦合在一起,可以自己独立进行,非常的方便,大大简化了大规模系统的开发成本。

不同的子系统之间,就是通过接口互相来回调用,每个子系统都有自己的数据库,大家看下面的图。

Java同学找工作最懵圈的问题:到底啥是分布式系统开发经验?(推荐)

4、分布式系统所带来的技术问题

那么大家这个时候可以思考一下,如果你的公司是采用这种分布式系统的方式来构建公司的一个大规模系统的,那么这个时候会涉及到哪些技术问题?

(1)分布式服务框架

你如果要让不同的子系统或者服务之间互相通信,首先必须有一套分布式服务框架。

也就是各个服务可以互相感知到对方在哪里,可以发送请求过去,可以通过HTTP或者RPC的方式。

在这里,最常见的技术就是dubbo以及spring cloud,当然大厂一般都是自己有服务框架

(2)分布式事务

一旦你的系统拆分为了多个子系统之后,那么一个贯穿全局的分布式事务应该怎么来实现?

这个你需要了解TCC、最终一致性、2PC等分布式事务的实现方案和开源技术。

(3)分布式锁

不同的系统之间如果需要在全局加锁获取某个资源的锁定,此时应该怎么来做?

毕竟大家不是在一个JVM里了,不可能用synchronized来在多个子系统之间实现锁吧,是不是?

(4)分布式缓存

如果你原来就是个单块系统,那么你其实是可以在单个JVM里进行本地缓存就可以了,比如搞一个HashMap来缓存一些数据。

但是现在你有很多个子系统,他们如果要共享一个缓存,你应该怎么办?是不是需要引入Redis等缓存系统?

(5)分布式消息系统

在单块系统内,就一个JVM进程内部,你可以用类似LinkedList之类的数据结构作为一个本地内存里的队列。

但是多个子系统之间要进行消息队列的传递呢?那是不是要引入类似RabbitMQ之类的分布式消息中间件?

(6)分布式搜索系统

如果在单块系统内,你可以比如在本地就基于Lucene来开发一个全文检索模块,但是如果是分布式系统下的很多子系统,你还能直接基于Lucene吗?

明显不行,你需要在系统里引入一个外部的分布式搜索系统,比如Elasticsearch。

(7)其他很多的技术

比如说分布式配置中心、分布式日志中心、分布式监控告警中心、分布式会话,等等,都是分布式系统场景下你需要使用和了解的一些技术。

因为沿用单块系统时代的那些技术已经不行了,比如说你单块系统的时候,直接在本地用一个properties文件存放自己的配置即可,

日志也写到本地即可。但是分布式时代呢?

你那么多的子系统,怎么共享同一份配置?怎么把各个系统的日志聚合写到一个地方来查看?

单块系统的时候,你一个web应用直接基于Servlet API提供的Session会话功能即可,那么分布式时代呢,你有N多个子系统如果要共享会话该怎么做?

5、一句话总结:什么是分布式系统设计和开发经验?

其实分析完了之后,大家应该就大概知道了,招聘JD上写这个分布式系统的设计和开发经验,其实他是一个很大的主题,里面包含很多的内容。

你的系统一旦分布式了之后,通信、缓存、消息、事务、锁、配置、日志、监控、会话,等等各种原来单块系统场景下很容易解决的问题,都会变得很复杂,需要引入大量外部的技术。

所以你有没有参与过类似这样的一个大的分布式系统?你有没有基于各种技术解决过分布式系统场景下的各种技术问题?这就是人家希望和要求的分布式系统设计和开发的经验。如果大家还没接触过,建议多去学习一下。

6、补充说明:中间件系统及大数据系统

最后给大家说明一点,一般这种招聘JD,如果是Java岗位要求分布式相关的经验,其实主要还是上面说的那些东西,他面向的是分布式的业务系统的构建。

但是其实分布式系统本身是一个非常复杂的话题,因为刚才说的只是一个分布式业务系统要依赖哪些技术来进行构建。

但是其实比如Kafka、Rocket等中间件,本身他也是分布式的,你要搞明白他们自己是如何实现分布式的,又是一个非常复杂的话题。

此外,像hadoop、spark、hbase等大数据系统,本身也都是世界上最最复杂的分布式系统,这又涉及到大数据领域的话题了,以后有机会可以单独聊聊。

以上所述是小编给大家介绍的分布式系统开发经验详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对码农之家网站的支持!

Java使用Redisson分布式锁实现原理

1. 基本用法

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson</artifactId>
  <version>3.8.2</version>
</dependency>
Config config = new Config();
config.useClusterServers()
  .setScanInterval(2000) // cluster state scan interval in milliseconds
  .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
  .addNodeAddress("redis://127.0.0.1:7002");

RedissonClient redisson = Redisson.create(config);

RLock lock = redisson.getLock("anyLock");

lock.lock();

try {
  ...
} finally {
  lock.unlock();
}

针对上面这段代码,重点看一下Redisson是如何基于Redis实现分布式锁的

Redisson中提供的加锁的方法有很多,但大致类似,此处只看lock()方法

更多请参见https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers

2. 加锁

Java使用Redisson分布式锁实现原理

Java使用Redisson分布式锁实现原理

可以看到,调用getLock()方法后实际返回一个RedissonLock对象,在RedissonLock对象的lock()方法主要调用tryAcquire()方法

Java使用Redisson分布式锁实现原理

由于leaseTime == -1,于是走tryLockInnerAsync()方法,这个方法才是关键

首先,看一下evalWriteAsync方法的定义

复制代码 代码如下:
<T, R> RFuture<R> evalWriteAsync(String key, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object ... params);

最后两个参数分别是keys和params

实际调用是这样的:

Java使用Redisson分布式锁实现原理

单独将调用的那一段摘出来看

commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
         "if (redis.call('exists', KEYS[1]) == 0) then " +
           "redis.call('hset', KEYS[1], ARGV[2], 1); " +
           "redis.call('pexpire', KEYS[1], ARGV[1]); " +
           "return nil; " +
         "end; " +
         "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
           "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
           "redis.call('pexpire', KEYS[1], ARGV[1]); " +
           "return nil; " +
         "end; " +
         "return redis.call('pttl', KEYS[1]);",
          Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));

结合上面的参数声明,我们可以知道,这里KEYS[1]就是getName(),ARGV[2]是getLockName(threadId)

假设前面获取锁时传的name是“abc”,假设调用的线程ID是Thread-1,假设成员变量UUID类型的id是6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c

那么KEYS[1]=abc,ARGV[2]=6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c:Thread-1

因此,这段脚本的意思是

  1、判断有没有一个叫“abc”的key

  2、如果没有,则在其下设置一个字段为“6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c:Thread-1”,值为“1”的键值对 ,并设置它的过期时间

  3、如果存在,则进一步判断“6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c:Thread-1”是否存在,若存在,则其值加1,并重新设置过期时间

  4、返回“abc”的生存时间(毫秒)

这里用的数据结构是hash,hash的结构是: key 字段1 值1 字段2 值2 。。。

用在锁这个场景下,key就表示锁的名称,也可以理解为临界资源,字段就表示当前获得锁的线程

所有竞争这把锁的线程都要判断在这个key下有没有自己线程的字段,如果没有则不能获得锁,如果有,则相当于重入,字段值加1(次数)

3. 解锁

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
  return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
      "if (redis.call('exists', KEYS[1]) == 0) then " +
        "redis.call('publish', KEYS[2], ARGV[1]); " +
        "return 1; " +
      "end;" +
      "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
        "return nil;" +
      "end; " +
      "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
      "if (counter > 0) then " +
        "redis.call('pexpire', KEYS[1], ARGV[2]); " +
        "return 0; " +
      "else " +
        "redis.call('del', KEYS[1]); " +
        "redis.call('publish', KEYS[2], ARGV[1]); " +
        "return 1; "+
      "end; " +
      "return nil;",
      Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));

}

我们还是假设name=abc,假设线程ID是Thread-1

同理,我们可以知道

KEYS[1]是getName(),即KEYS[1]=abc

KEYS[2]是getChannelName(),即KEYS[2]=redisson_lock__channel:{abc}

ARGV[1]是LockPubSub.unlockMessage,即ARGV[1]=0

ARGV[2]是生存时间

ARGV[3]是getLockName(threadId),即ARGV[3]=6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c:Thread-1

因此,上面脚本的意思是:

  1、判断是否存在一个叫“abc”的key

  2、如果不存在,向Channel中广播一条消息,广播的内容是0,并返回1

  3、如果存在,进一步判断字段6f0829ed-bfd3-4e6f-bba3-6f3d66cd176c:Thread-1是否存在

  4、若字段不存在,返回空,若字段存在,则字段值减1

  5、若减完以后,字段值仍大于0,则返回0

  6、减完后,若字段值小于或等于0,则广播一条消息,广播内容是0,并返回1;

可以猜测,广播0表示资源可用,即通知那些等待获取锁的线程现在可以获得锁了

4. 等待

以上是正常情况下获取到锁的情况,那么当无法立即获取到锁的时候怎么办呢?

再回到前面获取锁的位置

@Override
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
  long threadId = Thread.currentThread().getId();
  Long ttl = tryAcquire(leaseTime, unit, threadId);
  // lock acquired
  if (ttl == null) {
    return;
  }

  //  订阅
  RFuture<RedissonLockEntry> future = subscribe(threadId);
  commandExecutor.syncSubscription(future);

  try {
    while (true) {
      ttl = tryAcquire(leaseTime, unit, threadId);
      // lock acquired
      if (ttl == null) {
        break;
      }

      // waiting for message
      if (ttl >= 0) {
        getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
      } else {
        getEntry(threadId).getLatch().acquire();
      }
    }
  } finally {
    unsubscribe(future, threadId);
  }
//    get(lockAsync(leaseTime, unit));
}


protected static final LockPubSub PUBSUB = new LockPubSub();

protected RFuture<RedissonLockEntry> subscribe(long threadId) {
  return PUBSUB.subscribe(getEntryName(), getChannelName(), commandExecutor.getConnectionManager().getSubscribeService());
}

protected void unsubscribe(RFuture<RedissonLockEntry> future, long threadId) {
  PUBSUB.unsubscribe(future.getNow(), getEntryName(), getChannelName(), commandExecutor.getConnectionManager().getSubscribeService());
}

这里会订阅Channel,当资源可用时可以及时知道,并抢占,防止无效的轮询而浪费资源

当资源可用用的时候,循环去尝试获取锁,由于多个线程同时去竞争资源,所以这里用了信号量,对于同一个资源只允许一个线程获得锁,其它的线程阻塞

5. 小结

Java使用Redisson分布式锁实现原理

Java使用Redisson分布式锁实现原理

6. 其它相关

《基于Redis的分布式锁的简单实现》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

以上就是本次给大家分享的全部知识点内容总结,大家还可以在下方相关文章里找到儿童python编程入门书籍推、 vue项目中使用md5加密以及、 解决axios.interceptors.respon、 等java文章进一步学习,感谢大家的阅读和支持。

上一篇:java写一个锁Lock的步骤方法

下一篇:java动态静态代理原理实例代码

展开 +

收起 -

学习笔记
网友NO.896975

Java高级架构之FastDFS分布式文件集群详解

FastDFS简介 FastDFS是一款开源的轻量级分布式文件系统,使用C实现,支持Linux、BSD等unix-like操作系统。值得注意的是,fastdfs并不是通用的文件系统,只能通过专用的API访问。 fastdfs为互联网应用量身定做,解决了大容量文件存储的问题,fastdfs追求高性能和高扩展性。fastdfs的主要概念: tracker-server:跟踪服务器。用于跟踪文件,主要起调度作用。在内存中记录了所有存储组和存储服务器的状态信息,是客户端和数据存储的主要枢纽。相比GFS更为精简,因为不记录文件索引。 storage-server: 存储服务器。用于存储文件。直接使用操作系统的文件系统来管理和组织文件。 group: 组,卷。多个服务器存在一个组中,在一个组中的服务器存储的文件是完全相同的,并且同一个组的服务器地位是对等的。对于文件的操作可以在任意一个组中的服务器上进行。 metadata: 元数据。以键值对的方式存储,用于存储文件的相关信息。 各大存储系统的对比 话说没有对比就没有伤害,fastdfs也不是万能的,需要根据业务来选择适合的存储系统。 存储系统 适合存储的文件类型 文件分布情况 系统性能 复杂度 FUSE(用户文件系统) POSIX() 备份机制 通讯协议接口 社区情况 实现语言 FastDFS 4KB至500MB 将小文件合并存储 很高 简单 不支持 不支持 组内冗余备份 HTTP……

网友NO.392758

浅析JavaWeb项目架构之Redis分布式日志队列

摘要: 架构、分布式、日志队列,标题自己都看着唬人,其实就是一个日志收集的功能,只不过中间加了一个Redis做消息队列罢了。 为什么需要消息队列? 当系统中出现“生产“和“消费“的速度或稳定性等因素不一致的时候,就需要消息队列,作为抽象层,弥合双方的差异。 架构、分布式、日志队列,标题自己都看着唬人,其实就是一个日志收集的功能,只不过中间加了一个Redis做消息队列罢了。 为什么需要消息队列? 当系统中出现“生产“和“消费“的速度或稳定性等因素不一致的时候,就需要消息队列,作为抽象层,弥合双方的差异。 比如我们系统中常见的邮件、短信发送,把这些不需要及时响应的功能写入队列,异步处理请求,减少响应时间。 如何实现? 成熟的JMS消息队列中间件产品市面上有很多,但是基于目前项目的架构以及部署情况,我们采用Redis做消息队列。 为什么用Redis? Redis中list数据结构,具有“双端队列”的特性,同时redis具有持久数据的能力,因此redis实现分布式队列是非常安全可靠的。 它类似于JMS中的“Queue”,只不过功能和可靠性(事务性)并没有JMS严格。Redis本身的高性能和"便捷的"分布式设计(replicas,sharding),可以为实现"分布式队列"提供了良好的基础。 提供者端 项目采用第三方redis插件……

网友NO.927297

浅谈Java(SpringBoot)基于zookeeper的分布式锁实现

通过zookeeper实现分布式锁 1、创建zookeeper的client 首先通过CuratorFrameworkFactory创建一个连接zookeeper的连接CuratorFramework client public class CuratorFactoryBean implements FactoryBeanCuratorFramework, InitializingBean, DisposableBean { private static final Logger LOGGER = LoggerFactory.getLogger(ContractFileInfoController.class); private String connectionString; private int sessionTimeoutMs; private int connectionTimeoutMs; private RetryPolicy retryPolicy; private CuratorFramework client; public CuratorFactoryBean(String connectionString) { this(connectionString, 500, 500); } public CuratorFactoryBean(String connectionString, int sessionTimeoutMs, int connectionTimeoutMs) { this.connectionString = connectionString; this.sessionTimeoutMs = sessionTimeoutMs; this.connectionTimeoutMs = connectionTimeoutMs; } @Override public void destroy() throws Exception { LOGGER.info("Closing curator framework..."); this.client.close(); LOGGER.info("Closed curator framework."); } @Override public CuratorFramework getObject() throws Exception { return this.client; } @Override public Class? getObjectType() { return this.client != null ? this.client.getClass() : CuratorFramework.class; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { if (StringUtils.isEmpty(this.connectionString)) { throw new IllegalStateException("connectionString can not be empty."); } else { if (this.retryPolicy == null) { this……

<
1
>

Copyright 2018-2019 xz577.com 码农之家

版权责任说明