博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Eureka源码分析:Eureka不会进行二次Replication的原因
阅读量:7281 次
发布时间:2019-06-30

本文共 5399 字,大约阅读时间需要 17 分钟。

Eureka不会进行二次同步注册信息

Eureka会将本实例中的注册信息同步到它的peer节点上,这是我们都知道的特性。然而,当peer节点收到同步数据后,并不会将这些信息再同步到它自己的peer节点上。如果有A, B, C三个实例,A配B, B配C, C配A, 那么当向A注册一个新服务时,只有A, B两个Eureka实例会有新服务的注册信息,C是没有的。这一点在官方wiki上并没有明确说明。下面通过源码来查找一下原因。

构建

Eureka当前版本 () 使用gradle作为构建工具,不过仓库里提供了gradlew,所以也不用我们自己去下载。如果自己下载了gradle的最新版本3.X,那么反而会因为兼容性问题导致构建失败,因为官方使用的是2.1.0版本。构建之前一定要科学上网,否则很多依赖是下载不到的:

./gradlew build -x test // 跳过测试
  • 1
  • 1

源码定位与分析

Eureka本质上是一个Servlet应用,它使用jersey作为RESTful框架来构造自己的REST服务。在eureka-server模块中可以找到web.xml文件,在build/libs目录下可以看到构建完成后生成的war包。我们要找的代码,在eureka-core模块的com.netflix.eureka.resource包下。jersey相比SpringMVC来说它是一个更加标准的HTTP RESTful框架,因此很自然可以猜想到resource就是请求路由所在的包。

查阅官方wiki, 我们知道eureka对外暴露接口的形式为:

GET/POST/PUT/DELETE /eureka/v2/apps/{APP-ID}
  • 1
  • 1

我们在ApplicationsResource类中可以发现如下注解:

@Path("/{version}/apps")@Produces({
"application/xml", "application/json"})public class ApplicationsResource { // ... ... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

由此可以确定该类就是我们要找的重点。查阅该类,会发现有如下方法:

/**     * Gets information about a particular {@link com.netflix.discovery.shared.Application}.     *     * @param version     *            the version of the request.     * @param appId     *            the unique application identifier (which is the name) of the     *            application.     * @return information about a particular application.     */    @Path("{appId}") public ApplicationResource getApplicationResource( @PathParam("version") String version, @PathParam("appId") String appId) { CurrentRequestVersion.set(Version.toEnum(version)); return new ApplicationResource(appId, serverConfig, registry); // 交给ApplicationResource处理 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

通过注释得知该方法用来处理客户端查询某个服务信息的HTTP请求。但是方法体中并没有具体的查询逻辑,而是委托给了ApplicationReource类进行处理。查阅该类,可以发现注册信息的CRUD操作逻辑都是在这里实现的。我们重点关注一下注册方法,因为Eureka在收到新服务的注册信息时会马上将其同步到peer节点中:

@POST    @Consumes({
"application/json", "application/xml"}) public Response addInstance(InstanceInfo info, @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) { logger.debug("Registering instance {} (replication={})", info.getId(), isReplication); // validate that the instanceinfo contains all the necessary required fields // 参数检查 ... // 调用注册逻辑 registry.register(info, "true".equals(isReplication)); return Response.status(204).build(); // 204 to be backwards compatible }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注意register的第二个参数:

"true".equals(isReplication)
  • 1
  • 1

isReplication是从请求头中获取的字符串,因此可以得知,Eureka在向peer节点发送同步请求时会在请求头中携带自定义的x-netflix-discovery-replication头:

public static final String HEADER_REPLICATION = "x-netflix-discovery-replication";
  • 1
  • 1

peer节点通过该请求头来判断当前请求是其它Eureka节点的同步请求还是服务的注册请求。我们假定当前请求是上一个Eureka发来的同步请求,那么这里第二个参数的值应该为true

继续看register()方法,这是个PeerAwareInstanceRegistry接口的接口方法,该接口只有一个唯一实现PeerAwareInstanceRegistryImpl

/**     * Registers the information about the {@link InstanceInfo} and replicates     * this information to all peer eureka nodes. If this is replication event     * from other replica nodes then it is not replicated.     *     * @param info     *            the {@link InstanceInfo} to be registered and replicated.     * @param isReplication     *            true if this is a replication event from other replica nodes,     *            false otherwise.     */    @Override    public void register(final InstanceInfo info, final boolean isReplication) { int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } // 调用父类的实现执行注册逻辑 super.register(info, leaseDuration, isReplication); // 将该注册请求同步到peer节点中 replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

首先通过英文注释可以了解到,如果当前注册请求是一个Replication请求,那么该注册请求不会被再次Replicate到下一个peer节点中。我们进入到关键的replicateToPeers()方法中看看为什么不会被同步:

private void replicateToPeers(Action action, String appName, String id,                                  InstanceInfo info /* optional */,                                  InstanceStatus newStatus /* optional */, boolean isReplication) {        Stopwatch tracer = action.getTimer().start();        try {            // ... ...            // If it is a replication already, do not replicate again as this will create a poison replication            // 如果 isReplication 为true, 即当前是个同步注册信息的请求            // 这里就直接返回了            if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {                return; } // 向自己的peer节点同步注册信息 for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) { // If the url represents this host, do not replicate to yourself. if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) { continue; } replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node); } } finally { tracer.stop(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

可以看到下面的代码

if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {    return;}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

就是Eureka不进行二次Replication的原因,如果当前请求是从其他Eureka发来的同步请求,那么该方法就直接返回了,不再执行后面的同步逻辑。

结论

通过上面的追踪我们确认了Eureka不进行二次同步是作者有意而为之,并不是bug。但是官方wiki上并没有明确写明这一点,文档并不太完善。可以考虑提一个Issue提醒一下开发组。

http://blog.csdn.net/neosmith/article/details/52912645

你可能感兴趣的文章
1、Delphi 打开目录和txt文件模块
查看>>
WEB安全实战(五)XSS 攻击的第二种解决方式(推荐)
查看>>
Gliffy
查看>>
[51单片机] 以PWM控制直流电机为例建一个简单的51工程框架
查看>>
随记MySQL的时间差函数(TIMESTAMPDIFF、DATEDIFF)、日期转换计算函数(date_add、day、date_format、str_to_date)...
查看>>
左求值表达式,堆栈,调试陷阱与ORM查询语言的设计
查看>>
spring源码分析之spring-jdbc模块详解
查看>>
STM32的USART DMA传输(转)
查看>>
mac 刻录ISO系统盘
查看>>
Genymotion中SD卡目录在Eclipse中查看,以及创建SDCard
查看>>
BZOJ 2298: [HAOI2011]problem a 动态规划
查看>>
精度与分辨率是一回事吗
查看>>
徕卡×天猫超级品牌日 全球奢侈大牌的中国互联网之路
查看>>
奇葩说为小米开专场,Max2的续航能力真的那么强大吗?
查看>>
芝麻信用又有大动作!让外国公司也流口水的免押金新形态来了
查看>>
哪些你知道或不知道的css,在这里或许都齐全
查看>>
技术周刊(2019-01-07 CSS 动画 )
查看>>
runtime?虐我千百遍==、
查看>>
centos下实现pm2自动化部署
查看>>
JavaScript之this详解
查看>>