ServiceComb之Java-Chassis启动流程分析

项目网址:
https://github.com/apache/servicecomb-java-chassis/tree/master/samples/pojo-sample/pojo-provider

pojo-provider项目的启动类是PojoProviderMain

public class PojoProviderMain {

  public static void main(String[] args) throws Exception {
    Log4jUtils.init();
    BeanUtils.init();
  }
}

如上所示,Log4jUtils.init是初始化日志服务。init方法代码如下,默认会从classpath*:config/base/log4j.properties和classpath*:config/log4j.properties两个路径读取log4j配置并合并。

public static void init() throws Exception {
  init(Arrays.asList("classpath*:config/base/log4j.properties", "classpath*:config/log4j.properties"));
}

init()方法的实现也就是读取配置文件,并调用PropertConfigurator进行初始化。

public static void init(List<String> locationPatterns) throws Exception {
  if (inited) {
    return;
  }

  synchronized (LOCK) {
    if (inited) {
      return;
    }

    PropertiesLoader loader = new PropertiesLoader(locationPatterns);
    Properties properties = loader.load();
    if (properties.isEmpty()) {
      throw new Exception("can not find resource " + locationPatterns);
    }

    PropertyConfigurator.configure(properties);
    inited = true;

    if (OUTPUT_CONFIG_ENABLED_TRUE.equals(
        properties.getProperty(OUTPUT_CONFIG_ENABLED, OUTPUT_CONFIG_ENABLED_TRUE))) {
      // If the property file with the highest priority is on a hard disk(not in a jar package)
      // and we have write access, output the merged property file for the purpose of debugging
      outputFile(loader.getFoundResList(), properties);
    }
  }
}

BeanUtils.init对微服务进行初始化,init方法代码如下

public static final String DEFAULT_BEAN_CORE_RESOURCE = "classpath*:META-INF/spring/scb-core-bean.xml";

public static final String DEFAULT_BEAN_NORMAL_RESOURCE = "classpath*:META-INF/spring/*.bean.xml";

public static final String[] DEFAULT_BEAN_RESOURCE = new String[] {DEFAULT_BEAN_CORE_RESOURCE
    , DEFAULT_BEAN_NORMAL_RESOURCE};

public static void init() {
  init(DEFAULT_BEAN_RESOURCE);
}

public static void init(String... configLocations) {
  prepareServiceCombScanPackage();

  Set<String> locationSet = new LinkedHashSet<>();
  addBeanLocation(locationSet, DEFAULT_BEAN_RESOURCE);
  addBeanLocation(locationSet, configLocations);
  context = new ClassPathXmlApplicationContext(locationSet.toArray(new String[locationSet.size()]));
}

prepareServiceCombScanPackage配置扫描的包的范围。

public static void prepareServiceCombScanPackage() {
    Set<String> scanPackags = new LinkedHashSet<>();
    // add exists settings
    String exists = System.getProperty(SCB_SCAN_PACKAGE);
    if (exists != null) {
      for (String exist : exists.trim().split(",")) {
        if (!exist.isEmpty()) {
          addItem(scanPackags, exist.trim());
        }
      }
    }

    // ensure servicecomb package exist
    addItem(scanPackags, SCB_PACKAGE);

    // add main class package
    for (Class<?> mainClass : new Class<?>[] {JvmUtils.findMainClass(), JvmUtils.findMainClassByStackTrace()}) {
      if (mainClass != null && mainClass.getPackage() != null) {
        String pkg = mainClass.getPackage().getName();
        addItem(scanPackags, pkg);
      }
    }

    // finish
    String scbScanPackages = StringUtils.join(scanPackags, ",");
    System.setProperty(SCB_SCAN_PACKAGE, scbScanPackages);
    LOGGER.info("Scb scan package list: " + scbScanPackages);
  }

默认会从classpath*:META-INF/spring/*.bean.xml路径加载配置文件,并将其传递给Spring框架的ClassPathXmlApplicationContext完成应用上下文加载。在Spring context加载完成后,通过Spring Framework开源的事件调用机制和反射机制来触发ServiceComb自己的初始化逻辑。类似于观察者模型。首先我们知道Spring框架的AbstractApplicationContext中的refresh函数是用来加载和刷新spring配置。

/**
 * Create a new ClassPathXmlApplicationContext, loading the definitions
 * from the given XML files and automatically refreshing the context.
 * @param configLocations array of resource locations
 * @throws BeansException if context creation failed
 */
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
	this(configLocations, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
		throws BeansException {

	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}

其中,refresh方法完成了整个spring生命周期的初始化,参考博客

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备,记录容器的启动时间startupDate, 标记容器为激活,初始化上下文环境如文件路径信息,验证必填属性是否填写
        prepareRefresh();
        // 获取新的beanFactory,销毁原有beanFactory、为每个bean生成BeanDefinition等
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 初始化beanfactory的各种属性
        prepareBeanFactory(beanFactory);
        try {
            // 模板方法,此时,所有的beanDefinition已经加载,但是还没有实例化。
            //允许在子类中对beanFactory进行扩展处理。比如添加aware相关接口自动装配设置,添加后置处理器等,是子类扩展prepareBeanFactory(beanFactory)的方法
            postProcessBeanFactory(beanFactory);
            // 实例化并调用所有注册的beanFactory后置处理器(实现接口BeanFactoryPostProcessor的bean,在beanFactory标准初始化之后执行)
            invokeBeanFactoryPostProcessors(beanFactory);
            // 实例化和注册beanFactory中扩展了BeanPostProcessor的bean
            //例如:
            // AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
            // RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)
            // CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // 模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情。
            onRefresh();
            // 注册监听器,广播early application events
            registerListeners();
            // 实例化所有剩余的(非懒加载)单例
            // 比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。
            // 实例化的过程各种BeanPostProcessor开始起作用。
            finishBeanFactoryInitialization(beanFactory);
            // refresh做完之后需要做的其他事情。
            // 清除上下文资源缓存(如扫描中的ASM元数据)
            // 初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。
            // 发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

spring容器初始化过程如下所示:
在这里插入图片描述
在finishRefresh方法中通过publishEvent循环通知所有的观察者(ApplicationEvent的子类)的onApplicationEvent方法执行相应的操作,通过它我们可以定位到CseApplicationListener。

protected void finishRefresh() {
	// Initialize lifecycle processor for this context.
	initLifecycleProcessor();

	// Propagate refresh to lifecycle processor first.
	getLifecycleProcessor().onRefresh();

	// Publish the final event.
	publishEvent(new ContextRefreshedEvent(this));

	// Participate in LiveBeansView MBean, if active.
	LiveBeansView.registerApplicationContext(this);
}
/**
 * Publish the given event to all listeners.
 * @param event the event to publish (may be an {@link ApplicationEvent}
 * or a payload object to be turned into a {@link PayloadApplicationEvent})
 * @param eventType the resolved event type, if known
 * @since 4.2
 */
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
	Assert.notNull(event, "Event must not be null");

	// Decorate event as an ApplicationEvent if necessary
	ApplicationEvent applicationEvent;
	if (event instanceof ApplicationEvent) {
		applicationEvent = (ApplicationEvent) event;
	}
	else {
		applicationEvent = new PayloadApplicationEvent<>(this, event);
		if (eventType == null) {
			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
		}
	}

	// Multicast right now if possible - or lazily once the multicaster is initialized
	if (this.earlyApplicationEvents != null) {
		this.earlyApplicationEvents.add(applicationEvent);
	}
	else {
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}

	// Publish event via parent context as well...
	if (this.parent != null) {
		if (this.parent instanceof AbstractApplicationContext) {
			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
		}
		else {
			this.parent.publishEvent(event);
		}
	}
}
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

public class CseApplicationListener
    implements ApplicationListener<ApplicationEvent>, Ordered, ApplicationContextAware {
  private Class<?> initEventClass = ContextRefreshedEvent.class;

  private ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (this.applicationContext == applicationContext) {
      // same object. avoid initialize many times.
      return;
    }
    this.applicationContext = applicationContext;
    BeanUtils.setContext(applicationContext);
    HttpClients.load();
    RegistrationManager.INSTANCE.init();
    DiscoveryManager.INSTANCE.init();
  }

  public void setInitEventClass(Class<?> initEventClass) {
    this.initEventClass = initEventClass;
  }

  @Override
  public int getOrder() {
    // should run before default listener, eg: ZuulConfiguration
    return -1000;
  }

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    if (initEventClass.isInstance(event)) {
      if (applicationContext instanceof AbstractApplicationContext) {
        ((AbstractApplicationContext) applicationContext).registerShutdownHook();
      }

      SCBEngine scbEngine = SCBEngine.getInstance();
      //SCBEngine init first, hence we do not need worry that when other beans need use the
      //producer microserviceMeta, the SCBEngine is not inited.
//        String serviceName = RegistryUtils.getMicroservice().getServiceName();
//        SCBEngine.getInstance().setProducerMicroserviceMeta(new MicroserviceMeta(serviceName).setConsumer(false));
//        SCBEngine.getInstance().setProducerProviderManager(applicationContext.getBean(ProducerProviderManager.class));
//        SCBEngine.getInstance().setConsumerProviderManager(applicationContext.getBean(ConsumerProviderManager.class));
//        SCBEngine.getInstance().setTransportManager(applicationContext.getBean(TransportManager.class));
      scbEngine.setApplicationContext(applicationContext);
      scbEngine.setFilterChainsManager(applicationContext.getBean(FilterChainsManager.class));
      scbEngine.getConsumerProviderManager().getConsumerProviderList()
          .addAll(applicationContext.getBeansOfType(ConsumerProvider.class).values());
      scbEngine.getProducerProviderManager().getProducerProviderList()
          .addAll(applicationContext.getBeansOfType(ProducerProvider.class).values());
      scbEngine.addBootListeners(applicationContext.getBeansOfType(BootListener.class).values());

      scbEngine.run();
    } else if (event instanceof ContextClosedEvent) {
      if (SCBEngine.getInstance() != null) {
        SCBEngine.getInstance().destroy();
      }
    }
  }
}

scbEngine.run()方法如下:

  public synchronized SCBEngine run() {
    if (SCBStatus.DOWN.equals(status)) {
      try {
        doRun();
        waitStatusUp();
      } catch (TimeoutException e) {
        LOGGER.warn("{}", e.getMessage());
      } catch (Throwable e) {
        LOGGER.error("Failed to start ServiceComb due to errors and close", e);
        try {
          destroy();
        } catch (Exception exception) {
          LOGGER.info("destroy has some error.", exception);
        }
        status = SCBStatus.FAILED;
        throw new IllegalStateException("ServiceComb init failed.", e);
      } finally {
        printServiceInfo();
      }
    }

    return this;
  }

doRun()方法如下:

private void doRun() throws Exception {
    status = SCBStatus.STARTING;

    bootListeners.sort(Comparator.comparingInt(BootListener::getOrder));

    triggerEvent(EventType.BEFORE_HANDLER);
    HandlerConfigUtils.init(consumerHandlerManager, producerHandlerManager);
    triggerEvent(EventType.AFTER_HANDLER);

    triggerEvent(EventType.BEFORE_FILTER);
    filterChainsManager.init();
    triggerEvent(EventType.AFTER_FILTER);

    createProducerMicroserviceMeta();

    triggerEvent(EventType.BEFORE_PRODUCER_PROVIDER);
    producerProviderManager.init();
    triggerEvent(EventType.AFTER_PRODUCER_PROVIDER);

    triggerEvent(EventType.BEFORE_CONSUMER_PROVIDER);
    consumerProviderManager.init();
    triggerEvent(EventType.AFTER_CONSUMER_PROVIDER);

    triggerEvent(EventType.BEFORE_TRANSPORT);
    transportManager.init(this);
    triggerEvent(EventType.AFTER_TRANSPORT);

    triggerEvent(EventType.BEFORE_REGISTRY);

    triggerAfterRegistryEvent();

    RegistrationManager.INSTANCE.run();
    DiscoveryManager.INSTANCE.run();

    shutdownHook = new Thread(this::destroyForShutdownHook);
    Runtime.getRuntime().addShutdownHook(shutdownHook);
  }

具体CSE初始化过程如下所示:

  • 首先HandlerConfigUtils.init读取配置文件信息cse.handler.xml (HandlerConfigUtils.init())
  • filterChainsManager.init()完成对应filter的处理;
  • 然后producerProviderManager.init()进行Provider处理,主要是微服务元数据、Schema和契约的处理,并将其添加到RegistryUtils的微服务实例中;
  • consumerProviderManager.init()完成服务消费者的初始化;
  • 接着transportManager.init(this)启动一个异步无阻塞的网络框架vertx,并将初始化的EndPoint添加到RegistryUtils的微服务实例中;
  • 最后RegistrationManager.INSTANCE.run()在RegistryUtils中实现微服务在服务管理中心的注册。
    在这里插入图片描述
    在前面的流程分析中,CseApplicationListener类很关键,相当于Spring框架与ServiceComb的桥梁。在Spring初始化之后触发,初始化ServiceComb自身的配置。

初始化过程分析

CseApplicationListener最终触发SCBEngine.doInit方法,triggerEvent是一种AOP机制。
进一步,在HandlerConfigUtils中,可以看到处理链handler实例的初始化。首先获取了handler处理链的配置信息,然后根据这些配置初始化所有的handler实例。

public static void init(ConsumerHandlerManager consumerHandlerManager, ProducerHandlerManager producerHandlerManager)
    throws Exception {
  Config config = loadConfig();
  consumerHandlerManager.init(config);
  producerHandlerManager.init(config);
}

那么它会从哪里获取配置呢?

源码如下,可以看到其会获取类路径下config/cse.handler.xml的文件。其实断点进去PaaSResourceUtils.getSortedResources方法可知还会获取类路径下config/cse.*.handler.xml文件。在ProducerProviderManager初始化中,我们可以得到微服务的元数据信息microserviceMeta,并把它添加到RegistryUtils中定义的微服务当中↓

private static Config loadConfig() throws Exception {
  Config config = new Config();

  List<Resource> resList =
      PaaSResourceUtils.getSortedResources("classpath*:config/cse.handler.xml", ".handler.xml");
  for (Resource res : resList) {
    Config tmpConfig = XmlLoaderUtils.load(res, Config.class);
    config.mergeFrom(tmpConfig);
  }

  return config;
}

createProducerMicroserviceMeta()创建服务元数据,通过断点可知,微服务的元数据主要包括如下的内容:
在这里插入图片描述

TransportManager.init主要包括如下内容。transport.init()会启动一个异步无阻塞的网络框架vertx。启动成功后将初始化的EndPoint添加到RegistryUtils的微服务实例中。

  public void init(SCBEngine scbEngine) throws Exception {
    buildTransportMap();

    for (Transport transport : transportMap.values()) {
      if (transport.init()) {
        Endpoint endpoint = transport.getPublishEndpoint();
        if (endpoint != null && endpoint.getEndpoint() != null) {
          LOGGER.info("endpoint to publish: {}", endpoint.getEndpoint());
          RegistrationManager.INSTANCE.addEndpoint(endpoint.getEndpoint());
        }
        continue;
      }
    }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值