3

原来一个 Map 就能搞定 Eureka 注册表了

 2 years ago
source link: https://my.oschina.net/u/4499317/blog/5317537
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

原来一个 Map 就能搞定注册表了

Eureka 注册中心系列文章汇总:

领导让我研究 Eureka 源码 | 启动过程

领导“叕”让我研究 Eureka 源码:注册过程

值得收藏的 Eureka 控制台详解

大家好,我是悟空。

本篇从源码角度带你学习 Eureka 服务端接收注册的流程。另外我从源码中也发现了一些值得我们学习的地方,如 Eureka 存储注册表的数据结构、利用读写锁来控制更细粒度的并发性,提高程序的运行效率。

接下来,会从以下几个方面讲解:

  • 客户端发送注册请求。
  • Eureka 注册中心接收注册请求。
  • 服务端将客户端注册信息保存到一个 Map 里面。

关于源码的获取直接到官网下载就好了。https://github.com/Netflix/eureka

本文已收录到我的 github:https://github.com/Jackson0714/PassJava-Learning

@[toc]

一、注册入口

上一讲我们知道了 Eureka Client 是通过发送 http 请求来注册的,那么肯定是有一个地方来接收这个 http 请求的,也就是注册入口。这是怎么玩的呢?

其实是用到了 jersey 框架,这个框架不用深究,我们只需要知道这个框架在哪引用以及做什么事情的就可以了。

可以把 jersey 类比 mvc 框架,jersey 有 servlet 专门处理 http 请求。引用 jersey 框架的地方:

\eureka\eureka-server\src\main\webapp\WEB-INF\web.xml

然后处理 HTTP 请求的 controller 在哪呢?

其实是在 eureka-core 项目的 resources 目录下,里面定义了很多的 Resource 结尾的类,它们就是用来处理 HTTP 请求的。

\eureka\eureka-core\src\main\java\com\netflix\eureka\resources

通过XxResource 类的英文注释我们也可以知道,这个 jersey resource 类是用来处理 HTTP 请求的。

A jersey resource that handles request

ApplicationsResource

最后找到了 ApplicationResource 类的 addInstance 方法就是我们要找的处理注册请求的方法。

二、接收注册请求

整体流程如下:

2.1 接收注册请求的方法

addInstance 方法里面的核心代码就是

registry.register(info, true);

registry 就是 PeerAwareInstanceRegistryImpl 的实例对象。它实现了 PeerAwareInstanceRegistry 接口。

调用它的 register() 方法后会调用抽象类 AbstractInstanceRegistry 的 register() 方法,核心代码就是在这个抽象类的 register() 方法。另外要说下的就是上面的抽象类和接口分别实现和继承了接口 InstanceRegistry。

接口和类的关系图如下:

那么注册信息会放到哪个里面呢?

三、存放注册信息的地方

我们看到源码里面定义了一个 gNewMap,是 ConcurrentHashMap,然后赋值给了 gMap 变量

ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap 

所以其实是用 gMap 变量来存注册信息的。我们来分析 gMap 的结构。

首先 gMap 是 ConcurrentHashMap 结构,所以就是 key-value 这种键值对的。

  • key 就是一个 唯一 id,String 类型。值类似这种:i-00000004
  • value 里面存的是 Lease<InstanceInfo>。
  • Lease是一个类,里面持有一个 instanceInfo 的 holder。这个 instanceInfo 就是注册过来的服务实例信息,包含 ip 地址,端口号等。

把服务实例信息放到 gMap 中也很简单,调用 put 方法就可以了。

gMap.put(registrant.getId(), lease);

下面是我注册了两个服务实例的状态:

四、值得学习的地方

4.1 ConcurrentHashMap?

上面讲到 ConcurrentHashMap,为什么不是用 hashmap ?

ConcurrentHashMap<String, Lease<InstanceInfo>>()

原因

  1. 在并发编程中使用 HashMap 可能造成死循环 ( JDK 1.7 和 1.8 可能会造成数据丢失)
  2. HashTable 效率非常较低。

简单说下 ConcurrentHashMap 的底层原理是怎么样的?

ConcurrentHashMap 内部细分了若干个小的 HashMap,称之为段(Segment)。 默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果需要在 ConcurrentHashMap 中添加一个新的表项,并不是将整个 HashMap 加锁,而是首先根据 hashcode 得到该表项应该存放在哪个段中,然后对该段加锁,并完成 put 操作。在多线程环境中,如果多个线程同时进行put操作,只要被加入的表项不存放在同一个段中,则线程间可以做到真正的并行

4.2 readWriteLock?

我们看到源码中有用到读锁 ReentrantReadWriteLock,如下所示

readWriteLock = new ReentrantReadWriteLock();
Lock read = readWriteLock.readLock();
read.lock();
...
read.unlock();

4.2.1 为什么分为读锁和写锁?

原因

在没有读写锁之前,假设使用普通的 ReentrantLock,那么虽然保证了线程安全,但是也浪费了一定的资源,因为如果多个读操作同时进行,其实并没有线程安全问题,可以允许让多个读操作并行,以便提高程序效率。

但是写操作不是线程安全的,如果多个线程同时写,或者在写的同时进行读操作,便会造成线程安全问题。

读写锁就解决了这样的问题,它设定了一套规则,既可以保证多个线程同时读的效率,同时又可以保证有写入操作时的线程安全。

读锁: 允许多个线程获取读锁,同时访问同一个资源。

读锁

写锁: 只允许一个线程获取写锁,不允许同时访问同一个资源。

写锁

整体思路

是它有两把锁,第 1 把锁是写锁,获得写锁之后,既可以读数据又可以修改数据,而第 2 把锁是读锁,获得读锁之后,只能查看数据,不能修改数据。读锁可以被多个线程同时持有,所以多个线程可以同时查看数据。

在读的地方合理使用读锁,在写的地方合理使用写锁,灵活控制,可以提高程序的执行效率。

4.2.2 读写锁的获取规则

在使用读写锁时遵守下面的获取规则:

  • 如果有一个线程已经占用了读锁,则此时其他线程如果要申请读锁,可以申请成功。
  • 如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,因为读写不能同时操作。
  • 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,都必须等待之前的线程释放写锁,同样也因为读写不能同时,并且两个线程不应该同时写。

读写锁互斥总结

  • 读读共享。
  • 写写互斥、读写互斥、写读互斥。

本篇从源码的角度,分析了 Eureka 服务端接收注册信息的流程,核心逻辑就是将服务实例的注册信息放到 ConcurrentHashMap 里面,同时利用读锁来控制细粒度的并发注册。另外介绍了下我们不太熟悉的 Jersey 框架,它是用来处理 HTTP 请求的,比如用来处理客户端注册的 HTTP 请求。

从源码分析中,我学到了 Eureka 存储注册表用到的数据结构 ConcurrentHashMap<String, Lease<InstanceInfo>>(),大家可以借鉴下用到项目中。然后又复习了一遍 ConcurrentHashMap原理、可重入读写锁的原理,学习就是不断记忆,不断遗忘的过程,学习源码正好可以复习一波~

更多内容:

全网最细 | 21张图带你领略集合的线程不安全

5000字 | 24张图带你彻底理解Java中的21种锁

http://www.passjava.cn

https://www.cnblogs.com/zz-ksw/p/12774151.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK