实现原理:ribbon支持自定义rule路由规则,且提供了基于eureka-instance-meta进行路由的实现,可以根据客户端注册在eureka是的tag实现动态路由
核心依赖:
// 该依赖将指定MetadataAwareRule路由策略
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>
核心类:RibbonFilterContextHolder, MetadataAwarePredicate.class
public class MetadataAwarePredicate extends DiscoveryEnabledPredicate {
public MetadataAwarePredicate() {
}
protected boolean apply(DiscoveryEnabledServer server) {
RibbonFilterContext context = RibbonFilterContextHolder.getCurrentContext();
Set<Entry<String, String>> attributes = Collections.unmodifiableSet(context.getAttributes().entrySet());
Map<String, String> metadata = server.getInstanceInfo().getMetadata();
return metadata.entrySet().containsAll(attributes);
}
}
// 在zuul转发或者feign调用前写入动态路由策略,指定转发到有该tag-value的服务
RibbonFilterContextHolder.getCurrentContext()
.add("${tagName}", ${tagValue});
// 客户端服务配置
eureka:
instance:
metadata-map:
tagName1: tagValue1
tagName2: tagValue2
public abstract class ServiceLancherHandler {
protected boolean handle(String serviceId) {
String serviceLancher = getServiceLancher(serviceId);
if (StringUtils.isEmpty(serviceLancher)){
return false;
}
RibbonFilterContextHolder.clearCurrentContext();
RibbonFilterContextHolder.getCurrentContext()
.add("lancher", serviceLancher);
return true;
}
/**
子类通过实现该方法可自定义访问策略
*/
abstract String getServiceLancher(String serviceId);
}
// 配置指定tag的职责链,
// 例如可定义先从header中寻找,
// 再从redis中寻找,再从数据库中寻找,
//可自行实现ServiceLancherHandler,本demo只实现了从header中寻找
@Bean
ServiceLancherHandlerChain serviceLancherHandlerChain(){
ServiceLancherHandlerChain chain = new ServiceLancherHandlerChain();
chain.addHandler(new RequestHeaderServiceLancherHandler());
return chain;
}
// AbFilter extends ZuulFilter
// 继承zuulfilter,实现自定义zuul前置拦截器,在服务转发前指定路由策略
// 在自定义的zuulfilter中,获取到第一层转发服务id后,调用指定tag的职责链方法
public Object run() throws ZuulException {
String serviceId = getServiceId();
if(StringUtils.isBlank(serviceId)){
return null;
}
serviceLancherHandlerChain.handle(serviceId);
return null;
}
FeignRibbonFilterInterceptor.class
// 拦截 FeignClient 在feign远程调用前指定调用指定tag的职责链方法,调用后清除
@Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
// PS 需实现 RequestInterceptor,将需要的requestHead在feign进行服务间调用时转发到下一级服务,否则服务间调用将损失前端过来的header
FeignHeadersInterceptor.class
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
Map<String, String> headers = HeaderUtil.getHeaders(request);
if (headers!=null && headers.size() > 0) {
Iterator<Entry<String, String>> iterator = headers.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
template.header(entry.getKey(), entry.getValue());
}
}
// 可根据实际业务需求对转发的header进行增减
基于Ribbon动态路由实现:调用链控制/版本控制/灰度发布
原文:https://www.cnblogs.com/kenwar/p/13586943.html