spring cloud 源码追踪

本文深入探讨Spring Cloud微服务架构中的关键组件,包括Eureka服务注册与发现、Ribbon负载均衡、Hystrix断路器及Zuul网关的工作原理。通过源码分析,揭示了这些组件如何协作,实现服务间高效、稳定地通信。

Eureka

① 客户端配置属性:EurekaClientConfigBean(初始化相关线程,是否需要和eurekaserver沟通)
配置心跳的间隔时间:org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean#leaseRenewalIntervalInSeconds
③ eureka server端配置:org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean
④ 配置初始化:EurekaClientAutoConfiguration
⑤ client端发送心跳:com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#sendHeartBeat
⑥ eureka启动的springboot类上要加上@EnableDiscoveryClient

⑦ 在发送心跳时,如果指定server端请求异常就会尝试下一个server
com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient#execute
假设client端配置server如下

eureka.client.serviceUrl.defaultZone=http://localhost:8889/eureka/,http://localhost:8888/eureka/

也就是如果8889一直正常,就只会向它发送心跳

在这里插入图片描述

⑧ com.netflix.discovery.DiscoveryClient 用于发送心跳(com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#sendHeartBeat),定时拉取最新eureka server 上得serverList并更新(com.netflix.discovery.DiscoveryClient.CacheRefreshThread 这个才是真正地向eureka拉取最新地服务IP,然后更新到相应的map中,供其它地方从map中取,比如下面的PollingServerListUpdater
eureka client发送心跳走JerseyApplicationClient, 里面的addExtraHeaders表示isReplication 不为true

 @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();
            //client发送心跳和eureka server 互相同步心跳都会走这个方法
            //eureka server会在这里走子类JerseyReplicationClient,设置isReplication为true
            //client走子类JerseyApplicationClient,就没有设置
            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();
            }
        }
    }

在这里插入图片描述
⑨ eureka server集群同步,实现最终一致性

发送同步请求
com.netflix.eureka.cluster.ReplicationTaskProcessor#process(java.util.List<com.netflix.eureka.cluster.ReplicationTask>)
==>com.netflix.eureka.transport.JerseyReplicationClient#submitBatchUpdates

单server node接收其它server同步过来的请求,这个时候会将isRelication置为true,

接收同步请求
com.netflix.eureka.resources.PeerReplicationResource#batchReplication
===>com.netflix.eureka.resources.InstanceResource#renewLease(最终会调用到这里)

⑩com.netflix.loadbalancer.BaseLoadBalancer.Pinger用于校验服务是否仍在服务,防止网络异常,但是服务列表没有及时刷新
心跳请求,请求方式为put:http://域名:端口/eureka/apps/test/test:f26bf974da597329f6f7a63e689c8ed6?status=UP&lastDirtyTimestamp=1555913417898
eureka server接收eureka client端的心跳请求(实时更新 eureka server端得服务列表上服务的状态,具体值参考InstanceStatus)
在这里插入图片描述
eureka server端如何下线服务(这个避免不了,client端接着发送心跳,又注册上去):
http://localhost:8888/eureka/apps/SERVICE-DISCOVERY/service-discovery:c0c8f42019fadcfb7f189204f4fb06cc
在这里插入图片描述在这里插入图片描述
eureka server接收client的请求:com.netflix.eureka.resources.InstanceResource

Ribbon

初始化

初始化

org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration(由org.springframework.cloud.netflix.ribbon.RibbonClientConfigurationRegistrar导入)

org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration

org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

饥饿模式初始化ribbon client(ribbon.eager-load.enabled=true并且配置ribbon.eager-load.clients指定需要启动初始化的ribbonClient)

org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration#ribbonApplicationContextInitializer
在这会给每个ribbon client 创建一个AnnotationConfigApplicationContext作为独立容器
==>
org.springframework.cloud.context.named.NamedContextFactory#createContext
在每个ribbon client的context中注册:RibbonAutoConfiguration、RibbonClientConfiguration(defaultConfigType)、RibbonEurekaAutoConfiguration、EurekaRibbonClientConfiguration

ribbon client拉取eureka注册中心服务

初始化PollingServerListUpdater
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonServerListUpdater
==>
com.netflix.loadbalancer.DynamicServerListLoadBalancer#restOfInit
==>
com.netflix.loadbalancer.DynamicServerListLoadBalancer#enableAndInitLearnNewServersFeature(开启pollingServer线程)
==>
com.netflix.loadbalancer.ServerListUpdater.UpdateAction#doUpdate
==>
com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers
==>
com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList#obtainServersViaDiscovery
==>
com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateAllServerList
刷新了服务列表缓存

@RibbonClient注解原理,源码解释

作用:用于自定义ribbon client相关配置,比如:ILoadBalancer、ServerListFilter、IRule,可以参考RibbonClientConfiguration,替换RibbonClientConfiguration中某些bean
示例

@SpringBootApplication
@RibbonClients(value = {
        @RibbonClient(name = "productServer", configuration = ProductRibbonConfiguration.class)})
public class ProductCenterApplication {
	
    public static void main(String[] args) {
        SpringApplication.run(ProductCenterApplication.class, args);
    }
}
@Configuration
@ExcudeAnnotation
public class ProductRibbonConfiguration{
    @Bean
    public ServerList<Server> ribbonServerList() {
        // 实例列表
        String listOfServers = "http://localhost:8080";
        String[] splits = listOfServers.split(",");
        int len = splits.length;
        if (len == 0) {
            return new StaticServerList<Server>();
        }

        Server[] servers = new Server[len];
        for (int i = 0; i < len; i++) {
            servers[i] = new Server(splits[i].trim());
        }
        return new StaticServerList<Server>(servers);
    }

}

原理:就是把@RibbonClient配置的Configuration注入到每个ribbon client的ApplicationContext中,然后每个Ribbon client的IOC容器在初始化RibbonClientConfiguration时候,里面相应ILoadBalancer、ServerListFilter、ServerList等能够被替换掉,走自定义得。

RibbonClientConfigurationRegistrar
==>
org.springframework.cloud.netflix.ribbon.RibbonClientConfigurationRegistrar#registerClientConfiguration  注入RibbonClientSpecification(里面包含了我们定义的configuration)到当前容器中
==>
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration#springClientFactory
将上面所有的RibbonClientSpecification 作为springClientFactory.configurations即NamedContextFactory.configurations


每个ribbon client初始化ApplicationContext时候会调用
org.springframework.cloud.context.named.NamedContextFactory#createContext
==>
将RibbonClientSpecification中的configuration也就是@RibbonClient配置的Configuration注入到client的ApplicationContext中
==>
ribbon client 的IOC中RibbonClientConfiguration初始化
==>
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonServerList(拿这个举例,上面示例就是替换这个),就不会被初始化,因为当前IOC中的ServerList为:ProductRibbonConfiguration.ribbonServerList
==>
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonLoadBalancer初始化,就包含了自定义的ServerList,比如这个不去eureka拉取服务,走写死的服务

默认的负载均衡器:

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient

Hystrix

hystrix配置大集合:com.netflix.hystrix.HystrixCommandProperties、com.netflix.hystrix.HystrixThreadPoolProperties
在这里插入图片描述
获取fallBack回调:com.netflix.hystrix.contrib.javanica.utils.MethodProvider.FallbackMethodFinder#doFind
获取原始业务异常:com.netflix.hystrix.AbstractCommand#getFailedExecutionException
具体执行异常fallBack回调函数:com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback
hystrix线程:com.netflix.hystrix.HystrixThreadPool

使用示例:



@Service
public class HystrixTestSevice {

    @HystrixCommand(fallbackMethod = "testCircuitBreakerFallback", commandProperties = {
            //errorThresholdPercentage 断路器错误请求百分比
            //是否开启熔断器
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),

            //  @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "0"),//失败的比例
            //@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value ="1"), //滑动窗口内允许失败的次数
            // @HystrixProperty(name = "circuitBreaker.executionTimeoutInMilliseconds", value = "3000"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1000")
    })
    public String testCircuitBreaker(int id) {
        if (id % 2 == 0 && id < 40) {   // 直接返回
            return "consumer testCircuitBreaker " + id;
        } else {   // 无限循环模拟超时
            int j = 0;
            /*try {
                Thread.currentThread().sleep(6000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }*/
            System.out.println(1 / 0);
            return "";
        }
    }

    public String testCircuitBreakerFallback(int id, Throwable e) {
        e.printStackTrace(); //打印完整的错误堆栈
        return "fallback:failure";
    }

}


Zuul

zuul:用作网关,服务鉴权和限流,服务分发
主要核心流转类:com.netflix.zuul.http.ZuulServlet
配置初始化类:org.springframework.cloud.netflix.zuul.ZuulConfiguration
服务分发:com.netflix.zuul.FilterProcessor#route
ribbon分发调用请求并返回数据:org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter
对于Ribbon,Zuul默认封装了org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommand利用Hystrix去调用底层服务,实际调用:org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter#forward

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值