突发奇想,今天学到Ribbon的一点知识,OK,let's tell about Ribbon.
Load Balance解决一个机器(进程)无法解决所有的请求而产生的一种算法;就像nginx可以使用负载均衡分配流量。
好处
1.当有服务宕机后,剩余的服务可以继续运行,不会因为大量请求导致宕机。
2.多个系统保证不会有一台系统cpu彪满,保证了服务器的良性使用。
从服务器中随机选择一个服务器,简单实例如下
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List upList = lb.getReachableServers();
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = rand.nextInt(serverCount); // 使用jdk内部的Random类随机获取索引值index
server = upList.get(index); // 得到服务器实例
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
顾名思义,10台服务器:1,2,3,4,5,6,7,8,9,10,1.....如此往复交替
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List reachableServers = lb.getReachableServers();
List allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
根据响应时间分配权重,权重越低,选择的可能性越高。
至于使用那种策略还是根据自己项目业务的实际情况选择。择优(武当 or 少林)
现在都是基于这种方法
// 在RestTemplate中我们都是使用 ip 和 port
// 注意到 url 中的 ip 和 port 换成了服务名称
String requestUrl = String.format("http://%s/服务实例名/api资源路径",CommonConstant.AUTHORITY_CENTER_SERVICE_ID);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 这里的restTemplate是Ribbon底层基于的restTemplate,不要混淆了
return restTemplate.postForObject(requestUrl,new HttpEntity<>(JSON.toJSONString(json参数),header),JwtToken.class);
使用原生的Ribbon Api,看看Ribbon是如何完成:服务调用 + 负载均衡
public JwtToken thinkRibbon(请求参数) {
String urlFormat = "http://%s/服务名/api资源路径";
// 1. 找到服务提供方的地址和端口号
List targetInstances = discoveryClient.getInstances(
CommonConstant.AUTHORITY_CENTER_SERVICE_ID
);
// 构造 Ribbon 服务列表
List servers = new ArrayList<>(targetInstances.size());
targetInstances.forEach(i -> {
servers.add(new Server(i.getHost(), i.getPort()));
log.info("found target instance: [{}] -> [{}]", i.getHost(), i.getPort());
});
// 2. 使用负载均衡策略实现远端服务调用
// 构建 Ribbon 负载实例
BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder()
.buildFixedServerListLoadBalancer(servers);
// 设置负载均衡策略
loadBalancer.setRule(new RetryRule(new RandomRule(), 300));
String result = LoadBalancerCommand.builder().withLoadBalancer(loadBalancer)
.build().submit(server -> {
String targetUrl = String.format(
urlFormat,
String.format("%s:%s", server.getHost(), server.getPort())
);
log.info("target request url: [{}]", targetUrl);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String tokenStr = new RestTemplate().postForObject(
targetUrl,
new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
String.class
);
return Observable.just(tokenStr);
}).toBlocking().first().toString();
return JSON.parseObject(result, JwtToken.class);
}