做者:Kyle Galbraith翻译:疯狂的技术宅javascript
原文:https://blog.logrocket.com/me...前端
未经容许严禁转载java
在微服务架构的世界中,咱们经过一系列服务构建应用。集合中的每项服务都符合如下标准:程序员
微服务架构中的每一个服务都解决了应用中的业务问题,或至少支持一个。一个团队对应用中的一个或多个服务负责。面试
微服务架构能够解锁许多好处。数据库
这些好处是微服务愈来愈受欢迎的一个重要缘由。但有一些可能会破坏这些好处的坑。若是不当心掉进去了,你将获得一个不断产生技术债的架构。json
微服务之间的通讯就是一个坑,假如不提早考虑就会形成严重的破坏。segmentfault
该体系结构的目标是建立松散耦合的服务,而且通讯在实现这一目标中起着关键做用。在本文中,咱们将重点关注在微服务架构中进行通讯的三种方式,每一种都有其本身的利弊和权衡。api
选择服务如何相互通讯时,最直接的方式每每是 HTTP。事实上,咱们能够提出一个案例,即全部通讯渠道都来自这个渠道。可是除此以外,服务之间的 HTTP 调用是服务到服务通讯的可行选择。服务器
若是咱们的架构中有两个服务,它可能看起来像这样: ServiceA
能够请求并调用 ServiceB
来获取另外一条信息。
function process(name: string): Promise<boolean> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ return fetch('https://service-b.com/api/endpoint') .then((response) => { if (!response.ok) { throw new Error(response.statusText) } else { return response.json().then(({saved}) => { return saved }) } }) }
这是一段很容易理解的适合微服务架构的代码。 ServiceA
提供了一个业务逻辑。它运行其代码而后调用 ServiceB
来运行另外一个业务逻辑。在这段代码中,第一个服务在返回以前完成等待第二个服务完成。
这里有两个服务之间进行同步的 HTTP 调用。这是一种可行的通讯模式,但它确实在两种服务之间创建了耦合。
另外一个选择是异步 HTTP。这多是这样的:
function asyncProcess(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ return fetch('https://service-b.com/api/endpoint') .then((response) => { if (!response.ok) { throw new Error(response.statusText) } else { return response.json().then(({statusUrl}) => { return statusUrl }) } }) }
这种变化是微妙的。如今, ServiceB
不返回 saved
属性,而是返回一个 statusUrl
。这意味着此服务如今正在接收来自第一个服务的请求,而且当即返回一个URL。此 URL 可用来检查请求的进度。
将两种服务之间的通讯从同步转换为异步,第一个服务再也不停留等待第二个服务完成,而后再返回其工做。
经过这种方法可使服务彼此隔离,而且耦合松散。
缺点是须要在第二个服务上建立额外的 HTTP 请求,它从外部进行轮询,直到请求完成。这也引入了客户端的复杂性,由于必须检查请求的进度。
可是,异步通讯容许服务直接保持松散耦合。
另外一种通讯模式是基于消息的通讯。
与HTTP通讯不一样,所涉及的服务不直接相互通讯。相反,服务将消息推送到其余服务订阅的消息代理。这消除了许多与 HTTP 通讯相关的复杂性。
它不须要服务知道该如何相互交流,它消除了直接相互调用的服务需求。相反,全部服务都知道消息代理,而且它们将消息推送到该代理。其余服务能够订阅代理中本身关心的消息。
若是咱们的应用在 Amazon Web Services 中,能够用简单通知服务(SNS)做为消息代理。如今 ServiceA
能够将消息推送到 ServiceB
监听的 SNS 主题。
function asyncProcessMessage(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * send message to SNS that ServiceB is listening on */ let snsClient = new AWS.SNS() let params = { Message: JSON.stringify({ 'data': 'our message data' }), TopicArn: 'our-sns-topic-message-broker' } return snsClient.publish(params) .then((response) => { return response.MessageId }) }
ServiceB
侦听 SNS 主题上的消息,当收到一个关心的消息时,就会执行它的业务逻辑。
这引入了它本身的复杂性。请注意,ServiceA
再也不接收状态 URL 检查进度。这是由于咱们只知道消息已经被发送,而不知道 ServiceB
是否已经收到了它。
这能够经过许多不一样的方式解决。一种方法是将 MessageId
返回给调用者。能够用它来查询 ServiceB
,它将存储它收到的消息的 MessageId
。
注意,使用此模式的两个服务之间仍然存在一些耦合。例如,ServiceB
和 ServiceA
必须就消息结构的定义以及其中包含什么达成一致。
最后一种模式是事件驱动模式。这是另外一种异步方法,它看起来彻底消除了服务之间的耦合。
与消息传递模式不一样,事件驱动方法不须要服务必须知道公共消息结构。服务之间的通讯经过各个服务产生的事件进行。
此处仍然须要消息代理,由于各个服务会将其事件写入其中。可是与消息方法不一样,消费服务不须要知道事件的细节,它们对事件的发生作出反应,而不是产生能会或可能不会传递的信息。
在形式上,这一般被称为“仅事件驱动的通讯”。下面的代码和消息传递方法相似,但推送到SNS的事件是通用的。
function asyncProcessEvent(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ let snsClient = new AWS.SNS() let params = { Message: JSON.stringify({ 'event': 'service-a-event' }), TopicArn: 'our-sns-topic-message-broker' } return snsClient.publish(params) .then((response) => { return response.MessageId }) }
注意,咱们的 SNS 主题消息是一个简单的 event
属性。每一个服务都赞成以这种格式将事件推送到代理,这使得通讯松散耦合。服务能够监听他们关心的事件,而且提供为响应它们而须要运行的逻辑。
此模式使服务的耦合松散,由于事件中不包含任何有效负载。此方法中的每一个服务都会响应事件的发生并运行其业务逻辑。在这里,咱们经过 SNS 主题发送事件。也可使用其余事件,例如文件上传或数据库行更新。
这些是基于微服务的架构中全部可能的通讯模式吗?固然不是。基于同步和异步模式进行通讯的方式还有不少种。
可是这三个突出了支持同步与异步的优缺点。在选择时要考虑耦合因素,但也须要考虑开发和调试的具体状况与注意事项。