前言
Netflix Ribbon是netflix公司的开源项目,由SpringCloud整合成SpringCloud Ribbon,其主要作用是实现分布式服务中的负载均衡;其实现需要依赖Eureka,其从Eureka注册中心获取服务清单,然后根据某种算法(比如轮询、权重、最小连接数、随机算法等)来选择调用的服务器
一、项目结构
二、创建产品服务
2.1 创建goods-server-9200和goods-server-9201
2.2 pom文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>goods-server</artifactId>
<groupId>com.wj</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>goods-server-9200</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.wj</groupId>
<artifactId>cloud-common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2.3 配置文件
2.3.1 9200配置文件
# 端口号配置
server:
port: 9200
# 应用名称配置
spring:
application:
name: goods-server
#eureka客户端配置
eureka:
instance:
# 向注册中心发送心跳间隔时间,告诉注册中心自己还活着;默认时间是30s
lease-renewal-interval-in-seconds: 10
# 如果30秒内没有向注册中心发送心跳,代表发生故障,从注册中心移除掉,默认时间是90s
lease-expiration-duration-in-seconds: 30
#告诉服务端,服务实例以ip作为连接,而不是机器名,默认是false
prefer-ip-address: true
#告诉服务端,服务实例的名称
instance-id: ${spring.application.name}-9200
client:
#注册中心连接地址
service-url:
defaultZone: http://127.0.0.1:8761/eureka/,http://localhost:8762/eureka/
2.3.2 9201配置文件
# 端口号配置
server:
port: 9201
# 应用名称配置
spring:
application:
name: goods-server
#eureka客户端配置
eureka:
instance:
# 向注册中心发送心跳间隔时间,告诉注册中心自己还活着;默认时间是30s
lease-renewal-interval-in-seconds: 10
# 如果30秒内没有向注册中心发送心跳,代表发生故障,从注册中心移除掉,默认时间是90s
lease-expiration-duration-in-seconds: 30
#告诉服务端,服务实例以ip作为连接,而不是机器名,默认是false
prefer-ip-address: true
#告诉服务端,服务实例的名称
instance-id: ${spring.application.name}-9201
client:
#注册中心连接地址
service-url:
defaultZone: http://127.0.0.1:8761/eureka/,http://localhost:8762/eureka/
2.4 代码实现
2.4.1 启动类
package com.wj.goods.server9200;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author wangjian
*/
@EnableEurekaClient
@SpringBootApplication
public class GoodsServer9200Application {
public static void main(String[] args) {
SpringApplication.run(GoodsServer9200Application.class,args);
}
}
2.4.2 实体类
package com.common.api.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 产品实体类
* @author wangjian
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods implements Serializable {
//id
private String id;
//产品名称
private String name;
//描述
private String desc;
}
2.4.3 controller
package com.wj.goods.server9200.controller;
import com.common.api.base.Result;
import com.common.api.entity.Goods;
import com.wj.goods.server9200.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 产品controller
* @author wangjian
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
/**
* 获取产品列表
* @return
*/
@GetMapping("/list")
public Result<List<Goods>> list(){
List<Goods> list = goodsService.list();
return Result.ok("9200服务查询产品列表成功!",list);
}
}
2.4.4 service
package com.wj.goods.server9200.service.impl;
import com.common.api.entity.Goods;
import com.wj.goods.server9200.service.GoodsService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 产品service层实现
* @author wangjian
*/
@Service
public class GoodsServiceImpl implements GoodsService {
/**
* 查询产品列表
* @return
*/
@Override
public List<Goods> list() {
List<Goods> list = new ArrayList<>();
list.add(new Goods(UUID.randomUUID().toString(),"华为笔记本","i5+16G+256SSD+1T"));
list.add(new Goods(UUID.randomUUID().toString(),"联想笔记本","i7+16G+512SSD+1T+Max150"));
list.add(new Goods(UUID.randomUUID().toString(),"华硕笔记本","i5+8G+256SSD+1T"));
return list;
}
}
三、修改portal-server服务
其他配置请看:SpringCloud之Eureka注册中心
3.1 增加PortalConfig
package com.wj.portal.server.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 门户配置类
* @author wangjian
*/
@Configuration
public class PortalConfig {
/**
* 注入RestTemplate
* @LoadBalanced:Ribbon实现负载均衡
* @return
*/
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3.2 增加GoodsUrlConstant
package com.wj.portal.server.constants;
/**
* 产品接口常量类
* @author wangjian
*/
public class GoodsUrlConstant {
public static String GOODS_LIST_URL = "http://goods-server/goods/list";
}
3.3 增加RibbonController
package com.wj.portal.server.controller;
import com.wj.common.api.base.Result;
import com.wj.common.api.entity.Goods;
import com.wj.portal.server.constants.GoodsUrlConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* ribbion负载均衡
* @author wangjian
*/
@RestController
@RequestMapping("/ribbon")
public class RibbonController {
//注入RestTemplate
@Autowired
private RestTemplate restTemplate;
/**
* 获取产品列表
* @return
*/
@GetMapping("/goods/list")
public Result<List<Goods>> list(){
ResponseEntity<Result> forEntity = restTemplate.getForEntity(GoodsUrlConstant.GOODS_LIST_URL, Result.class);
Result result = forEntity.getBody();
return result;
}
}
四、postman测试结果
五、自定义负载均衡策略
5.1 IRule接口
Ribbon负载均衡策略有IRule接口定义,下面是结构图
负载均衡策略含义:
- RandomRule:随机策略
- WeightedResponseTimeRule:根据平均响应时间计算权重,响应时间越快权重越大;如服务刚启动信息统计不足,则使用RoundRobinRule策略,等统计信息足够,切换到WeightedResponseTimeRule策略
- AvailabilityFilteringRule:先过滤掉多次访问故障和并发数超过阀值的服务,在剩下的服务中进行轮询访问
- ZoneAvoidanceRule:综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务
- BestAvailableRule:过滤掉多次访问故障服务,然后选择一个并发量最小的服务
- RetryRule:先按照RoundRobinRule策略分发,如分发到的服务不能访问,则在指定时间内进行重试,还不行,重新分发其他可用的服务;
5.2 自定义负载均衡策略
5.2.1 MyRandomRule
package com.wj.portal.server.config;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.Random;
/**
* 自定义随机负载均衡策略
* @author wangjian
*/
public class MyRandomRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
System.out.println("MyRandomRule负载均衡策略..........");
//获取ILoadBalancer
ILoadBalancer lb = getLoadBalancer();
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = new Random().nextInt(serverCount);
server = upList.get(index);
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
}
5.2.2 PortalConfig 增加自定义策略注入
package com.wj.portal.server.config;
import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 门户配置类
* @author wangjian
*/
@Configuration
public class PortalConfig {
/**
* 注入RestTemplate
* @LoadBalanced:Ribbon实现负载均衡
* @return
*/
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
/**
* 注入自定义的负载均衡策略
* @return
*/
@Bean
public IRule getIRule(){
return new MyRandomRule();
}
}
总结
Ribbon原理解析参考:深入理解Ribbon之源码解析
文章内容有借鉴网络内容,如有侵权,请联系!!!