balancer:均衡器
如果是自己写一个负载均衡器思路应该是什么?
参考nginx,部署多个服务,形成一对多的关系
当一个请求发送,通过拦截这个请求,随机或者算法到其中的一个服务上去处理
那么,这中间关键的一点就是:拦截
最精简的LB需求:
- 设置添加和读取后端服务器的列表
- 能从中选择一个服务器去执行
代码实现思路就是:
读取后端服务,标记一个服务不可用,最主要是选择一个后端服务来提供服务
//使用负载均衡加载服务
public interface ILoadBalancer {
void addServers(List<Server> var1);向负载均衡器中维护的实例列表增加服务实例。
Server chooseServer(Object var1);通过某种策略,从负载均衡器中挑选出一个具体的服务实例。
void markServerDown(Server var1);用来通知和标识负载均衡器中某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的。
List<Server> getServerList(boolean var1);//目的兼容2.1.3以前的。(之后本方法拆分下面两个)
List<Server> getReachableServers();获取当前正常服务的实例列表。
List<Server> getAllServers();获取所有已知的服务实例列表,包括正常服务和停止服务的实例。
}
ILoadBalancer:
基础实现类 com.netflix.loadbalancer.BaseLoadBalancer
扩展功能 DynamicServerListLoadBalancer和ZoneAwareLoadBalancer
通过查看BaseLoadBalancer源码发现:
- 里面几个重要的对象
1.通过(@LoadBalanced)注解的形式去拦截:RestTemplate的请求
@Autowired
RestTemplate restTemplate;
/**
- 当网络不稳定的情况下,配置返回信息
- 当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,
- 会被LoadBalancerInterceptor类的intercept函数所拦截。
- 由于我们在使用RestTemplate时候采用了服务名作为host,
- 所以直接从HttpRequest的URI对象中通过getHost()就可以拿到服务名,
- 然后调用execute函数去根据服务名来选择实例并发起实际的请求。
- @return
*/
@HystrixCommand(fallbackMethod = "addServiceFallback")
public String addService() {
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
}
public String addServiceFallback() {
return "error";
}
2.LoadBalancerInterceptor中的intercept会拦截RestTemplate中的请求
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();//获得请求的地址,因为是host是服务名
String serviceName = originalUri.getHost();
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, new LoadBalancerRequest() {
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
LoadBalancerInterceptor.ServiceRequestWrapper serviceRequest = LoadBalancerInterceptor.this.new ServiceRequestWrapper(request, instance);
return execution.execute(serviceRequest, body);
}
});
}
3.通过搜索LoadBalancerClient,我们可以发现这是Spring Cloud中定义的一个接口:
public interface LoadBalancerClient {
ServiceInstance choose(String serviceId); //选择服务实例,根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例。
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; //使用从负载均衡器中挑选出的服务实例来执行请求内容。
URI reconstructURI(ServiceInstance instance, URI original);
}
这个接口的具体实现类是RibbonLoadBalancerClient
负载均衡器应用也是SpringBoot普通应用,不过要配置RestTemplate对象
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class RibbonApplication {
/**
* @LoadBalanced注解源码的注释中,我们可以知道该注解用来给RestTemplate标记,
* 以使用负载均衡的客户端(LoadBalancerClient)来配置它。
* @return
*/
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class, args);
}
}