1.系列文章
2.前言
在你必须懂也可以懂的微服务系列一:服务注册一文中介绍了应用程序在启动过程中会将服务名、服务ip、端口注册到注册中心
在你必须懂也可以懂的微服务系列三:服务调用一文中介绍了远程服务调用,在调用远程服务的时候首先根据服务名获取目标服务地址信息列表,再根据相关负载算法计算出目标服务地址。这其中就涉及到服务发现与负载均衡内容,也是本篇文章将要介绍的内容
3. 服务发现
在你必须懂也可以懂的微服务系列三:服务调用一文中讲解到会通过http方式发起远程调用并返回结果,其实在进行远程调用之间还会进行服务发现操作,用于获取服务机器信息,以便实现负载。
3.1 服务选择
@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
}
Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
if (loadBalancerResponse == null) {
return null;
}
return loadBalancerResponse.getServer();
}
复制代码
首先通过
LoadBalancerClientFactory创建负载均衡客户端,再通过负载均衡客户端获取目标服务实例信息
3.2 负载均衡客户端声明
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
复制代码
可以看到在创建负载均衡客户端时需要
ServiceInstanceListSupplier对象,从命名就能猜出此对象用于获取服务实例列表信息
3.3 ServiceInstanceListSupplier声明
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);
}
public ServiceInstanceListSupplierBuilder withBlockingDiscoveryClient() {
if (baseCreator != null && LOG.isWarnEnabled()) {
LOG.warn("Overriding a previously set baseCreator with a blocking DiscoveryClient baseCreator.");
}
this.baseCreator = context -> {
DiscoveryClient discoveryClient = context.getBean(DiscoveryClient.class);
return new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment());
};
return this;
}
复制代码
至此我们了解到
RoundRobinLoadBalancer中持有ServiceInstanceListSupplier,ServiceInstanceListSupplier中又持有DiscoveryClient,从using-the-discoveryclient官网文档中可以了解到DiscoveryClient可以实现服务发现功能
3.4 ServiceInstanceListSupplier构造
public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate, Environment environment) {
this.serviceId = environment.getProperty(PROPERTY_NAME);
resolveTimeout(environment);
this.serviceInstances = Flux.defer(() -> Flux.just(delegate.getInstances(serviceId)))
.subscribeOn(Schedulers.boundedElastic()).timeout(timeout, Flux.defer(() -> {
logTimeout();
return Flux.just(new ArrayList<>());
})).onErrorResume(error -> {
logException(error);
return Flux.just(new ArrayList<>());
});
}
复制代码
ServiceInstanceListSupplier在实例化构造时通过DiscoveryClient获取目标服务实例列表信息
3.5 负载算法获取目标服务实例
final AtomicInteger position;
复制代码
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
// TODO: enforce order?
int pos = Math.abs(this.position.incrementAndGet());
ServiceInstance instance = instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
复制代码
维护一个
AtomicInteger变量,每次 + 1与服务实例数取模,得到目标机器,从而实现轮询负载算法
3.6 总结
如上介绍这么多,总结一句话就是通过DiscoveryClient进行服务发现,获取目标服务实例列表,然后再通过取模算法实现轮询负载



















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)