写点什么

你必须懂也可以懂的微服务系列三:服务调用

  • 2022 年 4 月 21 日
  • 本文字数:3613 字

    阅读完需:约 12 分钟

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;


}


复制代码


每次先从集合中取,如果集合中没有对应的子容器则进行创建,然后让容器中注册了PropertyPlaceholderAutoConfigurationthis.defaultConfigType



通过FeignContext的构造函数,我们可以了解到this.defaultConfigType就是FeignClientsConfiguration


打开FeignClientsConfiguration可以看到里面声明了EncoderDecoderContractBean

3.8 小结

到这里脑海中应该有个大概的总结:


  • @FeignClient对应FeignClientFactoryBean

  • 注入@FeignClient标注的接口,实际上注入的是FeignClientFactoryBeangetObject()返回的对象

  • 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.FeignInvocationHandlerinvoke方法

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 来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。




篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)




由于内容太多,这里只截取部分的内容。

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
你必须懂也可以懂的微服务系列三:服务调用_Java_爱好编程进阶_InfoQ写作社区