Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients. Feign already uses Ribbon, so, if you use
@FeignClient
, this section also applies.
[1]
Ribbon是在客户端实现的负载均衡器,可以有助于你对HTTP和TCP客户端的行为进行控制。
架构图如下:
如何创建spring boot项目请参看以前文章。
注册中心A的pom文件:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
注册中心A的yml文件:
server:
#注册中心A使用的端口号
port: 30001
eureka:
instance:
#注册中心A主机地址
hostname: eurekaserver1
client:
#本服务为注册中心,所以不需要向注册中心注册自己
register-with-eureka: false
#本服务为注册中心,不需要进行检索服务
fetch-registry: false
service-url:
defaultZone: http://eurekaserver2:30002/eureka/
spring:
application:
name: eurekaserver1
在启动类上,需要加上EnableEurekaServer注解。
注册中心B的创建与注册中心A一致。
注册中心B的yml文件:
server:
#注册中心B使用的端口号
port: 30002
eureka:
instance:
#注册中心B主机地址
hostname: eurekaserver2
client:
#本服务为注册中心,所以不需要向注册中心注册自己
register-with-eureka: false
#本服务为注册中心,不需要进行检索服务
fetch-registry: false
service-url:
defaultZone: http://eurekaserver1:30001/eureka/
spring:
application:
name: eurekaserver2
服务提供者C的pom文件:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
服务提供者C的yml文件:
server:
#服务提供者A的端口号
port: 40001
spring:
application:
name: ServerProvide
eureka:
client:
service-url:
defaultZone: http://eurekaserver1:30001/eureka/
服务提供者C创建一个Controller提供服务:
@RestController
public class ServerProvideController {
//描述eureka客户端信息的类
@Autowired
private DiscoveryClient eurekaClient;
@RequestMapping(value="/ServerTest", method=RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
public TestModel getTest(HttpServletRequest request){
TestModel tm = new TestModel();
tm.setUrl(request.getRequestURL().toString());
tm.setMsg("请求访问成功");
return tm;
}
}
服务提供者C使用的domain类:
public class TestModel {
private String id;
private String url;
private String msg;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
服务提供者D的搭建与服务提供者C一致。
服务提供者D的yml文件
server:
#服务提供者B的端口号
port: 40002
spring:
application:
name: ServerProvide
eureka:
client:
service-url:
defaultZone: http://eurekaserver1:30001/eureka/
我们现在来改造一下服务调用者:
服务调用者的pom文件中需要增加对spring-cloud-starter-ribbon的引用。
我们来看一下服务调用者的pom文件:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.3.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
服务调用者的yml文件:
server:
#服务调用者端口号
port: 50002
spring:
application:
name: ServerClient
eureka:
client:
service-url:
#将当前服务也注册到注册中心
defaultZone: http://eurekaserver1:30001/eureka/
服务调用者对外提供的服务(Controller):
@RestController
@Configuration
public class ClientTestController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@RequestMapping("/getTestMsg")
public String getTestMsg(){
RestTemplate rt = getRestTemplate();
String result = rt.getForObject("http://ServerProvide/ServerTest", String.class);
return result;
}
}
服务调用者的启动类需要增加EnableDiscoveryClient注解。
运行一下,看看效果。
我们再用浏览器请求一下服务调用者:
再访问一下:
可以看到,使用默认的负载均衡策略已经起作用了。
如何使用自定义的负载均衡策略呢?
定义配置类(也可以使用配置文件)
/*
配置类,设置规则仅仅对ServerProvide服务有效,
*/
@RibbonClient(name="ServerProvide", configuration=LbConfig.class)
public class LbConfig {
@Bean
public IRule getRule(){
return new MyRule();
}
}
重写负载均衡策略
/*
自定义负载均衡器的策略
*/
public class MyRule implements IRule {
private ILoadBalancer lb;// 声明负载均衡器的接口
/*
这里主要实现了自定义分配请求的逻辑
*/
@Override
public Server choose(Object key) {
// 获取服务器列表
List<Server> servers = lb.getAllServers();
Random r = new Random();
//生成随机数
int rand = r.nextInt(10);
//这里设置自定义的负载均衡策略,如果随机数大于6,则转发到端口为40001的服务提供者上
//反之则转发到端口为40002的服务提供者上
//这里仅仅做模拟,后续需要根据业务逻辑重新实现
if(rand > 6){
return getServerByPort(servers, 40001);
}else{
return getServerByPort(servers, 40002);
}
}
private Server getServerByPort(List<Server> servers, int port){
for(Server s : servers){
if(s.getPort() == port){
return s;
}
}
return null;
}
@Override
public void setLoadBalancer(ILoadBalancer lb) {
this.lb = lb;
}
@Override
public ILoadBalancer getLoadBalancer() {
return this.lb;
}
}
运行一下,请求服务调用者,可以看出来,自定义的负载均衡策略已经起作用了。
Ribbon的组成:
- ILoadBalancer:负载均衡器的入口
- IPing:检查服务实例是否正常服务
- IRule:负载均衡器处理规则
- ServerList<Server>:服务列表的操作对象
- ServerListUpdater:服务更新器
- ServerListFilter<Server>:拦截器,用于实现对服务列表的过滤
- IclientConfig:配置接口
其中IRule最为重要,它为Ribbon提供了负载均衡规则。
代码使用:
public class LbConfig
{
@Autowired
private IClientConfig clientConfig;
@Bean
public IRule myRule(IClientConfig config) {
return new RandomRule();
// return new RoundRobinRule();
// return new RetryRule();
// return new WeightedResponseTimeRule();
// return new BestAvailableRule();
// return new AvailabilityFilteringRule();
// return new ZoneAvoidanceRule();
}
@Bean
public IPing myPing(IClientConfig config) {
return new PingUrl(false, "/Provide/");
}
}
Ribbon负载均衡支持的7中算法解释如下:
- RandomRule:从服务清单中随机选择一个服务实例
- RoundRobinRule:按照线性轮询的方式依次选择每个服务实例
- RetryRule:具备重试机制的实例选择功能,对内部定义的策略进行反复尝试,若期间能够选择到具体的服务实例就返回,超过设置的尝试时间就返回null
- WeightedResponseTimeRule:根据实例的运行情况来计算权重,并根据权重来挑选实例
- BestAvailableRule:遍历负责均衡器中维护的所有实例,找出并发请求数最小的一个,即选出最空闲的实例
- AvailabilityFilteringRule:先遍历所有节点进行过滤,在过滤的集合中选择实例
- ZoneAvoidanceRule:PredicateBasedRule的实现类,先过滤一部分实例,再以线性轮询的方式从过滤后的实例清单中选出一个
参考
- ^Ribbon解释(官方) https://cloud.spring.io/spring-cloud-netflix/reference/html/#spring-cloud-ribbon