你必须懂也可以懂的微服务系列三:服务调用
3.6
章节中this.get(context, xxxx.class))
这个方法频繁出现,需要了解一下该方法具体实现,此处以this.get(context, FeignLoggerFactory.class)
为例
protected <T> T get(FeignContext context, Class<T> type) {
T instance = context.getInstance(this.contextId, type);
if (instance == null) {
throw new IllegalStateException("No bean found of type " + type + " for " + this.contextId);
} else {
return instance;
}
}
复制代码
在
3.5
章节中介绍过FeignContext
,其维护了一个子容器集合,因此首先会先从子容器集合中获取指定名称的子容器
既然FeignContext
维护了子容器集合,那么就必须了解子容器是如何创建的
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
context.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
复制代码
每次先从集合中取,如果集合中没有对应的子容器则进行创建,然后让容器中注册了
PropertyPlaceholderAutoConfiguration
和this.defaultConfigType
通过
FeignContext
的构造函数,我们可以了解到this.defaultConfigType
就是FeignClientsConfiguration
打开
FeignClientsConfiguration
可以看到里面声明了Encoder
、Decoder
、Contract
等Bean
3.8 小结
到这里脑海中应该有个大概的总结:
@FeignClient
对应FeignClientFactoryBean
注入
@FeignClient
标注的接口,实际上注入的是FeignClientFactoryBean
中getObject()
返回的对象FeignContext
做为feign
的上下文,为每个@FeignClient
创建一个子容器,子容器中声明所需要的Bean
之所以为每个
@FeignClient
声明一个子容器,是会了让@FeignClient
与@FeignClient
的配置进行隔离
3.9 创建 Feign 实例
之前通过Feign Builder
链式方式设置了相关属性,现在就可以通过Feign Builder
来创建Feign
实例
public Feign build() {
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptor Java 开源项目【ali1024.coding.net/public/P7/Java/git】 s.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
// 1.InvocationHandlerFactory 用于创建 InvocationHandler
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
// 2.SynchronousMethodHandler 用于创建 SynchronousMethodHandler
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
复制代码
3.10 创建代理
@Override
public <T> T newInstance(Target<T> target) {
// 1.遍历 @FeignClient 标注接口中的方法,通过 SynchronousMethodHandler.Factory 创建 MethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
// 2. 构建 Method 与 MethodHandler 的映射关系,用于方法路由
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 3. 通过 InvocationHandlerFactory 创建 InvocationHandler
InvocationHandler handler = factory.create(target, methodToHandler);
// 4. 生成代理对象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
复制代码
到此我们可以得知
@Autowired
private IndexClient indexCl 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 ient;
复制代码
注入的是一个代理对象,当调用
IndexClient
方法的时候,会回调ReflectiveFeign.FeignInvocationHandler
的invoke
方法
3.11 方法调用
@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);
}
复制代码
如上根据方法路由到对应的
SynchronousMethodHandler
,然后调用其invoke()
@Override
public Object invoke(Object[] argv) throws Throwable {
// 1.构建请求模板
总目录展示
该笔记共八个节点(由浅入深),分为三大模块。
高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这 4 个方面重点介绍。
一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。
高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个 PlanB 来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。
篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)
由于内容太多,这里只截取部分的内容。
评论