负载均衡实现方案
jmeter_4">压测工具jmeter
Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它可以用于对服务器、网络或对象模拟繁重的负载来测试它们的强度或分析不同压力类型下的整体性能。你可以使用它做性能的图形分析或在大并发负载测试你的服务器/脚本/对象。使用jmeter主要是为了下文对ribbon和nginx各自最小连接数均衡策略的测试,最小连接数策略再低并发情况下,看不出分流效果
基于Ribbon的微服务调用负载均衡
Ribbon前言
SpringCloud通过组件ribbon实现微服务调用的负债均衡;ribbon成为消费端(调用端/客户端)的均衡,即A服务为消费端,B服务为提供服务的消费者;A服务再调用B服务时,需要从eureka拿到B服务注册的ip与端口;如果B服务注册有多个实例(即多个ip端口),则由ribbon配置的负载均衡策略,为A消费者选取合适的一个实例进行访问B服务;Eureka已经帮我们集成了负载均衡组件 Ribbon,简单修改代码即可使用。
搭建测试环境
服务方
这里,启动了三个bff微服务实例,端口分别为3001,3002,3003;我们通过ribbon负载均衡调用bff服务,看具体是哪个实例提供了服务
bff提供一个简单的方法,告诉调用分,是那个实例为其提供服务
调用方
springcloud组件间通信用restTemplate进行,子需要给restTemplate的配置方法上加上@LoadBalanced 注解,就能使的restTemplate在发起请求时,做请求分流(注意:因为现在项目很多使用Feign,Feign又是Ribbon继承上增强组件,故其实用Feign调接口,已经开启了对Ribbon的使用)
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
测试方法
@Test
public void testMethod01(){
//开启30个现场开始访问bff服务
for(int i=0;i<30;i++){
new Runnable(){
@Override
public void run() {
Object o = bffService.testMethod();
String s = JSON.toJSONString(o);
log.info(s);
}
}.run();
}
}
测试结果
2021-01-29 15:48:20.722 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 15:48:20.727 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 15:48:20.732 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 15:48:20.735 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 15:48:20.737 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 15:48:20.740 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 15:48:20.742 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 15:48:20.744 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 15:48:20.746 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 15:48:20.749 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 15:48:20.751 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 15:48:20.754 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 15:48:20.756 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
如上我们使用了一个最简单的ribbon demo;可以看出ribbon的默认负载均衡策略为 轮询
Ribbon负载均衡策略
换一个策略,只需要创建一个全局的负载策略,只需添加一个配置类
@Bean
public IRule ribbonRule() {
return new RandomRule();//随机策略
}
随机策略的访问结果
2021-01-29 16:29:53.825 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 16:29:53.828 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 16:29:53.832 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 16:29:53.834 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 16:29:53.837 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 16:29:53.840 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 16:29:53.844 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 16:29:53.847 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 16:29:53.849 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 16:29:53.852 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 16:29:53.854 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 16:29:53.856 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
Ribbon提供的策略rule类关系图
Ribbon复杂策略应用
Ribbon自带的四个大策略如下;重试和随机逻辑较为简单,不做赘述,主要研究 轮询和客户端可配置规则
RoundRobinRule轮询
RoundRobinRule时很简单的轮询逻辑,故主要讲解他的子类WeightedResponseTimeRule
子类WeightedResponseTimeRule:这个策略每30秒计算一次服务器响应时间,以响应时间作为权重,响应时间越短的服务器被选中的概率越大。
我们人为的将3003的响应时间变长(等待1s),看调用的情况如何
实际使用时,我发现还是普通轮询的效果,并没有体现出用响应时间加权的效果,原因是Robin需要30秒计算这些服务器的响应时间,junit test时间太短,故修改调用,
加入线程等待,让robin有时间统计服务器响应时间,修改调用的算法
public void testMethod01() throws InterruptedException {
//开启30个现场开始访问bff服务
for(int i=0;i<45;i++){
if(i<6){
Thread.sleep(10000);
}
new Runnable(){
@Override
public void run() {
Object o = bffService.testMethod();
String s = JSON.toJSONString(o);
log.info(s);
}
}.run();
}
}
统计各台服务器的请求次数,3001:37次,3002:17次,3003:1次;分析如下报文可知;WeightedResponseTimeRule为轮询策略的子策略,故当ribbon没有统计出各个服务器的响应时间时,他最开始时走轮询的;但他发现 3003的响应时间超过1000ms,而3001,3002为几ms时,ribbon直接不再访问3003,访问能较快响应的服务起3001,3002;起到了很好的智能分流,加快响应速度的效果;每30秒更新服务器的响应时间记录,故当3003的延迟降下来后,也能继续去访问3003
2021-01-29 17:39:58.777 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-01-29 17:40:08.780 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:12.747 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - Re-registering apps/IBS-DATA-SERVICE
2021-01-29 17:40:12.748 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1: registering service...
2021-01-29 17:40:14.266 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - registration status: 204
2021-01-29 17:40:18.783 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:25.937 [NFLoadBalancer-serverWeightTimer-BFF-SERVICE-DEV] INFO com.netflix.loadbalancer.WeightedResponseTimeRule - Weight adjusting job started
2021-01-29 17:40:27.697 [NFLoadBalancer-serverWeightTimer-BFF-SERVICE-DEV_defaultzone] INFO com.netflix.loadbalancer.WeightedResponseTimeRule - Weight adjusting job started
2021-01-29 17:40:27.697 [PollingServerListUpdater-0] INFO com.netflix.loadbalancer.WeightedResponseTimeRule - Weight adjusting job started
2021-01-29 17:40:27.697 [NFLoadBalancer-serverWeightTimer-BFF-SERVICE-DEV_defaultzone] INFO com.netflix.loadbalancer.WeightedResponseTimeRule - Weight adjusting job started
2021-01-29 17:40:28.787 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:38.790 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.792 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.794 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.796 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.797 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.799 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.800 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.802 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.804 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.805 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.807 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.808 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.810 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.812 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.814 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.816 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.817 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.819 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.821 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.822 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.824 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.826 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.827 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.829 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.830 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.832 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.833 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.835 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.836 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.838 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.839 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.840 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.842 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.844 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.845 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.848 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.849 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.851 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.852 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-01-29 17:40:48.854 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-01-29 17:40:48.855 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
子类ResponseTimeWeightedRule:已废弃,作用如上;
ClientConfigEnabledRoundRobinRule客户端可配置规则
ClientConfigEnabledRoundRobinRule就是普通的线性轮询,这里主要研究其子类BestAvailableRule
BestAvailableRule继承自ClientConfigEnabledRoundRobinRule,它在ClientConfigEnabledRoundRobinRule的基础上主要增加了根据loadBalancerStats中保存的服务实例的状态信息来过滤掉失效的服务实例的功能,然后顺便找出并发请求最小的服务实例来使用。然而loadBalancerStats有可能为null,如果loadBalancerStats为null,则BestAvailableRule将采用它的父类即ClientConfigEnabledRoundRobinRule的服务选取策略(线性轮询)
3001请求无延迟,3002延迟500ms,3003延迟1000ms
请求结果如下,3001请求45次,3002,3003无请求,按结果来看 BestAvailableRule 全部请求了延迟较低分服务器,下文解析造成这种现象的原因
2021-02-01 10:26:35.770 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:26:45.775 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:26:50.708 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - Re-registering apps/IBS-DATA-SERVICE
2021-02-01 10:26:50.708 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1: registering service...
2021-02-01 10:26:50.771 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - registration status: 204
2021-02-01 10:26:55.779 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:05.782 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:15.785 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.789 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.791 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.793 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.794 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.797 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.799 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.801 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.804 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.806 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.809 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.811 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.813 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.815 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.817 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.819 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.821 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.823 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.825 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.828 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.830 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.832 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.834 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.836 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.838 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.839 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.841 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.844 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.849 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.850 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.852 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.854 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.856 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.858 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.860 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.862 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.864 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.867 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.870 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.872 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 10:27:25.874 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
BestAvailableRule 源码解析
相比于统计响应时间来判断,BestAvailableRule 是通过查看服务的并发数来进行分流,相比WeightedResponseTimeRule并不是好理解其如何实现,故跟踪源码来查看实现
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
//服务器负债信息,存放每台实例的负载信息
private LoadBalancerStats loadBalancerStats;
public BestAvailableRule() {
}
public Server choose(Object key) {
if (this.loadBalancerStats == null) {
//当loadBalancerStats == null时,走ClientConfigEnabledRoundRobinRule的choose方法
//即 线性轮询
return super.choose(key);
} else {
//以下逻辑为通过并发数,进行选取的逻辑
//获取服务下所有实例信息
List<Server> serverList = this.getLoadBalancer().getAllServers();
int minimalConcurrentConnections = 2147483647;
long currentTime = System.currentTimeMillis();
//chosen 被选中得服务器
Server chosen = null;
Iterator var7 = serverList.iterator();
while(var7.hasNext()) {
Server server = (Server)var7.next();
//负载均衡LB需要依赖这些统计信息做为判断的策略,负载均衡器的统计类主要是LoadBalancerStats,其内部持有ServerStats对每个Server的运行 //情况做了相关统计如:平均响应时间、累计失败数、熔断(时间)控制等。下文详解
ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
//判断服务是否被熔断,选取能正常访问的服务
//该断路器和Hystrix无任何关系,无任何关系,无任何关系。它是ServerStats内部维护的一套熔断机制;
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
//获取当前服务器的活跃请求数
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
//选取请求数最少的服务
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
}
if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
}
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
if (lb instanceof AbstractLoadBalancer) {
this.loadBalancerStats = ((AbstractLoadBalancer)lb).getLoadBalancerStats();
}
}
}
ServerStats服务器状态
负载均衡LB需要依赖这些统计信息做为判断的策略,负载均衡器的统计类主要是LoadBalancerStats,其内部持有ServerStats对每个Server的运行情况做了相关统计如:平均响应时间、累计失败数、熔断(时间)控制等。如下是一个服务实例(3003)在Ribbon中记录得服务器信息
ServerStats属性解析
- connectionFailureThreshold:连接失败阈值,默认值3(超过就熔断)
- circuitTrippedTimeoutFactor:断路器超时因子,默认值10s。
- maxCircuitTrippedTimeout:断路器最大超时秒数(默认使用超时因子计算出来),默认值是30s。
- totalRequests:总请求数量。每次请求结束/错误时就会+1
- activeRequestsCount: 活跃请求数量(正在请求的数量,它能反应该Server的负载、压力)。但凡只要开始执行Sever了,就+1;但凡只要请求完成了/出错了,就-1
- successiveConnectionFailureCount:连续(successive)请求异常数量(这个连续发生在Retry重试期间);重试期间,但凡有一次成功了,就会把此参数置为0(失败的话此参数就一直加,只有在异常类型是callErrorHandler.isCircuitTrippingException(e)的时候,才会算作失败,才会+1(默认情况下只有SocketException/SocketTimeoutException这两种异常才算失败哦)
- lastActiveRequestsCountChangeTimestamp:简单的说就是
activeRequestsCount
的值最后变化的时间戳 - activeRequestsCountTimeout:
- lastConnectionFailedTimestamp:最后一次失败的时间戳;用于计算当前服务是否被熔断,是否可用;ServerStats自身的熔断机制
重要方法
private static final DynamicIntProperty activeRequestsCountTimeout = DynamicPropertyFactory.getInstance().getIntProperty("niws.loadbalancer.serverStats.activeRequestsCount.effectiveWindowSeconds", 600);
// 强调:如果当前时间currentTime距离上一次请求进来已经超过了时间窗口60s,那就返回0
// 简单一句话:如果上次请求距今1分钟了,那就一个请求都不算(强制归零)
public int getActiveRequestsCount(long currentTime) {
int count = this.activeRequestsCount.get();
if (count == 0) {
return 0;
} else if (currentTime - this.lastActiveRequestsCountChangeTimestamp <= (long)(activeRequestsCountTimeout.get() * 1000) && count >= 0) {
return count;
} else {
this.activeRequestsCount.set(0);
return 0;
}
}
BestAvailableRule 通过ServerStats记录得当前活跃得请求数,来选取负债较小得服务器访问;比如消费者访问A,B,C三个服务;对A发起服务且为结束,那么ServerStats记录对A服务的activeRequestsCount为1,没发起一个请求+1,每个请求结束或异常-1;这样,每个时刻Ribbon就能知道A,B,C三个服务器对消费者得请求负载情况;(注意,负载情况记录在消费端,不能反映A B C服务真实的负债情况,毕竟消费者也可能不止一个);BestAvailableRule 得choose方法,回获取多个实例得ServerStats,比较其当前活跃请求数,选取最小得那台服务,然后选择这台服务器;所以关键是activeRequestsCount的数值,其数值越小,被选中得程度就越高;
使用现象:当消费者对服务的调用并发很低时,会导致请求全去了一台服务器,看不到分流的效果;产生此情况的原因,比如3003的并发小,响应快,会导致BestAvailableRule 会导致每次选择时activeRequestsCount都是0,故BestAvailableRule 选择的服务器会一直不变,一直由3003承担服务
AvailabilityFilteringRule和ZoneAvoidanceRule
AvailabilityFilteringRule和ZoneAvoidanceRule: 是 ClientConfigEnabledRoundRobinRule下抽象类PredicateBasedRule的子类,她两都是轮询算法;
Ribbon默认的轮询算法使用的rule就是ZoneAvoidanceRule
//TODO
Ribbon自定义策略
我们来模拟一种场景,自定义均衡策略,假设3001 3002 3003三台服务起的cpu核数和内存都不同,分流更具服务器的cpu和内存大小分流
自定义Rule,继承ClientConfigEnabledRoundRobinRule类重写choose方法,主要通过serverConfig中每台服务器不同的属性值来分流.
package com.cet.electric.ibsdataservice.loadBalance;
import com.netflix.loadbalancer.*;
import java.util.*;
public class TttareRule extends ClientConfigEnabledRoundRobinRule {
private LoadBalancerStats loadBalancerStats;
private static Map<String,Integer> serverConfig = new HashMap<String,Integer>();
static {
// key 服务器ip或端口,value 服务器新能指标
serverConfig.put("3001",4);
serverConfig.put("3002",8);
serverConfig.put("3003",12);
}
@Override
public Server choose(Object key) {
if (this.loadBalancerStats == null) {
return super.choose(key);
} else {
List<Server> serverList = this.getLoadBalancer().getAllServers();
int minimalConcurrentConnections = 2147483647;
long currentTime = System.currentTimeMillis();
Server chosen = null;
Iterator var7 = serverList.iterator();
Map<Integer,Server> serverMap = new HashMap<Integer,Server>();
Integer total = 0;
while(var7.hasNext()) {
Server server = (Server)var7.next();
//serverStats 能获取服务起ip 端口 和 一下请求成功失败 熔断等信息
ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
//通过服务器性能分流
String hostPort = serverStats.getServer().getPort()+"";
Integer config = serverConfig.get(hostPort);
serverMap.put(config,server);
total+=config;
}
}
if(!serverMap.isEmpty()){
Set<Integer> integers = serverMap.keySet();
Random r = new Random();
int i = r.nextInt(total);
int leftIndex = 0;
int rightIndex = 0;
for (Integer config:integers){
rightIndex+=config;
if(i<=rightIndex && i>=leftIndex){
return serverMap.get(config);
}else{
leftIndex+=config;
}
}
}
}
return super.choose(key);
}
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
if (lb instanceof AbstractLoadBalancer) {
this.loadBalancerStats = ((AbstractLoadBalancer)lb).getLoadBalancerStats();
}
}
}
使用自定义Rule
@Bean
public IRule ribbonRule() {
return new TttareRule();
}
测试结果:3001:7次;3002:15次;3003:23次;基本实现了通过服务性能(1:2:3)来分流,将更多的流量给大性能服务器
2021-02-01 16:29:30.175 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:30.679 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:31.682 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:32.685 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:33.688 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:34.691 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:35.694 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:36.697 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:37.200 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:37.703 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:38.206 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:38.209 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:38.711 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:39.214 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:39.717 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:40.720 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:40.723 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:41.725 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:42.728 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:42.730 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:43.733 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:44.736 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:45.138 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - Re-registering apps/IBS-DATA-SERVICE
2021-02-01 16:29:45.138 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1: registering service...
2021-02-01 16:29:45.195 [DiscoveryClient-HeartbeatExecutor-0] INFO com.netflix.discovery.DiscoveryClient - DiscoveryClient_IBS-DATA-SERVICE/host.docker.internal:ibs-data-service:-1 - registration status: 204
2021-02-01 16:29:45.739 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:46.749 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:47.252 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:47.755 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:47.757 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:48.760 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:49.263 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:50.266 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:51.269 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:52.272 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:53.275 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:54.278 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:54.781 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:55.785 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:55.787 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:56.290 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:56.794 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:56.796 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3001"}
2021-02-01 16:29:57.799 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:58.802 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
2021-02-01 16:29:59.305 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:29:59.808 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3002"}
2021-02-01 16:30:00.812 [main] INFO c.c.e.ibsdataservice.api.service.LoadBalanceTest - {"msg":"loanBalance:处理该请求服务为端口3003"}
nginx_539">基于nginx的微服务调用负载均衡
Nginx前言
Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定。
应用场景
- http服务器:Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
- 虚拟主机:可以实现在一台服务器虚拟出多个网站。例如个人网站使用的虚拟主机。
- 反向代理,负载均衡:需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载
nginx_551">搭建nginx测试环境
下载nginx压缩包,解压即用;修改conf/nginx.conf 增加一个nginx server实例,监听88端口,让其反向代理三个bffService实例,并负责其负载均衡
//bffService 服务集群
upstream bffService {
server localhost:3001;
server localhost:3002;
server localhost:3003;
}
//nginx服务88端口实例,用来反向代理bffService服务集群
server {
listen 88;
server_name localhost;
location / {
proxy_pass http://bffService;
index index.html index.htm;
}
}
如上配置,访问http://localhost:88/时,nginx会将请求均衡的分流到 3001,3002,3003三台服务器上去
nginx启动:打开cmd命令窗口,切换到nginx解压目录下,输入命令 nginx.exe
nginx停止:如果使用cmd命令窗口启动nginx,关闭cmd窗口是不能结束nginx进程的,可使用两种方法关闭nginx
-
输入nginx命令 nginx -s stop(快速停止nginx) 或 nginx -s quit(完整有序的停止nginx)
-
使用taskkill taskkill /f /t /im nginx.exe
nginx_587">nginx负载均衡策略
nginx_589">nginx的配置参数
参数 | 参数解释 |
---|---|
fail_timeout | 与max_fails结合使用。 |
max_fails | 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了, |
fail_time | 服务器会被认为停机的时间长度,默认为10s。 |
backup | 标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里。 |
down | 标记服务器永久停机了。 |
upstream bffService {
server localhost:3001 max_fails=3 fail_timeout=20s;# 20s内最大失败次数3,满足则任务停机
server localhost:3002 down;# 3002被down机 会被直接跳过
server localhost:3003 backup;# 3003成了备用 除非3001也down调 否则不会访问
}
轮询策略
nginx 默认的负载均衡策略是轮询,每个请求会按时间顺序逐一分配到不同的后端服务器
默认负载均衡策略下,nginx的访问情况统计30次请求,根据请求结果,两次访问后轮询下一台服务器
访问结果统计
3002 3003 3003 3001 3001 3002 3002 3003 3003 3001 3001 3002 3002 3003 3003
权重weight
weight参数用于指定轮询几率,weight的默认值为1,;weight的数值与访问比率成正比
- 权重越高分配到需要处理的请求越多。
- 此策略可以与least_conn和ip_hash结合使用。
- 此策略比较适合服务器的硬件配置差别比较大的情况。
upstream bffService {
server localhost:3001 weight=4;
server localhost:3002 weight=2;
server localhost:3003 ;
}
访问结果统计:15次请求中 3001 9次 ,3002 4次,3003 2次
3001 3001 3002 3002 3001 3001 3003 3003 3001 3001 3002 3002 3001 3001 3001
ip_hash客户端IP的分配方式
指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。
upstream bffService {
ip_hash;
server localhost:3001 ;
server localhost:3002 ;
server localhost:3003 ;
}
- 在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。
- ip_hash不能与backup同时使用。
- 此策略适合有状态服务,比如session。
- 当有服务器需要剔除,必须手动down掉。
least_conn连接数较少的后端服务器
把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
upstream bffService {
least_conn;
server localhost:3001 ;
server localhost:3002 ;
server localhost:3003 ;
}
- 此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。
第三方策略方案
第三方的负载均衡策略的实现需要安装第三方插件。
fair 按照响应时间来分配
按照服务器端的响应时间来分配请求,响应时间短的优先分配
upstream bffService {
fair; #实现响应时间短的优先分配
server localhost:3001 ;
server localhost:3002 ;
server localhost:3003 ;
}
url_hash 按访问url的hash结果来分配请求
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。
upstream bffService {
hash $request_uri; #实现每个url定向到同一个后端服务器
server localhost:3001 ;
server localhost:3002 ;
server localhost:3003 ;
}
Ribbon与Nginx对比
处理请求分化的端不同
1.服务器端负载均衡Nginx
nginx是客户端所有请求统一交给nginx,由nginx进行实现负载均衡请求转发,属于服务器端负载均衡。
既请求有nginx服务器端进行转发。
2.客户端负载均衡Ribbon
Ribbon是从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,让后在本地实现轮训负载均衡策略。
既在客户端实现负载均衡。
编码方面
Ribbon的使用,需要一定的编码,有代码的侵入性,不需要启动,但目前只能与eureka等springCloud组件组合使用;ribbon自定义负载均衡策略实现很简单,只需要实现Ribbon已有的规则类;
Nginx基本不需要编码,只需要修改配置文件,但需要额外下载nginx安装,且nginx对运行环境又一定要求;但nginx不是java编写,想自定义负载均衡策略较难,但市面上还是有负载均衡插件;
使用场景
Nginx 适合于服务器端实现负载均衡 比如 Tomcat
Ribbon 适合与在微服务中 RPC 远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡
Nginx 一般是处理浏览器即前端的请求,分发浏览器发起的请求,而Ribbon 是处理springcloud微服务实例发起的请求
两者高并发环境负载均衡测试
编写压测接口,nginx采用restTemplate,ribbon采用feign调用;
nginx 出战代表 least_conn
ribbon出战代表BestAvailableRule
两者都是通过 服务提供方的并发连接数量来分流,选择并发连接较低的服务
@RestController
@RequestMapping("/tttare/test/")
@Slf4j
public class LoadBalanceController {
@Autowired
private RestTemplate restTemplate;//nginx使用restTemplate调用
@Autowired
private BffService bffService;//ribbon使用feign调用
//jmeter 高并发测试方法
@PostMapping("/forTest")
@RequestLogger(requestCode = RequestCodeConstants.BATCH_ENABLE_ACCOUNT)
public Object forTest(@RequestBody Map map){
String type = map.get("type").toString();
Object resp = null;
if(type.equals("1")){
//least_conn nginx的服务器并发数分流
resp = restTemplate.postForObject("http://localhost:88/bff/v1/account/forTest",null,Object.class);//ng在88端口运行
}else{
//BestAvailableRule ribbon的并发分流rule
resp = bffService.testMethod();//feign 已经整合了robbin的负载均衡
}
//打印返回结果 记录最后的分流数量
String s = JSON.toJSONString(resp);
log.info(s);
return resp;
}
}
jmeter 对接口 http://localhost:9998/tttare/test/forTest 进行压测 模拟300个并发请求
jmeter建立压测项目
执行压测项目,“负载均衡接口测试.jmx” 项目保存生成的jmx文件,"loadTest/nginx/result/result.txt"请求结果日志文档,"loadTest/nginx/report"统计报告 是一个html文件,在黑窗口执行如下命令开始压测
jmeter -n -t 负载均衡接口测试.jmx -l loadTest/nginx/result/result.txt -e -o loadTest/nginx/report
nginx_798">nginx负载均衡执行结果
nginx分流情况 3001 : 277次;3002 : 12次;3003:11次;
压测报告如下
- Average:平均响应时间——默认情况下是单个 Request 的平均响应时间
- Median:中位数,也就是 50% 用户的响应时间
- Min:最小响应时间
- Max:最大响应时间
- 90%Line:90%的用户响应时间小于这个值。
- 95%LIne:95%的用户响应时间小于这个值。
- 99%LIne:99%的用户响应时间小于这个值。
- Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second),当使用了 Transaction Controller 时,也可以表示类似 LoadRunner 的 Transaction per Second 数
nginx的压测表现,很平稳,多次压测 平均响应时间都是400ms,最多的响应时间是5ms,每秒能处理27个请求
ribbon_818">ribbon负载均衡执行结果
jmeter -n -t 负载均衡接口测试.jmx -l loadTest/ribbon/result/result.txt -e -o loadTest/ribbon/report
ribbon分流情况
3001:276次,3002:12次;3003:12次;
压测报告
ribbon的变现变化较大,最优变现平均响应也是400ms左右,最多响应时间,每秒处理响应的时间也和nginx相当
测试结果对比
这次测试的都是 两个负载均衡实现方法策略中的 **“选取被调用服务器并发连接数较少”**的策略,通过分流表现,两者都最多的调用了响应时间较短的服务器,3001响应较快,承担了80%以上的请求,但随着3001的并发连接越来越多,尽管3001响应快连接时间短,但并发连接还是不少,ribbon和nginx都将少数的请求分流到了响应较慢的服务器
对nginx进行了4次压测,性能比较平稳,每次数据都差不多;ribbon的4次压测有三次链接超时,导致结果差别很大,上文是选取了ribbon表现最好的压测数据,下文是出现feign调用超时是,ribbon分流造成的非常差的表现效果,多次Average超过5000,最多9000,随着并发的增加,feign的调用耗时越来越高,最大调用时间也突破了30000ms(增大的feign的超时时限,还是超过了,报超时异常,这种其实已经是失败了);nginx的最大调用时间基本没有超过15000ms的情况;
nginxribbon_849">补充测试,比较nginx和ribbon默认策略的压测表现
nginx和ribbon的默认都是轮询,查看在轮询在压测下的表现,即横向对比也纵向对比;
nginx 出战是默认轮询策略
jmeter -n -t 负载均衡接口测试.jmx -l loadTest02/nginx/result/result.txt -e -o loadTest02/nginx/report
ribbon出站是默认的轮询(ZoneAvoidanceRule)
jmeter -n -t 负载均衡接口测试.jmx -l loadTest02/ribbon/result/result.txt -e -o loadTest02/ribbon/report
nginx表现,最大请求时间60000ms,http调用也出现了超时
ribbon表现,最大请求时间也超过60000ms
**轮询相比上文的更高级的策略,表现更差;**高并发情况下能处理的请求更少
结语
nginx是代理服务器,客户端对服务器的请求经过nginx传递,nginx为我们选择合适的服务器,属于服务端的负载聚合。robbin则不同,客户端在发起请求前,自身要做的一件事情就是选择合适分服务,在请求发送前做负载均衡,是请求端的负载均衡
负载均衡工具的选取,不同情况下的差异很巨大;总体而已,nginx的负载均衡表现更好,性能优于ribbon,且产品面世时间长,有很多的使用案例,ribbon较为年轻;但相比之下,nginx不适合在微服务之间调用时做负载均衡,主要是不如ribbon整合的更好,ribbon直接抓取eureka的注册表信息,微服务加入新的服务器不需要特殊配置,即可以直接分流过去,且ribbon的api简单,结合feign的使用达到开发时看不到负载均衡代码实现但项目已经有了负载均衡实现的效果;ribbon使用java编写,可以自己做包装扩展,自己编写适合的负载均衡策略;nginx也是开源的,但是可能不适合java程序员去做包装做扩展;
同一个负载均衡的工具,不同策略下的表现差异也巨大;如上文,轮询策略在3001 3002 300三台服务器性能差别较大时,表现很差,其甚不如3001一台服务器的表现;要选择合适的策略,根据服务器性能,特定接口的请求频率等等去分流,也要主动即使剔除有问题的服务器,ribbon和nginx都有熔断机制;当有一台服务器响应慢时,可以选择按响应时间或并发连接数或者其他自定义的策略;从测试也能看出,面对高的并发场景,复杂的运维场景,简单的分流时起不到很大作用的,还得根据不同的场景,结合压测和实际使用体验,去做针对性的处理;