版权声明:本文为博主原创文章,版权归烟台华东数据科技有限公司与博主本人共同所有。烟台华东数据科技有限公司及其下属机构毋须博主授权即可在任意用途下转载、使用!
目录
前言
也许因为很多原因,也许是公司框架升级,或者业务升级,开始了Spring Cloud架构的设计和搭建,但是会出现一些问题,一些传统的Java Web项目也需要同时接入Spring Cloud环境中使用微服务。最简单粗暴的方案有以下两种:
- 把这个Java Web项目直接迁移到Spring Cloud项目下,使用Mavan管理jar包,添加Spring Cloud相关引用和配置
- 非Spring Cloud工程直接引入eureka、ribbon、feign依赖
- 使用网关访问微服务,仅限有配置网关的服务,而且针对网关还得自行写各个微服务的HTTP访问代码
如果以上两种方案可以解决问题,那么本篇博文后面的内容就可以不用看了。
我根据实际开发中的情况,我初步总结会遇到的问题如下:
- Spring不兼容,很多老项目的Spring版本甚至还是2.0版
- jersey不兼容,如有一些模块使用的还是jersey.1.10,这是2011年的版本了
JDK版本问题,JDK版本的问题反而不大,现在Spring Cloud环境最低版本要求使用JDK8,而很多老项目之前是没有怎么使用到老版本JDK特性的,可能会用到的是sun包里面的一些内容,但是新版的JDK都有替代,因此JDK升级到8版本的风险还是比较小的。
因此第一步,请先升级传统项目至JDK8!
另:如果是想把传统Java Web项目整入Spring Cloud提供微服务,那表示传统老项目是具有微服务潜力的,那就不如直接把代码新迁移到Spring Cloud的微服务模块中,因为核心代码还是通用的,只是之前可能是通过原始的Servlet或者jersey提供服务,可以直接修改为SpringMVC提供服务,风险也可控。就不要另外折腾把老代码改造成微服务模块了,有这时间微服务都开发出来了。
解决方案思路
初期在考虑解决方案时,参考了以下博文:
传统Java Web(非Spring Boot)、非Java语言项目接入Spring Cloud方案
刚开始我主要是参照第二篇博文的方案进行了相应的开发和试用,基本是可以满足要求的,但是还是存在一定的问题,首先代码里面方法过时的就不提了,一个最大的缺陷就是,要求传统的Java Web项目一定需要引用如下两个jar包:
<dependency> <groupId>com.netflix.eureka</groupId> <artifactId>eureka-client</artifactId> <version>1.4.12</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-core</artifactId> <version>1.1.7.RELEASE</version> </dependency>
因为那篇博主还是使用的eureka提供的方法来进行获注册和获取微服务地址。但是就会出现前面提到的,这两个引用里面可能会有一些jar包会与老项目冲突,造成老项目的功能不能正常使用。因为我这边进行了相应的修改和优化,具体解决思路如下:
- eureka提供此端点eureka/apps可以获取在eureka中注册的所有微服务和相关信息,因此我们可以自行实现微服务的获取功能,也不需要额外注册到eureka上面。
- 然后使用工厂模式和代理模式,使老项目可以直接调用微服务对外暴露的接口,就像在项目内部调用一样,减少后续的开发和维护工作量,Spring Cloud中的模式也不需要任何改变。
其实就是我们需要实现一个桥梁,用这个桥梁可以让传统的Java Web项目正常调用微服务的接口,从而得到服务。
具体实现
Spring Cloud基于之前两篇博文的设计框架和内容,我们使用Redis模块进行演示。
需要实现的功能模块就这样:
其中被我废弃的HddEurekaClient就是老版的使用eureka自己的方法获取注册的微服务信息。此文不展示。
整个模块使用Spring Boot搭建,从而可以打成jar包,然后给传统Java Web项目使用。
pom文件
<artifactId>HDD_SHJJHY_CLOUD</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> </dependency> <dependency> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty</artifactId> <version>6.1.26</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.51</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Eureka提供的apps信息
首先我们可以访问本地的eureka提供的端点信息,使用浏览器默认返回xml格式如下:
不过在请求访问的时候,把请求的结果设置为JSON格式,则会返回JSON格式,为了简便解析,我这里使用的是JSON格式。
实体类
首先根据eureka 返回的信息,设计实体类如下:
EurekaApplication(主要记录的是在eureka中注册的每一个微服务信息)
package net.huadong.cloud.entity;
import java.util.List;
/**
* @author yangzj
* @desc Eureka中的微服务实体信息
* @create 2019-06-17 11:34
**/
public class EurekaApplication {
private String name;
private List<EurekaApplicationInstance> instances;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<EurekaApplicationInstance> getInstances() {
return instances;
}
public void setInstances(List<EurekaApplicationInstance> instances) {
this.instances = instances;
}
}
每一个微服务可能都会对应多个不同端口的实例,而且关键的访问信息都是在每一个实例上面,因此再设计微服务的实例类:
EurekaApplicationInstance
package net.huadong.cloud.entity;
/**
* @author yangzj
* @desc Eureka中每个实例的不同多个服务的实体类
* @create 2019-06-17 11:36
**/
public class EurekaApplicationInstance {
private volatile String instanceId; // 实例ID
private volatile String hostName; // 实例主机名
private volatile String app; // 实例名定义
private volatile String ipAddr; // 实例所在IP
private volatile String status; // 实例状态,UP或DOWN
private volatile String overriddenstatus; // 未知
private volatile String port; // 实例端口
private volatile String securePort; // 实例加密端口
private volatile boolean securePortEnabled = false; // 实例是否开启加密
private volatile String countryId;
private volatile String appGroupName; // 集群名称,一般没用
private volatile String homePageUrl; // 实例访问url
private volatile String statusPageUrl; // 实例状态访问url
private volatile String healthCheckUrl; // 实例健康检查url
private volatile String vipAddress; // 实例的实际名(未大小写转换,基本没用)
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getIpAddr() {
return ipAddr;
}
public void setIpAddr(String ipAddr) {
this.ipAddr = ipAddr;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getOverrid