当前位置: 首页 > news >正文

二十一冶建设有限公司网站网站设计源代码

二十一冶建设有限公司网站,网站设计源代码,wordpress移动端顶部菜单,小百姓这个网站谁做的文章目录 前情提要发散探索从management.port开始确定否需要开启额外端口额外端口是如何开启的ManagementContextFactory的故事从哪儿来创建过程 management 相关API如何被注册 小结 前情提要 最近遇到一个需求,在单个服务进程上开启多网络端口,将API的…

文章目录

  • 前情提要
  • 发散探索
  • 从management.port开始
    • 确定否需要开启额外端口
    • 额外端口是如何开启的
    • ManagementContextFactory的故事
      • 从哪儿来
      • 创建过程
    • management 相关API如何被注册
  • 小结

前情提要

最近遇到一个需求,在单个服务进程上开启多网络端口,将API的访问通过端口强行隔离开。

发散探索

  1. SpringBoot自带的多端口配置,server.port和management.port,将常规的API与管理相关API通过端口拆分开;
  2. 1中的端口是基于HTTP/1.1通信的,如果需要基于HTTP/2.0(如gRPC)又会有些差别;
  3. 一般一个进程内SpringApplication对象只有1个,理论上搞多个自然就能开启多个端口,略显粗暴;
    本着探索的态度,我会逐个探讨上述3个方向,本文先关注在方向1上。如果你有更NB的问题或者建议,欢迎评论区留言。

从management.port开始

配置生效依赖SpringBoot的AutoConfiguration机制,management相关配置体现在ManagementContextAutoConfiguration中,完成ManagementContext的配置;其中涉及的问题如下。

确定否需要开启额外端口

@ConditionalOnManagementPort决定了是否开启新端口,该注解声明如下

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnManagementPortCondition.class)
public @interface ConditionalOnManagementPort {/*** The {@link ManagementPortType} to match.* @return the port type*/ManagementPortType value();}

其依赖的OnManagementPortCondition会在配置过程中被处理,处理概要过程如下:

  1. 基于当前的Environment决定实际的ManagementPortType;
  2. 获得当前注解中value对应的设定ManagementPortType;
  3. 基于1和2决定被其注解的bean是否初始化,ManagementPortType的定义如下
public enum ManagementPortType {/*** The management port has been disabled.*/DISABLED,/*** The management port is the same as the server port.*/SAME,/*** The management port and server port are different.*/DIFFERENT;/*** Look at the given environment to determine if the {@link ManagementPortType} is* {@link #DISABLED}, {@link #SAME} or {@link #DIFFERENT}.* @param environment the Spring environment* @return {@link #DISABLED} if {@code management.server.port} is set to a negative* value, {@link #SAME} if {@code management.server.port} is not specified or equal to* {@code server.port} and {@link #DIFFERENT} otherwise.* @since 2.1.4*/public static ManagementPortType get(Environment environment) {Integer managementPort = getPortProperty(environment, "management.server.");if (managementPort != null && managementPort < 0) {return DISABLED;}Integer serverPort = getPortProperty(environment, "server.");return ((managementPort == null || (serverPort == null && managementPort.equals(8080))|| (managementPort != 0 && managementPort.equals(serverPort))) ? SAME : DIFFERENT);}private static Integer getPortProperty(Environment environment, String prefix) {return environment.getProperty(prefix + "port", Integer.class);}}

额外端口是如何开启的

到这里,我们可以回到ManagementContextAutoConfiguration,针对性地关注@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)相关类, 也就是下面这段

    @Configuration(proxyBeanMethods = false)@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)static class DifferentManagementContextConfiguration implements ApplicationListener<WebServerInitializedEvent> {private final ApplicationContext applicationContext;private final ManagementContextFactory managementContextFactory;DifferentManagementContextConfiguration(ApplicationContext applicationContext,ManagementContextFactory managementContextFactory) {this.applicationContext = applicationContext;this.managementContextFactory = managementContextFactory;}@Overridepublic void onApplicationEvent(WebServerInitializedEvent event) {if (event.getApplicationContext().equals(this.applicationContext)) {ConfigurableWebServerApplicationContext managementContext = this.managementContextFactory.createManagementContext(this.applicationContext,EnableChildManagementContextConfiguration.class,PropertyPlaceholderAutoConfiguration.class);if (isLazyInitialization()) {managementContext.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}managementContext.setServerNamespace("management");managementContext.setId(this.applicationContext.getId() + ":management");setClassLoaderIfPossible(managementContext);CloseManagementContextListener.addIfPossible(this.applicationContext, managementContext);managementContext.refresh();}}protected boolean isLazyInitialization() {AbstractApplicationContext context = (AbstractApplicationContext) this.applicationContext;List<BeanFactoryPostProcessor> postProcessors = context.getBeanFactoryPostProcessors();return postProcessors.stream().anyMatch(LazyInitializationBeanFactoryPostProcessor.class::isInstance);}private void setClassLoaderIfPossible(ConfigurableApplicationContext child) {if (child instanceof DefaultResourceLoader) {((DefaultResourceLoader) child).setClassLoader(this.applicationContext.getClassLoader());}}}

从中我们可以发现几个点:

  1. ManagementContext是在主WebApplicationContext发布事件WebServerInitializedEvent后开始初始化;
  2. ManagementContext全新的ConfigurableWebServerApplicationContext,这意味着最终启动后,内存中存在至少两个ApplicationContext;如果说开启两个SpringApplication对象是表象的话,那么创建两个WebApplicationContext应该说就是底层的本质了;
  3. 整个处理过程就是创建ConfigurableWebServerApplicationContext,后续做必要的配置,最后refresh。如果你分析过SpringApplication.run(),那想必你看到了熟悉的味道。

ManagementContextFactory的故事

从哪儿来

对应的子类有两个,ReactiveManagementContextFactory和ServletManagementContextFactory,具体初始化则由xxxManagementContextFactoryAutoConfiguration影响,更进一步的细节藏在
@ConditionalOnWebApplication(type=xxx)中。

创建过程

以ReactiveManagementContextFactory为例

@Overridepublic ConfigurableWebServerApplicationContext createManagementContext(ApplicationContext parent,Class<?>... configClasses) {// 复用EnvironmentEnvironment parentEnvironment = parent.getEnvironment();ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT.createEnvironment(WebApplicationType.REACTIVE);if (parentEnvironment instanceof ConfigurableEnvironment) {// 复用ConversionServicechildEnvironment.setConversionService(((ConfigurableEnvironment) parentEnvironment).getConversionService());}AnnotationConfigReactiveWebServerApplicationContext child = new AnnotationConfigReactiveWebServerApplicationContext();child.setEnvironment(childEnvironment);// 这里可以看到最终会是parent和child关系child.setParent(parent);/*
这里以编程的方式完成BeanDefinition注册, 这里的configClass =     {EnableChildManagementContextConfiguration.class,PropertyPlaceholderAutoConfiguration.class}		*/Class<?>[] combinedClasses = ObjectUtils.addObjectToArray(configClasses,ReactiveWebServerFactoryAutoConfiguration.class);child.register(combinedClasses);// 最后保持parent和child的WebFactory一致, 直接复用parent的BeanDefinitionregisterReactiveWebServerFactory(parent, child);return child;}

management 相关API如何被注册

经过前面一波分析,我们已经看到了创建ApplicationContext,注册必要的Class,而后refresh整个容器就要启动,但没有看到management相关API,其实就藏在EnableChildManagementContextConfiguration.class这里, 从这里出发最终找到配置文件ManagementContextConfiguration.imports,链路如下

EnableManagementContext -> ManagementContextConfigurationImportSelector -> META-INFO/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports

ManagementContextConfiguration.imports的内容如下:

org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseyChildManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementChildContextConfiguration
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementChildContextConfiguration
org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration

这些Configuration最终受如下2个注解的解决决定是否初始化
@ManagementContextConfiguration(value = ManagementContextType.xxx, proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.xxx)
最终不同的EndPoint由不同的EndPointDiscover对整个ApplicationContext中的类进行扫描并提取出来,组装为EndPointHandlerMapping。

到这里可以解释一个现象,就是在manage端口上,无法访问到应用端口上的API,因为两者各自关联了一个独立的HandlerMapping。虽然通常child context可以访问到parent context中的所有bean实例,但是各自可见的API被各自的RequestHandlerMapping限制而隔离。

小结

以上是针对mangement API开放独立端口的探究,从中有几个点值得借鉴:

  1. 独立的ApplicationContext可以作为资源隔离的一种方式,同时又不完全失去与parent的联系;
  2. 条件初始化在SpringBoot自动配置中的广泛应用;
  3. WebApplicationContext与ApplicationContext的区别在于WebServerFactory是否存在,以及RequestHandlerMapping+必要的注解识别,有了这点发现,做API隔离可用的方案也会更加丰富;
    后续会对另外2种实现方式做探讨,感谢你的阅读。
http://www.yayakq.cn/news/827442/

相关文章:

  • 湖南响应式网站方案联想网站建设摘要
  • 网站开发的著作权归谁推广普通话的意义30字
  • 湖南网站营销seo哪家好苏州网站建设优化
  • 邢台学校网站建设费用wordpress评论ajax
  • 电影网站开发库表结构网页美工设计总结
  • 触屏手机网站设计小欢喜林磊儿什么网站做家教
  • 《教师网站建设与应用管理制度》wordpress 分类权限
  • 福建省住房城乡建设部网站在建工程项目一览表
  • 西安网站建设求职简历重庆菜谱制作
  • 植物网站模板唐山建设企业网站
  • 江门市建设工程安全监督网站建立一个同城网站要怎么做
  • 建设wap手机网站制作百度网站申诉
  • 建设领域信用系统网站crm系统架构图
  • 网上移动厅官方网站建设外贸网站多少钱
  • 做分析仪器推广的网站宁波外贸推广网络营销
  • 黑龙江省建设银行网站唐山网络运营推广
  • 小程序开发平台花多少钱seo网站布局
  • 东阿企业做网站推广产品设计ppt
  • 昆明市 网站建设网站开发人员是干什么的
  • 广东网站建设方便招聘网站开发时间
  • 河北邢台wap网站建设免费投放广告平台
  • 什么网站发布任务有人做wordpress动态页面
  • 山东网站建站系统平台内江网站建设
  • python网站开发实战公司网站推广方案模板
  • 企业网站设计与建设php语言做网站
  • 网站策划和运营推广普通话作文500字
  • 淄博知名的做网站推广学做面食最好的网站
  • 使用vue路由做网站郴州市网站建设
  • 做pc端网站市场影楼网站制作
  • 网站开发需要用到哪些软件wordpress 中文图片不显示