传统Java Web(非Spring Boot)、非Java语言项目接入Spring Cloud方案
技术架构在向spring Cloud转型时,一定会有一些年代较久远的项目,代码已变成天书,这时就希望能在不大规模重构的前提下将这些传统应用接入到Spring Cloud架构体系中作为一个服务以供其它项目调用。我们需要使用原生的Eureka/Ribbon手动完成注册中心、查询服务列表功能。如果是非Java项目,可以使用 Spring Sidecar 项目接入Spring Cloud形成异构系统。
JDK版本的选择
强烈建议使用JDK8, 因为Eureka Client的最新版本已经要求JDK8起了,JDK8以下的版本会出现No such method运行时错误。如果不能使用JDK8, 可以选择较早版本的eureka client, 但最低也只能支持到JDK7。对于老项目来说,在不动代码的前提下升级JDK不会有太大的风险,除非你使用了JDK特定版本的功能。风险最大的其实是升级开发框架(如Spring3到Spring4)。
服务列表查询
非Spring Cloud要调用Cloud体系内的服务接口,核心问题就是如何获取到目标服务地址。我们可以直接使用原生的eureka, ribbon库实现这一功能:
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
<version>1.7.0</version>
</dependency>
ServiceAddressSelector.xml
package com.dfs.pos.gateway.cloud; import java.io.IOException; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ServiceAddressSelector {
/**
* 默认的ribbon配置文件名, 该文件需要放在classpath目录下
*/
public static final String RIBBON_CONFIG_FILE_NAME = "ribbon.properties";
private static final Logger log = LoggerFactory.getLogger(ServiceAddressSelector.class);
private static RoundRobinRule chooseRule = new RoundRobinRule();
static {
log.info("开始初始化ribbon");
try {
// 加载ribbon配置文件
ConfigurationManager.loadPropertiesFromResources(RIBBON_CONFIG_FILE_NAME);
} catch (IOException e) {
e.printStackTrace();
log.error("ribbon初始化失败");
throw new IllegalStateException("ribbon初始化失败");
}
// 初始化Eureka Client
DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), new DefaultEurekaClientConfig());
log.info("ribbon初始化完成");
} /**
* 根据轮询策略选择一个地址
*
* @param clientName
* ribbon.properties配置文件中配置项的前缀名, 如myclient
* @return null表示该服务当前没有可用地址
*/
public static AlanServiceAddress selectOne(String clientName) {
// ClientFactory.getNamedLoadBalancer会缓存结果, 所以不用担心它每次都会向eureka发起查询
DynamicServerListLoadBalancer lb = (DynamicServerListLoadBalancer) ClientFactory
.getNamedLoadBalancer(clientName);
Server selected = chooseRule.choose(lb, null);
if (null == selected) {
log.warn("服务{}没有可用地址", clientName);
return null;
}
log.debug("服务{}选择结果:{}", clientName, selected);
return new AlanServiceAddress(selected.getPort(), selected.getHost());
} /**
* 选出该服务所有可用地址
*
* @param clientName
* @return
*/
public static List<AlanServiceAddress> selectAvailableServers(String clientName) {
DynamicServerListLoadBalancer lb = (DynamicServerListLoadBalancer) ClientFactory
.getNamedLoadBalancer(clientName);
List<Server> serverList = lb.getReachableServers();
if (serverList.isEmpty()) {
log.warn("服务{}没有可用地址", clientName);
return Collections.emptyList();
}
log.debug("服务{}所有选择结果:{}", clientName, serverList);
return serverList.stream().map(server -> new AlanServiceAddress(server.getPort(), server.getHost()))
.collect(Collectors.toList());
}
}
使用方法很简单:
// 选择出myclient对应服务全部可用地址
List<AlanServiceAddress> list = AlanServiceAddressSelector.selectAvailableServers("myclient");
System.out.println(list); // 选择出myclient对应服务的一个可用地址(轮询), 返回null表示服务当前没有可用地址
AlanServiceAddress addr = AlanServiceAddressSelector.selectOne("myclient");
System.out.println(addr);
这样就获取到了目标服务的URL,然后可以通过Http Client之类的方式发送HTTP请求完成调用。当然这样远没有Spring Cloud体系中使用Feign组件来的方便,但是对于代码已经变成天书的老项目来说这不算什么了。
上面这个类工作的前提是提供ribbon.properties文件,该文件指定了eureka地址和服务名相关信息:
# myclient对应的微服务名
myclient.ribbon.DeploymentContextBasedVipAddresses=S3 # 固定写法,myclient使用的ribbon负载均衡器
myclient.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList # 每分钟更新myclient对应服务的可用地址列表
myclient.ribbon.ServerListRefreshInterval= # 控制是否注册自身到eureka中
eureka.registration.enabled=false # eureka相关配置
eureka.preferSameZone=true
eureka.shouldUseDns=false
eureka.serviceUrl.default=http://x.x.x.x:/eureka eureka.decoderName=JacksonJson
另外,DiscoveryManager.getInstance().initComponent()方法已经被标记为@Deprecated了,但是ribbon的DiscoveryEnabledNIWSServerList组件代码中依然是通过DiscoveryManager来获取EurekaClient对象的:
DiscoveryClient discoveryClient = DiscoveryManager.getInstance()
.getDiscoveryClient();
因此这里只能用过时方法,否则ribbon获取不到Eureka Client,程序跑不通。
使用原生Feign调用HTTP接口
如果你的老项目有幸可以使用Feign, 那就能大大简化HTTP调用流程。我们可以使用原生Feign代替Http Client。先定义Feign接口:
public interface ClientIdRemoteService {
@RequestLine("POST /client/query")
@Headers("Content-Type: application/x-www-form-urlencoded")
@Body("uuid={uuid}")
String getClientId(@Param("uuid") String uuid);
}
下面是Spring配置类:
@Configuration
public class NonCloudFeignConfig {
private static final Logger log = LoggerFactory.getLogger(NonCloudFeignConfig.class); @Autowired
private ObjectFactory<HttpMessageConverters> messageConverters; @Bean
public ClientIdRemoteService clientIdRemoteService() {
log.info("初始化获取uuid服务的Feign接口");
return Feign.builder()
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters))
.target(ClientIdRemoteService.class, "http://xxxx.com");
}
}
这时在代码中就可以通过
@Autowired
private ClientIdRemoteService service; String result = service.getClientId("uuid");
的方式调用了。做异常处理的话可以自定义Feign的ErrorDecoder,然后在调用Feign.builder()的时候将你的ErrorDecoder传进去。
如果你项目的Spring版本不支持注解式配置,那么也可以通过编程的方式手动将Feign代理对象放到上下文中。
非Java应用接入Spring Cloud的技术方案
正是因为Spring Cloud Netflix架构体系中所有的服务都是通过HTTP协议来暴露自身,利用HTTP的语言无关性,我们才有了将老项目甚至非Java应用纳入到该体系中的可能。假如某个使用Node.js实现的项目想将自己变成服务供其它服务调用(或自己去调用别人的服务),可选择的方案有:
Spring Sidecar项目
原理是启动一个node.js对应的代理应用sidecar, sidecar本身是用spring cloud实现的,会将自身注册到eureka中,此时这个sidecar应用逻辑上就代表使用nodejs实现的服务,并且它同时也集成了ribbon, hystrix, zuul这些组件。 其他服务在调用node.js时,eureka会返回sidecar的地址,因此请求会发到sidecar,sidecar再将你的请求转发到node.js。当node.js想要调用别人的服务时,node.js需要向sidecar发请求, 由sidecar替node.js发HTTP请求,最后再将结果返回给node.js。直接使用eureka的HTTP接口
由于eureka也是通过HTTP协议的接口暴露自身服务的,因此我们可以在node.js中手动发送HTTP请求实现服务的注册、查询和心跳功能。eureka接口描述信息可以在官方github的wiki中找到。
总结
通过HTTP协议的语言无关性优势,我们得到了非java应用接入Spring Cloud架构体系中的能力。但带来的其实是性能上的开销,毕竟HTTP是基于字符的协议,它的解析速度不如二进制协议。同时Spring Boot基于Tomcat和SpringMVC也是比较臃肿笨重的。但近几年Spring Boot的流行说明Javaweb正在向着简单化、轻量化的方向发展,说不定以后可能会彻底淘汰Servlet容器,转而使用Netty这样的通讯框架代替。
转自:http://blog.csdn.net/neosmith/article/details/70049977
传统Java Web(非Spring Boot)、非Java语言项目接入Spring Cloud方案的更多相关文章
- 传统Java Web(非Spring Boot)、非Java语言项目接入Spring Cloud方案--temp
技术架构在向spring Cloud转型时,一定会有一些年代较久远的项目,代码已变成天书,这时就希望能在不大规模重构的前提下将这些传统应用接入到Spring Cloud架构体系中作为一个服务以供其它项 ...
- ASP.NET CORE(C#)与Spring Boot MVC(JAVA)
干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结 目录 C# VS JAVA 基础语法类比篇: 一.匿名类 二.类型初始化 三.委托(方 ...
- 利用spring boot创建java app
利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...
- 一起学JAVA之《spring boot》03 - 开始spring boot基本配置及项目结构(转)
<div class="markdown_views"> <h3 id="一导航"><a name="t0"& ...
- Java Spring Boot VS .NetCore (九) Spring Security vs .NetCore Security
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- Spring Boot 获取 java resources 下文件
Spring Boot 获取 java resources 下文件 Spring Boot 获取 resources 目录下的目录(例:获取 resources 目录下的 template 目录): ...
- 曹工说Spring Boot源码(25)-- Spring注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提Jar包破解
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用
前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...
- Spring Boot入门教程1、使用Spring Boot构建第一个Web应用程序
一.前言 什么是Spring Boot?Spring Boot就是一个让你使用Spring构建应用时减少配置的一个框架.约定优于配置,一定程度上提高了开发效率.https://zhuanlan.zhi ...
随机推荐
- HPU第三次积分赛-D:Longest Increasing Subsequence(DP)
Longest Increasing Subsequence 描述 给出一组长度为n的序列,a1,a2,a3,a4...an, 求出这个序列长度为k的严格递增子序列的个数 输入 第一行输入T ...
- Fedora初体验
========1. 下载https://getfedora.org/zh_CN/workstation/download/下载如下2个文件:Fedora-Workstation-Live-x86_6 ...
- shutil模块(高级的文件、文件夹、压缩包处理模块)
shutil 模块 高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length])将文件内容拷贝到另一个文件中 import shutil s ...
- python------模块定义、导入、优化 ------->re模块
该部分内容引用自:http://www.cnblogs.com/alex3714/articles/5161349.html 谢谢原博主的文章,只是自己学习,无其他使用. re模块 常用正则表达式符号 ...
- spring+springMVC,声明式事务失效,原因以及解决办法
http://blog.csdn.net/z69183787/article/details/37819627#comments 一.声明式事务配置: <bean id="transa ...
- Go Example--值运算
package main import "fmt" //通过import导入fmt标准包 func main() { //+号可以用做连接字符串 fmt.Println(" ...
- baidu voice tts build
C++: g++ -c base64.cpp -o base64.o copy json.a curl.a to project root dir g++ sample.cpp -L. -lj ...
- linux常见系统调用函数列表
以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数.这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完 ...
- TP5 中引入第三方类库
通过了解tp5的目录结构,我们知道其中有两个目录是纺织扩展类库文件的. extend是放置自定义或者其他类文件的. vendor目录是放置composer类库文件的. 当我们的第三方类库文件是下载的, ...
- 十二、springboot(七)打包启动
1.打包 mvn clean package -Dmaven.test.skip=true2.运行 a.不能后台运行 java -jar 项目.jar b.可后台运行 java ...