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包 | |
---|---|---|
compile | N | Y |
provided(比如私有Framework) | N | N |
runtime(比如JDBC) | Y | Y |
因此如果我们代码中使用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-cloud
与spring-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