负载均衡分为软负载均衡、硬负载均衡、DNS负载均衡。
先上一图:
区别看这:几种负载均衡分类 - 简书 (jianshu.com)
Ribbon是软负载均衡的客户端工具。用于客户端的负载均衡。
补充:Nginx是软负载均衡的服务器端工具。用于服务器端的负载均衡。
Ribbon实现负载均衡:
Ribbon是软负载均衡的客户端工具。所以肯定是用在客户端了,即消费者端。注意:消费者端也需要注册进Eureka。
使用前提:已经实现通过服务名来访问服务。即已经完成服务注册与服务发现。
代码:
生产者端application.yml代码:
server:
port: 8083
spring:
application:
name: ProviderCRUD
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis_plus_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
username: root
password: root
initial-size: 5
min-idle: 5
max-active: 20
#最大超时时间 ms
max-wait: 60000
#设置多长时间进行一次检测 ms
time-between-eviction-runs-millis: 60000
#配置一个连接在连接池中最小的生存时间 ms
min-evictable-idle-time-millis: 300000
validation-query: select 1 from dual
test-on-borrow: false
test-on-return: false
test-while-idle: false
#打开PSCache,并指定每个连接上的PSCache大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
#配置监控拦截器,去掉后监控界面sql无法显示 wall用于防火墙
filters: stat,slf4j,wall,log4j
#通过connectProperties属性打开mergeSql功能,可记录慢sql ms
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#合并多个DruidDataSource的监控记录
use-global-data-source-stat: true
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.log4j.Log4jImpl
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8082/eureka/,http://127.0.0.1:8084/eureka/,http://127.0.0.1:8085/eureka/
instance:
#自定义服务名称
instance-id: SpringCloudTestSGG-ProviderCRUD1
#访问路径是否显示IP地址
prefer-ip-address: true
生产者端controller代码:
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserMapper userMapper;
@Resource
private UserService userService;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/getAllUser")
public String getAllUser(){
List<User> users = userMapper.selectList(null);
String jsonString = JSONObject.toJSONString(users);
return jsonString;
}
//服务发现
@RequestMapping("/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
System.out.println("***********"+services);
List<ServiceInstance> instances = discoveryClient.getInstances("PROVIDERCRUD");
for (ServiceInstance instance : instances) {
System.out.println(instance.getHost()+"\t"+instance.getServiceId()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
}
生产者端启动类代码:
@EnableEurekaClient //本服务启动后,自动注册进Eureka
@SpringBootApplication
@EnableDiscoveryClient //服务发现
public class SpringCloudEurekaClient {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClient.class, args);
}
}
使用Ribbon负载均衡的时候,生产者启动类上一定要加上@EnableDiscoveryClient 注解,表示服务可以被发现。
既然是负载均衡那肯定不止一个生产者端了,多建几个,都注册进Eureka。
生产者端application.yml中配置代码几乎都是一样的。
主要区别在端口号一定不同,要不然端口冲突,服务启动不起来。
因为每一个服务都可以有自己的数据库,所以数据库方面的配置可能不同。
服务实例名spring.application.name一定是相同的,因为是通过服务名来负载均衡,所以必须保证一样。
在消费者端或者说客户端Maven中引入Ribbon依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
消费者端application.yml配置相对简单,因为负责请求的转发,调用其他服务,所以不需要向Eureka中注册自己
server:
port: 8086
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8082/eureka/,http://127.0.0.1:8084/eureka/,http://127.0.0.1:8085/eureka/
#表示不把自己注册到Eureka
register-with-eureka: false
instance:
instance-id: SpringCloudTestSGG-Consumer1
prefer-ip-address: true
spring:
application:
name: ConsumerCRUD
消费者端RestTemplate配置代码:
@Configuration
public class RestConfig {
@Bean
@LoadBalanced //负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
RestTemplate配置类中一定要添加@LoadBalanced 负载均衡注解。
消费者端controller代码:
@RestController
@RequestMapping("/rest")
public class UserRestController {
@Resource
private RestTemplate restTemplate;
//通过服务名来进行访问服务,这里演示用
//PROVIDERCRUD服务名字
private static String PREFIX_URL="http://PROVIDERCRUD/user/";
@GetMapping("/getAllUser")
public String getAllUser(){
return restTemplate.getForObject(PREFIX_URL+"getAllUser",String.class);
}
@RequestMapping("/discovery")
public Object discovery(){
return restTemplate.getForObject(PREFIX_URL+"discovery",Object.class);
}
}
这样就可以简单实现Ribbon的负载均衡了。
Ribbon默认负载均衡的策略是轮询。就是一个一个的轮流访问服务。
下图是Ribbon自带的负载均衡策略。
切换负载均衡策略:Ribbon的IRule接口:
IRule源码:
/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.loadbalancer;
/**
* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
* as a Strategy for loadbalacing. Well known loadbalancing strategies include
* Round Robin, Response Time based etc.
*
* @author stonse
*
*/
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
切换策略代码:
@Configuration
public class IRuleConfig {
@Bean
public IRule iRule(){
/**
* RoundRobinRule
* 轮询
*/
return new RoundRobinRule();
/**
* RandomRule
* 随机
*/
return new RandomRule();
/**
* AvailabilityFilteringRule
* 过滤访问故障和超过访问或并发阈值的服务。在剩下的服务中轮询。
*/
return new AvailabilityFilteringRule();
/**
* WeightedResponseTimeRule
* 按照权重访问。权重计算方法:响应时间越快的权重越高,被选中的概率越大。刚启动时如果统计信息不足,先按轮询策略,
* 后续统计信息足够了再按照权重策略。
*/
return new WeightedResponseTimeRule();
/**
* RetryRule
* 先按照轮询策略访问服务,如果获取服务失败,则会在指定时间内进行重试,重新获取服务。
*/
return new RetryRule();
/**
* BestAvailableRule
* 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
*/
return new BestAvailableRule();
/**
* ZoneAvoidanceRule
* 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
*/
return new ZoneAvoidanceRule();
}
}
实现自定义的Ribbon负载均衡策略:
实现IRule接口:
步骤:在客户端即消费者端启动类上添加
@RibbonClient(name = "xxx",configuration=yyy.class) 注解,
name表示执行此策略的服务名;configuration表示自定义负载策略的类。
简而言之就是针对名为xxx的服务,执行yyy.class中的负载策略。
注意:
也就是说,自定义的负载均策略类不能和启动类在同一个包或者父包下。
消费者启动类:
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "PROVIDERCRUD",configuration= IRuleConfig.class)
public class SpringCloudConsumer {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsumer.class,args);
}
}
MyRuleConfig,自定义负载算法类:
/**
* 自定义负载策略
* 要求:轮询服务,并且每个服务连续五次
*/
public class MySelfIRule extends AbstractLoadBalancerRule {
private Logger log;
private Integer total = 0;
private int index = 0;
public MySelfIRule() {
System.out.println("执行无参构造方法");
}
public MySelfIRule(ILoadBalancer lb) {
this();
setLoadBalancer(lb);
System.out.println("执行有参构造方法");
}
Server server = null;
public Server choose(ILoadBalancer lb,Object key) {
System.out.println("执行双参数choose查找返回server");
if (lb==null){
log.log(Level.WARNING,"没有对应的服务...");
}
List<Server> allServers = lb.getAllServers();
if (allServers.size()==0){
log.log(Level.WARNING,"没有指定的>>>"+lb+"<<<服务");
return null;
}
if (total<4){//0,1,2,3,4
server = allServers.get(index);
total++;
}else {//5
total=0;
index++;
if (index>=allServers.size()){
index=0;
}
}
if (server==null){
return null;
}
return server;
}
public Server choose(Object key) {
System.out.println("执行单参数choose");
return choose(getLoadBalancer(),key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
System.out.println("执行初始化方法initWithNiwsConfig,获取服务信息...");
String clientName = clientConfig.getClientName();
System.out.println("获取服务名>>:"+clientName);
System.out.println("获取配置信息...");
Map<String, Object> properties = clientConfig.getProperties();
for (Map.Entry<String, Object> stringObjectEntry : properties.entrySet()) {
System.out.println(stringObjectEntry);
}
}
}
在切换策略代码中,直接切换即可:
@Configuration
public class IRuleConfig {
@Bean
public IRule iRule(){
/**
* 自定义
*/
return new MySelfIRule();
}
}
到此完成自定义的Ribbon负载均衡算法。
Nginx负载均衡待更新。。。