openfeign 转发header 实现全链路灰度发布

    技术2022-07-11  104

    openfeign 转发header 实现全链路灰度发布

    引言实现在服务中修改Predicate加一个Interceptor配置ribbon规则,使用我们自己的规则

    引言

    网关层看这里 之前写了网关层实现灰度发布,但是这个只能被路由一次 像是这样: 客户端->网关->根据版本号路由到应用 之后应用再调用其他服务,调用链的路由就没办法了,它只会被路由一次,因为服务调用的openfeign默认不带header转发。

    实现

    因为我们要实现全链路转发,所以需要在所有服务中加入之前在网关层写好的rule和predicate,predicate需要改一下,因为web中没有zuul的RequestContext,改为使用(ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); 其他的具体代码去看网关层文章,我这里不重写了。

    在服务中修改Predicate

    import com.google.common.base.Optional; import com.netflix.loadbalancer.AbstractServerPredicate; import com.netflix.loadbalancer.PredicateKey; import com.netflix.loadbalancer.Server; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.List; public class MyPredicate extends AbstractServerPredicate { @Override public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) { if (loadBalancerKey == null) { return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate())); } else { List<Server> results = Lists.newArrayList(); for (Server server: servers) { //这里是下面的apply方法 if (this.apply(new PredicateKey(loadBalancerKey, server))) { results.add(server); } } //这里我们需要重写 //如果results.size为0,说明所有服务都没有写metadata,或者和headers不匹配,所以这时候我们就得忽略这个规则,否则servers返回一个空集,就会找不到服务报错。 if (results.size()>0) return results; else return servers; } } @Override public boolean apply(@NullableDecl PredicateKey predicateKey) { Server server = predicateKey.getServer(); //Object loadBalancerKey = predicateKey.getLoadBalancerKey(); //仅仅允许元数据中有"version"的服务 if (server instanceof DiscoveryEnabledServer){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //从http头获取version final String contextVersion = attributes.getRequest().getHeader("version"); final String metaVersion = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata().get("version"); return StringUtils.isEmpty(contextVersion) || contextVersion.equals(metaVersion); } return true; } @Override public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) { return super.chooseRoundRobinAfterFiltering(servers, loadBalancerKey); } }

    加一个Interceptor

    利用Feign的ReqeustTemplate实现全链路Header转发,因为无论是gateway还是zuul,它们虽然自带了header转发,但是Feign默认是没有header转发的,所以我们在服务中需要统一加入一个Interceptor来转发header.

    import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; @Component public class AddHeaderInterceptor implements RequestInterceptor { @Autowired HttpServletRequest request; @Override public void apply(RequestTemplate template) { Enumeration<String> headerNames = request.getHeaderNames(); String hdrName = null; while(headerNames.hasMoreElements()){ hdrName = headerNames.nextElement(); template.header(hdrName,request.getHeader(hdrName)); } } }

    配置ribbon规则,使用我们自己的规则

    指定服务才具有灰度发布全链路转发 @RibbonClient(name="服务名",configuration = MyPredicateRule.class)

    如果想使全局都直接支持,可以不用上面的方法,直接全局即可。 官网的例子显示@RibbonClient的confiuration是必须带有@Configuration的配置类,但是如果带有@Configuration会被Spring扫描到,注册成全局Bean,经过我测试,可以不用写配置类,直接让configuration为Rule即可。

    全局都支持 import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration @AutoConfigureBefore(RibbonClientConfiguration.class) @ConditionalOnProperty(value = "ribbon.filter.metadata.enabled", matchIfMissing = true) public class RuleConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnClass(DiscoveryEnabledNIWSServerList.class) @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public MyPredicateRule metaDataAwareRule() { return new MyPredicateRule(); } }
    Processed: 0.013, SQL: 9