写BUG的派大星

Patrick Star

  • 首页
  • 归档

  • 搜索
设计模式 Gis Kafka Druid 微信小程序 Java 开源项目源码 物体识别 机器学习 Mybatis 微服务 Feign OpenVPN CSS Streamsets CDH SpringCloud SpringBoot maven 分布式 Shell Tree Linux js WebSocket 多线程 集群 Hadoop 大数据 JDK ElasticSearch MySQL 数据库 Redis Http Nginx

Feign请求头设置/传递问题(同步方法设置Header/异步方法设置Header)

发表于 2020-07-02 | 分类于 Java | 0 | 阅读次数 3757

我们都知道Feign其实也是通过HTTP请求来实现的通信
那么自然绕不开HTTP相关的东西,比如很多系统中权限校验都是通过Header中的参数来实现,需要将前端传过来的header转发到目标服务,这里主要记录一下关于Header的设置。

下面提到的同步/异步 只是记录一下遇到问题的情景, 异步方法的实现方式同样适用于同步方法

同步

同步方法一: 通过拦截器

这种方法是比较通用而且也比较常见的,通过实现Feign的RequestInterceptor接口,重写里面的apply方法,为RequestTemplate添加请求头信息

代码如下

@Component
public class FeignClientsConfigurationCustom implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
	// 此种方式是线程安全的
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
	// 不为空时取出请求中的header 原封不动的设置到feign请求中
        if (null != attributes) {
            HttpServletRequest request = attributes.getRequest();
            if (null != request) {
		// 遍历设置 也可从request取出整个Header 写到RequestTemplate 中
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        String values = request.getHeader(name);
                        requestTemplate.header(name, values);
                    }
                }
            }
        }
    }
}

完成上述代码之后 在FeignClient注解中加入 configuration = FeignClientsConfigurationCustom.class 即可
如

@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{
}

同步方法二:@Headers注解

这种方法比较适用于一些不变的参数,如Content-Type等

@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{

	@GetMapping("/test")
	@Headers({"Content-Type: application/json","Accept: application/json"})
	String test(@RequestParam String param);
}

异步

此处说的异步场景是通过Spring中的@Async实现的

如

// 带有@Async注解,异步调用Feign
@Autowired
private TestService testService;

@GetMapping("/test")
public String test(){
	// 此方法是一个异步方法, 在该方法中调用了Feign服务
	testService.testMethod();
	return "Hello world!";
}

此时的业务场景是 前端向我发送一个服务,我异步地调用其他微服务的方法, 由于此方法执行耗时可能会比较长,而且对用户来说没有下一步操作,所以直接return掉,那么主线程因return而关闭,此时在刚刚的FeignConfig中就无法获取到请求头了。

image.png

如图所示,红色方框圈起来的地方都不为null,但是最终获取到的Header是一个空的Map。

所以猜测是因为主线程退出触发了JVM的回收机制。

那么此时的情况将是 主线程已经退出,子线程没有执行完

所以显然,此时不能通过这种方式传递Header了。

异步方法一:线程私有变量ThreadLocal。

既然无法直接通过获取HttpServletRequest来获取Header,那么可以稍微改造一下,在原来的基础上添加一个拦截器。
所有的请求过来的时候,在拦截器中将Header先取出来,然后设置到本线程私有的Map中。

原来的apply方法在提交请求的时候再通过ThreadLocal提供的remove方法,清除掉。

只要把对该Map的操作封装一个工具类,工具类中实现get/set方法即可。

其实这种方式就是换了一个地方保存请求头,因此实用性与便捷性都还可以。

代码与截图待补充

异步方法二:通过传参

该方式是在方法执行前,先将需要的参数取出来,比如我需要一个token 就在Header中取出token,需要一个Content-Type就取出Content-Type。

然后将取出来的值作为参数传递到待执行的方法中。

该方法所调用的Feign接口需要做一个改造,在参数中添加带有@RequestHeader的注解,该注解表示将变量放在请求头,而不是请求的参数或者请求体里面。

如

@FeignClient(name = "testClient", configuration = FeignClientsConfigurationCustom.class)
public interface testServer{

	@GetMapping("/test")
	String test(@RequestParam String param,@RequestHeader String token);
}

代码与截图待补充

这种方法对于原来代码的改动较小,如果异步的场景比较少的话可以选择这种方法。
但是如果项目中用到了较多的异步方法,那么就需要异步方法一里面的拦截器 + ThreadLocal + RequestInterceptor了。

  • 本文作者: Patrick
  • 本文链接: https://www.write1bug.cn/archives/feign请求头设置问题同步异步
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 设计模式 # Gis # Kafka # Druid # 微信小程序 # Java # 开源项目源码 # 物体识别 # 机器学习 # Mybatis # 微服务 # Feign # OpenVPN # CSS # Streamsets # CDH # SpringCloud # SpringBoot # maven # 分布式 # Shell # Tree # Linux # js # WebSocket # 多线程 # 集群 # Hadoop # 大数据 # JDK # ElasticSearch # MySQL # 数据库 # Redis # Http # Nginx
StreamSets安装(CentOS7)
一次使用YoloV5进行物体识别的记录
  • 文章目录
  • 站点概览
Patrick

Patrick

不是在改BUG,就是在写BUG。

52 日志
9 分类
36 标签
RSS
E-mail
Creative Commons
© 2018 — 2023 Patrick
人生如逆旅|我亦是行人
鲁ICP备18043140号-1