当前位置: 首页 > news >正文

汕头网站建设搭建做自媒体关注的网站

汕头网站建设搭建,做自媒体关注的网站,电商推广文案,域名代备案平台LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主#xff0c;学习Spring Cloud LoadBalance#xff0c;暂不讨论Ribbon…LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件我们讨论Spring负载均衡以Spring Cloud2020之后版本为主学习Spring Cloud LoadBalance暂不讨论Ribbon。 两者有什么区别、Spring Cloud为什么移除了Ribbon转向了Spring Cloud LoadBalancer改日研究。 回顾 上篇文章我们学习了Spring Cloud LoadBalance负载均衡底层原理中的 LoadBalanced注解的使用与Bean一起作用在RestTemplate上 BeanLoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}可以实现在注入RestTemplate对象到Spring IoC容器的同时启用Spring的负载均衡机制。 2. LoadBalanced注解的底层原理在LoadBalancerAutoConfiguration初始化的过程中创建拦截器LoadBalancerInterceptor对请求进行拦截从而实现负载均衡。 3. LoadBalancerInterceptor拦截器在执行请求前调用其intercept方法intercept负责负载均衡的实现具体的实现逻辑尚未研究 其中第3点intercept方法是怎么实现负载均衡的我们还没有深入研究这是我们今天这篇文章的主要目的。 Spring Cloud负载均衡原理 LoadBalancerClient及ReactorLoadBalancer初始化 LoadBalancerInterceptor拦截器的intercept方法究竟是怎么实现负载均衡的 拦截方法intercept中会通过LoadBalancerClient对象从Spring IoC容器中获取实现负载均衡LoadBalancerClient对象的注入以及拦截原理这个过程稍微复杂一点所以我们先用简单的方式描述其实现逻辑然后再从源码角度进行跟踪。 我们在上一篇文章中说过的spring-cloud-commons包下的自动配置类(如图 比如对LoadBalanced注解的解析、LoadBalancerInterceptor的注入等等就是上面自动配置类LoadBalancerAutoConfiguration完成的。 Spring cloud有两个名字一样的自动配置类LoadBalancerAutoConfiguration位于不同的包下上面一个是在spring-cloud-commes包下下面还要提到的一个是在spring-cloud-loadbalancer包下。 spring-cloud-loadbalancer包下的自动配置类LoadBalancerAutoConfiguration负责注入LoadBalancerClientFactory对象LoadBalancerClientFactory负责创建子容器SpringCloud通过子容器来隔离各微服务的访问参数、负载均衡策略等。创建LoadBalancerClientFactory对象的过程中将LoadBalancerClientConfiguration设置给他的defaultConfigType属性在子容器初始化的过程中将LoadBalancerClientConfiguration注册为配置类从而通过LoadBalancerClientConfiguration配置类完成ReactorLoadBalancer的创建并注入子容器中。ReactorLoadBalancer是负载均衡策略接口默认的负载均衡策略为RoundRobinLoadBalancer。 spring-cloud-loadbalancer包下的另外一个自动配置类BlockingLoadBalancerClientAutoConfiguration负责注入拦截器中的LoadBalancerClient实际注入的是BlockingLoadBalancerClient对象BlockingLoadBalancerClient会持有LoadBalancerClientFactory对象。 LoadBalancerInterceptor的intercept方法会转交给BlockingLoadBalancerClient处理BlockingLoadBalancerClient通过LoadBalancerClientFactory对象向子容器子容器不存在的话首先创建子容器获取相关配置以及负载均衡策略RoundRobinLoadBalancer最终通过RoundRobinLoadBalancer实现负载均衡。 需要注意子容器不是在系统初始化过程中创建的而是在处理请求的过程中创建的。 下面分析源码。 LoadBalancerClient 从应用层入手分析先看上一篇文章的案例中的orderServicede的代码 Service public class OrderService {Autowiredprivate RestTemplate restTemplate;public String getOrder(){//通过userService获取user信息String urlhttp://userservice/user/getUser;System.out.println(urlurl);User userrestTemplate.getForObject(url,User.class);System.out.println(user);return user.getName();} }restTemplate.getForObject最终会调用到LoadBalancerInterceptor的intercept方法 private LoadBalancerClient loadBalancer;public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {final URI originalUri request.getURI();String serviceName originalUri.getHost();Assert.state(serviceName ! null, Request URI does not contain a valid hostname: originalUri);return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));}调用loadBalancer的execute方法而loadBalancer是LoadBalancerClient 对象、是LoadBalancerInterceptor初始化过程中通过方法参数从SpringIoC容器中注入进来的。 前面提到过自动配置类BlockingLoadBalancerClientAutoConfiguration负责注入拦截器中的LoadBalancerClient实际注入的是BlockingLoadBalancerClient对象。为了不影响可读性我们稍后再看这部分源码。 继续跟踪loadBalancer的execute方法。首先看一下LoadBalancerClient 的类结构 接口LoadBalancerClient继承自接口ServiceInstanceChooser接口定义了choose方法及execute方法包括其重载方法。其中execute是调用入口、也是模板方法根据请求的服务serviceId比如userService通过调用choose方法获取到最终要调用的服务实例serviceInstance最终调用到服务实例所提供的服务 Overridepublic T T execute(String serviceId, LoadBalancerRequestT request) throws IOException {String hint getHint(serviceId);LoadBalancerRequestAdapterT, TimedRequestContext lbRequest new LoadBalancerRequestAdapter(request,buildRequestContext(request, hint));SetLoadBalancerLifecycle supportedLifecycleProcessors getSupportedLifecycleProcessors(serviceId);supportedLifecycleProcessors.forEach(lifecycle - lifecycle.onStart(lbRequest));ServiceInstance serviceInstance choose(serviceId, lbRequest);if (serviceInstance null) {supportedLifecycleProcessors.forEach(lifecycle - lifecycle.onComplete(new CompletionContext(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse())));throw new IllegalStateException(No instances available for serviceId);}return execute(serviceId, serviceInstance, lbRequest);}继续跟踪choose方法是在BlockingLoadBalancerClient类中实现的。 BlockingLoadBalancerClient 我们已经知道注入到Spring Ioc容器中的LoadBalancerClient其实是BlockingLoadBalancerClient对象所以继续跟踪BlockingLoadBalancerClient的choose方法 Overridepublic T ServiceInstance choose(String serviceId, RequestT request) {ReactiveLoadBalancerServiceInstance loadBalancer loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer null) {return null;}ResponseServiceInstance loadBalancerResponse Mono.from(loadBalancer.choose(request)).block();if (loadBalancerResponse null) {return null;}return loadBalancerResponse.getServer();}我们需要重点关注的是两个方法 第一个是LoadBalancerClientFactory的getInstance方法通过serviceId从子容器中拿到ReactiveLoadBalancer参数serviceId服务Id指的就是我们注册到Eureka注册中心的服务Id比如前面案例中的userService。 第二个是ReactiveLoadBalancer的choose方法根据不同的负载均衡策略从服务队列中拿到serviceInstance。Spring cloud提供了两种负载均衡策略随机策略RandomLoadBalancer和循环策略RoundRobinLoadBalancer。 我们先来看第一步从子容器中获取ReactiveLoadBalancer对象。 子容器的创建 如果是首次调用、子容器不存在的情况下LoadBalancerClientFactory负责创建子容器。 LoadBalancerClientFactory是reactiveLoadBalancer.Factory的实现类继承自虚拟类NamedContextFactory创建子容器的大部分代码都在NamedContextFactory类中。 我们首先看一下这个LoadBalancerClientFactory是怎么初始化的Spring IoC容器中的。其实前面已经说过了是通过spring-cloud-loadbalancer包下的自动配置类LoadBalancerAutoConfiguration负责注入。 我们从源码角度验证一下LoadBalancerAutoConfiguration源码 ConditionalOnMissingBeanBeanpublic LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties) {LoadBalancerClientFactory clientFactory new LoadBalancerClientFactory(properties);clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));return clientFactory;}看一下LoadBalancerClientFactory的构造方法 public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);this.properties properties;}父类的构造方法 public NamedContextFactory(Class? defaultConfigType, String propertySourceName, String propertyName) {this.defaultConfigType defaultConfigType;this.propertySourceName propertySourceName;this.propertyName propertyName;}可以看到LoadBalancerClientFactory创建的时候将LoadBalancerClientConfiguration.class赋值给他的父类NamedContextFactory的defaultConfigType属性在创建子容器的时候LoadBalancerClientConfiguration类会被注册为子容器的配置类、从而通过LoadBalancerClientConfiguration完成ReactorLoadBalancer对象的注入注入到子容器中。 NamedContextFactory 先对LoadBalancerClientFactory做一个简单的认识。 LoadBalancerClientFactory继承自虚拟类NamedContextFactory实现了接口DisposableBean和ApplicationContextAware这两个接口我们并不陌生在Spring生命周期回调的学习过程中中我们了解过这两个接口Spring会在容器创建完成后通过ApplicationContextAware的setApplicationContext方法把ApplicationContext送回来、在容器销毁的时候回调DisposableBean接口的destroy方法。LoadBalancerClientFactory实现了这两个接口所以LoadBalancerClientFactory就可以获取到Spring IoC根容器的applicationContext Overridepublic void setApplicationContext(ApplicationContext parent) throws BeansException {this.parent parent;}检查NamedContextFactory的setApplicatonContext方法发现他把Spring IoC容器设置为自己的父容器了这也很好理解从NamedContextFactory类名称判断这个类的目的就是要创建“Named”容器、也就是命名容器其实我们后面会发现就是用serviceId命名的容器比如我们有userservice那就会创建一个名字叫userservice的容器。通过ApplicationContextAware回调setApplicationContext方法将Spring Ioc容器设置为命名容器的“父容器”。 继续跟踪LoadBalancerClientFactory的getInstance方法调用到父类NamedContextFactory的getInstance Overridepublic ReactiveLoadBalancerServiceInstance getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);}public T T getInstance(String name, ClassT type) {AnnotationConfigApplicationContext context getContext(name);try {return context.getBean(type);}catch (NoSuchBeanDefinitionException e) {// ignore}return null;}最终是向Spring的ApplicationContext获取类型为ReactorServiceInstanceLoadBalancer的bean。其中ApplicationContext通过getContext方法获取 private MapString, AnnotationConfigApplicationContext contexts new ConcurrentHashMap();protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);}首先从contexts查找contexts是以serviceId为键值的ConcurrentHashMap缓存创建的ApplicationContext如果尚未创建则调用createContext方法创建后缓存到contexts中。 这个名字为contexts的ConcurrentHashMap其实就是NamedContextFactory的核心创建的ApplicationContext缓存在以serviceId为键值的HashMap中获取的时候以serviceId到contexts中去查找查找到则直接返回、查找不到则创建后缓存。 createContext方法 protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;if (this.parent ! null) {// jdk11 issue// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101// https://github.com/spring-cloud/spring-cloud-openfeign/issues/475DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();if (parent instanceof ConfigurableApplicationContext) {beanFactory.setBeanClassLoader(((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());}else {beanFactory.setBeanClassLoader(parent.getClassLoader());}context new AnnotationConfigApplicationContext(beanFactory);context.setClassLoader(this.parent.getClassLoader());}else {context new AnnotationConfigApplicationContext();}if (this.configurations.containsKey(name)) {for (Class? configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}for (Map.EntryString, C entry : this.configurations.entrySet()) {if (entry.getKey().startsWith(default.)) {for (Class? configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}//注意这里会注册this.defaultConfigType到容器中context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.String, ObjectsingletonMap(this.propertyName, name)));if (this.parent ! null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));context.refresh();return context;}代码比较长但是并不复杂仔细看一下其实就是Spring IoC容器的初始化过程 创建DefaultListableBeanFactory创建AnnotationConfigApplicationContext加载属于当前serviceId的配置加载所有的“默认”配置也就是以default.开头的配置项加载配置文件从配置文件及环境变量中加载注册配置类 this.defaultConfigType其实就是LoadBalancerClientConfiguration配置类设置父容器Spring Ioc的主容器设置为父容器刷新容器返回容器 一个需要关注的重点就是子容器创建的过程中将配置类LoadBalancerClientConfiguration注册到容器中在容器刷新的时候这个配置类会被加载。 ReactorLoadBalancer LoadBalancerClientConfiguration 子容器创建出来之后我们还是返回到上面的NamedContextFactory的getInstance方法中 Overridepublic ReactiveLoadBalancerServiceInstance getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);}会向子容器获取ReactorServiceInstanceLoadBalancer对象。 所以我们现在两个任务第一个是了解一下ReactorServiceInstanceLoadBalancer类第二个是要了解到注入到子容器中的ReactorServiceInstanceLoadBalancer究竟是个什么对象。 第一步看一眼ReactorLoadBalancer的类结构 ReactorServiceInstanceLoadBalancer接口继承自ReactorLoadBalancerSpring Cloud提供了他的两个实现类随机策略类和轮询策略类。 第二步注入到子容器中的ReactorServiceInstanceLoadBalancer究竟是个什么对象就需要研究一下ReactorLoadBalancer的初始化过程。 子容器通过配置类LoadBalancerClientConfiguration实现ReactorLoadBalancer的注入默认实现类是RoundRobinLoadBalancer BeanConditionalOnMissingBeanpublic ReactorLoadBalancerServiceInstance reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}OK 真相大白了默认就是轮询策略RoundRobinLoadBalancer。 负载均衡策略的配置 Spring Cloud默认的负载均衡策略是RoundRobinLoadBalancer我们可以通过配置调整负载均衡策略为随机策略RandomLoadBalancer。 调整方法很简单官网说了 余事以后再说吧。
http://www.pierceye.com/news/554899/

相关文章:

  • 岳阳网站开发网站运营怎么做建设网站设计
  • 能打开各种网站的浏览器下载合集建设农产品网络营销网站
  • 陕西网站建设方案优化如何做网站挂qq
  • 无锡市网站WordPress分类id在哪
  • 网站建设金网站建设 需求模板
  • 提高网站转化率营销网站制作都选ls15227
  • 一级页面的网站怎么做爱疯卷网站怎么做
  • 网站企业快速备案大气的企业网站
  • 一个好的网站建设微网站手机制作
  • 广州市做民宿什么网站比较好图盛网站建设
  • 深圳做网站佰达科技二十七易语言做网站源码
  • 水禾田网站建设公司南沙区做网站
  • 江西赣州网站上海企业服务云电话
  • 洱源网站建设品牌名字大全
  • 网站建设阶段要做什么帝国cms对比WordPress
  • 盐城做企业网站多少钱网页设计个人总结800
  • 北京做兼职网站温州网站建设模板下载免费
  • 推进门户网站建设方案wordpress插件自动更新
  • 学院网站建设成效做网站需要什么功能
  • o2o手机网站建设技术网站设计师专业
  • 传媒网站建设方案wordpress开源博客系统最新版
  • 三合一网站一般多少钱浙江省和住房建设厅网站
  • 网站开发背景知识论文网页设计表格
  • 广州优秀网站建设怎么寻找国外客户资源
  • 松江新城投资建设集团有限公司网站华能电子商务平台
  • 网站建设设计制作公司微网站微商城
  • 长宁企业网站建设个人做外贸怎么做
  • 饲料 东莞网站建设免费推广app
  • 手机平台网站开发品牌网站设计首选
  • 哪些网站可以做调查赚钱图片生成软件