Spring Cloud Ribbon 基于HTTP和TCP的客户端负载均衡工具组件-进程内负载均衡=如:Ribbon组件

-1- 负载均衡是对系统的高可用、网络压力缓解和处理能力扩容的重要手段。
-2- 利用特定方式将流量分摊到多个操作单元手段。
-3- 通常所说的负载均衡都指的是=服务端负载均衡=,如Nginx(软件负载均衡)与F5(硬件负载均衡)。
-4- 服务端负载均衡(集中式负载均衡)Nginx 与 客户端负载均衡(进程内负载均衡)Ribbon。
-5- 此为,Feign(服务间调用)与Zuul(网关)组件中已经默认整合集成了Ribbon。

- 集中式负载均衡(服务端负载均衡)-需要单独部署服务器 如:Nginx :

集中式负载均衡(服务端负载均衡)

- 客户端负载均衡(进程内负载均衡如)-不需要单独部署服务器,整合使用服务器即可:Ribbon :

客户端负载均衡(进程内负载均衡如)

-1- 客户端负载均衡(进程内负载均衡如)是指:从一个实例库选取一个实例进行流量导入,
-2- 实例库一般是存储在 Eureka、Consul、Zookeeper 等 注册中心,
-3- 而此时的负载均衡器就是类似Ribbon的IPC(Inter-Process Communication) 进程间通信。

-上面👆图多看多理解-

1 > Ribbon 入门案例
<!-- Eureka-Server 服务依赖-->
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>
<!-- Eureka-Server 主程序Main类-->
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
<!-- Eureka-Server application.yml 配置文件-->
server:
  port: 8888
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
> Eureka-Client-R 服务,是为了测试Ribbon负载均衡功能的定义的:-源服务器
<!-- Eureka-Client-R 服务依赖-->
<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
	</dependencies>
<!-- Eureka-Client-R 主程序Main类-->
@SpringBootApplication
@EnableDiscoveryClient <!-- -->
<!-- 注意:这里使用的并不是 @EnableEurekaClient注解 -->
public class ClientAApplication {
	
    public static void main(String[] args) {
        SpringApplication.run(ClientAApplication.class, args);
    }
}

**@EnableEurekaClient与@EnableDiscoveryClient区别 **1

<!-- Eureka-Client-R application.yuml -->
server:
  port: 7070
spring:
  application:
    name: Client-R
eureka:
  client:
    serviceUrl:
      defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8888}/eureka/
  instance:
    prefer-ip-address: true
<!-- Eureka-Client-R Controller 层,用于测试,在该API中除了计算结果之外,
										还携带当前实例的端口号 -->
@RestController
public class TestController {
	@GetMapping("/add")
	public String add(Integer a, Integer b, HttpServletRequest request){
		return " From Port: "+ request.getServerPort() + ", Result: " + (a + b);
	}
}
> Ribbon-loadbalancer (负载均衡器)Client 客户端服务
<!-- Ribbon-loadbalancer (负载均衡器)Client 客户端 依赖包 -->
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
	</dependencies>
<!-- Ribbon-loadbalancer (负载均衡器)Client 客户端  application.yml -->
spring:
  application:
    name: ribbon-loadbalancer
server:
  port: 7777
eureka:
  client:
    serviceUrl:
      defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8888}/eureka/
  instance:
    prefer-ip-address: true
<!-- Ribbon-loadbalancer (负载均衡器)Client 客户端 主程序Main入口 -->
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonLoadbalancerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RibbonLoadbalancerApplication.class, args);
    }
    @Bean <!--注入RestTemplate(模版)的Bean -->
    @LoadBalanced <!--该注解声明该RestTemplate用于负载均衡 -->
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
<!-- Ribbon 客户端需要创建一个API来调用Client-R 源服务的那个自定义AIP
		这里需要用到RestTemplate来调用,http后面的IP:port变成具体的服务名 如下: -->
@RestController
public class TestController {
    @Autowired
    private RestTemplate restTemplate;
	@GetMapping("/add")
	public String add(Integer a, Integer b) {
		String result = restTemplate
				.getForObject("http://CLIENT-R/add?a=" + a + "&b=" + b, String.class);
		System.out.println(result);
		return result;
	}
}

-上面👆操作完整后,开始测试:-

1)启动 Eureka Server之后,修改Client-R服务的端口号为7070与7075分别启动。
2)访问http://localhost:8888 可以清晰看到已经启动来两个为Client-R的服务实例。UP(2)
3)访问http:localhost:7777/add?a=100 &b=300,会有两种结果
4)From Prot:7075,Result:400
5)From Prot: 7070,Result :400
6) 由于我们程序中也实现打印调用的结果,控制台打印信息如 上面👆交错结果集。
7)Ribbon默认使用轮询的方式访问-源服务。

2 > Ribbon负载均衡策略与自定义配置

– 先说说别人家的孩子:Nginx 呵-
– Nginx常用的负载均衡策略:
– 轮询(Round Robin)策略
– 权重(Weight)策略
– IP—Hash 策略 等-

表2-1 Ribbon 负载均衡策略:
策略类命名描述
RandomRule随机策略随机选择 Sever 服务器
RoundRobinRule轮询策略(Ribbon默认策略)[BaseLoadBalancer类中默认设置的是RoundPobinRule策略]按顺序循环♻️选择 Server
RetryRule重试策略在一个配置时间端内当选择Server不成功,则一直尝试选择一个可用的Server
BestAvailableRule最低并发策略逐个考察Server,如果Server断路器打开,则忽略,再选择其中并发连接最低的Server
AvailabilityFilteringRule可用过滤策略过滤掉一直连接失败并被标记为Circuit(环形) Tripped(旅行)的Server,过滤掉那些并高并发连接的Server(Active(活跃)Connections 超过配置的阀值)
ResponseTimeWeigHtedRule响应时间家权策略根据Server的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低,响应时间越短,权重越高,被选择到的概率就越高,这个策略很贴切,综合类各种因素,如:网络、磁盘、IO等,这些因素直接影响者响应时间。
ZoneAvoidanceRule区域权衡策略综合判断Server所在区域的性能和Server的可用性轮询选择Server,并且判定一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有Sever.
> 1.全局更改Ribbon负载均衡默认策略-配置类
<!-- 如此简单的一个简单的配置,加上以后凡是通过Ribbon的请求都会按照配置的规则来进行-->
@Configuration
public class TestConfiguration {
	@Bean
	public IRule ribbonRule() {
		return new RandomRule();
		<!-- 从默认轮询策略-RoundRobinRule 更改成 -随机策略-RandomRule-->
	}
}
> 2.基于注解的策略-配置
<!-- 针对-源服务器设置其特定的策略=可以使用用@RibbnClient注解 如下:-->
@Configuration
@AvoidScan<!--这里的@AvoidScan注解是一个空的声明-->
public class TestConfiguration {
	@Autowired
    IClientConfig config;<!--注入的IClientConfi是针对客户端的配置管理器-->
	@Bean
	public IRule ribbonRule(IClientConfig config) {
		return new RandomRule();
	}
	<!-- 最后在启动类上加上@RibbonClient注解-来对源服务进行负载约束。-->
}
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(name = "client-r", configuration = TestConfiguration.class)
<!-- 此外,可以使用@RibbonClients注解来对多个源服务进行策略指定-->
@RibbonClients(value = {		
	@RibbonClient(name = "client-a", configuration = TestConfiguration.class),	
	@RibbonClient(name = "client-b", configuration = TestConfiguration.class)
 })

@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {AvoidScan.class})})
<!--这里使用@ComponentScan注解的意思是让Sping不去扫描被@avoidScan注解标记的配置类
	因为我们的配置是对单个源服务生效的,所有不能应用与全局,如果不排除,启动就会报错❌
	-->
public class RibbonLoadbalancerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RibbonLoadbalancerApplication.class, args);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
> 3.基于文件策略-配置
client-r:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule#
> 4.Ribbon超时与重试
<!-- 使用HTTP发起请求,免不了要经历极端环境,此时调用进行时限控制以及时限之后的重试尤为重要
	需要添加对于超时时间与重试策略的配置-如下:-->
client-r:
  ribbon:
    ConnectTimeout: 3000
    ReadTimeout: 60000
    MaxAutoRetries: 1 #对第一次请求的服务的重试次数
    MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务)
    OkToRetryOnAllOperations: true
> 4.Ribbon饥饿加载
<!-- Ribbon在进行客户端负载均衡的时候并不是在启动时加载上下文,而是在实际请求的时候才去创建,
	因此这个特性汪汪会让我们第一此调用显得颇为疲软乏力,严重的时候会引起调用超时。
	所以可通过指定Ribbon具体的客户端的名称来开启饥饿加载,即在启动过的时候便加载
	所以配置项的应用程序上下文,如下:
 -->
eager-load:
    enabled: true
    clients: client-r, client-b, client-c
> 5.利用配置文件自定义Ribbon客户端

自定义定制 Ribbon客户端,其实就是使用配置文件来指定一个默认加载类,
从而该改变Ribbon客户端的行为方式,并且使用这种方式优先级最好,优先级高于使用
注解@RibbonClent的指定的配置和源码中加载的相关Bean。

-5-1表.配置文件自定义Ribbon客户端的语法说明:
配置项说明
< clientNmae >. ribbon.NFLoadBalancerClassName指定ILoadBalancer(I负载均衡器)的实现类
< clientNmae >. ribbon.NFLoadBalancerRuleClassName指定IRule(I规则)的实现类
< clientNmae >. ribbon.NFLoadBalancerPingClassName指定IPing(I抨击)的实现类
< clientNmae >. ribbon.NFNIWSSercerListClassName指定SercerList(I服务列表)的实现类
< clientNmae >. ribbon.NFNIWSSercerListFilterClassName指定SercerListFilter(I服务列表过滤器)的实现类
<!-- 使用Ribbon自带实现类,也可以自实现-如下:-->
client-r: 
  ribbon:
    NiwSServerListClassName: com.netfix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.net.loadbaleancer.WeightedReponseTimeRule
> 6.Ribbon脱离Eureka使用:

1-默认情况下,Ribbon客户端会从Eureka注册中心读取服务注册信息列表,来达到动态负载均衡的功能。
2-如果Eureka是一个供跟多人使用的公共注册中心(比如:http://eureka.springcloud.cn),
3-那么此时极易产生服务侵入型问题,所以就不要从Eureka中读取服务列表来,
4-而应该在Ribbon客户端自行指定-源服务-地址,让Ribbon脱离Eureka来使用。

<!--为了达到这一目的,首先需要在Ribbon中禁用Eureka的功能-->
ribbon:
	eureka:
		enabled:false
<!--然后对源服务设定地址列表:-->
client:
	ribbon:
	listOPfServers: http://localhost:7070,http://localHost:7071
> 7.Ribbon核心工作原理:

奕小生,重点分享/讲解官方文档中提到的那些上层接口及Ribbon从启动到负载均衡器选择服务实例的过程

> 7-1.官方文档中提到Ribbon核心接口:
接口描述默认实现类
IClientConfig定义Ribbon中管理配置的接口DefaultCientConfigImpl
IRule定义Ribbon中负载均衡策略的接口ZoneAvidanceRule(区证明规则)
IPing定义定期Ping服务检查可用性的接口DummyPing(假抨击)
ServerList< Server >定义获取服务列表方法的接口ConfigurationBasedServerLIit(构造基于服务列表)
ServerListFilter< Server >定义特定期望获取服务列表方法的接口ZonePreferenceServerListFilter(区优先权服务列表过滤器)
ILoadBalancer定义负载均衡选择服务的核心方法的接口ZoneAwareLoadBalancer(区自觉加载负载均衡)
ServerListUpdater为Dynamic(动态/活跃)ServerListLoadBalacer定义动态更新服务列表的接口PoliingServerListUpdater(警务服务列表更新)

== > 最关心疑问🤔️:Ribbon如何做到/实现-使用RestTemplater达到负载均衡的 ?< ==

<!--1、使用Ribbon来对客户端实例进行负载均衡-->
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonLoadbalancerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RibbonLoadbalancerApplication.class, args);
    }
    @Bean<!--2、基本使用方式都要注入一个RestTemplate的Baen-->
    @LoadBalanced <!--3、并且使用@LoadBalanced注解才能具备负载均衡能力--> 
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

<!--1、使用Ribbon来对客户端实例进行负载均衡-->
<!--2、基本使用方式都要注入一个RestTemplate的Baen-->
<!--3、并且使用@LoadBalanced注解才能具备负载均衡能力--> 
 -- 👇下面👇是:@LoadBalanced注解里的源码:
/** 源码
 * Annotation to mark a RestTemplate bean to be configured to use a 
 * LoadBalancerClient (译:将RestTemplate bean配置为使用 LoadBalancerClient)
	== issue(疑问🤔️):>那么问题又来了,LoadBalancerClient 是什么 ?看下面源码👇👇👇< ==
 * @author Spencer Gibb -- @作者 斯宾塞 吉布
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
====================================================================================
<!--LoadBalancerClienta(负载均衡客户端)接口扩展自:
									 ServiceInstanceChooser(服务实例选项器)接口-->
/** 源码
 * Represents a client side load balancer(译:代表客户端负载均衡器)
 * @author Spencer Gibb -- @作者 斯宾塞 吉布
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {
<!--源文注释的意思:使用来自LoadBalancer(负载均器)ServiceInstance(服务实例)为指定的服务执行请求。-->
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) 
					throws IOException;
<!--源文注释的意思:使用来自LoadBalancer(负载均器)ServiceInstance(服务实例)
	为指定的服务执行请求,是上一个方法的重载,在实现类中可以看到它们的关系,其中就是前一个方法的细节实现-->					
	<T> T execute(String serviceId, ServiceInstance serviceInstance, 
					LoadBalancerRequest<T> request) throws IOException;
<!--源文注释的意思:使用主机IP和port(端口)构建特定的URL供Ribbon内部使用。Ribbon使用
具有逻辑服务名称的URL作为host,例如:http//myserverce/path/to/service-->					
	URI reconstructURI(ServiceInstance instance, URI original);
}
--------------------------------------------------------------------------------
/** 源码*/
public interface ServiceInstanceChooser {
	<!--源文注释的意思:根据Serverid结合负载均衡器选择一个服务实例-->
    ServiceInstance choose(String serviceId);
}

Ribbon被初始化的类,同包下LoadBalancerAutoConfiguration(负载均衡器自动配置类)
-Ribbon功能核心配置类:

package org.springframework.cloud.client.loadbalancer;
/** 源码
 * Auto configuration for Ribbon (client side load balancing).
 * (译:自动配置 Ribbon - 客户端负载平衡)
 *
 * @author Spencer Gibb -- @作者 斯宾塞 吉布
 * @author Dave Syer
 * @author Will Tran
 * @author Gang Li
 */
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
/** 从类的注解可以看出,它配置加载的时机:
 *			1.当前工程环境必须有RestTemplater的实例,
 *			2.在工程环境中必须初始化类 LoadBalancerClient(负载均衡器客户端) 的实现类,
 *			3.其中 LoadBalancerRequestFactory(负载均衡器请求工厂)用于,
 *	 创建 LoadBalancerRequest(负载均衡器请求)供 LoadBalancer(负载均衡器)-Interceptor(拦截器)使用.
 *			4.loadBalancerInterceptorConfig(负载均衡器拦截器配置)内部类中则:
 * 维护了LoadBalancerInterceptor(负载平衡器拦截器) 与 RestTemplateCustomizer(Rest模版定制器)的实例.
 *			5.LoadBalancerInterceptor 作用:拦截每一次HTTP请求,将请求绑定进Ribbon负载均衡的生命周期。
 *			6.RestTemplateCustomizer 作用:为每一个 RestTemplate 绑定 LoadBalancerInterceptor拦截器。
 */
       --接下来我们瞅瞅 LoadBalancerInterceptor(负载平衡器拦截器) 类里面做了什么 ? 往下翻看啦~--

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
	}
	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
	}
	@Configuration
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}
	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryAutoConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public LoadBalancedRetryFactory loadBalancedRetryFactory() {
			return new LoadBalancedRetryFactory() {};
		}
	}
	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryInterceptorAutoConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public RetryLoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
				LoadBalancerRequestFactory requestFactory,
				LoadBalancedRetryFactory loadBalancedRetryFactory) {
			return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
					requestFactory, loadBalancedRetryFactory);
		}
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}
}
==================================================================================================
 --接下来我们瞅瞅 LoadBalancerInterceptor(负载平衡器拦截器) 类 源码~--
package org.springframework.cloud.client.loadbalancer;
/**
 * @author Spencer Gibb -- @作者 斯宾塞 吉布
 * @author Dave Syer
 * @author Ryan Baxter
 * @author William Tran
 */
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
/**
 *	Class 的实现上利用了 ClientHttpRequestInterceptor-客户端Http请求拦截器 来对每次HTTP请求进行拦截的,
 *	此类是Spirng中维护的请求拦截器,实现它的 intercept()-拦截-方法就可以使请求进入方法体,从而做一些处理。
 *	请求拦截下来之后使用了LoadBalancerClient-负载均衡器客户端-的 execute()-执行-方法来处理请求
 *	LoadBalancerClient接口只有一个实现类[IDEA编辑器中Ctrl(Command) + H -查看接口实现类列表],
 *	它就是RibbonLoadBalancerClient(Ribbon负载均衡器客户端)。
 */
 			--再接下来我们瞅瞅 RibbonLoadBalancerClient(Ribbon负载均衡器客户端) 类 再往下翻~~--
 			
	private LoadBalancerClient loadBalancer;
	private LoadBalancerRequestFactory requestFactory;
	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
	}
	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
		// for backwards compatibility
		this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
	}
	@Override
	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, requestFactory.createRequest(request, body, execution));
	}
}
===========================================================================================
 --瞅瞅 RibbonLoadBalancerClient(Ribbon负载均衡器客户端) 类 源码~ 直接找到execute()-执行-方法--
 package org.springframework.cloud.netflix.ribbon;
 /**
 * @author Spencer Gibb -- @作者 斯宾塞 吉布
 * @author Dave Syer
 * @author Ryan Baxter
 * @author Tim Ysewyn
 */
public class RibbonLoadBalancerClient implements LoadBalancerClient {
	private SpringClientFactory clientFactory;
	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
		this.clientFactory = clientFactory;
	}
..................
	/** execute()-执行-方法,它首先要得到一个ILoadBalancer-I负载均衡器 接口,再使用它去得到一个Server,
	 *	这里Server就是具体服务实例的封装类,所以,getServer(loadBalancer)就是发生负载均衡过程的地方!
	 *	往下翻,一起儿瞅瞅getServer() 做了什么?--->
	 */
	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
				serviceId), serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
	
	@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
		Server server = null;
		if(serviceInstance instanceof RibbonServer) {
			server = ((RibbonServer)serviceInstance).getServer();
		}
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);
		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
		try {
			T returnVal = request.apply(serviceInstance);
			statsRecorder.recordStats(returnVal);
			return returnVal;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
							.............................
	/** 接着看 getServer()方法的内容:
	 *	它运用了ILoadBalancer接口的chooseServer()方法
	 */
	 -------------------------------------------------------------------------
	protected Server getServer(String serviceId) {
		return getServer(getLoadBalancer(serviceId));
	}
	protected Server getServer(ILoadBalancer loadBalancer) {
		if (loadBalancer == null) {
			return null;
		}
		return loadBalancer.chooseServer("default"); // TODO: better handling of key
	}
	-------------------------------------------------------------------------
	/**	紧追 chooseServer()实现类 
	 *	ILoadBalancer接口唯一实现类是:AbstractLoadBalancer类但它是个抽象类因此没有方法体,
	 *	继承抽象类的是: BaseLoadBalancer类,chooseServer()方法的具体实现就它这个类中编写的;
	 */
	 -------------------------------------------------------------------------
	 <!-- 直接给‘码’同志们瞅 BaseLoadBalancer类中的 chooseServer()方法的具体实现-->
	 package com.netflix.loadbalancer;
	 ...................
					 /* 源码
				     * Get the alive server dedicated to key(译:获取专用于密钥的在线服务器)
				     * 
				     * @return the dedicated server(译:@返回 这个专用服务器)
				     */
				    public Server chooseServer(Object key) {
				        if (counter == null) {
				            counter = createCounter();
				        }
				        counter.increment();
				        if (rule == null) {
				            return null;
				        } else {
				            try {
				                return rule.choose(key); 
				                // ‘这步骤操作后,发现rule.choose(key)其实就是IRule(IRule是定义Ribbon负载均衡策略的父接口,
				                // 所有策略都要基于它实现的) 至此,拦截的HTTP请求与负载均衡策略得以关联起来嘀!’
				                -------------------------------------------------------------------------
				                --进来瞅瞅父接口IRule接口--
				                package com.netflix.loadbalancer;
									public interface IRule{
									    public Server choose(Object key);
									    
									    public void setLoadBalancer(ILoadBalancer lb);
									    
									    public ILoadBalancer getLoadBalancer(); 
								 /** IRule接口一共定义类三个方法:
								   *	1.实现类实现choose()方法会加入具体的负载均衡策略逻辑,
								   *	2.另外两个方法与ILoadBalancer关联起来,
								   *	3.在调用Ribbon是通过ILoadBalacer来关联IRule的,
								   *	4.ILoadBalancer的chooseServer()方法会转换为调用IRule的choose()方法,
								   *	5.抽象类 AbstractLoadBalancerRule实现类这两个方法,
								   *	6.从而将ILoadBalancer与IRule关联起来,
								   *	7.再往下走就是实现类了,每一种实现逻辑需要慢慢看实现类喽!
								   */   
									}
				                 -------------------------------------------------------------------------
				            } catch (Exception e) {
				                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
				                return null;
				            }
				        }
				    }
				    ........................
					 
	 -------------------------------------------------------------------------

== 《Ribbon 负载均衡家族体系图 》==
在这里插入图片描述

小结一下 喽喽-:‘码’软文软的冒汗~虚了虚了~枸杞枸杞,反手一个赞👍 嘻嘻~


    • @EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用,
      – @EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现;
      – 1、两者的作用范围差异,前者单一,后者比前者对于Eureka注册中心相对选择注册中心多选项的,
      – 2、@EnableDiscoveryClient 关键词 Discovery 发现机制。
      – 3、在该项目中使用的目的:
      – 4、启动类上表示 该服务被注册中心发现即可,
      – 5、.yml 文件中指定注册中心地址与初始的端口,后面开始多个实例只需要修改端口即可。
      – 6、如直接使用 @EnableEurekaClient 注解,则达不到目的。
    ↩︎
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值