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
进行服务发现
,获取目标服务实例列表,然后再通过取模算法
实现轮询负载