Original: www.liaochuntao.cn/2019/09/04/…

Problem description

Some users reported that when using nacOS, Java threads were constantly being created as the program ran, resulting in the CPU Load index reaching 100 percent

To solve the process

When you look at nacOS, you see that these threads are created in large numbers and the final object to hook up to is NacosConfigService

public NacosConfigService(Properties properties) throws NacosException {
  String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
  if (StringUtils.isBlank(encodeTmp)) {
    encode = Constants.ENCODE;
  } else {
    encode = encodeTmp.trim();
  }
  initNamespace(properties);
  agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
  agent.start();
  worker = new ClientWorker(agent, configFilterChainManager, properties);
}
Copy the code

The actual hook object is ClientWorker

@SuppressWarnings("PMD.ThreadPoolCreationRule")
public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager, final Properties properties) {
  this.agent = agent;
  this.configFilterChainManager = configFilterChainManager;

  // Initialize the timeout parameter

  init(properties);

  executor = Executors.newScheduledThreadPool(1.new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
        t.setDaemon(true);
        returnt; }}); executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(),new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
        t.setDaemon(true);
        returnt; }}); executor.scheduleWithFixedDelay(new Runnable() {
      @Override
      public void run(a) {
        try {
          checkConfigInfo();
        } catch (Throwable e) {
          LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); }}},1L.10L, TimeUnit.MILLISECONDS);
}

Copy the code

So I initially wondered if the user was creating a lot of NacosConfigService objects

The userjmapdata

As you can see, there are currently more than two thousand ClientWorker objects in the JVM, and as you can see from the nacos source code analysis above, ClientWorker objects hang on thread pools

User self-verification

Firstly, users were asked to check whether they created a large number of NacosConfigService instances by themselves, which indicated that some users did create a large number of NacosConfigService objects due to their misoperations

Spring-Cloub-AlibabaThe component to check

However, some users said that they only relied on the Spring-Cloud-Alibaba-Nacos component and did not operate the NacosConfigService object themselves, and there was still a large number of threads being created. The spring-cloud-Baba-nacos BUG was eventually identified by a user’s self-checking feedback

@ConfigurationProperties(NacosConfigProperties.PREFIX)
public class NacosConfigProperties {...privateConfigService configService; .@Deprecated
	public ConfigService configServiceInstance(a) {

		if (null! = configService) {return configService;
		}

		Properties properties = newProperties(); .try {
			configService = NacosFactory.createConfigService(properties);
			return configService;
		}
		catch (Exception e) {
			log.error("create config service error! properties={},e=,".this, e);
			return null; }}}Copy the code

This configuration class caches an instance of the ConfigService object. It is intended to maintain a singleton of the object itself, but in reality, the NacosConfigProperties bean is recreated every time the Spring-Cloud context is refreshed. Once there is a configuration update — >Context refresh — >NacosConfigProperties is recreated — >ConfigService cache lapsed — >ConfigService is recreated

Therefore, because of this causality, the ConfigService cache Context will not work after being refreshed

To solve the PR

public class NacosConfigManager implements ApplicationContextAware {

	private ConfigService configService;

	public ConfigService getConfigService(a) {
		return configService;
		return ServiceHolder.getInstance().getService();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		NacosConfigProperties properties = applicationContext
				.getBean(NacosConfigProperties.class);
		configService = properties.configServiceInstance();
		ServiceHolder holder = ServiceHolder.getInstance();
		if (!holder.alreadyInit) {
			ServiceHolder.getInstance().setService(properties.configServiceInstance());
		}
	}

	static class ServiceHolder {
		private ConfigService service = null;

		private boolean alreadyInit = false;

		private static final ServiceHolder holder = new ServiceHolder();

		ServiceHolder() {
		}

		static ServiceHolder getInstance(a) {
			return holder;
		}

		void setService(ConfigService service) {
			alreadyInit = true;
			this.service = service;
		}

		ConfigService getService(a) {
			returnservice; }}}Copy the code