概述
我们在实际开发中,常常用到一些基础参数,比如用户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-基础参数在微服务间的传递所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复