feign服务间调用session变更(失效)问题

在springcloud项目中,发现在服务之间的fegin调用时,session无法有效传递。我项目中用到了websocket,需要session保持一致性才能建立长连接。因此,需要保证fegin调用session的一致性——即服务1(user服务)调用服务2(teacher服务),服务1和服务2的sessionid一致。下面谈谈如何解决这个问题。

这是我在参考了大佬的写法之后自己整理的笔记

大佬原文:https://blog.csdn.net/zl1zl2zl3/article/details/79084368

解决步骤:

1,搭建分布式redis

1.1  服务1和服务2都引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

1.2在服务1和服务2的启动类上都加上@EnableRedisSession注解(这里只粘贴一个做示例)

@EnableFeignClients
@EnableDiscoveryClient
@EnableRedisHttpSession
@SpringBootApplication
@MapperScan("com.o2o.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
}

 1.3.两个服务都需要添加redis的配置

spring:
  redis:
    host: ***.***.***.***
    port: 6379
    password: "(没有设置不用填)"

2.在zuul网关服务中,为保证网关转发请求session的一致,需要修改zuul的配置

zuul:

 sensitive-headers: "*"

3.通过实现RequestInterceptor接口来实现Header的传递

package com.o2o.user.client;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@Configuration
@EnableFeignClients
public class FeignClientsConfigurationCustom implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return;
        }

        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                Enumeration<String> values = request.getHeaders(name);
                while (values.hasMoreElements()) {
                    String value = values.nextElement();
                    template.header(name, value);
                }
            }
        }
    }
}

 4.服务消费者Feign自定义策略

package com.o2o.user.config;

import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

  private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class);
  private HystrixConcurrencyStrategy delegate;

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

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

  @Override
  public <T> Callable<T> wrapCallable(Callable<T> callable) {
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    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 this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
            unit, workQueue);
  }

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

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

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

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

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

    @Override
    public T call() throws Exception {
      try {
        RequestContextHolder.setRequestAttributes(requestAttributes);
        return target.call();
      } finally {
        RequestContextHolder.resetRequestAttributes();
      }
    }
  }
}  

5.在服务消费者启动类里注入策略

package com.o2o;

import com.o2o.user.config.FeignHystrixConcurrencyStrategy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import tk.mybatis.spring.annotation.MapperScan;

@EnableFeignClients
@EnableDiscoveryClient
@EnableRedisHttpSession
@SpringBootApplication
@MapperScan("com.o2o.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
    @Bean
    public FeignHystrixConcurrencyStrategy feignHystrixConcurrencyStrategy() {
        return new FeignHystrixConcurrencyStrategy();
    }
}

测试截图:

服务1(user):

服务2(teacher):

二者的sessionid相同,目的达到

发布了3 篇原创文章 · 获赞 0 · 访问量 912

猜你喜欢

转载自blog.csdn.net/weixin_42661168/article/details/100732130