code.zip
一、思路
1.1、代码组件-ServerListUpdater
功能:获取到所有的服务列表
1.2、代码组件-ILoadBalancer
功能:提供客户端的负载均衡功能
1.3、组件-IRule
功能:提供具体的负载均衡策略
二、具体实现
2.1、ServerListUpdater
服务列表更新
默认实现: DefaultServerListUpdater
/**
* <p> 默认服务列表更新 </p>
*
* @Author 彳失口亍
*/
public class DefaultServerListUpdater extends Thread implements ServerListUpdater {
public DefaultServerListUpdater(ILoadBalancer loadBalancer) {
this.loadBalancer = loadBalancer;
}
/** 负载均衡对象 **/
private ILoadBalancer loadBalancer;
/** 最后更新时间 **/
private volatile String lastUpdate;
/** 服务列表集合 **/
private static List<Server> serverList = new ArrayList<>();
@Override
public void start(UpdateAction updateAction) {
updateAction.doUpdate();
lastUpdate = System.currentTimeMillis() + "";
}
@Override
public void run() {
while(true){
this.start(()->{
// update action
synchronized (this){
// 模拟从其他平台拉取到的服务数据
List<Server> serversOnRepository = Arrays.asList(
new Server("ribbon-server", "127.0.0.1", "9530"));
// 更新服务列表 (增量更新-剔除 host 和 port 相同的新增数据)
List<Server> addServers = new ArrayList<>();
serversOnRepository.forEach(item ->{
if(!serverList.contains(item)){
addServers.add(item);
}
});
if(!CollectionUtils.isEmpty(addServers)){
serverList.addAll(addServers);
loadBalancer.addServers(addServers);
}
}
});
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
break;
}
}
}
@Override
public String getLastUpdate() {
return lastUpdate;
}
}
主要方法逻辑在 run 方法中,定时从某个地址获取服务列表(这里空实现),然后将新增的服务信息保存起来。
2.2、ILoadBalancer
负载均衡功能实现类
默认实现:DefaultLoadBalancer
/**
* <p> 默认的负载均衡器 </p>
*
* @Author 彳失口亍
*/
public class DefaultLoadBalancer implements ILoadBalancer{
public DefaultLoadBalancer(IRule rule) {
this.rule = rule;
}
/** 服务列表集合 key:serverName,value Server.class **/
private static List<Server> serverList = new ArrayList<>();
/** 服务获取算法 **/
private IRule rule;
@Override
public void addServers(List<Server> newServers) {
serverList.addAll(newServers);
}
@Override
public Server chooseServer(Object key) {
if(null == key){
throw new RuntimeException("not found the server " + key);
}
// 获取到指定服务名称的服务列表
List<Server> servers = serverList.stream()
.filter(filter -> filter.getServerName().equals(key))
.collect(Collectors.toList());
if(StringUtils.isEmpty(servers)){
throw new RuntimeException("not found the server " + key);
}
return rule.choose(servers);
}
@Override
public List<Server> getAllServers() {
return Collections.unmodifiableList(serverList);
}
}
DefaultLoadBalancer
维护了 服务列表 serverList
该服务列表是由 ServerListUpdater
更新后同步过来的。
具体的负载均衡策略委派给了 IRule
来完成。
2.3、IRule
负载均衡策略实现
默认实现:RandomRule
/**
* <p> 随机算法 </p>
*
* @Author 彳失口亍
*/
public class RandomRule implements IRule{
@Override
public Server choose(List<Server> servers) {
int serverIndex = new Random().nextInt(servers.size());
return servers.get(serverIndex);
}
}
2.4、RestTemplate 请求过滤器
默认实现:LoadBalanceClientHttpRequestInterceptor
/**
* <p> 负载均衡过滤器 RestTemplate 过滤器</p>
*
* @Author 彳失口亍
*/
public class LoadBalanceClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public LoadBalanceClientHttpRequestInterceptor(ILoadBalancer loadBalancer) {
this.loadBalancer = loadBalancer;
}
private ILoadBalancer loadBalancer;
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
LoadBalanceHttpRequest loadBalanceHttpRequest = new LoadBalanceHttpRequest(request, loadBalancer);
ClientHttpResponse originalResponse = execution.execute(loadBalanceHttpRequest, body);
return originalResponse;
}
/** 请求包装类 **/
public static class LoadBalanceHttpRequest extends HttpRequestWrapper{
private ILoadBalancer loadBalancer;
public LoadBalanceHttpRequest(HttpRequest request,ILoadBalancer loadBalancer) {
super(request);
this.loadBalancer = loadBalancer;
}
@Override
public URI getURI() {
URI originUri = super.getURI();
URI newUri = null;
// 如果能从服务列表中获取到服务信息,则进行 url 的重新拼接
Server server = loadBalancer.chooseServer(originUri.getHost());
if(null != server){
StringBuffer sb = new StringBuffer();
sb.append(server.getHost())
.append(":")
.append(Optional.ofNullable(server.getPort()).orElse("80"));
String newHost = sb.toString();
try{
String url = originUri.toURL().toString();
String newUrl = url.replaceFirst(originUri.getHost(), newHost);
newUri = new URI(newUrl);
}catch (URISyntaxException e){
} catch (MalformedURLException e) {
e.printStackTrace();
}
return newUri;
}
return super.getURI();
}
}
}
三、测试
3.1、测试代码1
自己手动拼接 url
/**
* <p> 客户端 API 测试 </p>
*
* @Author 彳失口亍
*/
@RestController
public class ClientApi {
@Autowired
private ILoadBalancer loadBalancer;
@RequestMapping("/ribbon-client/demo")
public String demo(){
Server server = loadBalancer.chooseServer("ribbon-server");
StringBuffer sb = new StringBuffer();
sb.append("http://")
.append(server.getHost())
.append(":")
.append(server.getPort())
.append("/ribbon-server/api");
// url = http://127.0.0.1:9530/ribbon-server/api
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> entity = restTemplate.getForEntity(sb.toString(), String.class);
String result = entity.getBody();
return result;
}
}
3.2、测试代码2
让 RestTemplate 帮我们拼接
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/ribbon-client/demo2")
public String demo2(){
String serverName = "ribbon-server";
StringBuffer sb = new StringBuffer();
sb.append("http://")
.append(serverName)
.append("/ribbon-server/api");
// url = http://ribbon-server/ribbon-server/api
ResponseEntity<String> entity = restTemplate.getForEntity(sb.toString(), String.class);
String result = entity.getBody();
return result;
}