《重新定义Spring Cloud实战》——3.5.7 基于metadata路由实例
3.5.7 基于metadata路由实例
对于Eureka来说,最常见的就是通过metadata属性,进行灰度控制或者是不宕机升级。这里结合Netflix Ribbon的例子,介绍一下这类应用场景的实现。
1. ILoadBalancer接口
Netflix Ribbon的ILoadBalancer接口定义了loadBalancer的几个基本方法,如下:
public interface ILoadBalancer
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
@Deprecated
public List<Server> getServerList(boolean availableOnly);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
可以看到这里有个chooseServer方法,用于从一堆服务实例列表中进行过滤,选取一个Server出来,给客户端请求用。
在Ribbon中,ILoadBalancer选取Server的逻辑主要由一系列IRule来实现。
2. IRule接口
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
最常见的IRule接口有RoundRobinRule,采用轮询调度算法规则来选取Server,其主要代码如下:
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<Server> reachableServers = lb.getReachableServers();
List<Server> 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;
}
3. MetadataAwarePredicate
这里,由于我们需要根据实例的metadata进行过滤,因此,可以自定义实现自己的rule。Netflix提供了PredicateBasedRule,可以基于Guava的Predicate进行过滤。jmnarloch在《Spring Cloud: Ribbon dynamic routing》(https://jmnarloch.wordpress.com/2015/11/25/spring-cloud-ribbon-dynamic-routing/)中给出了针对metadata过滤的rule,如下:
public class MetadataAwarePredicate extends DiscoveryEnabledPredicate {
@Override
protected boolean apply(DiscoveryEnabledServer server) {
final RibbonFilterContext context = RibbonFilterContextHolder.getCurrentContext();
final Set<Map.Entry<String, String>> attributes = Collections.unmodifiableSet(context.getAttributes().entrySet());
final Map<String, String> metadata = server.getInstanceInfo().getMetadata();
return metadata.entrySet().containsAll(attributes);
}
}
这个Predicate将Server的metadata跟上下文传递的attributes信息进行匹配,全部匹配上才返回true。比如attributes的map有个entry,key是env,value是canary,表示该实例是canary实例,如果请求上下文要求路由到canary实例,可以从request url参数或者header中标识这个路由请求,然后携带到上下文中,最后由Predicate进行判断,完成整个ILoadBalancer的choose。
- 点赞
- 收藏
- 关注作者
评论(0)