Wireshark抓包分析Eureka注册发现协议

Posted 程序员欣宸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Wireshark抓包分析Eureka注册发现协议相关的知识,希望对你有一定的参考价值。

欢迎访问我的GitHub

  • 前面的系列文章中,我们分析源码对Eureka有了深入了解,其中获取服务列表、注册、续约等操作都涉及到client和server端的交互,今天我们通过Wireshark抓包来分析这些交互的内容,以印证之前的代码分析,加深印象

源码分析系列文章

  • 以下是整理的Eureka源码分析系列,用于参考:
  1. 《Spring Cloud源码分析之Eureka篇第一章:准备工作》
  2. 《Spring Cloud源码分析之Eureka篇第二章:注册中心启动类上的注解EnableEurekaServer》
  3. 《Spring Cloud源码分析之Eureka篇第三章:EnableDiscoveryClient与EnableEurekaClient的区别(Edgware版本)》
  4. 《Spring Cloud源码分析之Eureka篇第四章:服务注册是如何发起的》
  5. 《Spring Cloud源码分析之Eureka篇第五章:更新服务列表》
  6. 《Spring Cloud源码分析之Eureka篇第六章:服务注册》
  7. 《Spring Cloud源码分析之Eureka篇第七章:续约》
  8. 《Spring Cloud源码分析之Eureka篇第八章:服务注册名称的来历》

实战环境简介

  • 为了组建Spring cloud环境,本次实战用了三台电脑,部署情况如下图:

  • 三台电脑的详细情况如下表所示:
应用名称 身份 IP地址 端口 启动顺序 操作系统 备注
springclouddeepeureka Eureka server 192.168.31.147 8081 第一 Linux
springclouddeepprovider Eureka client 192.168.31.104 8082 第二 Windows10 这台电脑上安装了Wireshark2.6.3
springclouddeepconsumer Eureka client 192.168.31.88 8083 第三 Linux 此应用先不启动,<br>等抓包后再启动,以便观察变化

应用源码下载

名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本章源码分别在springclouddeepeureka、springclouddeepprovider、springclouddeepconsumer这三个文件夹下,如下图红框所示:

实战步骤

  1. 修改应用的配置文件;
  2. 编译构建三个应用,得到三个jar包;
  3. 在三台电脑上分别部署应用;
  4. 启动springclouddeepeureka;
  5. 在springclouddeepprovider所在电脑上启动Wireshark,并配置好过滤规则;
  6. 启动springclouddeepprovider;
  7. Wireshark抓包,分析注册、续约、下载服务列表等请求响应;
  8. 启动springclouddeepconsumer;
  9. Wireshark抓包,分析服务列表的变化;
  10. 停止应用springclouddeepconsumer;
  11. Wireshark抓包,分析服务列表的变化;

关于机器的小建议

  • 三台电脑的成本不低,除了用老旧电脑东拼西凑,也可以考虑租用云服务器,我这里的Linux电脑是树莓派,装64位Linux,再装上JDK8就能部署和运行springboot应用了,树莓派安装64位系统请参考《树莓派3B安装64位操作系统(树莓派无需连接显示器键盘鼠标) 》

  • 接下来开始实战吧;

修改应用的配置文件

  • 根据前面提供的Github地址下载三个应用源码;
  • springclouddeepprovider和springclouddeepconsumer这两个应用的application.yml文件都要修改,配置项eureka.client.serviceUrl.defaultZone的值都改成http://192.168.31.147:8081/eureka/,如下图红框所示:

编译构建三个应用

  • 在每个应用的目录下分别执行命令mvn clean package -Dmaven.test.skip=true docker:build,即可在target目录得到应用对应的jar文件,例如springclouddeepeureka工程的jar文件是springclouddeepeureka-0.0.1-SNAPSHOT.jar,如下图所示:

在三台电脑上分别部署应用

  • 将三个jar文件分别部署到对应的三台电脑上,并保证这三台电脑上都已装有JDK1.8版本;

启动springclouddeepeureka

  • 登录springclouddeepeureka所在的电脑,在springclouddeepeureka-0.0.1-SNAPSHOT.jar文件所在目录下执行命令java -Xms256m -Xmx256m -jar springclouddeepeureka-0.0.1-SNAPSHOT.jar,这样Eureka server就启动了;

启动和配置Wireshark

  1. 应用springclouddeepprovider所在的电脑是Windows10操作系统,上面安装了Wireshark2.6.3,现在请运行它;
  2. 首先在网卡列表中确定自己用的是哪一个,我这里用的是无线网卡,因此双击下图红框位置:

  1. 配置过滤规则,本次抓的包是HTTP的,并且只关注和Eureka server之间的请求响应,因此过滤规则是ip.addr == 192.168.31.147 && http,将此规则填入下图红框中的位置,再按下回车键:

  • 如上图,之前的数据都没有了,内容窗口空空如也,这是因为应用springclouddeepprovider还未启动,和Eureka server之间并没有什么请求响应;

启动springclouddeepprovider

  • 执行以下命令启动应用springclouddeepprovider
java -Xms256m -Xmx256m -jar springclouddeepprovider-0.0.1-SNAPSHOT.jar
  • 启动后,在Eureka server的管理页面http://192.168.31.147:8081/,可见springclouddeepprovider的注册信息,如下图红框所示:

Wireshark抓包,分析注册、续约、下载服务列表等请求响应

  • 此时再去看Wireshark的内容窗口,发现已有很多数据包,如下图:

  • 接下来依次分析获取服务列表、注册、续约等请求响应;

获取服务列表的请求响应

  • Eureka client启动后,最先向Eureka server发起的并不是注册请求,而是获取服务列表,先从代码层面分析,下图是DiscoveryClient类的构造方法:

  • 上图中,红框中的方法是获取服务列表操作,比蓝框中的注册服务代码先执行,接下来看抓包的结果是否与代码一致;

  • 下图就是Wireshark抓到的springclouddeepeureka和springclouddeepprovider之间的请求和响应:

  • 从上图我们能看出以下几个信息:
  • 第一:461号数据包就是获取服务列表的请求,472号是注册到Eureka server的请求,目前看来的确是获取服务列表在前,注册在后;
  • 第二:467号数据包是Eureka server返回的服务列表,此时由于还没有应用注册,因此获取服务列表返回的数据中,服务列表为空、一致性哈希码也为空(更多关于一致性哈希码的信息请参考《Spring Cloud源码分析之Eureka篇第五章:更新服务列表》);

  • 《Spring Cloud源码分析之Eureka篇第五章:更新服务列表》一文中已看过更新服务列表的源码,分为全量更新和增量更新两部分,调用的都是AbstractJerseyEurekaHttpClient类的getApplicationsInternal方法,只是urlPath参数不同而已:

    private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) 
        ClientResponse response = null;
        String regionsParamValue = null;
        try 
            WebResource webResource = jerseyClient.resource(serviceUrl).path(urlPath);
            if (regions != null && regions.length > 0) 
                regionsParamValue = StringUtil.join(regions);
                webResource = webResource.queryParam("regions", regionsParamValue);
            
            Builder requestBuilder = webResource.getRequestBuilder();
            addExtraHeaders(requestBuilder);
            response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);
    
            Applications applications = null;
            if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) 
                applications = response.getEntity(Applications.class);
            
            return anEurekaHttpResponse(response.getStatus(), Applications.class)
                    .headers(headersOf(response))
                    .entity(applications)
                    .build();
         finally 
            if (logger.isDebugEnabled()) 
                logger.debug("Jersey HTTP GET /?; statusCode=",
                        serviceUrl, urlPath,
                        regionsParamValue == null ? "" : "regions=" + regionsParamValue,
                        response == null ? "N/A" : response.getStatus()
                );
            
            if (response != null) 
                response.close();
            
        
    
  • springclouddeepprovider注册成功后,增量更新的服务列表中就能取得自身的信息了,如下图:

  • 上图中的1036就是增量更新的请求,红框中是path,1039是Eureka server的响应,有两处需要注意:
  • 第一:蓝框中的actionType为ADDED,这样springclouddeepprovider就会将当前记录添加到本地缓存中,对应的代码在DiscoveryClient类的updateDelta方法中,如下图,红框中就是对不同actionType的处理逻辑:

  • 第二:绿框中是Eureka server计算出的一致性哈希码,Eureka client更新了服务列表的本地缓存后,也会计算出一致性哈希码,然后与Eureka server返回的进行对比,正常情况下应该是一致的,如果Eureka client的增量更新出了问题,就会导致双方的一致性哈希码不同,这时Eureka client就会发起一次全量更新,对应的代码在DiscoveryClient类的getAndUpdateDelta方法中,如下图所示,红框中就是比较逻辑,而蓝框中的注释是很重要的提醒--reconcileAndLogDifference方法中有远程请求:

服务注册的请求响应

@Override
    public EurekaHttpResponse<Void> register(InstanceInfo info) 
        String urlPath = "apps/" + info.getAppName();
        ClientResponse response = null;
        try 
            Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
            addExtraHeaders(resourceBuilder);
            response = resourceBuilder
                    .header("Accept-Encoding", "gzip")
                    .type(MediaType.APPLICATION_JSON_TYPE)
                    .accept(MediaType.APPLICATION_JSON)
                    .post(ClientResponse.class, info);//info就是POST到服务端的数据
            return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
         finally 
            if (logger.isDebugEnabled()) 
                logger.debug("Jersey HTTP POST / with instance ; statusCode=", serviceUrl, urlPath, info.getId(),
                        response == null ? "N/A" : response.getStatus());
            
            if (response != null) 
                response.close();
            
        
    
  • 以上请求在Wireshark上可以观察到,如下图:

  • 对于注册请求,Eureka server的响应如下所示:

  • 上图红框可见Status Code为204,在w3网站对204的解释如下,强调了无body返回:

续约的请求响应

  • 《Spring Cloud源码分析之Eureka篇第七章:续约》一文中已看过续约的源码,是AbstractJerseyEurekaHttpClient类的sendHeartBeat方法,源码如下,可见是个PUT请求:
    @Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) 
        String urlPath = "apps/" + appName + / + id;
        ClientResponse response = null;
        try 
            WebResource webResource = jerseyClient.resource(serviceUrl)
                    .path(urlPath)
                    .queryParam("status", info.getStatus().toString())
                    .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
            if (overriddenStatus != null) 
                webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name());
            
            Builder requestBuilder = webResource.getRequestBuilder();
            addExtraHeaders(requestBuilder);
            response = requestBuilder.put(ClientResponse.class);
            EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response));
            if (response.hasEntity()) 
                eurekaResponseBuilder.entity(response.getEntity(InstanceInfo.class));
            
            return eurekaResponseBuilder.build();
         finally 
            if (logger.isDebugEnabled()) 
                logger.debug("Jersey HTTP PUT /; statusCode=", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
            
            if (response != null) 
                response.close();
            
        
    
  • 以上请求在Wireshark上可以观察到,如下图,注意绿框中的内容:

  • Eureka server的响应如下图,返回码200,无内容:

启动springclouddeepconsumer

  • 登录springclouddeepconsumer应用所在机器,在springclouddeepconsumer-0.0.1-SNAPSHOT.jar文件所在目录执行如下命令,启动应用:
java -Xms256m -Xmx256m -jar springclouddeepconsumer-0.0.1-SNAPSHOT.jar

Wireshark抓包,分析服务列表的变化

  • 现在springclouddeepprovider和springclouddeepconsumer这两个应用都已启动,并且注册到Eureka server,此时再去看Wireshark抓的增量更新接口的响应包,应该会有springclouddeepconsumer这个应用的信息,如下图:

停止应用springclouddeepconsumer

  • 在启动springclouddeepconsumer的控制台上,Ctrl+c键同时按下,结束springclouddeepconsumer应用;

Wireshark抓包,分析服务列表的变化

  • springclouddeepconsumer应用停止后,该变化在springclouddeepprovider的增量更新数据中应该能体现出来,去Wireshar上抓包,果然发现了对应的变更信息,如下图:

  • 如上图所示,本次增量更新收到了一条类型为DELETED的记录,于是Eureka client就会在本地缓存中寻找此服务信息,如果找到就删除掉;

  • 至此,本次Wireshar抓包分析实战就完成了,在学习Eureka源码时,我们可以通过这种方式来验证和分析代码,对学习起到了很好的辅助作用;

欢迎关注51CTO博客:程序员欣宸

以上是关于Wireshark抓包分析Eureka注册发现协议的主要内容,如果未能解决你的问题,请参考以下文章

CMCC portal 协议wireshark 抓包分析

CMCC portal 协议wireshark 抓包分析

CMCC portal 协议wireshark 抓包分析

为啥我用wireshark抓包结果全是乱码

Wireshark抓包rtmp

Wireshark抓包rtmp