xinrong2019 / xinrong2019.github.io

My Blog
https://xinrong2019.github.io
1 stars 1 forks source link

20190715 分布式配置中心 #89

Open xinrong2019 opened 5 years ago

xinrong2019 commented 5 years ago

最近计划在项目中引入分布式配置中心,解决开发中改配置重启麻烦的痛点,大概列了下面三个,都是国内的:

Apollo

disconf

Nacos

xinrong2019 commented 5 years ago

项目中引入Nacos配置中心(Sping Cloud Alibaba方式)

我首先新建了一个模块(工程),采用Sping Cloud方式使用Nacos。

1 Spring Cloud Alibaba与Spring Boot版本问题导致用例始终无法通过

这是我遇到的第一个问题。具体可以参考版本说明

2 包含到现有项目,现有项目已有parent pom依赖,如何解决依赖spring boot parent依赖的问题

解决方法:

maven中只允许有一个parent依赖,将spring boot parent依赖,加入dependencyManagement中

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.0.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

遇到了一个版本兼容问题,参考了 spring boot不用parent引入,采用dependencyManagement引入后的坑

xinrong2019 commented 5 years ago

3 启动项目存在quartz兼容问题,无法找到CronTriggerBean这个类

在原有项目启动时,这个import存在问题,找不到CronTriggerBean这个类。

import org.springframework.scheduling.quartz.CronTriggerBean;

官方文档中,找不到兼容的信息,只能去github上搜issue,一大堆,没有看的欲望。

去StackOverflow上搜索了“Where is CronTriggerBean in Spring 5”,原来CronTriggerBean替换成了CronTriggerFactoryBean,Spring4的时候改变的。然后就百度搜了搜spring4整合quartz。

搜到了这个答案

然后改了一些API。调整API适配也是麻烦事。。

xinrong2019 commented 5 years ago

Spring方式引入Nacos配置中心

上面一种方式放弃,因为项目还是Spring开发,所以试试直接以Spring方式集成

还是报一堆错误

12:04:10.993 [RMI TCP Connection(2)-127.0.0.1] INFO com.alibaba.nacos.client.identify.CredentialWatcher - [] [] [] No credential found
12:04:11.190 [RMI TCP Connection(2)-127.0.0.1] WARN com.alibaba.nacos.spring.core.env.AnnotationNacosPropertySourceBuilder - There is no content for NacosPropertySource from dataId[alibaba-nacos-config-client] , groupId[DEFAULT_GROUP] , properties[{encode=${nacos.encode:UTF-8}, namespace=${nacos.namespace:}, endpoint=${nacos.endpoint:}, contextPath=${nacos.context-path:}, accessKey=${nacos.access-key:}, secretKey=${nacos.secret-key:}, serverAddr=${nacos.server-addr:}, clusterName=${nacos.cluster-name:}}].
12:04:11.196 [RMI TCP Connection(2)-127.0.0.1] ERROR org.springframework.web.context.ContextLoader - Context initialization failed

刚才想到是不是又是版本问题,于是将nacos-spring-context换成了0.3.0版本,结果启动成功了。。。好坑啊。。

14:26 目前启动了,但是设置的值没有获取到

    @NacosValue(value = "${ftp.host.url}", autoRefreshed = true)
    private String ftpHost;

    @RequestMapping(value = "/test", method = GET)
    @ResponseBody
    public String test() {
        return ftpHost;
    }
xinrong2019 commented 5 years ago

在baidu和stackoverflow搜索spring nacos config结果都不好,都是和spring cloud集成,但是我们的项目是Spring的,如果改成Spring Cloud的,需要做很多兼容性的工作,就偏离了我想引入配置中心的初衷,转而去搞各种版本兼容?

宁愿去开发一套新的,也不想搞这个兼容,除非是去了新公司接手老项目。但是就算是接手老项目,也不想在其上做兼容升级版本,最好是能新开发的新开发,不动已有的。。。


既然上面遇到了一系列问题,最终还是没有解决,我将尝试看网上最多的Spring Cloud版本,看看其原理,然后从内部改造适配。(官方的Spring nacos config example我一直没有跑成功,没有报错,连映射都映射不到)

需要搞清楚的问题:

xinrong2019 commented 5 years ago

补充

1、官网上有关于nacos-spring-context的讲解和Java SDK

2、相关项目:

nacos-spring-project

15:20 下载nacos-spring-project源码中。。

xinrong2019 commented 5 years ago

nacos-spring-project

运行起来

示例代码以内置的HTTP Server端口作为Nacos Server端口,直接改成默认nacos端口,方便测试

集成到现有系统

一开始引用的nacos-spring-context是0.2.3-RC1版本,总是启动不了,现有系统使用的是Spring4,在官方issues找到了,是不兼容的bug,换成0.3.0后,项目可以启动了,但是nacos配置没有生效。

再回到0.2.3-RC1版本启动,很多nacos的配置没有找到,比如nacos.cluster-name:,所以设想应该0.3.0没有解决这个问题,只是让项目启动起来。

引入0.2.3-RC1版本启动时,有如下错误:

LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: 

DeferredApplicationEventPublisher类中的publishEvent方法,需要用try-catch将方法内容捕获,0.3.0就是这么干的。


debug了源码后发现,我在nacos控制台发布的配置已经获取到了。

Bean实例化了,属性也成功注入了。

图片

我跟踪到AbstractAutowireCapableBeanFactory的createBean方法,打了一个条件断点,

图片

发现断点进来两次,第二次通过nacos拉取的配置是null

图片

xinrong2019 commented 5 years ago

项目中Spring Context初始化的流程如下:

1、contextloader初始化spring容器,初始化了configXController

2、rpc-servlet初始化spring容器,没有做任何改变,通过wac.setParent(parent);将上面的context传递下去

3、spring-mvc初始化spring容器,configXController此时被置空了。

使用spring-mvc.xml初始化spring容器,虽然也会通过wac.setParent(parent);将上面的context传递下去,但是在AbstractRefreshableApplicationContext类中的refreshBeanFactory方法中,执行loadBeanDefinitions(beanFactory);后,beanfactory中的configXController的两个属性被置空了。

逐一排查spring-mvc.xml配置,看到这一行

    <!-- 自动扫描且只扫描@Controller -->
    <context:component-scan base-package="cn.evun.ime.**.controller,cn.evun.qns.**.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

我的整合用例是在controller中直接写的,spring-mvc.xml文件中又对controller进行一次扫描,会不会是这里扫描的时候将前面的属性设置冲掉。带着这个猜想,我把controller中的属性注入移到另外一个专门的配置类中,然后注入配置类,调用配置的中属性的get方法,居然成功了。

总结:

总结一下,版本是spring4,nacos1.0.0,nacos-spring-context0.3.0。

目前,SpringMVC项目的Controller中不能直接注入属性,因为扫了两次包,应该还是项目配置的问题,明天再看。