上手指南
安装
要在另一个项目中安装和使用:
$ npm install hydra-express
用法
'use strict'; const config = require('./config/properties').value; const version = require('./package.json').version; const hydraExpress = require('hydra-express'); function registerRoutesCallback() { hydraExpress.registerRoutes({ '/v1/offers': require('./offers-v1-api') }); } function registerMiddlewareCallback() { let app = hydraExpress.getExpressApp(); app.use((req, res, next) => { console.log('req.headers', req.headers); next(); }); } hydraExpress.init(config, version, registerRoutesCallback, registerMiddlewareCallback) .then((serviceInfo) => { console.log('serviceInfo', serviceInfo); }) .catch((err) => { console.log('err', err); });
在上面的示例中,then 语句上的 serviceInfo
返回一个对象, 其中包含 serviceName
,servicePort
和其他有用值。
日志记录和错误报告
HydraExpress 包含一个 log
成员,允许您输出日志到控制台和日志文件。
hydraExpress.log('error', message);
log
的第一个参数是日志消息的类型:fatal
、error
、debug
或 info
。第二个参数是要存储的字符串消息。强烈建议您利用这个机会创建描述性很强的日志消息,因为此函数不记录堆栈跟踪。
此外,将 fatal
或 error
类型的日志消息发送到 hydra-core
, 以便在服务运行状况检查(health check
)日志中进行日志记录。
服务静态 Web 内容
hydra-express 服务可以服务静态 Web 内容。只需创建一个名为 public
的文件夹,然后将网站文件复制到其中即可。可以在 demo/webserver
文件夹中找到一个示例。
Hydra Cli
上手指南
首先,您需要安装 hydra-cli
:
$ sudo npm install -g hydra-cli
您只需在终端中输入程序名称即可查看 hydra-cli
的所有选项。
$ hydra-cli hydra-cli version 0.5.7 Usage: hydra-cli command [parameters] See docs at: https://github.com/flywheelsports/hydra-cli A command line interface for Hydra services Commands: help - this help list cfg pull label - download configuration file cfg push label filename - update configuration file cfg list serviceName - display a list of config versions config instanceName - configure connection to redis config list - display current configuration use instanceName - name of redis instance to use health [serviceName] - display service health healthlog serviceName - display service health log message create - create a message object message send message.json - send a message nodes [serviceName] - display service instance nodes refresh node list - refresh list of nodes rest path [payload.json] - make an HTTP RESTful call to a service routes [serviceName] - display service API routes services [serviceName] - display list of services shell - display command to open redis shell
如您所见,hydra-cli
可以做很多事情。
配置 hydra-cli
要使用大多数 hydra-cli
命令,您首先需要对其进行配置,方法是将其指向您正在使用的 Redis 实例。
$ hydra-cli config local
config
命令需要一个你想要关联到 Redis 连接信息的名称。这允许您为多个环境存储配置设置。例如,您可能已经为您的项目 local
、staging
和 production
存储了设置。
在存储的设置之间切换很容易:
$ hydra-cli use staging
您可以使用 config list
命令查看当前选择的设置。
$ hydra-cli config list
与 hydra 配置文件一起工作
Hydra 配置文件,不要与 hydra-cli 配置设置混淆,服务在初始化 hydra 或 hydra-express 时会使用它们。
这些配置文件通常在运行时加载,并将其内容传递给 Hydra。
在启动过程中,如果 Hydra 看到 HYDRA_REDIS_URL
和 HYDRA_SERVICE
环境变量, 则 Hydra
会向指定的 Redis
实例询问其配置文件的副本。
应该通过以下方式定义环境变量:
HYDRA_REDIS_URL='redis://10.0.0.2:6379/15' HYDRA_SERVICE='myservice:0.12.1'
这通常用于 Docker 容器中启用 hydra 的应用。
Hydra-cli 提供 cfg
命令,用于列出(listing
)、加载(loading
)和上传(uploading
)配置文件数据到 Redis。
你可以使用下面的命令来获取配置列表:
$ hydra-cli cfg list myservice
为了存储配置,您必须指定由冒号和服务版本分隔的服务名称。
$ hydra-cli cfg pull myservice:0.12.1
使用上面的 cfg pull
命令,检索到的配置将显示在终端中。要将调出的配置保存到一个文件中,你可以使用:
$ hydra-cli cfg pull myservice:0.12.1 > config.json
要上传一个配置,你可以使用 cfg push
命令:
$ hydra-cli cfg push myservice:0.12.2 config.json
列出配置,检索一个配置并将其保存到文件中——然后在上传之前修改它,这就是管理服务配置的方法。
列出服务信息
Hydra 的一个非常好的特性是, 运行 Hydra 的每个应用程序都会发出运行状况(health
)和存活状态(presence
)信息。使用 hydra 的任何应用程序都可以检查这些信息。
hydra-cli
程序实际上只是一个运行 Hydra
的命令行客户端——它的大部分功能都是由 Hydra 提供的。
我们可以使用 nodes
命令查看节点列表:
$ hydra-cli nodes
许多 Hydra 驱动的应用程序导出 API 路由。我们可以使用以下方法查看服务路由列表
:
hydra-cli routes
您可以使用 health
命令检索服务的健康状态。
$ hydra-cli health
如果指定了服务名称,则只能看到该服务的运行状况信息。
$ hydra-cli health myservice
节点列表清理
如果您启动和停止服务,最终将看到不再处于活动状态的服务。这出现在 hydra-cli
节点命令期间。这个列表没有被自动清除的关键原因是它对于调试和监视非常有用。您必须使用 refresh
命令手动清除死服务列表。
$ hydra-cli refresh
快速连接到 Redis
如果需要,您可以要求 hydra-cli
提供与 redis-cli
客户端一起使用的连接字符串。
$ hydra-cli shell
在运行 Mac 或 Linux 的计算机上,您可以发出以下命令来自动调用 redis-cli
:
$(hydra-cli shell)
下一步
玩 hydra-cli
。我们发现它是使用 Hydra 应用程序时必不可少的工具。
阅读项目仓库中的完整文档
Hydra 生产器
Hydra Generator 是一个命令行工具,可让您快速构建 Hydra 或 Hydra-Express 应用程序的完整脚手架。生成器依赖于称为 Yeoman 的工具。
生成器的伟大之处在于,您可以在不到15秒的时间内构建微服务。然后,您可以继续自定义生成的代码以适合您的特定需求。
快速上手
首先全局安装 Yeoman
和 generator
:
$ sudo npm install -g yo generator-fwsp-hydra
要使用生成器,只需使用生成器的名称调用 yeoman
即可。在我们的案例中,hydra-generator
被称为 fwsp-hydra
。您收到的第一个提示要求您为服务命名。
$ yo fwsp-hydra ? Name of the service (`-service` will be appended automatically) hello
在出现许多其他问题(您可以选择 default
)之后,该过程以关于如何构建和启动新项目的说明结束。
Done! 'cd example-service' then 'npm install' and 'npm start'
请记住 Hydra 服务需要使用 Redis 实例。所以在你运行你的应用程序之前,你需要 redis 可用。默认情况下,Hydra 生成器将创建一个配置文件,该文件需要一个本地的 Redis 实例。
{ "environment": "development", "hydra": { "serviceName": "hello-service", "serviceIP": "", "servicePort": 5000, "serviceType": "hello", "serviceDescription": "says hello", "redis": { "url": "redis://127.0.0.1:6379/15" } } }
Hydra-Router
Hydra Router 是一种服务感知路由器,可以将 HTTP
和 WebSocket
消息请求定向到已注册的服务。
尝试 Hydra-router
尝试使用 hydra-router
的最简单方法是获取现成的Docker容器。
$ docker pull flywheelsports/hydra-router
尽管以上命令将起作用,但我们建议您访问 https://hub.docker.com/r/flywheelsports/hydra-router/tags/ 并提取特定版本,例如:
$ docker pull flywheelsports/hydra-router:1.3.3
运行容器要求您具有正在运行的 Redis
本地实例, 并且需要使用计算机上的 ifconfig
或 ipconfig
工具标识计算机的 IP
地址。
$ docker rm -f hydra-router $ docker run -d -p 5353:5353 --add-host host:10.1.1.175 --name hydra-router flywheelsports/hydra-router:1.3.3
然后,您应该能够将Web浏览器指向 http://10.1.1.175:5353
并发出路由器请求,例如:
http://10.1.1.175:5353/v1/router/version
拉取最新的 Docker 容器
Hydra-router docker 镜像被存储在这里:https://hub.docker.com/r/flywheelsports/hydra-router/tags/
从源码构建
您还可以获取 hydra-router
源代码并在本地使用它。
https://github.com/flywheelsports/hydra-router
简介
使用 HydraRouter
外部客户端可以连接到服务,而不需要知道它们的IP或端口信息。HydraRouter 负责服务发现和路由。
此外,HydraRouter
还可以路由到由某个服务托管的网站。 如果使用服务名作为第一个 url
路径段访问路由器,并且请求是 HTTP GET
调用, 那么请求将被路由到一个可用的服务实例。
当一种服务类型存在多个服务实例时,通过 HydraRouter
发出的请求将在可用实例之间进行负载平衡。
Hydra
路由器还公开了 RESTful
端点,可用于查询服务运行状况(health
)和存活状态(presence
)信息。
使用 Hydra 微服务可以使用 findService
、sendServiceMessage
和 makeAPIRequest
等函数相互定位。 这一切都运行得很好,不需要 DNS 或 service router
。
但是,当远程API请求到达云基础架构时,确定如何灵活地路由针对上游服务的请求就成为问题。 考虑服务可以使用不同的IP和/或随机端口启动。 为了满足这些要求,一种方法涉及使用DNS,弹性负载均衡器和弹性IP。 仍然必须管理连接到每个负载均衡器的机器, 并且在一台机器上运行多种服务会使情况进一步复杂化。
这是动态服务注册和路由器起作用的地方。它们旨在通过感知服务并执行智能路由来简化上述要求。
Hydra-Router
使用 Hydra 来实现动态服务注册表和路由器。 为此,它使用启用了 Hydra 的服务在其启动和初始化阶段发布的路由信息。 然后,它将传入的消息直接路由到服务,而不考虑以下挑战:
- 可能有一个或多个服务实例可用于处理特定请求。
- 服务可以来来去去,每次都以不同的IP地址或端口开始。
- 随着服务的添加或改进,服务路由可能会更改(更新或删除)。
- 不需要对基础设施进行任何更改来解决上述问题。
那么这是如何运作的呢?
如前所述,在启动期间,Hydra 服务执行自动注册。 这是在调用 hydra.registerService
方法时在后台完成的。 使用 Hydra-Express 构建服务时,可以在初始化阶段自动注册服务的路由。服务的路由可以在初始化阶段自动注册。
hydraExpress.init(config, version, () => { hydraExpress.registerRoutes({ '/v1/offers': require('./offers-v1-api') }); });
然后,HydraRouter
使用生成的服务注册信息将消息路由到特定服务。
服务可以在网络上的任何机器上启动,无论是否使用随机 IP 端口。 因为每个服务都是自己注册的-它可以被一个 HydraRouter 定位。这是动态服务注册表位。
但它真的是 router 吗?是的! Hydra-Router 使用 route-parser
— 一种基于 AST 的树解析器来匹配路由。
当消息被发送到 HydraRouter
时,它会检查请求是否与已注册的路由匹配。如果是,则将请求消息路由到注册了该路由的服务的活动实例。当一个服务存在多个服务实例时,Hydra-Router
将对请求进行负载平衡,以将负载分布在可用的服务之间。
这里的一个关键要点是,这是自动发生的,不需要更新配置和基础设施。 这可以使用 Hydra
内置的服务发现和路由功能。
消息网关
除了将普通的 HTTP 消息路由到它们指定的服务之外,HydraRouter 还为其他传入消息公开一个 HTTP 端点。
/v1/router/message
消息预期采用UMF消息格式,因此可以路由到网络中的其他微服务。
网站流量透传
Hydra-router 能够将站点请求路由到微服务。 因此,除了响应 RESTful API 调用和处理消息外,微服务还可以为网站提供服务。此特性并不适用于高流量使用的场景。 相反,该特性用于管理页面、状态页面和其他低流量的情况。 虽然可以提供图像和其他二进制资产——建议您使用CDN来卸载对公共静态资产的请求。
使用此特性的好处是,您可以在任意IP上的动态端口上启动服务,并利用路由器查找各个服务实例。 通过这种方式,网站请求可以由多个负载均衡的服务实例来处理。
HTTP 代理透传
HydraRouter 允许您指定到非 hydra 服务的路由。 本质上,这允许外部客户端通过 hydra 向后端服务器发出 API 请求。
要启用此功能,只需在配置文件中的 externalRoutes
键下定义外部路由。 externalRoutes
key 由url对象和它们的路由数组组成。
: 'externalRoutes': { 'https://someotherservice.com': [ '[post]/api/v2/token/create', '[get]/api/v2/user/self.json' ] }, :
WebSockets
Hydra Router 支持 WebSocket
连接。支持以下方案:
- 客户端连接到
hydra-router
并将消息发送到后端服务 - 后端服务可以将异步消息发送回特定客户端
- 客户端可以通过
hydra-router
向彼此发送消息
有关构建此类应用程序的更多信息,请参见 Hydra Router Message Client 文档。
不过,这里有一个问题——只支持 stringified UMF 格式的消息。
let ws = new WebSocket('ws://127.0.0.1:5353'); ws.on('open', () => { let msg = { 'to': 'a-hydra-service:/', 'version': 'UMF/1.4.3', 'from': 'tester:/', 'mid': '4736ef3d-fcbb-46aa-80a0-f4f3493e1d74', 'timestamp': '2017-01-12T20:16:29.157Z', 'body': {} }; ws.send(JSON.stringify(msg)); });
您可以使用 Hydra UMFMessage helper class 创建 UMF 消息。
WebSocket 重连接和消息传递
如果客户端的 WebSocket
连接中断,Hydra-Router 支持为您的 WebSocket 客户端提供消息队列。 您的客户端仅需要重新连接并发出重新连接消息即可开始接收以前的消息。
初始连接后,您的 WebSocket 客户端将收到一条类似于以下内容的消息:
{ "to": "2945p8eigxz@client:/", "frm": "c274b25909aee5cbec2857361f425fa7@hydra-router:/", "mid": "dffc2949-0e2a-4417-8f28-46addb5fc716", "ts": "2017-01-12T19:31:54.831Z", "typ": "connection", "ver": "UMF/1.4.3", "bdy": { "id": "2945p8eigxz" } }
bdy.id
值是 WebSocket session ID
。 如果您的客户端死亡并且需要重新连接,它可以发出一条重新连接消息,例如:
{ "to": "hydra-router:/", "frm": "client:/", "mid": "e173a0da-2785-4f83-8b39-0dea954dd91b", "typ": "reconnection", "ver": "UMF/1.4.3", "bdy": { "id": "2945p8eigxz" } }
上面有三件事要注意。首先,消息被发送到 hydra-router
,后者管理实际的 WebSocket 连接。 其次,bdy.id
与客户端崩溃或失去连接之前所拥有的 WebSocket 会话ID相同。 第三,我们使用“reconnection
”的消息类型(typ
)。
收到这样的消息后,Hydra-Router
将加载排队的消息(如果有)并将其开始发送到新连接的客户端。
保护 Websocket 消息
Hydra-Router 支持使用加密签名的UMF消息。 启用此功能后,仅接受签名的消息。 未签名的消息将导致底层 socket 连接终止。
签名的消息仅确保消息是由已知客户端创建的,而其本身并不对消息内容进行加密。 要加密消息内容,请考虑使用其他加密方法并转换为 BASE64。
要启用签名消息,请在 config.json
文件中添加两个字段。 确保 forceMessageSignture
设置为 true
,并且 signatureSharedSecret
包含 UUID
或密码
"forceMessageSignature": true, "signatureSharedSecret": "d632dd6d-fb75-44cc-bdbf-ee1364f3716c",
HydraRouter 使用 HMAC SHA-256 通过提供的 signatureSharedSecret
对消息进行签名。
crypto .createHmac('sha256', signatureSharedSecret) .update(JSON.stringify(this.message)) .digest('hex');
因为 Websocket 消息是从外部客户端发送的, 所以每个客户端必须能够使用 HMAC SHA-256, 使用与 HydraRouter
的 config.json
文件中存储的相同的共享 secret
对 UMF
消息进行签名。 由于很难在 Web浏览器客户端中保护 secret
,因此不建议将其用于 Web 客户端。 建议编译并能够使用安全存储的微服务和移动应用程序。
从 1.4.28
版本开始,Hydra
支持对 UMF 消息进行签名, 从而可以轻松保护微服务之间的消息。 为 IOS
或 Android
编写的客户端应用程序需要使用加密库。
在 Hydra 中,您可以使用:
'use strict'; const WebSocket = require('ws'); const hydra = require('hydra'); const signatureSharedSecret = 'd632dd6d-fb75-44cc-bdbf-ee1364f3716c'; let ws = new WebSocket('http://localhost:5353'); ws.on('open', () => { let umf = hydra.createUMFMessage({ 'to': 'hydra-router:[GET]/v1/router/list/nodes', 'from': 'client:/', 'body': {} }); umf.signMessage('sha256', signatureSharedSecret); ws.send(JSON.stringify(umf)); }); ws.on('message', (data, flags) => { console.log(data); });
路由仪表板
Hydra-Router 可以显示一个仪表板,以显示其自身和其他服务的状态。 仪表板每15秒更新一次,红色显示有问题的服务。
要访问路由器,只需将浏览器指向 hydra-router
的根目录即可:
http://localhost:5353/
如果您需要停用或限制对仪表板的访问,请参阅保护 hydra-router 一节。
可选的 Router API
Hydra-Router
提供了 HTTP API
,以公开其正在使用的路由和服务。 这是完全可选的,旨在用于调试和监视方案。
Router version: /v1/router/version
查询 Hydra-Router
的版本。
$ curl -X 'GET' 'http://localhost:8000/v1/router/version'
响应:
{ 'status': 200, 'statusText': 'Success', 'result': { 'version': '1.0.0' } }
Listing routes: /v1/router/list/routes
用于显示已注册路由的列表。请注意,Hydra-Router
本身是一种服务,它显示自己的 API
。
{ 'status': 200, 'statusText': 'Success', 'result': [ { 'serviceName': 'hydra-router', 'routes': [ '/v1/router/version', '/v1/router/refresh', '/v1/router/list/:thing', '/v1/router/message', '/v1/router/refresh/:service' ] }, { 'serviceName': 'red-service', 'routes': [ '/v1/red/hello', '/v1/red/say' ] }, { 'serviceName': 'blue-service', 'routes': [ '/v1/blue/hello', '/v1/blue/say' ] } ] }
Listing services: /v1/router/list/services
显示活动服务实例。 在这里,我们可以看到服务存活状态信息(presence
),包括健康(health
)和正常运行时间(uptime
)等数据点。 如果服务崩溃,它将不再出现在响应中。
{ 'status': 200, 'statusText': 'Success', 'result': [ { 'serviceName': 'blue-service', 'instanceID': 'bd579b2384701aba617af40c0ff75580', 'updatedOn': '2016-05-22T00:21:11.908Z', 'processID': 51947, 'ip': '127.0.0.1', 'port': 3686, 'sampledOn': '2016-05-22T00:21:11.908Z', 'architecture': 'x64', 'platform': 'darwin', 'nodeVersion': 'v4.2.4', 'memory': { 'rss': 28045312, 'heapTotal': 31148896, 'heapUsed': 26754472 }, 'uptime': '2 minutes, 7.358 seconds', 'usedDiskSpace': '82%', 'log': [] }, { 'serviceName': 'hydra-router', 'instanceID': '4d5831c3de6feb69a6b150946753065c', 'updatedOn': '2016-05-22T00:21:11.103Z', 'processID': 51755, 'ip': '127.0.0.1', 'port': 8000, 'sampledOn': '2016-05-22T00:21:11.103Z', 'architecture': 'x64', 'platform': 'darwin', 'nodeVersion': 'v4.2.4', 'memory': { 'rss': 27168768, 'heapTotal': 18740576, 'heapUsed': 17638920 }, 'uptime': '3 minutes, 2.337 seconds', 'usedDiskSpace': '82%', 'log': [ { 'ts': '2016-05-22T00:18:10.383Z', 'serviceName': 'hydra-router', 'type': 'info', 'processID': 51755, 'message': 'Starting hydra-router service hydra-router on port 8000' } ] }, { 'serviceName': 'red-service', 'instanceID': 'a3e9a88912b49238e7254ef3cec2e4cd', 'updatedOn': '2016-05-22T00:21:09.766Z', 'processID': 51759, 'ip': '127.0.0.1', 'port': 1185, 'sampledOn': '2016-05-22T00:21:09.767Z', 'architecture': 'x64', 'platform': 'darwin', 'nodeVersion': 'v4.2.4', 'memory': { 'rss': 30908416, 'heapTotal': 31148896, 'heapUsed': 27060712 }, 'uptime': '2 minutes, 47.579 seconds', 'usedDiskSpace': '82%', 'log': [ ] } ] }
Listing nodes: /v1/router/list/nodes
列表节点请求显示可能存在也可能不存在的节点(服务的实例)。 此调用与 /list/services
调用的不同之处在于,将显示不活动的实例。
$ curl -X 'GET' 'http://localhost:8000/v1/router/nodes'
{ 'statusCode': 200, 'statusMessage': 'OK', 'statusDescription': 'Request succeeded without error', 'result': [ { 'serviceName': 'music', 'serviceDescription': 'Music service', 'version': '0.0.9', 'instanceID': '07eb06f8f8b346a78704a5d9e672a780', 'updatedOn': '2016-07-27T19:38:28.773Z', 'processID': 2209, 'ip': '10.1.1.176', 'port': 5000, 'elapsed': 2 }, { 'serviceName': 'hydra-router', 'serviceDescription': 'Service Router', 'version': '1.1.1', 'instanceID': 'ecf72192389ff6212bf88da03802adc9', 'updatedOn': '2016-07-27T19:38:29.705Z', 'processID': 2864, 'ip': '10.1.1.176', 'port': 5353, 'elapsed': 1 }, { 'serviceName': 'auth-service', 'serviceDescription': 'Authentication service', 'version': '0.0.10', 'instanceID': '5b3ade39a70aba675223edc46d8c710c', 'updatedOn': '2016-07-27T19:38:13.371Z', 'processID': 2487, 'ip': '10.1.1.176', 'port': 1337, 'elapsed': 17 } ] }
Route Refresh: /v1/router/refresh/:service
当基于 hydra 启用的 web 服务它们在线自动启用。
示例路由透传:
通过 Hydra-Router
将消息发送到称为 red-service
的服务的示例:
$ curl -X 'GET' 'http://localhost:8000/v1/red/hello'
响应:
{ 'code': 200, 'result': { 'message': 'Hello from red-service' } }
您可能已经注意到上面的响应中有一点不一致。 前面的示例显示 status
、statusText
和 result
JSON 字段。 上面的例子不是这样的!原因是 Hydra-Router 返回从服务端点发送的确切的(未翻译的)服务器响应。
保护 router
默认情况下,路由器API是公开的。 在生产部署中,您可能会禁用或限制对路由器 API 的访问权限。 您可以通过在 config.json
文件中定义两个 key
来完成此操作:
"disableRouterEndpoint": false, "routerToken": "",
如果 disableRouterEndpoint
设置为 true
, 则将禁用对路由器 API 的访问,并且调用者将收到 HTTP 404
响应。 如果启用,则对路由器 API
的访问取决于 routerToken
的值。 如果令牌为空,则允许访问-如果存在值,则它必须是 UUIDv4
token。
"disableRouterEndpoint": false, "routerToken": "098ebe18-7e1b-4ddd-ae2a-cc6521e5b641",
破折号和段的大小很重要,并使用正则表达式进行了验证。
Hydra-Router 将搜索 token
的存活状态(presence
)作为查询字符串参数。
http://localhost:5353/v1/router/list/routes?token=098ebe18-7e1b-4ddd-ae2a-cc6521e5b641
问题
当服务更改了它的路由时会发生什么?
好问题! 启动服务时,除了注册自身和发布其路由外,它还会向所有 Hydra-Router
服务广播一条消息, 以便它们可以为新更新的服务更新其路线信息。 这是基于每个服务完成的,因此其他服务路由不会受到影响。
如果服务实例不可用怎么办?
如果找不到服务的活动实例,则 Hydra-Router 会回复并显示标准 HTTP 503
(HTTP_SERVICE_UNAVAILABLE)错误。
那么有什么问题吗?
HydraRouter
只能与启用了 Hydra
的服务一起使用,并且只能路由 JSON 消息有效负载。 但是,支持最常见的 HTTP 动词,因此您可以发送 GET,POST,DELETE 和 PUT 请求。
Hydra Router 消息客户端
注意:hrmc
需要 hydra-router
1.6.0 或更高版本
作为一个消息传递网关,Hydra Router 支持 WebSocket 连接和消息路由到连接的客户端和微服务。 在开发基于消息的客户端和服务时,有必要测试消息流。 这使得我们创建了一个名为 hrmc
的工具 —— Hydra Router Message Client。 HRMC 是基于 NodeJS 的 REPL,您可以在其中交互式地连接到 Hydra Router,以发送消息。
$ hrmc Hydra Router Message Client v.1.0.3 Use client.help() for help. Use client members or .exit to close repl.
hrmc REPL 导出一个名为 client 的类, 其中包含用于将消息连接并发送到 Hydra Router 实例的成员函数。 例如,要打开与在本地主机端口 5353
上运行的 hydra router 实例的连接,请执行以下操作:
➤ client.open('ws://localhost:5353')
关闭连接:
➤ client.close()
安装 hrmc
您可以使用 NPM 安装 hrmc:
$ npm install -g hrmc
我们应该使用 -g
全局标志来安装它,以确保它可以从您的任何项目中启动。hrmc 项目托管在github上:https://github.com/cjus/hrmc
使用 hrmc
使用 hrmc
之前,请确保您正在运行 hydra-router
实例。 如前所述,您可以使用客户端对象的成员函数在 Hydra Router
上打开和关闭连接。
要获取可用命令的列表,请使用:
➤ client.help()
可用的客户成员列表包括:open
close
reopen
createMessage
和 sendMessage
让我们通过一个示例 session 来了解这一切是如何工作的。 在这个 session 中,我们将连接到 hydra router 并调用其内部API。 然后我们将与 user service 对话。
我们首先连接到 hydra-router。在这个例子中,我正在连接 192.168.1.221:5482
的 hydra router
:
➤ client.open('ws://192.168.1.221:5482')
为了保持一致,所有 hrmc client 函数都需要一个参数-期望该参数为字符串! 因为 JSON 使用双引号,所以我们将使用单引号字符。
返回的响应为:
➤ Connection established: 27ce6oxplm5 {"to":"27ce6oxplm5@client:/","frm":"5d77f8ac3d784bc2946e4d2a2f806805@hydra-router:/","mid":"72ffb790-6634-4e97-8e48-276c223b7b0f","ts":"2018-02-19T14:43:37.407Z","typ":"connection","ver":"UMF/1.4.6","bdy":{"id":"27ce6oxplm5","ip":"::ffff:192.168.1.221"}}
上面的代码可能有点难以阅读,所以您可以使用 client.jsonPrint()
函数来漂亮地打印 JSON。
{ "to": "27ce6oxplm5@client:/", "frm": "5d77f8ac3d784bc2946e4d2a2f806805@hydra-router:/", "mid": "72ffb790-6634-4e97-8e48-276c223b7b0f", "ts": "2018-02-19T14:43:37.407Z", "typ": "connection", "ver": "UMF/1.4.6", "bdy": { "id": "27ce6oxplm5", "ip": "::ffff:192.168.1.221" } }
这就是 hydra-router 的响应。 JSON 文档采用一种名为 UMF 的格式,hydra 使用该格式进行消息传递。
有关更多信息,请参见 hydra消息传递
在 to
字段中,我们看到消息被视为来自 27ce6oxplm5@client:/
您将注意到来自 @client
的 ID
。 这是我们连接的 hrmc 的唯一客户端 ID。它与我们在上面建立连接时返回的 ID 相同。
frm
字段告诉我们,以上消息是从具有唯一服务ID 5d77f8ac3d784bc2946e4d2a2f806805
的 hydra-router 发送的,该消息的 bdy
正文部分特别重要,因为它为客户端分配了唯一的ID 27ce6oxplm5
。
让我们继续前进。 接下来,我们将创建一条新消息以发送到 Hydra Router。
➤ client.createMessage() {"to":"hydra-router:/","frm":"27ce6oxplm5@client:/","mid":"c677bf50-80be-4d2e-87e1-4d048c372b47","ts":"2018-02-19T15:26:35.835Z","ver":"UMF/1.4.6","bdy":{}}
这将创建一条基本消息,我们可以将JSON复制到编辑器并对其进行自定义。 更新 to
字段到 hydra-router:[get]/v1/router/version
当我们使用 client.jsonPrint()
函数获取响应并查看它时,我们看到:
{ "to": "27ce6oxplm5@client:/", "frm": "5d77f8ac3d784bc2946e4d2a2f806805@hydra-router:/", "mid": "312da70c-8b72-48a9-a481-77d32653e867", "ts": "2018-02-19T15:35:34.114Z", "ver": "UMF/1.4.6", "bdy": { "version": "1.6.0-experimental" } }
在撰写本文时,我使用的是 Hydra-Router 的实验版本 1.6.0。
现在,让我们谈谈 user service。
我们可以使用 client.createMessage
创建另一个消息,然后将 to
字段更改为:user-service:[get]/v1/user/health
因此,在我们要发送给 Hydra Router 的新消息中, 我们将要求它将其传递给名为 user-service
的服务, 并请求 v1/user/health
端点。 使用 client.jsonPrint()
函数查看时,响应类似于:
{ "to": "27ce6oxplm5@client:/", "frm": "user-servcie:[get]/v1/user/health", "mid": "1a92789c-e6e4-43e8-b4ad-c08999c26141", "rmid": "c677bf50-80be-4d2e-87e1-4d048c372b47", "ts": "2018-02-19T15:58:25.440Z", "ver": "UMF/1.4.6", "bdy": { "result": { "serviceName": "user-svcs", "instanceID": "a125395c70f643dfb6743dc5aba32e17", "hostName": "dac3865f9fd0", "sampledOn": "2018-02-19T15:58:25.454Z", "processID": 1, "architecture": "x64", "platform": "linux", "nodeVersion": "v8.0.0", "memory": { "rss": 54370304, "heapTotal": 28356608, "heapUsed": 25279784, "external": 66409 }, "uptimeSeconds": 287.767 } } }
更高级的例子
现在,如果我们希望后端服务将消息异步发送回连接的客户端,该怎么办?
后端服务只需要知道客户端ID即可路由消息!这是消息的格式。
{ "to": "hydra-router:/", "frm": "abackend-service:/", "fwd": "27ce6oxplm55@client:/", "mid": "5cc4e3f5-ad72-41f3-a23f-212bdabc331f", "ts": "2018-02-17T16:02:48.902Z", "ver": "UMF/1.4.6", "bdy": { "msg": "Hello from a backend service" } }
后端服务只需在 to
字段中指定网络中任何可用的 hydra-router
实例。 然后,您的服务将在 frm
字段中标识自己。 然后它将使用 fwd
(转发)字段指定应接收消息的客户端实例。 消息的 bdy
将包括您的服务要发送的任何 JSON 负载。 接收 hydra-router
实例将确保将消息路由到适当的客户端。
这种路由机制也允许客户端互相发送消息:
{ "to": "hydra-router:/", "frm": "1objkd63kfd@client:/", "fwd": "27ce6oxplm55@client:/", "mid": "5cc4e3f5-ad72-41f3-a23f-212bdabc331f", "ts": "2018-02-17T16:02:48.902Z", "ver": "UMF/1.4.6", "bdy": { "msg": "Hello from 1objkd63kfd" } }
因此,客户端又会通过 hydra-router
发送打算发送给另一个客户端的消息。
重要的是要注意,客户端不是直接与对方通话,而是通过 hydra router。
由于 Hydra-Router
旨在与其他 hydra-routers
进行通信, 因此可以将消息路由到客户端和服务,无论它们连接到哪个 hydra-router
。 自然地,所讨论的 hydra-routers
和服务需要在同一网络或可访问的网络上。
因此,在此示例中,尽管客户端 1objkd63kfd
和 27ce6oxplm55
没有连接到相同的 Hydra-Router
,但上面的相同消息仍将被路由。
支持以下方案:
- 客户端连接到
hydra-router
并将消息发送到后端服务 - 后端服务可以将异步消息发送回特定客户端
- 客户端可以通过
hydra-router
向彼此发送消息
在 Docker 上使用 Hydra
Hydra 和 HydraExpress 应用程序需要使用 Redis 服务器。 如果您在 Docker 容器中运行启用了 hydra 的微服务,则需要确保您的服务可以访问 Redis。
如:hello-service
用作 docker 容器时,您需要更新服务的配置文件,因为正在运行的容器将具有与主机不同的IP地址。 这样就不会在容器内找到 Redis!
有几种方法可以解决此问题。
方法1:在容器生成上使用硬编码 config.json
第一种方法是简单地用 Redis 服务器的硬编码配置条目构建容器。
方法2:使用 DNS 条目
另一个选择是在配置文件中指定一个 DNS 条目,它映射到你的 Redis 服务器。参见下面的 redislocation 条目。
{ "environment": "development", "hydra": { "serviceName": "hello-service", "serviceIP": "", "servicePort": 5000, "serviceType": "hello", "serviceDescription": "says hello", "redis": { "url": "redis://redislocation:6379/15" } } }
接下来,使用上面的配置重建容器,然后可以使用以下命令运行它:
$ docker run -it -d -p 5000:5000 \ --name hello-service \ --add-host redislocation:192.168.1.186 \ --workdir=/usr/src/app \ cjus/hello-service:0.0.7
然后,您可以使用以下方法测试访问容器的服务:
$ curl localhost:5000/v1/hello/test {"statusCode":200,"statusMessage":"OK","statusDescription":"Request succeeded without error","result":{"msg":"hello from hello-service - da3a2e99becc03abed949080d8fa3185"}}
方法3:映射虚拟文件夹
然而,另一种方法是运行带有映射卷的容器。在本例中,本地项目文件夹 ~/dev/hello-service/config
映射到容器的内置 /usr/src/app/config
文件夹上。因此,运行中的容器将使用您在项目文件夹中指定的配置文件。这样一来,您就可以将 config.json 文件保留在容器中,并在容器外部对其进行覆盖。
$ docker run -it -d -p 5000:5000 \ --name hello-service \ --workdir=/usr/src/app \ -v ~/dev/hello-service/config:/usr/src/app/config \ cjus/hello-service:0.0.7