使用SpringBoot开发微服务时,需要通过注册中心来实现服务之间的发现机制,Eureka和Zookeeper都是常用的
注册中心框架,我们可以选择其中之一,现在面临一个特殊的需求,我们同时存在Eureka和ZK,但是有时候会使用
Eureka,有时候使用ZK,怎么办?首先一点需要明确的是,不管使用哪一个,肯定只能选择其一,不存在说同时使
用2个,但情况是我两者都有可能使用,怎么办?比如环境A使用Eureka,另有一套环境B使用ZK,如何在代码侵入尽
量低的情况下去适配注册中心的服务发现框架,涉及改动越小越好;
  • 1.首先我们简单分析一下大概的情况
    不管使用哪一个注册中心,我么你都通过在启动类添加注解@@EnableDiscoveryClient来实现的,这个注解是在
    spring-cloud-commons包里面的,这个注解和具体的注册中心实现细节解耦的,不管使用Eureka还是ZK都是这个注解;
  • 2.我们先尝试引入Eureka和ZK的注册依赖坐标,这里就不具体指定版本了,版本会和springcloud版本适配:
 
		 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId> 
        </dependency>
		 <dependency>
	            <groupId>org.springframework.cloud</groupId>
	            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>
  • 然后启动工程,会发现报下面的错:
    Field registration in org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration$
    ServiceRegistryEndpointConfiguration required a single bean, but 2 were found:
  • eurekaRegistration: defined by method ‘eurekaRegistration’ in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.class] - zookeeperServiceDiscovery: defined by method ‘zookeeperServiceDiscovery’ in class path resource [org/springframework
    /cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistrationAutoConfiguration.class]
    注意这里如果工程没有依赖spring-boot-starter-actuator组件的话,是不会报错的,具体原因未深究,
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • 提示上面的错,看的很明白,说有一个单例bean但是找到了两个实例,全局搜索这两个类,发现这两个类的位置如下:
    EurekaClientAutoConfiguration:类位于spring-cloud-netflix-eureka-cleint包下,这个包是通过spring-cloud-starter-eureka
    传递依赖进来的
    ZookeeperAutoServiceRegistrationAutoConfiguration:类位于spring-cloud-zookeeper-discovery包下,这个是spring-cloud-starter-zookeeper-discovery传递依赖进来的,
  • 3.找到了这两个冲突的bean所在的jar包之后,我们找到spring-cloud-netflix-eureka-cleint jar包的resources/META-INF/spring.factories文件,发现如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration


  • 同理我们找spring-cloud-zookeeper-discoveryjar包的resources/META-INF/spring.factories文件,发现如下:

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.DependencyFeignClientAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.DependencyRibbonAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.DependencyRestTemplateAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherAutoConfiguration,\
org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration,\
org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistryAutoConfiguration,\
org.springframework.cloud.zookeeper.support.CuratorServiceDiscoveryAutoConfiguration

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.zookeeper.discovery.dependency.DependencyEnvironmentPostProcessor

org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration

  • 4.到此我们大概捋一捋思路:注册中心只能选择某一个,我们同时引入了ZK和Eureka的注册客户端jar包,运行提示我们一个单例的bean找到了俩,我们找到了俩类,发现确实在我们引入的jar包里面,然后在jar包里面,我们发现了spring.factories文件里面就写着这两个类的名字,我们仔细看看这个文件,发现里面写的是一些键值对,键key就是org.springframework.boot.autoconfigure.EnableAutoConfiguration,值是一系列的配置类,到这里,我们简单了解下spring.factories的原理,工程扫描META-INF下面的spring.factories文件来决定需要加载哪些配置类,这给我们解决该问题提供了一个思路:
	我们把spring-cloud-netflix-eureka-cleint包下面的spring.factories文件里面
	的org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\去掉,
	把spring-cloud-zookeeper-discovery包里面的org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration,\去掉,
	理论上,再次启动时就不会保证错误了,验证确实如此,因为不会自动配置这两个类了,但是,
	这两行内容写在自己的工程的spring.factories里面,然后不就可以做一个简易的开关了吗?
  • 5.解决:
    在自己的工程下新建resources/META-INF/spring.factories文件,文件内容如下:
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
#org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration,\
org.springframework.cloud.zookeeper.serviceregistry.ZookeeperServiceRegistryAutoConfiguration

说明:
	这里有五行代码,前面两行说明设置自动配置的是eureka的注册发现,后面四行是ZK的注册发现,ZK配置了三个类,为什
么是三个后面简单解释,是因为我在测试的时候,只有一个的话会有问题;此时,我们工程引入的原始spring-cloud-netflix-eureka-cleint和spring-cloud-zookeeper-discovery包的spring.factories里面的配置被我们去掉了,挪到了我们自
己的工程下面,然后我们现在按照上面的配置启动,就发现可以注册到ZK了,且不会报bena的冲突错误了,然后我们注释掉后四行,
放开前两行,就可以注册到Eureka了,
  • 6.问题
    为什么第五点里面,ZK有三项配置挪出来了,因为仅仅把ZookeeperServiceRegistryAutoConfiguration挪出来的话,再注册到ZK的时候回报一个zk相关的bean找不到,后来把ZookeeperAutoServiceRegistrationAutoConfiguration挪出来之后就没问题了,我猜想是把ZookeeperServiceRegistryAutoConfiguration挪出来后,他的配置时机提前了,导致一些依赖的bean没有在它之前初始化,至于RibbonZookeeperAutoConfiguration为什么要挪出来,这个是ribbon服务调用相关的,不挪出来之前,测试注册到Eureka是ok的,但是访问不到其他的服务,此时RibbonZookeeperAutoConfiguration还在spring-cloud-zookeeper-discovery包的spring.factories里面,还是会自动配置,因此我在使用Eureka的时候发现还会走zookeeper的ribbon,这自然就有问题,把这项弄到外面作为开关配置后就可以了,注册到Eureka时会使用对应的ribbon;
  • 7.扩展
    如果还有其他的服务发现机制,也可以按照这样的思路去做,这样的话我们的pom里面可以引入多种注册依赖,不需要修改pom文件了,在工程的spring.factories文件里面做多路切换;
  • 8.好处
    不要修改pom,支持多个注册中心的切换,但是有个问题,spring.factories文件也是打进去jar包的,因此它实际上也不算配置项,只不过相比修改pom而言这样会好一些,这里有个简单的思路,比如我们的源码不需要修改,我们可以写一个打包脚本,例如使用maven打包,那么打包脚本肯定是使用mvn命令编译成jar包,我们可以在打包脚本里面做一个简单的处理,接受一个参数什么的,通过参数,在打包脚本里面去修改spring.factories文件,修改完成之后,我们再执行mvn package命令,这样的话我们就可以通过命令打出适配不同的注册中心的jar包了,这里我自己实现的是ZK,Eureka和k8三种环境的注册切换机制,刚刚提到脚本是一个思路,我也还没做,不过上面的问题要完全做好依赖的东西很多,在第九点说明
  • 9.难点
    比如我们注册到Eureka,我们引入的是

    org.springframework.cloud
    spring-cloud-starter-eureka

    他会引入

    org.springframework.cloud
    spring-cloud-netflix-eureka-client

    我们要修改的jar包是spring-cloud-netflix-eureka-client,这里我们就需要自己封装一下spring-cloud-netflix-eureka-client再引用,否则其他人可能引用的就是没有修改的spring-cloud-netflix-eureka-client,可以修改这个jar,然后重新命名放到私服,供团队一起使用,另外我们直接使用 spring-cloud-starter-eureka也会有问题,因为这个会引入原始的spring-cloud-netflix-eureka-client,我们必须手动排除,但是这部稳妥,最好的办法是我们改造上面这两个包,比如改造spring-cloud-starter-eureka为myCompany-spring-cloud-starter-eureka ,在这个包的pom里面排除对原始spring-cloud-netflix-eureka-client的依赖,引入对myCompany-spring-cloud-netflix-eureka-client的依赖,然后开发过程中,就只需要引入myCompany-spring-cloud-starter-eureka即可,对ZK也是一样,需要改造两个包,这里稍微有点麻烦。
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐