SpringCloud微服务间参数的传递( threadLocal+Feign的RequestInterceptor方式)

9 篇文章 0 订阅

我们在开发过程中,常有这样的需求,要将当前的微服务参数传递到下一个微服务,比如userId或者token之类,如果我们使用SpringCloud,把参数放到Header里就可以轻松实现了参数的传递。

下面是一个利用Feign的RequestInterceptor实现的例子

 

这里使用ThreadLocal来保持参数,这样在当前的环境中就可以随时使用了

package app.gateway.threadLocal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

public class PassParameters {

        private static final Logger log = LoggerFactory.getLogger(PassParameters.class);

        private static final ThreadLocal localParameters = new ThreadLocal();

        public static <T> T get(){
            T t = (T) localParameters.get();
            log.info("ThreadID:{}, threadLocal {}", Thread.currentThread().getId(), JSON.toJSONString(t));
            return t;
        }

        public static <T> void set(T t){
            log.info("ThreadID:{}, threadLocal set {}", Thread.currentThread().getId(), JSON.toJSONString(t));
            localParameters.set(t);
        }
    }

设计一个切面,这里是gateway的切面,存放相关的参数,我这里放到map中,页可以自定义一个对象,放对象中

package app.gateway.aop;


import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import app.gateway.threadLocal.PassParameters;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
 
/**
 * @author hsn
 */
@Aspect
@Component
public class ApiRequestAspect {
    private static Logger logger = LoggerFactory.getLogger(ApiRequestAspect.class);
 

    @Pointcut("execution (* app.gateway.controller.ApiController.*(..)) ")
    private void anyMethod() {
    }
 
    /**
     * 方法调用之前调用
     */
    @Before("anyMethod()")
    public void doBefore(){
        logger.info("开始处理请求信息!");
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
 
        Map<String,String> map = new HashMap<>();
        String username = request.getParameter("username");
        String token = request.getParameter("token");
        
        map.put("username", username);
        map.put("token", token);
        
        //将map放到threadLocal中
        PassParameters.set(map);
    }
 
    /**
     * 方法之后调用
     */
    @AfterReturning(pointcut = "anyMethod()")
    public void  doAfterReturning(){
        
    }
    
}

自定义 RequestInterceptor

 

package app.gateway.config;


import feign.Feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;

import app.gateway.threadLocal.PassParameters;
 
@Configuration
@ConditionalOnClass(Feign.class)
public class DefaultFeignConfig implements RequestInterceptor {
 
    @Value("${spring.application.name}")
    private String appName;
 
    @Override
    public void apply(RequestTemplate requestTemplate)
    {
        Map<String,String> map = PassParameters.get();
        
        String username = map.get("username");
        if(StringUtils.isNotEmpty(username)){
            requestTemplate.header("username", username);
        }
        String token = map.get("token");
        if(StringUtils.isNotEmpty(token)){
            requestTemplate.header("token", token);
        }
        
    }
 
}
 

 

在后端的service,用切面取出保存在head中的参数

package app.service.aop;


import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import app.service.threadLocal.PassParameters;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
 
/**
 * @author hsn
 */
@Aspect
@Component
public class ApiRequestAspect {
    private static Logger logger = LoggerFactory.getLogger(ApiRequestAspect.class);
 

    @Pointcut("execution (* app.service.controller.AppServiceController.*(..)) ")
    private void anyMethod() {
    }
 
    /**
     * 方法调用之前调用
     */
    @Before(value= "anyMethod()")
    public void doBefore(JoinPoint jp){
        logger.info("开始处理请求信息!");
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
 
        Map<String,String> map = new HashMap<>();
        String username =  request.getHeader("username");
        String token = request.getHeader("token");
        
        map.put("username", username);
        map.put("token", token);
        
        //将map放到threadLocal中
        PassParameters.set(map);
    }
 
    /**
     * 方法之后调用
     */
    @AfterReturning(pointcut = "anyMethod()")
    public void  doAfterReturning(){
        
    }
    
}

 

详细代码请参考:https://github.com/hsn999/springCloud/tree/master/springCloud-feign-pass-parameters

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Feign 是一个基于 Java 的 HTTP 客户端库,用于与 RESTful 服务进行交互。在 Feign 中,如果需要在请求之传递数据,通常不推荐直接使用 ThreadLocal,因为 Feign 的请求处理是异步的,每个请求在单独的线程中执行。然而,如果你确实遇到了某种场景需要跨请求共享信息,比如认证令牌,可以考虑以下方法: 1. **使用装饰者模式**:你可以创建一个自定义的 `Interceptor`(拦截器),在每个请求开始前设置 ThreadLocal,然后在拦截器链的回调中获取这个值。 ```java public class MyInterceptor implements ClientRequestInterceptor { private final ThreadLocal<String> threadLocal; public MyInterceptor(String value) { threadLocal = new ThreadLocal<>(); threadLocal.set(value); } @Override public void apply(RequestTemplate requestTemplate) { // 在这里添加你需要的操作,如设置请求头等 // requestTemplate.header("Authorization", threadLocal.get()); } } ``` 使用时,将 `MyInterceptor` 添加到 Feign client 的拦截器列表中: ```java Feign.builder() .client(new OkHttpClient()) .interceptors(Arrays.asList(new MyInterceptor("your_value"))) .target(MyService.class, "http://example.com"); ``` 2. **使用响应拦截器**:对于返回的响应,你也可以设置一个全局的响应拦截器来处理 ThreadLocal 的值。 ```java public class ResponseInterceptor implements ClientResponseInterceptor { @Override public ClientResponse intercept(ClientRequest originalRequest, byte[] originalBody, Response response) { String value = threadLocal.get(); // 对响应进行处理,例如修改响应体或添加头部 return new ClientResponse(response.request(), response.response(), response.headers(), originalBody, new ByteArrayInputStream(response.body())); } } ``` 然后添加到客户端配置: ```java Feign.builder() .client(new OkHttpClient()) .responseInterceptors(Arrays.asList(new ResponseInterceptor())) .target(MyService.class, "http://example.com"); ``` 但是需要注意的是,这种做法可能会导致代码难以理解和维护,因为它绕过了 Feign 的初衷和设计。在大多数情况下,应该优先考虑使用标准的参数传递方式,如请求头、URL 查询参数或请求体。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值