您觉得本文档还缺少什么内容?可以自己补充~

某些情况下,后台服务需要互相调用,比如: 在订单服务创建订单时,需要调用用户服务的用户扣减积分等场景。 这里我们就讨论几种类型的远程调用和解决方案:

  1. @RequestBody 类型参数调用
  2. @RequestParam 标注的的普通类型的参数调用
  3. 没有任何注解的参数 调用
  4. 跨服务文件上传
  5. 透传请求头和线程变量

演示各种类型的参数配置

@FeignClient(name = "${bc.feign.demo-server:bc-demo-server}", path = "/date")
public interface TestDateApi {
    @PostMapping("/post1")
    R<DateDTO> bodyPos1(@RequestBody DateDTO data);
// 调用这个接口会报错,原因是FeignClient 不支持GET请求,传复杂对象
    @GetMapping("/get1")
    R<DateDTO> get(DateDTO data);
    @GetMapping("/get2")
    R<DateDTO> get2(@RequestParam(required = false, value = "date") Date date,
                    @RequestParam(required = false, value = "dt") LocalDateTime dt,
                    @RequestParam(required = false, value = "d") LocalDate d,
                    @RequestParam(required = false, value = "t") LocalTime t);
}

远程调用时,如何透传请求头和线程变量?

由于项目中使用的是ThreadLocal在存储当前用户信息,但出现跨线程请求时,就无法获取用户身份了,但使用FeignClient进行远程调用时,默认会新启动一个线程继续调用,故而feign的被调用方和调用方就不在同一个线程,所有被调用方无法从ContextUtil中获取用户信息。 具体实现参考:bc-cloud-starter 模块的 FeignAddHeaderRequestInterceptor。 原理如下:

FeignAddHeaderRequestInterceptor 拦截器,将线程变量中的信息再次封装套请求头

@Slf4j
public class FeignAddHeaderRequestInterceptor implements RequestInterceptor {

    public static final List<String> HEADER_NAME_LIST = Arrays.asList(
            ContextConstants.JWT_KEY_TENANT, ContextConstants.JWT_KEY_USER_ID,
            ContextConstants.JWT_KEY_ACCOUNT, ContextConstants.JWT_KEY_NAME, ContextConstants.GRAY_VERSION,
            ContextConstants.TRACE_ID_HEADER, "X-Real-IP", "x-forwarded-for"
    );

    public FeignAddHeaderRequestInterceptor() {
        super();
    }

    @Override
    public void apply(RequestTemplate template) {
        String xid = RootContext.getXID();
        if (StrUtil.isNotEmpty(xid)) {
            template.header(RootContext.KEY_XID, xid);
        }

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            HEADER_NAME_LIST.forEach((headerName) -> template.header(headerName, ContextUtil.get(headerName)));
            return;
        }

        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        if (request == null) {
            log.warn("path={}, 在FeignClient API接口未配置FeignConfiguration类, 故而无法在远程调用时获取请求头中的参数!", template.path());
            return;
        }
        HEADER_NAME_LIST.forEach((headerName) -> {
            String header = request.getHeader(headerName);
            template.header(headerName, ObjectUtil.isEmpty(header) ? ContextUtil.get(headerName) : header);
        });
    }
}

results matching ""

    No results matching ""