订单服务
- 电商系统涉及到3流,分别是信息流、资金流、物流,而订单系统作为中枢将三者有机的集合起来。
订单模块是电商系统的枢纽,在订单这个环节上需要获取多个模块的数据和信息,同时对这些信息进行加工处理后流向下个环节,这一系列就构成了订单的信息流通。 - 幂等性
- feign远程调用丢失请求属性问题
-在调用feign方法时会调用feign底层的实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
dispatch.get(method).invoke(args);继续进去
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
return executeAndDecode(template, options);
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
这里对我们的远程请求做了处理
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
feign会对将容器里存在的所有RequestInterceptor处理原生的请求,将处理之后的请求返回,如果容器里没有RequestInterceptor处理请求,相当于把原生的请求属性丢失。
可以在容器里定义一个RequestInterceptor处理请求
5. feign远程异步调用丢失请求属性问题
@Configuration
public class GlFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
// Feign在远程调用之前都会先经过这个方法
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// RequestContextHolder拿到刚进来这个请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes != null){
HttpServletRequest request = attributes.getRequest();
if(request != null){
// 同步请求头数据
String cookie = request.getHeader("Cookie");
// 给新请求同步Cookie
template.header("Cookie", cookie);
}
}
}
};
}
}
由于自定义的RequestInterceptor获取原生请求要用到RequestContextHolder
public abstract class RequestContextHolder {
private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
RequestContextHolder底层使用ThreadLocal获取原来的请求对象
当feign异步时,导致只有主线程的ThreadLocal有原生的请求属性,异步任务的线程的ThreadLocal没有请求属性,就会导致获取不到session,判断不了用户状态