美文网首页
SpringCloud系列之服务治理Eureka·6-Eurek

SpringCloud系列之服务治理Eureka·6-Eurek

作者: 那钱有着落吗 | 来源:发表于2021-03-12 09:08 被阅读0次
image.png

https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ

1.一个注解引发的血案

我们跟着这一个注解来看下源码的实现


image.png

从这个注解点进去查看:


image.png

接下来我们再点击进入@Import(EnableDiscoveryClientImportSelector.class) 这个类中:
这个类从名称上看是要导入什么类

我们如果要查看源码,最好是可以断点进入,这样就可以看到真实的数据是怎样的,比如如下类中,我们可以看到


image.png

AnnotationMetadata 的数据就是main函数中挂的所有的注解:


image.png

下面的代码就是获取到Eureka的注解,然后查看autoRegister这个值是true还是false,意思就是是否自动注册,然后继续往下看:


image.png
if (autoRegister) {
           List<String> importsList = new ArrayList<>(Arrays.asList(imports));
           importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
           imports = importsList.toArray(new String[0]);
       } else {
           Environment env = getEnvironment();
           if(ConfigurableEnvironment.class.isInstance(env)) {
               ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
               LinkedHashMap<String, Object> map = new LinkedHashMap<>();
               map.put("spring.cloud.service-registry.auto-registration.enabled", false);
               MapPropertySource propertySource = new MapPropertySource(
                       "springCloudDiscoveryClient", map);
               configEnv.getPropertySources().addLast(propertySource);
           }

       }

我们从源码中看到如果自动注册是true,就会手动加入AutoServiceRegistrationConfiguration类:

org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration

然后我们就看下AutoServiceRegistrationConfiguration这个类是干嘛的:

/**
 * @author Spencer Gibb
 */
@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {
}

发现就是一个类,什么都没有,除了一些注解,我们查看一下这个注解:
AutoServiceRegistrationProperties

image.png

发现这是一个配置类,既然是配置类,那么肯定是有些地方会引用到这个类,我们查询使用这个类的地方:

image.png

然后就看到:

protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
        this.serviceRegistry = serviceRegistry;
        this.properties = properties;
    }

发现这个类是把配置给注入进来而已,同时我们发现他同时还注册了一个serviceRegistry,我们查看一下这个类:

image.png

发现这个类是一个接口,既然是一个接口,那就会有实现类,我们查看下他的实现类:

image.png

我们可以看到他实现了注册的方法:

public void register(EurekaRegistration reg) {
        this.maybeInitializeClient(reg);
        if (log.isInfoEnabled()) {
            log.info("Registering application " + reg.getInstanceConfig().getAppname() + " with eureka with status " + reg.getInstanceConfig().getInitialStatus());
        }

        reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
        reg.getHealthCheckHandler().ifAvailable((healthCheckHandler) -> {
            reg.getEurekaClient().registerHealthCheck(healthCheckHandler);
        });
    }

这边我们继续使用打断点的方式来查看:

this.maybeInitializeClient(reg);

断点进去查看:
说明是强制初始化一些代理:


image.png
reg.getApplicationInfoManager().getInfo();

我们看到getinfo方法,点进去然后查看到info类,如图:

image.png

从断点上的信息来看:


image.png

可以发现他是一个Eureka客户端的实例信息。

然后我们就知道了this.maybeInitializeClient(reg);其实就是初始化一些Eureka的实例信息,那么我们继续看注册的代码:

 reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
        reg.getHealthCheckHandler().ifAvailable((healthCheckHandler) -> {
            reg.getEurekaClient().registerHealthCheck(healthCheckHandler);
        });

继续断点我们看到

reg.getInstanceConfig().getInitialStatus()

这个值是up

这个register查看下来,我们发现就是初始化了实例的信息,但是并没有发现往注册中心发送数据的代码。那我们最终是希望看到这块的代码,所以在debug中找:

image.png

查看这个方法的上一层是干嘛的:

image.png

查看代码:

if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
            this.serviceRegistry.register(this.registration);
            this.context.publishEvent(new InstanceRegisteredEvent(this, this.registration.getInstanceConfig()));
            this.running.set(true);
        }

可以看到register这个执行之后就执行这个代码:

            this.context.publishEvent(new InstanceRegisteredEvent(this, this.registration.getInstanceConfig()));
            this.running.set(true);

我们查看代码知道this.context 是ApplicationContext context 这个类,springcloud的上下文。

这个上下文发布了一个事件:InstanceRegisteredEvent,从名字上看是 实例已注册事件。

但是我们再去注册中心查看,发现并没有有注册的实例


image.png

这就说明在发布之前或之后程序肯定做了什么处理,使得Eureka向注册中心发送了请求。然后this.running.set(true);就把实例的运行状态初始化为true了。

注意:在发布事件之后,服务注册到了注册中心,这中间发生了什么可以参考这个链接:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ

下面我们继续讲客户端注册到注册中心的过程:

image.png
boolean register() throws Throwable {
        logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
        EurekaHttpResponse<Void> httpResponse;
        try {
            httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
        } catch (Exception e) {
            logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
            throw e;
        }
        if (logger.isInfoEnabled()) {
            logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
        }
        return httpResponse.getStatusCode() == 204;
    }

我们断点查看下,发现这个class是:SessionedEurekaHttpClient

image.png

我们打开SessionedEurekaHttpClient查询register方法,发现在父类中有实现:
EurekaHttpClientDecorator

image.png
protected abstract <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor);

    @Override
    public EurekaHttpResponse<Void> register(final InstanceInfo info) {
        return execute(new RequestExecutor<Void>() {
            @Override
            public EurekaHttpResponse<Void> execute(EurekaHttpClient delegate) {
                return delegate.register(info);
            }

            @Override
            public RequestType getRequestType() {
                return RequestType.Register;
            }
        });
    }

我们看下这个代码 有一个execute方法,这个方法是一个抽象方法,也就是说每一个子类都会去实现这个方法,然后下面还有一个register方法,这个方法调用了execute,然后还传入了一个delegate 一个代理类。

这里讲到代理类了,就讲下两个设计模式,代理模式和装饰器模式:

image.png
关于代理模式想要详细了解的参见链接:
静态代理和动态代理有什么区别?--乐字节java - dirft的文章 - 知乎
https://zhuanlan.zhihu.com/p/159112639 image.png
关于装饰器模式如果想详细了解的参见链接:java中什么是装饰者模式? - 不知道的回答 - 知乎
https://www.zhihu.com/question/32007641/answer/54428318

了解完之后大家会产生疑问,代理模式和装饰器模式都是对一个对象的功能增强,那到底区别是什么,在什么场景下用什么模式,下面这个帖子就写的不错:

Java中“装饰模式”和“代理模式”有啥区别? - 海岸红杉的回答 - 知乎
https://www.zhihu.com/question/41988550/answer/567925484

我们继续来看Eureka的源码:


打断点来分析,进入到这个execute方法,首先获取一些时间的值:

if (delay >= currentSessionDurationMs) {
            logger.debug("Ending a session and starting anew");
            lastReconnectTimeStamp = now;
            currentSessionDurationMs = randomizeSessionDuration(sessionDurationMs);
            TransportUtils.shutdown(eurekaHttpClientRef.getAndSet(null));
        }

如果这个delay值大于currentSessionDurationMs,那么就结束当前的session然后重启一个新的。

然后获取eurekaHttpClient实例,然后调用父类的execute方法。


image.png

可以看到这里是在配置的可重复的次数里面来进行一些逻辑。

如果节点中有这么的节点是坏的,那么就再重试一次。在下面的逻辑就是查询出所有坏的和正常的节点。

我们继续看之前的逻辑:


image.png

下面就直接进入到最后:

image.png

这里就是服务端往注册中心发送请求的源码。

本帖子把源码非常粗糙的过了一遍,因为源码确实非常复杂,我本人也只了解个大概,所以更细节的地方还需要个人仔细debug和查阅资料的。

相关文章

网友评论

      本文标题:SpringCloud系列之服务治理Eureka·6-Eurek

      本文链接:https://www.haomeiwen.com/subject/pdmsqltx.html