Eureka集成遗留项目

How to use eureka without spring boot?

虽然SpringCloud开发效率很爽,但是现有项目很多是遗留老项目(比如Struts等),基本上不可能升级到SpringBoot。本文将介绍SpringCloud中Eureka组件与老项目的集成方法。

一听到最新的SpringCloud组件与老项目集成,有经验的老码农可能会说“不可能吧,Jar包兼容性就一堆坑”。针对这个问题,本文已经解决了。

本文目标

  • 保持现有非SpringBoot项目结构不变,确保兼容性
  • 集成Eureka服务

清理Maven依赖遗留问题

使用eureka很简单,只用引入一个Jar包maven依赖就可以了。实际上并不然,对于老项目来说,拔出萝卜带出泥,引入一个Jar包,就像npm一样下载了一堆文件,这些同名不同版本的jar包混到一起,让开发者无从下手。

maven的runtime依赖介绍

在eureka的scope中有一种叫runtime的依赖,这些包在打包(比如War包)时将被导入,而在使用时如果你没有显示申明依赖了它,它在IDE中仍然是红的代码

引入pom后代码中是否仍然变红是否打入war包
compileNY
provided(比如私有Framework)NN
runtime(比如JDBC)YY

因此如果我们代码中使用Eureka中依赖的jar包库(比如javax.inject),需要在dependency中重复再次显性声明依赖。

maven中的systemPath处理

有些很老的系统是从Jar包迁移到Maven来的,经历过几次转手,项目源码中本身就有很多Jar包,比如有如下结构

- common
	-lib
		- spring-4.0.9.jar
		- mybatis.jar
    -src
    	- java
    		...

这些jar包在maven中是这样定义的

<!-- bad practice in maven -->
<dependency>
  <groupId>xxxx</groupId>
  <artifactId>xxxx</artifactId>
  <version>1.3.2</version>
  <type>jar</type>
  <scope>system</scope>
  <systemPath>${project.basedir}/libs/spring-4.0.9.jar</systemPath>
</dependency>

对于这类万年大坑,建议花专门的时间去把这些本地的jar包换成maven正常依赖的形式,以免产生jar包版本冲突,这类问题与Eureka无关,反正迟早要做,不如趁现在改了,对于这类问题,建议最小化分批替换原则,并多留出测试时间,建议发完版本立刻开搞

至于版本兼容性,可以考虑对Jar包进行MD5判断

使用Dependency Management管理版本号

由于eureka引入了很多第三方的jar包(比如guava),而这些第三方的Jar包对我来说,我不希望一点点去exclude,并折腾各种版本兼容性。因此可以使用Dependency Management来统一管理此问题,在老项目中使用spring-cloudspring-boot作为依赖管理。

<project>
    <!-- 虽然这里是Parent,但是不会引入任何包 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
    </parent>
    
    <properties>
        <!-- 这里可以覆盖parent中的版本 -->
        <spring-cloud.version>Edgware.SR5</spring-cloud.version>
    </properties>
    
    <dependencies>
      <dependency>
        <groupId>com.netflix.eureka</groupId>
        <artifactId>eureka-client</artifactId>
      </dependency>
      <dependency>
        <groupId>com.netflix.archaius</groupId>
        <artifactId>archaius-core</artifactId>
      </dependency>
    </dependencies>
    <dependencyManagement>
      	<dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement> 
</project>

这样,你在项目中就复用了SpringBoot的版本号管理维护工作量,以后你只用修改这里的version与其它同级微服务中的SpringCloud版本一致,内部的Eureka,Jackson等版本号就不用自己折腾了。

Intellij中的暗坑

在Intellij中,当你从SVN中删除某个文件,这个文件只是Scheduled to remove from svn。并没有真的从FS删除,因此当你删除了Jar包后,需要立刻提交到SVN中,这样FS中才会被删除掉。这个地方坑了我一天,导致出现多个Jackson,而导致反序列化失败

如果你反复删不掉,可以使用Everything等工具,jar包可能放在你意想不到的位置(比如WEB-INF中)

集成Eureka

集成Eureka的难度不大,它内部也就是2个定时发请求的线程池,并不存在高大上的东西。

Log4j调试配置

分析任何框架第一件事就是配置Log

比较偷懒的方法是在Log4j中配置如下为Debug

com.netflix=DEBUG

配置后就可以看到200,204,400等各种RPC回掉了

Java侧配置

集成Eureka主要需要控制它的生命周期,如果你的项目还在用web.xml,那么可以把它挂到Listener中,或者用纯Java的@PostConstruct来控制Bean。此处可以参考Github配置

@Configuration
public EurekaClassicConfig{
    @PostConstruct
    public void init(){
        // 详见GitHub配置, 这一步会阻塞30s
        EurekaClient client = initializeEurekaClient(applicationInfoManager, new DefaultEurekaClientConfig());
    }
}

唯一注意的就是client初始化非常久,需要多等待

Properties的配置

配置文件名一定是: eureka-client.properties,因为这个是硬编码写死的,不能自定义

另外,Properties中最重要的是配置VIP(Virtual IP, 类似Nginx的UpSource)名称,而SpringBoot中已经默认配置好了,如果没配置将导致后续注册后仍然无法使用,下面是最简配置

eureka.vipAddress="miaomiao"
eureka.name="miaomiao"
# 与Tomcat一致
eureka.port="8080"
eureka.serviceUrl.default=http://localhost:8080/eureka/v2

官网的Wiki比较旧,可以参考如下对象进行配置

  • PropertyBasedClientConfigConstants

此外,虽然在官网中的Wiki中的url是以“/”结尾的,但是我发现请求时出现了//这样的问题,因此我建议在serviceUrl不要加入/后缀,具体可以打Log验证

功能验证

通过下面的Controller获取ServiceInstance,打断点查看是否能获取它的host与port信息

@RestController
class ServiceInstanceRestController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private LoadBalancerClient client;

    @RequestMapping("/service-instances/{applicationName}")
    public List<ServiceInstance> serviceInstancesByApplicationName(
    @PathVariable String applicationName) {
        // 打断点
        return this.discoveryClient.getInstances(applicationName);
    }

    @RequestMapping("/service-instance/{applicationName}")
    public ServiceInstance serviceInstanceByApplicationName(
    @PathVariable String applicationName) {
        return this.client.choose(applicationName);
    }
}

任意客户端进行curl调用

GET /service-instance/miaomiao

如果获取到了JSON数据,就说明注册成功了。其它组件就可以通过调用RestController的形式来请求了。

集成后常见问题

启动速度变慢

由于默认注册的间隔是30s,因此测试环境中可以把它改小点

注册中心单点故障

当注册中心挂掉后,启动就会失败。更深的问题是EurekaServer没有实现HA(High Availability)。针对这种问题,最简单的是配置多台Server,它们之间通过P2P进行Replicate

怎么输入鉴权密码

在URL中直接配置,输入方法同配置HTTP代理,如果你不希望明文放密码,可以使用 {cipher}进行加密

http://USERNAME:PASSWORD@PROXYIP:PROXYPORT