上两篇文章我们分别介绍了微服务中基于nacos做灰度测试/发布的样例,这篇文章我们介绍下request的header透传。
首先我们想象一下这样一个场景:
像上图中,我们整个微服务链路非常长,因此这里的恶化,我们可能会在最后面去调用product-service,那么此时当用户在前端的request中的header中添加一个头之后,这里的request header头应该一级级的往下透传,这样子order-service才知道需要调用灰度的product-service还是调用正常的product-service。所以这里肯定是需要把request-header做一级级的透传的。那么这里的透传会涉及到哪些方面呢?下面介绍下:
1、spring cloud gateway的透传
这里用户的请求都需要通过spring cloud gateway,因此在spring cloud gateway里面我们会涉及到吧request 头里面的值透传到下一级的服务里面去。
2、使用feign client调用的透传
因为feign client 调用的服务他是不走gateway的,而是直接feign client调用,因此在feign client层面我们也需要进行透传。
因此基于上诉两个方面的解决方案如下(这里我们还是以灰度测试/发布案例为例):
通过spring cloud gateway进行透传
这里我们只需要在filter中创建一个filter,获取request的头再转发下去即可,示例代码如下:
package org.shop.gateway.filter; import org.shop.common.http.handler.GrayRequestContextHolder; import org.shop.common.utils.ConmonConstants; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import com.alibaba.fastjson.JSON; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; /** * 网关中的灰度发布全局过滤器 * @author Administrator * */ @Component @Slf4j public class GlobalGrayFilter implements GlobalFilter , Ordered{ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("进入到了灰度的filter上"); // ① 解析请求头,查看是否存在灰度发布的请求头信息,如果存在则将其放置在ThreadLocal中 HttpHeaders headers = exchange.getRequest().getHeaders(); if (headers.containsKey(ConmonConstants.GRAY_HEADER)) { String gray = headers.getFirst(ConmonConstants.GRAY_HEADER); if (StrUtil.equals(gray, ConmonConstants.GRAY_VALUE)) { // ②设置灰度标记 GrayRequestContextHolder.setGrayTag(true); } } // ③ 将灰度标记放入请求头中 ServerHttpRequest tokenRequest = exchange.getRequest().mutate() // 将灰度标记传递过去 .header(ConmonConstants.GRAY_HEADER, GrayRequestContextHolder.getGrayTag().get().toString()).build(); HttpHeaders headers2 = tokenRequest.getHeaders(); log.info("网关灰度设置之后,获取到的request是:{}",JSON.toJSONString(headers2)); ServerWebExchange build = exchange.mutate().request(tokenRequest).build(); return chain.filter(build); } @Override public int getOrder() { return 0; } }
备注:
1、这里的order顺序需要提高下,建议为0.
通过feign client进行透传
这里的话,我们相当于从当前的request中获取到对应的header里面的key-value,然后编写一个统一的feign拦截器把这个key-value转发下去,示例代码如下:
package org.shop.feignclient.interceptor; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.shop.common.http.handler.GrayRequestContextHolder; import org.shop.common.utils.ConmonConstants; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSON; import cn.hutool.core.util.StrUtil; import feign.RequestInterceptor; import feign.RequestTemplate; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class FeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest httpServletRequest = attributes.getRequest(); Map<String, String> headers = getHeaders(httpServletRequest); log.info("feign 拦截获取到的request header是:{}",JSON.toJSONString(headers)); for (Map.Entry<String, String> entry : headers.entrySet()) { // ② 设置请求头到新的Request中 template.header(entry.getKey(), entry.getValue()); } } /** * 获取原请求头 */ private Map<String, String> getHeaders(HttpServletRequest request) { Map<String, String> map = new LinkedHashMap<>(); Enumeration<String> enumeration = request.getHeaderNames(); if (enumeration != null) { while (enumeration.hasMoreElements()) { String key = enumeration.nextElement(); String value = request.getHeader(key); // 将灰度标记的请求头透传给下个服务 if (StrUtil.equals(ConmonConstants.GRAY_HEADER.toLowerCase(), key.toLowerCase()) && Boolean.TRUE.toString().equals(value)) { // ① 保存灰度发布的标记 GrayRequestContextHolder.setGrayTag(true); map.put(key, value); } } } return map; } }
以上就是在spring cloud微服务中进行request header自定义头信息的透传解决方案。最后按照案例,附上本案例的源码,登录后即可下载。
还没有评论,来说两句吧...