zoukankan      html  css  js  c++  java
  • 微服务通信之feign的配置隔离

    前言

    由上文我们知道针对某一个Feign接口,我们可以给他设置特定的配置类。那如果现在有一个服务,我们只想对A服务配置一个拦截器拦截请求而不影响其他服务,那应该怎么做呢?

    一、feign接口配置

    由前面的文章我们知道了feign的代理过程以及调用过程。现在我们看一下feign都有哪些配置?

    • @FeignClient可配置属性列表
    属性 值类型 含义
    value string 服务名,无论是否有URL,服务名称都不能为空,它可以设置为属性值${server_name}这种形式
    name string 同value
    qualifier string 指定feign接口在容器中的bean实例名称(我们知道每一个feign接口最终都会向容器注入一个实例),等同于@Qualifier
    url string 绝对URL或可解析主机名(协议是可选的)
    decode404 boolean 是否应该解码404而不是抛出假异常
    fallback class 指定一个会退类(比如发生未处理的异常后)。回退类必须实现feign接口,并注册到容器。
    fallbackFactory class 为指定的外部客户机接口定义回退工厂。回退工厂必须实现FallbackFactory接口,此工厂接口返回一个feign接口对应的回滚类。回退工厂必须注册到容器。
    path string 指定相对路径
    primary boolean,默认true 当容器中存在同名的bean的实现,以当前代理对象的bean为主。
    configuration class 当前feign接口的配置类,他会将配置类注册到当前feign对象的容器中(非应用程序的上下文环境)
    • 配置是何时被加载的

    这里直接上核心代码(FeignClientsRegistrar.registerClientConfiguration)

            // name @FeignClient的name属性值
            // configuration @FeignClient的class对象
    	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
    			Object configuration) {
    		BeanDefinitionBuilder builder = BeanDefinitionBuilder
    				.genericBeanDefinition(FeignClientSpecification.class);
    		builder.addConstructorArgValue(name);
    		builder.addConstructorArgValue(configuration);
    		registry.registerBeanDefinition(
    				name + "." + FeignClientSpecification.class.getSimpleName(),
    				builder.getBeanDefinition());
    	}
    
    

    由上面的代码我们知道,他是将@FeignClient对象的configuration属性对应的配置类作为成员FeignClientSpecification的属性注入到应用上下文。那看到这里我们就会产生一个疑问:configuration对应的配置类到底什么时候被初始化呢?最终发现在为@FeignClient生成代理对象的时候,他初始化了FeignClient对应的配置。如下(NamedContextFactory.createContext):

    
    // 在获取一个@FeignClient接口对应的代理对象前,先为其创建Context对象
    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注册环境变量  
    		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;
    	}
    
    

    显然,在为@FeignClient接口的代理对象创建了Context后,将配置初始化到了该Context。这里可以明确的是每一个@FeignClient只要name属性不一致(2.1版本以后可以手动指定contextId),那么他们将拥有不同的上下文。

    二、feign调用的沙箱设计思路

    在现实中的sandbox,是一种儿童玩具,类如KFC中一个装满小球的容器,儿童可以在随意玩耍,起到保护儿童的作用。(也可以理解为一种安全环境)。同样在网络技术中也是一种按照安全策略限制程序行为的执行环境。在feign的调用过程中,针对不同的服务为其创建一个上下文,我们可以将这个上下文理解为sandbox。执行调用所需要的资源是从各自的沙箱环境中取。整体的职责分工如下图所示。

    从中我们可以看到,我们最终调用的执行器来源是来自沙箱,而沙箱的配置可以各不相同,创建不同的Client,这给了我们决定不同的服务怎样去调用高度的自主权。也解决了文章开篇提出的问题。

    三、小结

    至此,feign调用暂时告一段落。由于后续要花时间做一个开源框架,文章的书写便只能告一段落了。

  • 相关阅读:
    PCLVisualizer可视化类
    【Leetcode】课程表(拓扑排序)
    不是充许的静态以太网地址,它与vmware保留的mac地址冲突
    vmware虚拟机linux添加硬盘后先分区再格式化操作方法
    卸载mysql时,提示libmysqlclient.so.16()(64bit) is needed by (installed) postfix
    创建mysql数据库,在新数据库中创建表,再尝试删除表
    忘记linux下的mysql密码,需要重新创建密码123456
    创建mysql表
    程序员的冷笑话 python版本
    python的__mro__与__slot__
  • 原文地址:https://www.cnblogs.com/enjoyall/p/13924961.html
Copyright © 2011-2022 走看看