客户端负载均衡与服务端负载均衡

服务端负载均衡

通过服务端负载均衡设备维护的服务清单根据算法(轮训 权重负载 流量负载)取出服务地址 进行转发

客户端负载

将指定服务的服务清单订单(注册中心)下来 在客户端根据算法取出服务地址进行请求

Ribbon实现客户端负载均衡

rabbon是通过代理RestTemplate来实现负载均衡的 只需要在application配置并引入pom文件

同时还有从注册中心订阅服务的相关配置

    //LoadBalanced 通过代理RestTemplate 实现客户端负载均衡的能力
@LoadBalanced
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
  <!--ribbon客户端的负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

RestTemplate使用

Get请求

  String str=restTemplate.getForEntity("http://PROVIDER/hello",String.class).getBody();
//url传递参数
User user =restTemplate.getForEntity("http://PROVIDER/hello/{1}", User.class,1).getBody();
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.getForEntity("http://PROVIDER/hello?name={name}&id={id}",User.class,paramters).getBody();

或者使用getForObject 可以理解成getForEntity的封装

 String str=restTemplate.getForObject("http://PROVIDER/hello",String.class);
//url传递参数
User user =restTemplate.getForObject("http://PROVIDER/hello/{1}", User.class,1);
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.getForObject("http://PROVIDER/hello?name={name}&id={id}",User.class,paramters);

Post请求

 User parameter=new User();
//返回string
String str=restTemplate.postForEntity("http://PROVIDER/hello",parameter,String.class).getBody();
Map<String,Object> paramters=new HashMap<String,Object>();
//url传递参数
User user =restTemplate.postForEntity("http://PROVIDER/hello/{1}",parameter, User.class,1).getBody();
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.postForEntity("http://PROVIDER/hello?name={name}&id={id}",parameter,User.class,paramters).getBody(); User parameter=new User();
//返回string
String str=restTemplate.postForObject("http://PROVIDER/hello",parameter,String.class);
Map<String,Object> paramters=new HashMap<String,Object>();
//url传递参数
User user =restTemplate.postForObject("http://PROVIDER/hello/{1}",parameter, User.class,1);
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.postForObject("http://PROVIDER/hello?name={name}&id={id}",parameter,User.class,paramters);

Put Delete

与上面类似

ribbon负载均衡实现原理

LoadBalancerClient是rabbon的一个重要接口

execute 从负载均衡器中挑选一个实例执行请求

reconstructURL负责重构url restTemplate.getForEntity("http://PROVIDER/hello",String.class) 将PROVIDER转换为 host:port形式

choose从负载均衡器中选择一个实例

LoadBalancerAutoConfiguration

ribbon自动化配置类

@Configuration
@ConditionalOnClass(RestTemplate.class) //RestTemplate 必须存在当前环境中
@ConditionalOnBean(LoadBalancerClient.class)//LoadBalancerClient 必须存在当前环境中
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
/**
* 维护一个被 @LoadBalanced 标识的 RestTemplate对象 通过RestTemplateCustomizer增加LoadBalancerInterceptor拦截器
* @param restTemplateCustomizers
* @return
*/
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
.......
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
/**
* LoadBalancerInterceptor 用于实现对客户端请求进行拦截 实现负载均衡
* @param loadBalancerClient
* @param requestFactory
* @return
*/
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
/**
* 用于对RestTemplate增加 LoadBalancerInterceptor拦截器
* @param loadBalancerInterceptor
* @return
*/
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
} }

自动化配置类主要做以下几件事

1.LoadBalancerInterceptor 初始化一个拦截器对象 主要用于拦截器RestTemplate请求 实现负载均衡

2.初始化RestTemplateCustomizer 用于给RestTemplate增加LoadBalancerInterceptor拦截器

3.通过loadBalancedRestTemplateInitializerDeprecated 加载@LoadBalanced 标识的 RestTemplate对象 通过RestTemplateCustomizer增加LoadBalancerInterceptor拦截器

LoadBalancerinterceptor类

/**
* 拦截通过LoadBalancer注解修饰的Restempate的请求信息
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { 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));
}
/**
* 对restempate请求进行拦截
* @param request
* @param body
* @param execution
* @return
* @throws IOException
*/
@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);
//通过注入LoadBalancerClient 实现负载均衡 requset封装了restrempate的请求信息
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}

LoadBalancerinterceptor拦截@LoadBalanced 标识的 RestTemplate对象发送的请求 并交给LoadBalancerClient的实现类的execute方法处理

RibbonLoadBalancerClient

public class RibbonLoadBalancerClient implements LoadBalancerClient {

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//serviceId此时已经是服务名字如前面demoPROVIDER
return this.execute(serviceId, (LoadBalancerRequest)request, (Object)null);
} public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//通过ILoadBalancer 根据ServiceId获得具体服务实例
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
//根据loadBalancer 获得服务实例
return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
} public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
} if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
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 var8) {
statsRecorder.recordStats(var8);
throw var8;
} catch (Exception var9) {
statsRecorder.recordStats(var9);
ReflectionUtils.rethrowRuntimeException(var9);
return null;
}
}
}
}

最终是通过loadBalancer.chooseServe(serverId) 来获得具体实例 而不是

ILoadBalancer

public interface ILoadBalancer {
//向负载均衡器中维护的实例列表增加服务实例。
public void addServers(List<Server> newServers);
//通过某种策略, 从负载均衡器中挑选出 一 个具体的服务实例
public Server chooseServer(Object key);
//用来通知和标识负载均衡器中某个具体实例已经停止服务, 不
//然负载均衡器在下 一 次获取服务实例清单前都会认为服务实例均是正常服务的。
public void markServerDown(Server server); // 获取服务列表 已过期 已过时
@Deprecated
public List<Server> getServerList(boolean availableOnly);
//获取当前正常服务的实例列表。
public List<Server> getReachableServers();
//获取所有已知的服务实例列表, 包括正常服务和停止服务的实例
public List<Server> getAllServers();
}

BaseLoadBalancer对负载均衡做了基本的实现

DynamicServerListLoadBalancer继承BaseLoadBalanceer 做了扩展

ZoneAwareLoadBalancer继承DynamicServerListLoadBalancer 在原有的基础上做了扩展

@Configuration
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration { @ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
} }

通过RibbonClientConfiguration 可以看出默认是使用ZoneAwareLoadBalancer

根据ZoneAwareLoadBalancer负载均衡策略获得server后 继续调用RibbonLoadBalancerClient.execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request)

 public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
} if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try {
//调用拦截器传入LoadBalancerRequest回调apply 这个时候serviceInstance封装了host信息端口等信息
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
} catch (IOException var8) {
statsRecorder.recordStats(var8);
throw var8;
} catch (Exception var9) {
statsRecorder.recordStats(var9);
ReflectionUtils.rethrowRuntimeException(var9);
return null;
}
}
}

serviceInstance接口定义

public interface ServiceInstance {
default String getInstanceId() {
return null;
} String getServiceId(); String getHost(); int getPort(); boolean isSecure(); URI getUri(); Map<String, String> getMetadata(); default String getScheme() {
return null;
}
}

当拿到实例最终会根据RibbonLoadBalancerClient.reconstructURI 将服务组织正常url形式并发起请求

最新文章

  1. 【干货分享】流程DEMO-请休假
  2. Spring ApplicationContext 简解
  3. 无线路由!RTS DTIM阈值、Beacon 周期如何设置多少可以加快路由
  4. 你真的理解 new 了吗?
  5. react-redux(1)
  6. Report_客制化以PLSQL输出XLS标记实现Excel报表(案例)
  7. html5—— 应用程序缓存
  8. Codeforces 600 E. Lomsat gelral (dfs启发式合并map)
  9. 2336: [HNOI2011]任务调度 - BZOJ
  10. XML解析(转)
  11. AngularJS&ndash;Animations(动画)
  12. ROS机器人程序设计(原书第2版)补充资料 (肆) 第四章 在ROS下使用传感器和执行器
  13. Python文件的读写
  14. 061 SparkStream数据接收原理
  15. 别人的Linux私房菜(11)认识与学习BASH
  16. React-使用imutable.js来管理store中的数据
  17. [转]ubuntu bits/predefs.h:没有那个文件或目录
  18. [administrative][CentOS][NetworkManager] networkmanager (二)
  19. How to Get Text inside a Canvas using Webdriver or Protractor
  20. elasticsearch中文发行版 安装

热门文章

  1. Codeforces Round #332 (Div. 2)A. Patrick and Shopping 水
  2. luogu4218 [JSOI2008] 最小生成树计数
  3. operator[] 重载
  4. 删除项目中的版本控制(SVN)
  5. 部署webservice常见问题汇总
  6. Shuffle&#39;m Up(串)
  7. Gym - 101981I The 2018 ICPC Asia Nanjing Regional Contest I.Magic Potion 最大流
  8. [Apple开发者帐户帮助]五、管理标识符(1)注册应用程序ID
  9. C/C++中输入多组数据方法
  10. C#Cookie操作类,删除Cookie,给Cookie赋值