Springcloud分布式服务如何保证会话一致性

  在Springcloud分布式项目中,服务与服务之间调用是非常常见的。有时候服务与服务间调用的时候涉及到用户的身份,比如当前登录的用户的身份获取与传递。

  在之前boot单体应用前后端分离的时候,常见的两种方式,一种是基于session机制+nginx代理;另一种是基于token,也就是每个请求的header中携带token,token的值就是当前登录的标识,可以是后端返回的一个UUID,该UUID对应redis的一个key,每次访问根据UUID从redis获取到当前用户的信息。

1. sessionId不一致问题

  基于feign调用的sessionId不一致问题。

1. 被调用方controller(payment服务)

    @GetMapping("/getSessionId")
    public JSONResultUtil<Map<String, Object>> getSessionId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String id = sessionId();
        System.out.println(id);
        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            System.out.println("====S cookie");
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + "	" + cookie.getValue());
            }
            System.out.println("====E cookie");
        }
        Enumeration<String> headerNames = request.getHeaderNames();
        System.out.println("====S header");
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            System.out.println(s + "	" + request.getHeader(s));
        }
        System.out.println("====E header");

        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("payment", id);
        return JSONResultUtil.successWithData(stringObjectHashMap);
    }

    public String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }    

2.调用方(order服务)

1.Controller

    /************s 测试获取sessionId*********/
    @GetMapping("/getSessionId")
    public JSONResultUtil<Map<String, Object>> getSessionId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String id = sessionId();
        System.out.println(id);
        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            System.out.println("====S cookie");
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + "	" + cookie.getValue());
            }
            System.out.println("====E cookie");
        }
        Enumeration<String> headerNames = request.getHeaderNames();
        System.out.println("====S header");
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            System.out.println(s + "	" + request.getHeader(s));
        }
        System.out.println("====E header");

        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("order", id);
        Map<String, Object> data = paymentHystrixService.getSessionId().getData();
        stringObjectHashMap.putAll(data);
        return JSONResultUtil.successWithData(stringObjectHashMap);
    }

    public String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }
    /************E 测试获取sessionId*********/

2.Feign接口

package cn.qz.cloud.service;

import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.Map;

/**
 * @Author: qlq
 * @Description
 * @Date: 14:31 2020/10/18
 */
@Component
// 只调用,不降级。
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
//@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {

    @GetMapping("/hystrix/payment/getSessionId")
    JSONResultUtil<Map<String, Object>> getSessionId();
}

3. 启动后测试

1.访问接口

liqiang@root MINGW64 /e/IDEAWorkSpace/cloud (master)
$ curl http://localhost:8011//consumer/hystrix/payment/getSessionId
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   139    0   139    0     0   1275      0 --:--:-- --:--:-- --:--:--  1782{"success":true,"code":"200","msg":"","data":{"payment":"30e4e45e-258e-499f-9170-e9c7e0f4acb7","order":"6A261DF8C0CBFB3867FA1DE3A785D7FA"}}

2.order服务日志

6A261DF8C0CBFB3867FA1DE3A785D7FA
====S header
host    localhost:8011
user-agent    curl/7.54.1
accept    */*
====E header

3. payment日志

30e4e45e-258e-499f-9170-e9c7e0f4acb7
====S header
accept    */*
user-agent    Java/1.8.0_121
host    root:8081
connection    keep-alive
====E header

  可以看到两个服务的sessionId不一样。

2. 引入Spring-session解决会话共享-session信息存入redis

用第一个服务payment服务测试。

1.pom增加

        <!--session 共享-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.4.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

2.yml增加配置

spring:
  application:
    name: cloud-provider-hystrix-payment
  redis:
    port: 6379
    host: localhost
  session:
    store-type: redis

  上面关于redis合session的配置也是默认的配置,可以参考:org.springframework.boot.autoconfigure.session.SessionProperties 和 org.springframework.boot.autoconfigure.data.redis.RedisProperties。

3. 修改Controller

    @GetMapping("/getSessionId")
    public JSONResultUtil<Map<String, Object>> getSessionId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();

        // 模拟当前用户登录 zs 存进去
        request.getSession().setAttribute("currentUser", "zs");

        String id = sessionId();
        System.out.println(id);
        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            System.out.println("====S cookie");
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + "	" + cookie.getValue());
            }
            System.out.println("====E cookie");
        }
        Enumeration<String> headerNames = request.getHeaderNames();
        System.out.println("====S header");
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            System.out.println(s + "	" + request.getHeader(s));
        }
        System.out.println("====E header");

        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("payment", id);
        stringObjectHashMap.put("paymentCurrentUser", request.getSession().getAttribute("currentUser"));
        return JSONResultUtil.successWithData(stringObjectHashMap);
    }

    public String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }

4.测试:

1.curl:

$ curl http://localhost:8081/hystrix/payment/getSessionId
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   122    0   122    0     0    748      0 --:--:-- --:--:-- --:--:--   753{"success":true,"code":"200","msg":"","data":{"paymentCurrentUser":"zs","payment":"a8547342-c959-4209-a964-b58f60585a10"}}

2.控制台日志如下:

a8547342-c959-4209-a964-b58f60585a10
====S header
host    localhost:8081
user-agent    curl/7.71.1
accept    */*
====E header

3. 用浏览器访问查看返回的响应头如下:

 可以看到,返回的setcookie中SESSION为:ZmViZjBkM2YtNGQ5Zi00ZGU3LThjZjAtNDE3MzgxNWNmZDhj。 SESSION也是Spring-session存sessionId的name,tomcat默认的为JSESSIONID。

程序控制台日志如下:

febf0d3f-4d9f-4de7-8cf0-4173815cfd8c
====S cookie
sensorsdata2015jssdkcross    %7B%22distinct_id%22%3A%2217608e35e0b169-09416062df57be-51a2f73-1327104-17608e35e0c18d%22%2C%22%24device_id%22%3A%2217608e35e0b169-09416062df57be-51a2f73-1327104-17608e35e0c18d%22%2C%22props%22%3A%7B%22%24latest_referrer%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_referrer_host%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_traffic_source_type%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_search_keyword%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%7D%7D
====E cookie
====S header
accept    text/html, application/xhtml+xml, image/jxr, */*
accept-language    zh-Hans-CN,zh-Hans;q=0.5
user-agent    Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
accept-encoding    gzip, deflate
host    127.0.0.1:8081
connection    Keep-Alive
cookie    sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2217608e35e0b169-09416062df57be-51a2f73-1327104-17608e35e0c18d%22%2C%22%24device_id%22%3A%2217608e35e0b169-09416062df57be-51a2f73-1327104-17608e35e0c18d%22%2C%22props%22%3A%7B%22%24latest_referrer%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_referrer_host%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_traffic_source_type%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%2C%22%24latest_search_keyword%22%3A%22url%E7%9A%84domain%E8%A7%A3%E6%9E%90%E5%A4%B1%E8%B4%A5%22%7D%7D
====E header

注意:

(1)这里发行程序打出的会话ID,sessionId为:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c,而程序返回给设置Cookie头的是:ZmViZjBkM2YtNGQ5Zi00ZGU3LThjZjAtNDE3MzgxNWNmZDhj。其实ZmViZjBkM2YtNGQ5Zi00ZGU3LThjZjAtNDE3MzgxNWNmZDh 是base64 编码之后的,我们经过base64 解码后就是febf0d3f-4d9f-4de7-8cf0-4173815cfd8c,这也是Spring-session 默认做的操作。 可以参考类:org.springframework.session.web.http.DefaultCookieSerializer。 

(2)到redis查看如下:

127.0.0.1:6379> keys *
1) "spring:session:sessions:expires:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c"
2) "spring:session:expirations:1610786700000"
3) "spring:session:sessions:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c"
127.0.0.1:6379> hget spring:session:sessions:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c
(error) ERR wrong number of arguments for 'hget' command
127.0.0.1:6379> keys *
1) "spring:session:sessions:expires:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c"
2) "spring:session:expirations:1610786700000"
3) "spring:session:sessions:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c"
127.0.0.1:6379> hlen spring:session:sessions:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c
(integer) 4
127.0.0.1:6379> hgetall spring:session:sessions:febf0d3f-4d9f-4de7-8cf0-4173815cfd8c
1) "maxInactiveInterval"
2) "xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00a"
3) "lastAccessedTime"
4) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01w
BDs"
5) "creationTime"
6) "xacxedx00x05srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x01w
BDs"
7) "sessionAttr:currentUser"
8) "xacxedx00x05tx00x02zs"

  可以看到存到session中的属性也被存储到redis中

补充:如果想去掉base编码或者修改cookie中session的名称,可以用手动注入一个CookieSerializer,亲测有效

package cn.qz.cloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;

/**
 * @author: 乔利强
 * @date: 2021/1/16 16:29
 * @description:
 */
@Configuration
public class SpringSessionConfiguration {

    @Bean
    public CookieSerializer defaultCookieSerializer() {
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setUseBase64Encoding(false);
        defaultCookieSerializer.setCookieName("mysession");
        return defaultCookieSerializer;
    }
}

测试:访问后返回的响应头如下:

  这种做法是Spring在自动注入的时候留了个入口:参考类:org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration。 自动注入的时候非必须,在代码中判断注入的为null就使用默认配置。

 补充:Spring-session封装了session,同时抽取了操作的工具类

1.接口如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.session;

public interface SessionRepository<S extends Session> {
    S createSession();

    void save(S var1);

    S findById(String var1);

    void deleteById(String var1);
}

2.测试:

    @Autowired
    private SessionRepository sessionRepository;

    @GetMapping("/test")
    public void test() {
        Session byId = sessionRepository.findById(sessionId());
        System.out.println(ToStringBuilder.reflectionToString(byId, ToStringStyle.MULTI_LINE_STYLE));
    }

3.访问后控制台如下:

org.springframework.session.data.redis.RedisIndexedSessionRepository$RedisSession@a2fcd88[
cached=org.springframework.session.MapSession@a2aeb552
originalLastAccessTime=2021-01-16T08:49:30.766Z
delta={}
isNew=false
originalPrincipalName=<null>
originalSessionId=5fddf9ec-53fe-4f73-8205-7d3a3f9431a8
]

3. Order调用者服务配置Feign调用的时候携带请求头,可以做到session共享

1.修改被调用者服务payment

    @GetMapping("/getSessionId")
    public JSONResultUtil<Map<String, Object>> getSessionId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String id = sessionId();

        System.out.println(id);
        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            System.out.println("====S cookie");
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + "	" + cookie.getValue());
            }
            System.out.println("====E cookie");
        }
        Enumeration<String> headerNames = request.getHeaderNames();
        System.out.println("====S header");
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            System.out.println(s + "	" + request.getHeader(s));
        }
        System.out.println("====E header");

        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("payment", id);
        stringObjectHashMap.put("paymentCurrentUser", request.getSession().getAttribute("currentUser"));
        return JSONResultUtil.successWithData(stringObjectHashMap);
    }

    public String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }

2.修改调用者Order

1.修改Controller

    /************s 测试获取sessionId*********/
    @GetMapping("/getSessionId")
    public JSONResultUtil<Map<String, Object>> getSessionId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();

        // 模拟登录成功之后存user信息
        request.getSession().setAttribute("currentUser", "张三");
        String id = sessionId();
        System.out.println(id);

        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("order", id);
        stringObjectHashMap.put("orderCurrentUser", request.getSession().getAttribute("currentUser"));
        Map<String, Object> data = paymentHystrixService.getSessionId().getData();
        stringObjectHashMap.putAll(data);
        return JSONResultUtil.successWithData(stringObjectHashMap);
    }

    public String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }
    /************E 测试获取sessionId*********/

2.修改Feign调用的时候增加header(实际cookie也是在header中传递的,header的key为cookie)

package cn.qz.cloud.config;

import com.google.common.collect.Lists;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
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.*;

/**
 * @author: 乔利强
 * @date: 2020/12/30 20:35
 * @description:
 */
@Configuration
@Slf4j
public class FeignConfiguration {

    public final static String HEADER_CALLER = "cloud_caller";

    public final static String COOKIE_COOKIE_NAME = "cookie";
    public final static String SESSIONID_COOKIE_NAME = "SESSION";

    @Value("${spring.application.name}")
    String application;

    // 对系统中允许后向传递的头进行控制
    private static List<String> disallowhHeaderNames = Lists.newArrayList(
            "cache-control"
            , "origin"
            , "upgrade-insecure-requests"
            , "user-agent"
            , "referer"
            , "accept"
            , "accept-language"
            , "connection"
    );

    @Bean
    public RequestInterceptor requestHeaderInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                //记录调用方信息
                requestTemplate.header(HEADER_CALLER, application);
                if (RequestContextHolder.getRequestAttributes() == null) {
                    return;
                }

                // 注意子线程问题(RequestContextHolder 内部使用的是ThreadLocal)
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getRequest();
                // 当前请求携带的header
                Enumeration<String> headerNames = request.getHeaderNames();
                // 已经存在feign的requestTemplate的header
                Map<String, Collection<String>> headers = requestTemplate.headers();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        boolean exists = false; // 避免重复header往后传播,如content-type传播会影响后续body解析
                        for (String th : headers.keySet()) {
                            if (name.equalsIgnoreCase(th)) {// 忽略大小写
                                exists = true;
                                break;
                            }
                        }
                        if (!exists && !disallowhHeaderNames.contains(name.toLowerCase())) {
                            requestTemplate.header(name, Collections.list(request.getHeaders(name)));
                        }
                    }
                }
            }
        };
    }

    private String sessionId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return request.getSession().getId();
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * 自定义feignlogger
     *
     * @return
     */
    @Bean
    public feign.Logger feignLogger() {
        return new FeignLogger();
    }
}

3.测试

1. 访问http://localhost:8011/consumer/hystrix/payment/getSessionId, 返回结果如下:

{"success":true,"code":"200","msg":"","data":{"orderCurrentUser":"张三","paymentCurrentUser":"张三","payment":"53972a2b-9324-4078-8446-268e13071f06","order":"53972a2b-9324-4078-8446-268e13071f06"}}

2.查看控制台日志如下:

(1)order 调用者

53972a2b-9324-4078-8446-268e13071f06
2021-01-16 23:06:18.022  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] GET http://CLOUD-PROVIDER-HYSTRIX-PAYMENT/hystrix/payment/getSessionId HTTP/1.1, log request begin. <---
2021-01-16 23:06:18.023  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] ---> GET http://CLOUD-PROVIDER-HYSTRIX-PAYMENT/hystrix/payment/getSessionId HTTP/1.1
2021-01-16 23:06:18.023  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] accept-encoding: gzip, deflate, br
2021-01-16 23:06:18.024  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] cloud_caller: order80
2021-01-16 23:06:18.024  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] cookie: Idea-599589ea=7e4185c4-b2de-45e1-9f54-6ae34bfbe678; Webstorm-e34e7d83=831b2048-d1c1-45e6-b4e2-77d667e3f1c4; Hm_lvt_b393d153aeb26b46e9431fabaf0f6190=1605535026; SESSION=NTM5NzJhMmItOTMyNC00MDc4LTg0NDYtMjY4ZTEzMDcxZjA2
2021-01-16 23:06:18.025  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] host: localhost:8011
2021-01-16 23:06:18.025  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] sec-fetch-dest: document
2021-01-16 23:06:18.025  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] sec-fetch-mode: navigate
2021-01-16 23:06:18.026  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] sec-fetch-site: none
2021-01-16 23:06:18.026  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] sec-fetch-user: ?1
2021-01-16 23:06:18.027  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] ---> END HTTP (0-byte body)
2021-01-16 23:06:18.028  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] GET http://CLOUD-PROVIDER-HYSTRIX-PAYMENT/hystrix/payment/getSessionId HTTP/1.1, log request end. <---
2021-01-16 23:06:18.089  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] 200 HTTP/1.1 (59ms) log logAndRebufferResponse begin. <---
2021-01-16 23:06:18.091  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] <--- HTTP/1.1 200 (59ms)
2021-01-16 23:06:18.091  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] connection: keep-alive
2021-01-16 23:06:18.092  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] content-type: application/json
2021-01-16 23:06:18.092  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] date: Sat, 16 Jan 2021 15:06:18 GMT
2021-01-16 23:06:18.093  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] keep-alive: timeout=60
2021-01-16 23:06:18.094  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] transfer-encoding: chunked
2021-01-16 23:06:18.094  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] 
2021-01-16 23:06:18.096  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] {"success":true,"code":"200","msg":"","data":{"paymentCurrentUser":"张三","payment":"53972a2b-9324-4078-8446-268e13071f06"}}
2021-01-16 23:06:18.097  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] <--- END HTTP (126-byte body)
2021-01-16 23:06:18.098  INFO 16260 --- [nio-8011-exec-5] cn.qz.cloud.config.FeignLogger           : [PaymentHystrixService#getSessionId] 200 HTTP/1.1 (59ms) log logAndRebufferResponse end. <---

(2) payment 被调用者服务日志如下:

53972a2b-9324-4078-8446-268e13071f06
====S cookie
Idea-599589ea    7e4185c4-b2de-45e1-9f54-6ae34bfbe678
Webstorm-e34e7d83    831b2048-d1c1-45e6-b4e2-77d667e3f1c4
Hm_lvt_b393d153aeb26b46e9431fabaf0f6190    1605535026
SESSION    NTM5NzJhMmItOTMyNC00MDc4LTg0NDYtMjY4ZTEzMDcxZjA2
====E cookie
====S header
accept-encoding    gzip, deflate, br
cloud_caller    order80
cookie    Idea-599589ea=7e4185c4-b2de-45e1-9f54-6ae34bfbe678; Webstorm-e34e7d83=831b2048-d1c1-45e6-b4e2-77d667e3f1c4; Hm_lvt_b393d153aeb26b46e9431fabaf0f6190=1605535026; SESSION=NTM5NzJhMmItOTMyNC00MDc4LTg0NDYtMjY4ZTEzMDcxZjA2
accept    */*
user-agent    Java/1.8.0_121
host    root:8081
connection    keep-alive
====E header

  至此实现了Cloud分布式项目Feign调用共享session,其实就是传递header,同时服务调用者与被调用者服务共享header中的session标识。基于Token共享方式也是这样的思路。

【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
原文地址:https://www.cnblogs.com/qlqwjy/p/14279958.html