说明

有时候存在这么一种需求,openfeign 调用的时候需要动态变化请求地址

1. 实现拦截

@Slf4j
@Configuration
public class FeignInterceptor implements RequestInterceptor {
 
    @Resource
    private FeignServiceUrlConfig feignServiceUrlConfig;
 
    @Override
    public void apply(RequestTemplate template) {
        Target<?> target = template.feignTarget();
        Map<String, String> urlMap = feignServiceUrlConfig.getUrl();
        if(!CollectionUtils.isEmpty(urlMap) && urlMap.containsKey(target.name())){
            String url = urlMap.get(target.name());
            if (StringUtils.hasLength(url)) {
                log.info("获取的动态 feign 服务配置信息,service-name: {} ,url : {}",target.name(),url);
                template.target(urlMap.get(target.name()));
            }else {
                // 避免 从 URL 到 服务发现的时候调用失败
                Target<?> hardCodedTarget = new Target.HardCodedTarget(target.type(),target.name());
                template.feignTarget(hardCodedTarget);
            }
        }
    }
}

2. 服务配置

@Data
@Configuration
// 配合 nacos 进行动态刷新配置
@RefreshScope
@ConfigurationProperties("feign.service")
public class FeignServiceUrlConfig {
    /**
     * key微服务名,value服务地址
     */
    private Map<String, String> url;
}

3. 覆盖 feign 原有的 SynchronousMethodHandler

主要修改代码

        Response response = null;
        long start = System.nanoTime();
        try {
            Target<?> target = template.feignTarget();
            String url = target.url();
            if (StringUtils.hasLength(url) ) {
                String name = target.name();
                int findIndex = url.indexOf(name);
                // 默认是 url 中包含 name, 默认情况下,url是http://nacos-demo/xxxxm,name是nacos-demo
                if (findIndex > 0) {
                    if (client instanceof FeignBlockingLoadBalancerClient) {
                       // 这里的 FeignBlockingLoadBalancerClient 装饰了最原始的 client 
                        response = ((FeignBlockingLoadBalancerClient)client).getDelegate().execute(request, options);
                    }
                }
            }
            if (Objects.isNull(response)) {
                response = client.execute(request, options);
            }

            // ensure the request is set. TODO: remove in Feign 12
            response = response.toBuilder()
                    .request(request)
                    .requestTemplate(template)
                    .build();
        } catch (IOException e) {
            if (logLevel != Logger.Level.NONE) {
                logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
            }
            throw errorExecuting(request, e);
        }

4. 配置文件

spring:
  application:
    name: springboot-openfeign
  cloud:
    loadbalancer:
      retry:
        enabled: true
      enabled: true
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true
        namespace: 
        username: nacos
        password: nacos
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml
        extension-configs:
          - dataId: springboot-common.yaml
            refresh: true
            group: DEFAULT_GROUP
#        username: ${spring.cloud.nacos.discovery.username}
#        password: ${spring.cloud.nacos.discovery.password}
        namespace: ${spring.cloud.nacos.discovery.namespace}

springboot-common.yaml

demo:
    config: nacos

feign:
  service:
    url:
    # 微服务名
      nacos-demo: http://127.0.0.1:8080 

5. feign 调用


@FeignClient(value = "nacos-demo")
public interface EngineServiceApi {

    /**
     * 获取引擎详情
     * @param param
     * @return
     */
    @PostMapping("/feign/engine/getInfo")
    Object info(@RequestBody EngineIdDTO param);
}