1、简介
Ribbon是Netflix公司开源的一个负载均衡的项目,是一个客户端负载均衡器,运行在客户端上。它是一个经过了云端测试的IPC库,可以很好地控制HTTP和TCP客户端的一些行为。 Feign已经默认使用了Ribbon。
2、实现
在使用Eureka做注册中心的前提下,客户端简单实现RestTemplate+@LoadBalanced
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Service
public class UserClient {
@Autowired
RestTemplate restTemplate;
public boolean checkToken(String loginToken){
boolean result = false;
String reqJson = "{\"loginToken\":\""+loginToken+"\"}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/json;UTF-8"));
HttpEntity<String> strEntity = new HttpEntity<String>(reqJson,headers);
Object res = restTemplate.postForObject(Constants.SERVER_PREFIX_4_CUSTOMER+"/login/checkToken",strEntity,Object.class);
System.out.println(res.toString());
return result;
}
}
3、原理
通过LoadBalancer自动配置类LoadBalancerAutoConfiguration的初始化方法为所有使用@LoadBalanced修饰的RestTemplate添加拦截器LoadBalancerInterceptor,拦截器的intercept方法中调用LoadBalancer的execute方法实现负载均衡(详情见源码)。
通过实现ILoadBalancer接口,具体是chooseServer方法,默认实现类ZoneAwareLoadBalancer,调用IRule的choose方法选择服务实例,默认实现RoundRobinRule
注:
ILoadBalancer需要一组可供选择的注册列表信息(注册列表信息从eureka获取,每30s更新一次),DynamicServerListLoadBalancer的initWithNiwsConfig方法中通过调用restOfInit方法调用updateListOfServers调用DiscoveryEnabledNIWSServerList类的getUpdatedListOfServers方法获取注册列表
主要包括添加服务,下线服务、获取可用列表、获取所有实例列表
IRule路由策略,默认顺序轮顺RoundRobinRule。
- BestAvailableRule 选择最小请求数
ClientConfigEnabledRoundRobinRule 轮询
RandomRule 随机选择一个server
- RoundRobinRule 轮询选择server
- RetryRule 根据轮询的方式重试
- WeightedResponseTimeRule 根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
- ZoneAvoidanceRule 根据server的zone区域和可用性来轮询选择
IPing默认10秒对服务实例ping一次,确认服务是否可用
核心类图
![](https://i-blog.csdnimg.cn/blog_migrate/db0f1dbb37bb6137b2df3e8f6949a6e8.png)
源码
LoadBalancerAutoConfiguration自动配置类
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(
required = false
)
private List<RestTemplate> restTemplates = Collections.emptyList();
......初始化方法
customizer.customize(restTemplate);
......设置拦截器
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
LoadBalancerInterceptor拦截器
//默认实现类RibbonLoadBalancerClient
private LoadBalancerClient loadBalancer;
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
RibbonLoadBalancerClient实现类
//根据实例名通过负载均衡算法,选择对应服务
public interface ServiceInstanceChooser {
ServiceInstance choose(String var1);
}
public interface LoadBalancerClient extends ServiceInstanceChooser {
//执行请求
<T> T execute(String var1, LoadBalancerRequest<T> var2) throws IOException;
<T> T execute(String var1, ServiceInstance var2, LoadBalancerRequest<T> var3) throws IOException;
//url重组
URI reconstructURI(ServiceInstance var1, URI var2);
}
public class RibbonLoadBalancerClient implements LoadBalancerClient
......
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//负载均衡实现
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
Server server = this.getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, ribbonServer, request);
}
}
ZoneAwareLoadBalancer
@Override
public Server chooseServer(Object key) {
......
BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key);
......
}
//BaseLoadBalancer的chooseServer方法
//默认负载规则,顺序调用
private final static IRule DEFAULT_RULE = new RoundRobinRule();
private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
RoundRobinRule负载规则
public Server choose(ILoadBalancer lb, Object key) {
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 = rand.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;
ILoadBalancer、IRule、IPing
public interface ILoadBalancer {
//添加一个Server集合
void addServers(List<Server> var1);
//根据key去获取Server
Server chooseServer(Object var1);
//标记某个服务下线
void markServerDown(Server var1);
/** @deprecated */
@Deprecated
List<Server> getServerList(boolean var1);
//获取可用的Server集合
List<Server> getReachableServers();
List<Server> getAllServers();
}
public interface IRule {
Server choose(Object var1);
void setLoadBalancer(ILoadBalancer var1);
ILoadBalancer getLoadBalancer();
}
public interface IPing {
boolean isAlive(Server var1);
}
DynamicServerListLoadBalancer、DiscoveryEnabledNIWSServerList获取注册列表
//DynamicServerListLoadBalancer
public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
initWithNiwsConfig(clientConfig);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
try {
super.initWithNiwsConfig(clientConfig);
String niwsServerListClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.NIWSServerListClassName,
DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);
ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
this.serverListImpl = niwsServerListImpl;
if (niwsServerListImpl instanceof AbstractServerList) {
AbstractServerListFilter<T> niwsFilter = ((AbstractServerList) niwsServerListImpl)
.getFilterImpl(clientConfig);
niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
this.filter = niwsFilter;
}
String serverListUpdaterClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.ServerListUpdaterClassName,
DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
);
this.serverListUpdater = (ServerListUpdater) ClientFactory
.instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
restOfInit(clientConfig);
} catch (Exception e) {
throw new RuntimeException(
"Exception while initializing NIWSDiscoveryLoadBalancer:"
+ clientConfig.getClientName()
+ ", niwsClientConfig:" + clientConfig, e);
}
}
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}
//DiscoveryEnabledNIWSServerList
public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
return this.obtainServersViaDiscovery();
}
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
List<DiscoveryEnabledServer> serverList = new ArrayList();
if (this.eurekaClientProvider != null && this.eurekaClientProvider.get() != null) {
EurekaClient eurekaClient = (EurekaClient)this.eurekaClientProvider.get();
if (this.vipAddresses != null) {
String[] var3 = this.vipAddresses.split(",");
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
String vipAddress = var3[var5];
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, this.isSecure, this.targetRegion);
Iterator var8 = listOfInstanceInfo.iterator();
while(var8.hasNext()) {
InstanceInfo ii = (InstanceInfo)var8.next();
if (ii.getStatus().equals(InstanceStatus.UP)) {
if (this.shouldUseOverridePort) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding port on client name: " + this.clientName + " to " + this.overridePort);
}
InstanceInfo copy = new InstanceInfo(ii);
if (this.isSecure) {
ii = (new Builder(copy)).setSecurePort(this.overridePort).build();
} else {
ii = (new Builder(copy)).setPort(this.overridePort).build();
}
}
DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, this.isSecure, this.shouldUseIpAddr);
des.setZone(DiscoveryClient.getZone(ii));
serverList.add(des);
}
}
if (serverList.size() > 0 && this.prioritizeVipAddressBasedServers) {
break;
}
}
}
return serverList;
} else {
logger.warn("EurekaClient has not been initialized yet, returning an empty list");
return new ArrayList();
}
}
4、参考资料
https://blog.csdn.net/forezp/article/details/74820899