本节我们主要讨论一下异构平台(比如,nodejs、python、php等提供的Rest接口服务)的服务,怎么通过spring cloud组件对这些服务注册到eureka中心以及与在微服务中怎么和异构平台的服务进行通信。这里主要是通过spring cloud的sidecar来构建异构平台的服务注册与通信。
sidecar灵感来自Netflix Prana。它可以获取注册中心的所有微服务实例的信息(例如host,端口等)的http api。也可以通过嵌入的Zuul代理来代理服务调用,该代理从Eureka获取其路由条目。 Spring Cloud配置服务器可以通过主机查找或通过嵌入的Zuul直接访问。
涉及到的服务如下:
服务名 | 端口 | 用途 |
---|---|---|
eureka-server | 8100 | 服务注册与发现 |
nodeSidecar | 8130 | 异构服务对接服务 |
node | 3000 | nodejs服务 |
consumer | 8106 | 消费者服务,与node服务存在声明式调用 |
先来看一下引入Sidecar需要做一些什么。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-netflix-sidecarartifactId>
dependency>
dependencies>
@EnableCircuitBreaker
@EnableZuulProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(SidecarConfiguration.class)
public @interface EnableSidecar {
}
包含了网关 Zuul 以及微服务结构中不可或缺的熔断器 Hystrix
server:
port: 8130
spring:
application:
name: nodeSidecar
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:9090/eureka/
sidecar:
port: 3000
health-uri: http://localhost:${sidecar.port}/health
声明服务名和注册中心地址都没什么问题,最核心的就是 sidecar 的几个配置,包括
需要注意的是:Node.js 的微服务应用必须实现一个/health健康检查接口,Sidecar 应用会每隔几秒访问一次该接口,并将该服务的健康状态返回给 Eureka,该接口只需要返回{ status: 'UP' }
这样一串Json即可。
var http = require('http');
var url = require("url");
var path = require('path');
// 创建server
var server = http.createServer(function(req, res) {
// 获得请求的路径
var pathname = url.parse(req.url).pathname;
res.writeHead(200, { 'Content-Type' : 'application/json; charset=utf-8' });
if (pathname === '/index') {
res.end(JSON.stringify({ "index" : "欢迎来到首页" }));
}
else if (pathname === '/health') {
res.end(JSON.stringify({ "status" : "UP" }));
}
// 其他情况返回404
else {
res.end("404");
}
});
// 创建监听,并打印日志
server.listen(3000, function() {
console.log('listening on localhost:3000');
});
准备好eureka,nodeSidecar,node服务。按顺序启动eureka->node服务->nodeSidecar
http://localhost:8100/eureka-server
,nodeSidecar服务以被注册到Eureka上,并且状态为UP。[
{
"host": "192.168.216.1",
"port": 3000,
"uri": "http://192.168.216.1:3000",
"metadata": {
"management.port": "8130"
},
"serviceId": "NODESIDECAR",
"secure": false,
"instanceInfo": {
"instanceId": "localhost:nodeSidecar:8130",
"app": "NODESIDECAR",
"appGroupName": null,
"ipAddr": "192.168.216.1",
"sid": "na",
"homePageUrl": "http://192.168.216.1:3000/",
"statusPageUrl": "http://192.168.216.1:8130/actuator/info",
"healthCheckUrl": "http://192.168.216.1:8130/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "nodeSidecar",
"secureVipAddress": "nodeSidecar",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "192.168.216.1",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1537408212824,
"lastRenewalTimestamp": 1537408361916,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1537408212022
},
"isCoordinatingDiscoveryServer": false,
"metadata": {
"management.port": "8130"
},
"lastUpdatedTimestamp": 1537408212824,
"lastDirtyTimestamp": 1537408211890,
"actionType": "ADDED",
"asgName": null
},
"scheme": null
}
]
可以看到,该实例维护了 Node 应用的访问地址"uri": “http://localhost:3000”,这也是接下来要说的:其他微服务可以通过 Sidecar 的服务名声明式调用 Node 服务。以上,我们已经验证了 Eureka 可以通过 Sidecar 间接的管理基于 Node 的微服务。而在微服务体系中,还有非常重要的一点,就是服务间的调用。Spring Cloud 允许我们使用服务名进行服务间的调用,摒弃了原先的固定写死的 IP 地址,便于服务集群的横向拓展及维护。那么,Non-JVM 的微服务与其他服务间是否可以通过服务名互相调用呢,答案是可以的。
我们假设下面一个场景,node服务提供了/index接口,返回json字符串。而 consumer服务需要访问node服务拿到返回的json数据。也就是 consumer服务需要访问 node服务 的/index接口拿到json字符串。
// 创建server
var server = http.createServer(function(req, res) {
// 获得请求的路径
var pathname = url.parse(req.url).pathname;
res.writeHead(200, { 'Content-Type' : 'application/json; charset=utf-8' });
// 访问http://localhost:8060/,将会返回{"index":"欢迎来到首页"}
if (pathname === '/index') {
res.end(JSON.stringify({ "index" : "欢迎来到首页" }));
}
// 访问http://localhost:8060/health,将会返回{"status":"UP"}
else if (pathname === '/health') {
res.end(JSON.stringify({ "status" : "UP" }));
}
// 其他情况返回404
else {
res.end("404");
}
});
@FeignClient("nodeSidecar")
public interface NodeServiceClient {
@GetMapping("/index")
String getIndex();
}
注意到@FeignClient注解中调用的服务名填写的是 nodeSidecar (大小写不敏感),因为自始至终 Eureka 中注册的是 Sidecar 的信息,而 Sidecar 实例维护了 node服务 的地址信息,所以它可以将请求转发至 node服务。
其他微服务可以通过 Sidecar 实例的服务名间接调用 Node 服务。同样的,node服务 也可以通过服务名调用其它微服务,这要归功于@EnableZuulProxy。
访问http://localhost:8130/consumer/index惊讶的发现,这和我们访问http://localhost:8106/index结果是一样的,这是由于 Sidecar 引入了 Zuul 网关,它可以获取 Eureka 上注册的服务的地址信息,从而进行路由跳转。因此,node访问其它微服务的地址格式为:http:localhost:8130/{serviceName}/method
另外,可以直接使用eureka的HTTP接口集成异构服务。由于eureka也是通过HTTP协议的接口暴露自身服务的,因此我们可以在node.js中手动发送HTTP请求实现服务的注册、查询和心跳功能。eureka接口描述信息可以在官方github的wiki中找到。但是这种方式存在弊端: