SoFunction
Updated on 2025-03-06

SpringCloud Feign forwarding request header (preventing session failure) solution

There are often such needs in microservice development. The company customizes the general request header and needs to forward it in the microservice call chain, such as adding a token or a custom information uniqueId to the request header. In short, it is a custom key-value pair. Service A calls service B, service B calls service C. How can such a general thing be passed continuously in a call chain? Take Service A as an example:

Plan 1

The stupidest way is to get it in the program, forward it when calling B. How to get it in Controller China through annotations, or through request objects. This is not difficult. When requesting B service, just put the value in through annotations; the simple code is as follows:
Get:
@RequestMapping(value = "/api/test", method = )
public String testFun(@RequestParam String name, @RequestHeader("uniqueId") String uniqueId) {
  if(uniqueId == null ){
     return "Must defined the uniqueId , it can not be null";
  }
  (uniqueId, "begin testFun... ");
 return uniqueId;
}

Then when A uses Feign to call B service, it passes over:

@FeignClient(value = "DEMO-SERVICE")
public interface CallClient {

  /**
  * Access the /api/test interface of the DEMO-SERVICE service, and pass logId to the downstream service through annotations
  */
 @RequestMapping(value = "/api/test", method = )
  String callApiTest(@RequestParam(value = "name") String name, @RequestHeader(value = "uniqueId") String uniqueId);

}

Solution drawbacks: There is no doubt that this solution is not good because it has intrusion into the code and requires developers to manually obtain and add it, so they are discarded.

Plan 2

The service uses the request interceptor to add what it needs to the request header after the request is sent from A to B:
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;

/**
  * Custom request header processing class, processing request header when service is sent;
  * Take out the uniqueId and token fields in the request header received by the service, and set it to the new request header to forward it to the downstream service.
  * For example, service A receives a request, and the request header contains uniqueId and token fields. When A processes, service B will be called using Feign client.
  * Then the two fields uniqueId and token will be added to the request header and sent to service B;
  *
  * @author mozping
  * @version 1.0
  * @date 2018/6/27 14:13
  * @see FeignHeadConfiguration
  * @since JDK1.8
  */
@Configuration
public class FeignHeadConfiguration {
  private final LoggerUtilI logger = (().getName());

  @Bean
  public RequestInterceptor requestInterceptor() {
    return requestTemplate -> {
      ServletRequestAttributes attrs = (ServletRequestAttributes) ();
      if (attrs != null) {
        HttpServletRequest request = ();
        Enumeration<String> headerNames = ();
        if (headerNames != null) {
          while (()) {
            String name = ();
            String value = (name);
            /**
              * traverse the attribute fields in the request header, add logId and token to the new request header and forward to the downstream service
              * */
            if ("uniqueId".equalsIgnoreCase(name) || "token".equalsIgnoreCase(name)) {
              ("Add a custom request header key:" + name + ",value:" + value);
              (name, value);
            } else {
              ("FeignHeadConfiguration", "Non-custom request header key:" + name + ",value:" + value + "No need to add!");
            }
          }
        } else {
          ("FeignHeadConfiguration", "Failed to get the request header!");
        }
      }
    };
  }

}

There are many blog posts or materials about this method on the Internet, which are similar, but there is a problem. After turning on the fuse, the attrs here is null, because the default isolation strategy of the fuse is thread, that is, thread isolation. In fact, the received object and this are sent to B are not the same thread. What should I do? There is a way to modify the isolation strategy = SEMAPHORE and change it to the semaphore isolation mode, but it is not recommended, because thread is the default, and the fatal one is the semaphore mode. The fuse does not take effect. For example, the fuse time = 5000, five seconds is set. If the B service sleeps for 10 seconds, you have to wait until B is executed before returning. Therefore, this solution is not acceptable; but is there any way to let the interceptor get the request header of the upstream service in the default Thread mode? Custom policy: The code is as follows:

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import org.;
import org.;
import ;
import ;
import ;

import ;
import ;
import ;
import ;

/**
  * Customize Feign's isolation policy;
  * When forwarding Feign's request header, if Hystrix is ​​enabled, Hystrix's default isolation strategy is Thread (thread isolation policy). Therefore, the request header information of the request cannot be obtained in the forwarding interceptor. You can modify the default isolation strategy to be semaphore mode: =SEMAPHORE. In this way, the forwarding thread and the request thread are actually the same thread. This is not the best solution, and the semaphore mode is not the most recommended isolation strategy by the official; another solution is to customize Hystrix's isolation strategy. The idea is to use the existing concurrency strategy as a member variable of the new concurrency strategy. In the new concurrency strategy, return the thread pool and Queue of the existing concurrency strategy; add the policy to the Spring container;
  *
  * @author mozping
  * @version 1.0
  * @date 2018/7/5 9:08
  * @see FeignHystrixConcurrencyStrategyIntellif
  * @since JDK1.8
  */
@Component
public class FeignHystrixConcurrencyStrategyIntellif extends HystrixConcurrencyStrategy {

  private static final Logger log = ();
  private HystrixConcurrencyStrategy delegate;

  public FeignHystrixConcurrencyStrategyIntellif() {
    try {
       = ().getConcurrencyStrategy();
      if ( instanceof FeignHystrixConcurrencyStrategyIntellif) {
        // Welcome to singleton hell...
        return;
      }
      HystrixCommandExecutionHook commandExecutionHook =
          ().getCommandExecutionHook();
      HystrixEventNotifier eventNotifier = ().getEventNotifier();
      HystrixMetricsPublisher metricsPublisher = ().getMetricsPublisher();
      HystrixPropertiesStrategy propertiesStrategy =
          ().getPropertiesStrategy();
      (eventNotifier, metricsPublisher, propertiesStrategy);
      ();
      ().registerConcurrencyStrategy(this);
      ().registerCommandExecutionHook(commandExecutionHook);
      ().registerEventNotifier(eventNotifier);
      ().registerMetricsPublisher(metricsPublisher);
      ().registerPropertiesStrategy(propertiesStrategy);
    } catch (Exception e) {
      ("Failed to register Sleuth Hystrix Concurrency Strategy", e);
    }
  }

  private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
                         HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
    if (()) {
      ("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
          +  + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
          + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
      ("Registering Sleuth Hystrix Concurrency Strategy.");
    }
  }

  @Override
  public <T> Callable<T> wrapCallable(Callable<T> callable) {
    RequestAttributes requestAttributes = ();
    return new WrappedCallable<>(callable, requestAttributes);
  }

  @Override
  public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                      HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize,
                      HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    return (threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
        unit, workQueue);
  }

  @Override
  public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                      HystrixThreadPoolProperties threadPoolProperties) {
    return (threadPoolKey, threadPoolProperties);
  }

  @Override
  public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
    return (maxQueueSize);
  }

  @Override
  public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
    return (rv);
  }

  static class WrappedCallable<T> implements Callable<T> {
    private final Callable<T> target;
    private final RequestAttributes requestAttributes;

    public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
       = target;
       = requestAttributes;
    }

    @Override
    public T call() throws Exception {
      try {
        (requestAttributes);
        return ();
      } finally {
        ();
      }
    }
  }
}

Then, using the default fuse isolation strategy, you can also obtain the request header information of the upstream service in the interceptor;
The blog referenced here, thanks to this big guy: /Crystalqy/article/details/79083857

This is the end of this article about the solution of SpringCloud Feign forwarding request header (preventing session failure). For more related SpringCloud Feign forwarding request header content, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!