我是靠谱客的博主 朴素大地,最近开发中收集的这篇文章主要介绍SpringCloud-基础参数在微服务间的传递,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

        我们在实际开发中,常常用到一些基础参数,比如用户ID、终端版本等,在单体服务中controller层和service层都可以随时取用,基本上是用ThreadLocal实现的,相当方便。但是在微服务中相互调用时,JVM不是一个,甚至不到一台机器上,ThreadLocal肯定不能满足要求,如果都加到参数里传递,和业务参数又不能分离,那我们应该怎么处理呢?如果使用SpringCloud,把参数放到Header里就可以。

首先要有一个UserContext存储基础参数,其内部也是ThreadLocal实现的

package ai.peanut.common.aspects;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class UserContext {

    public static final ThreadLocal<Map<String,Object>> contextData = new ThreadLocal<>();
    
    public static final String X_USER = "xUser";

    public static final String MEMBER_ID = "memberId";

    public static final String MOBILE = "mobile";
    
    public static final String SHOP_ID = "shopId";
    
    public static final String DEVICE_ID = "deviceId";

    public static final String X_PLATFORM = "xPlatform";

    public static final String X_VERSION = "xVersion";

    public static final String X_DIRECT_INVOKER = "xDirectInvoker";

    public static String getXUser() {
        return getStringValue(X_USER);
    }
    public static void putXUser(String value, String platfrom) {
        contextData.get().put(X_USER, value);
        parseJson(value, platfrom);
    }

    public static Long getMemberId() {
    	return getLongValue(MEMBER_ID);
    }
    public static void putMemberId(Object value) {
        contextData.get().put(MEMBER_ID, value);
    }
    
    public static String getMobile() {
    	return getStringValue(MOBILE);
    }
    public static void putMobile(Object value) {
        contextData.get().put(MOBILE, value);
    }
    
    public static Long getShopId() {
    	return getLongValue(SHOP_ID);
    }
    public static void putShopId(Object value) {
        contextData.get().put(SHOP_ID, value);
    }
    
    public static Long getDeviceId() {
    	return getLongValue(DEVICE_ID);
    }
    public static void putDeviceId(Object value) {
        contextData.get().put(DEVICE_ID, value);
    }

    public static String getPlatform() {
        return getStringValue(X_PLATFORM);
    }
    public static void putPlatform(Object value) {
        contextData.get().put(X_PLATFORM, value);
    }

    public static String getDirectInvoker() {
        return getStringValue(X_DIRECT_INVOKER);
    }
    public static void putDirectInvoker(Object value) {
        contextData.get().put(X_DIRECT_INVOKER, value);
    }

    public static String getVersion() {
        return getStringValue(X_VERSION);
    }
    public static void putVersion(Object value) {
        contextData.get().put(X_VERSION, value);
    }
    
    public static void put(String key, Object value) {
    	contextData.get().put(key, value);
    }
    
    public static void init() {
    	contextData.set(new HashMap<>());
    }
    
    public static void remove() {
    	contextData.remove();
    }
    
    
    private static Long getLongValue(String key) {
    	if(contextData.get() == null) return null;
    	Object value = contextData.get().get(key);
    	if(value == null) {
    		return null;
    	}
    	
    	return (Long)value;
    }
    
    private static String getStringValue(String key) {
    	if(contextData.get() == null) return null;
    	Object value = contextData.get().get(key);
    	if(value == null) {
    		return null;
    	}
    	
    	return (String)value;
    }

    public static void parseJson(String jsonStr, String platform) {
        try {
            jsonStr = jsonStr.replace("\", "");
            jsonStr= jsonStr.substring(1, jsonStr.length()-1);
            JSONObject userJson = JSON.parseObject(jsonStr);
            //add by shenghua.hu修改为通用返回对象
            try{
                putMemberId(Long.valueOf(userJson.get("memberId").toString()));
                putMobile(userJson.get("mobile"));
                } catch (Exception ex){
                    putMemberId(0L);
                    putMobile("13888886666");
                }
        } catch (Exception e) {
            System.out.println("header里x-user解析错误:x-user=" + jsonStr + e.getMessage());
        }
    }

    
}

其次,请求上要加一个切面,收到请求之后要把前一个服务携带在Header的参数解析出来

package ai.peanut.common.aspects;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

/**
 * @author fei
 */
@Aspect
@Order(85)
@Component
public class UserRequestAspect {
	private static Logger logger = LoggerFactory.getLogger(UserRequestAspect.class);

	@Pointcut("(@target(org.springframework.web.bind.annotation.RestController)) && (execution(public * ai.peanut..*.*(..)))")
	public void executionService() {
	}

	/**
	 * 方法调用之前调用
	 */
	@Before("executionService()")
	public void doBefore(){
		logger.info("开始处理请求头部信息!");
		UserContext.init();
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();

		// 系统码
		String platform = request.getHeader("x-platform");
		if(StringUtils.isNotEmpty(platform)){
			UserContext.putPlatform(platform);
		}

		// 直接调用方,上级服务
		String directInvoker = request.getHeader("x-direct-invoker");
		if(StringUtils.isNotEmpty(directInvoker)){
			UserContext.putDirectInvoker(directInvoker);
		}

		// 通用用户信息
		String userInfo = request.getHeader("x-user");
		try {
			if (userInfo != null) {
				userInfo = URLDecoder.decode(userInfo, "UTF-8");//解码
			}
		} catch (UnsupportedEncodingException e) {
			logger.info("处理通用用户信息时,发生错误");
			userInfo = "{}";
		}
		if(StringUtils.isNotEmpty(userInfo)){
			UserContext.putXUser(userInfo, platform);
		}

		// 客户端版本
		String version = request.getHeader("x-version");
		if(StringUtils.isNotEmpty(version)){
			UserContext.putVersion(version);
		}
	}

	/**
	 * 方法之后调用
	 */
	@AfterReturning(pointcut = "executionService()")
	public void  doAfterReturning(){
		UserContext.remove();
	}
	
}

这样收到的参数就能完好地解析并保存起来,但是调用下一个服务的时候怎么传递出去呢,Feign里其实已经有实现了,我们只需要添加一个Feign的配置就可以。

package ai.peanut.common.conf;

import ai.peanut.common.aspects.UserContext;
import feign.Feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(Feign.class)
public class DefaultFeignConfig implements RequestInterceptor {

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

    @Override
    public void apply(RequestTemplate requestTemplate)
    {
        String traceId = MDC.get("traceID");
        if(StringUtils.isNotEmpty(traceId)){
            requestTemplate.header("x-trace-id", traceId);
        }
        String xUser = UserContext.getXUser();
        if(StringUtils.isNotEmpty(xUser)){
            requestTemplate.header("x-user", xUser);
        }
        String platform = UserContext.getPlatform();
        if(StringUtils.isNotEmpty(platform)){
            requestTemplate.header("x-platform", platform);
        }
        String version = UserContext.getVersion();
        if(StringUtils.isNotEmpty(version)){
            requestTemplate.header("x-version", version);
        }
        if(StringUtils.isNotEmpty(appName)){
            requestTemplate.header("x-direct-invoker", appName);
        }
    }

}

这样参数就会放到Header里传递到下一个服务,并且完成了接收参数--解析参数--存储参数--传输参数的循环,不管一次请求调用多少个服务,都可以用UserContext随时取用了。

如取终端版本,调用 String version = UserContext.getVersion(); 即可。

 

 

 

最后

以上就是朴素大地为你收集整理的SpringCloud-基础参数在微服务间的传递的全部内容,希望文章能够帮你解决SpringCloud-基础参数在微服务间的传递所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部