微服务架构基本原理学习笔记(三)
上一篇:微服务架构基本原理学习笔记(二)
五、微服务之间的通信
微服务通信模式
微服务本身并没有规定通信规则,换句话说,一个微服务并没有规定可以被哪些应用程序访问,或者被哪些其它的微服务调用。应用程序与微服务间的直接通信,或者微服务与微服务间的直接调用,往往会因为其中错综复杂的关系而导致级联故障,任何一个环节的错误都会导致与其关联的其它部分无法正常工作。而且,如果执行某个操作需要调用多个不同的微服务才能完成,也会导致整个系统性能下降。我们应该尽量较少微服务之间的通信。
下图展示了一个较好的微服务之间的通信架构图:
微服务之间可以将数据以消息的方式发布到事件总线上,这样其它的微服务就可以以消息订阅的方式来完成微服务之间的数据通信,后面我们会讨论这样做的好处。另外,我们也可以在前端应用程序和微服务之间建立一个API网关,用来隔离它们之间的直接通信。对于前端应用程序而言,后端的微服务是透明的,所有来自前端应用程序的调用都可以通过API网关路由到对应的微服务。这样的设计可以带来几个好处:首先我们可以在API网关级别实现用户身份认证;其次是将前端应用程序与后端微服务之间进行解耦,从而使我们可以更加灵活地对微服务进行修改,同时保证面向公众的API的一致性。假如有第三方的开发团队基于我们的公共API来开发客户端程序,那么保证API的一致性就变得尤其重要,因为我们无法控制第三方开发团队的升级计划。在API网关中,我们甚至可以针对不同的客户端来聚合或转换数据格式,以满足前端的需要。
在示例eShopOnContainers的微服务架构中,我们可以看到,它有一个用来接收前端请求的API网关,并且移动应用和两个网站都通过API网关来访问后端的微服务。此外,我们从架构图中也可以看到,微服务之间并不会直接访问,而是通过事件总线以消息订阅的方式来实现异步数据通信。
同步通信
这里所说的同步通信与我们在编程语言中(例如Node.js中的同步与异步)所遇到的同步是两个不同的概念。在前端应用程序通过API网关调用后端服务的过程中,有时我们需要等待服务器马上返回结果,而不是将该请求发送到消息队列,然后服务器在处理完之后再返回结果。例如eShopOnContainers中有一个需要显示前10个最受欢迎的商品的功能,它是通过API网关向Catalog微服务发送一个请求,然后Catalog微服务从数据库中获取对应的数据并返回给前端应用程序。就整个过程而言,这是一种同步通信机制,因为前端应用程序在发送请求之后一直处于等待状态,直到后端服务返回结果,才进行下一步的动作(在网页上显示数据,或者给出错误提示)。
有许多方式可以用来实现通信,但HTTP仍是迄今为止最为流行的方式,它作为行业标准,几乎所有的编程语言都支持,这意味着我们可以非常方便地在网页或移动应用中来调用我们的API和微服务。另外,HTTP还具备一些其它的特性,例如响应头文件和状态标识、缓存功能、代理机制等等。在用HTTP请求数据时,通常使用JSON或XML数据格式,但推荐使用JSON,其优势之一是绝大多数编程语言都原生支持JSON格式,而且如果你使用JavaScript作为编程语言,JSON本身就是其数据类型的一部分。所以如果你正在开发网页应用程序,那么JSON数据格式是最完美的选择。
另外一种经常与微服务架构相关的模式是将API封装为RESTful资源,这是另一种用来组织和有效表达服务器资源的实践方式。我在《RESTful服务最佳实践》一文中对此有非常详细的介绍。
异步通信
有时我们在调用后端服务时不希望一直等待服务器返回结果,服务器会在适当的时候处理我们的请求。例如eShopOnContainers中提交订单的过程:
当客户通过前端应用程序提交订单时,后端系统需要完成一系列的操作,包括从第三方支付系统检查客户是否已成功完成支付,从供应商处检查商品库存状态并启动发货流程等。这个过程可能会持续几天的时间,所以对前端用户而言,他只需要知道订单已经成功提交,而并不需要一直等待整个过程处理完毕,他可以在稍后查询订单的处理进度。我们甚至可以在这个过程中添加订单状态更新通知功能,当订单的状态更新时,通过电子邮件或者短信告知用户订单的状态,并在成功发货后通知用户。
常用的微服务异步通信模式是将消息发送到事件总线(即消息队列),微服务间并不直接进行通信,而是创建一条消息并发送给消息代理,其它的微服务订阅这些消息并在适当的时候处理消息的内容。这种模式带来许多好处,首先,它将微服务彼此完全解耦,微服务间通过消息代理完成通信。当某一个微服务暂时不可用时,仍然不影响其它微服务的正常运行,它可以继续向消息代理发送消息,当不可用的微服务再次上线后,便可以从消息队列中继续处理消息内容。其次,这种架构也有利于支持更高级的缩放,如果消息队列中的消息数量不断增加,我们可以对微服务进行扩展,以帮助快速处理积压的消息。如果你使用云托管平台,通常可以自动支持这一操作。如果使用容器,则可以通过为运行容器的虚拟机集群配置一些自动扩展的功能来实现这一操作。
消息的类型有很多,但最常用的有两种,即命令和事件。
命令表示要执行某种特定的操作,例如发送电子邮件,它不一定需要同步完成,该命令只需要将电子邮件的地址和内容说明清楚,剩下的事情就交给电子邮件微服务来完成。如果我们将大量的命令消息发布到消息队列中,电子邮间微服务可能不会马上处理这些消息,但最终它们都会被一一处理完。
另一种消息类型是事件,它表示发生了某件事。这有点像语法中的过去式,当事件发布时,系统中任何对此事件“感兴趣”并订阅了该事件的微服务都可以响应并执行它们各自定义的操作。在面向对象编程中,这一模式被称为“发布-订阅”模式,例如C#中的事件处理机制就是采用的这种模式。在eShopOnContainers中,提交订单的过程就存在OrderPlaced事件,该事件会执行多个不同的操作,如支付、发送电子邮件、检查商品库存等。这里每个需要响应该事件的微服务都从发布到事件总线上的消息触发对应的操作。
弹性通信模式
我们无法保证系统中所有的微服务都一直正常运行,甚至我们无法保证网络通信没有故障,所以,我们需要提前预料到可能出现的各种问题。这里有几种技术和模式可以用来帮助我们提高系统的稳定性和通信弹性。
- 自动重试功能。有时会因为网络通信故障或者微服务本身不可用而导致某个操作在一开始的时候失败,如果系统能够在稍后自动重试会是一个不错的设计。例如通过一个HTTP请求对数据库进行查询,第一次尝试失败了,然后过几秒钟再试一次,如果仍然失败则等待一段时间后再试一次。许多现代的编程框架已经内置了对这种功能的支持,可以使我们非常容易地实现这一特性,例如.NET中的Polly。
- 断路器。自动重试功能固然好用,但是如果由于某种原因导致重试的次数过多,或者一直处于重试状态,则可能会导致系统性能下降,或者由于过于频繁的请求而让下游服务拒绝响应。断路器可以用来很好地解决这一问题。断路器位于客户端和服务器之间,一开始它允许所有的请求通过,此时我们称断路器处于关闭状态。一旦检测到任何错误,例如服务器返回特定的错误代码或者根本没有响应,那么断路器就会打开,这意味着客户端的所有请求都会立刻失败而不会发送给服务器。我们可以在断路器上配置超时时间,超时时间过后,断路器再次关闭,所有客户端的请求会被正常发送给服务器。如果服务器仍然没有响应,断路器会再次打开并保持一段时间,所有来自客户端的请求都被会拒绝。这是一种实现起来非常简单但是功能却很强大的技术。同样,许多编程框架也都内置了该功能,你完全不必自己来实现。
- 缓存。灵活地运用缓存可以提高系统的弹性。如果我们缓存从服务器或者下游服务中接收的数据,那么如果短时间内服务不可用,我们仍然可以使用缓存中的数据。当然,前提是我们已经考虑过旧数据给系统带来的影响。
消息代理在微服务架构中如此受欢迎的原因之一是它具有对弹性的内在支持,我们可以向消息代理发布消息,下游服务当前是否在线都没有关系,当服务再次启动时,可以继续处理积压的消息。另外,消息代理通常还具备重试发送消息的能力,如果消息处理程序因为某种原因无法正常处理消息,消息可以返回给消息代理以便稍后重新发送。当然,重试的次数也不是无限的,如果消息在被进行一定数量的重试后仍然无法被消息处理程序正常处理,消息代理会认为这是一条“死”消息而将它放入“死”消息队列中。
另外需要注意的一点是我们无法保证消息总是按顺序被接收的,这是由异步通信的本质所决定的。所以对消息处理程序而言,即便收到的消息是无序的,也能按照正确的逻辑进行处理。还有一种情况是消息处理程序可能会接收到重复的消息,这往往是因为消息代理重复发送消息而导致的,我们需要确保消息处理是幂等的。对于一条消息而言,消息处理程序处理一次和处理两次的结果相同,则认为消息处理是幂等的。在eShopOnContainers中,提交订单时的扣款和发货过程就属于这种情况,我们不希望这个过程中客户被扣款两次,而且也不希望对同一订单重复发货,所以,我们必须确保在代码中进行了严格的检查来避免出现这样的情况。
微服务发现
为了让微服务能够相互通信,每个微服务都需要有一个地址,如何才能知道所有微服务的地址呢?假设我们有三台虚机,每台虚机上都运行着各种微服务,甚至同一个微服务的多个不同实例分别运行在不同的虚机中,我们不可能给每一个在虚机上运行的微服务都分配一个固定的IP地址,那么如果解决这个问题呢?
一种方法是使用注册中心,它记录了所有当前运行的微服务的地址。每个微服务在启动时都向注册中心报告并注册自己的信息,这样其它微服务在需要的时候就可以通过注册中心找到该微服务。如果你使用云托管平台,通常它都支持这部分功能,我们不需要自己创建注册中心。而且,在云托管平台上,我们部署的每个微服务通常都会自动分配一个DNS名称,同时它还支持负载均衡,这意味着我们可以通过DNS名称来找到微服务,而不用关心微服务是在哪台虚拟机上。
如果使用容器,则可以考虑使用Kubernetes。Kubernetes内置了DNS,我们不需要知道每个容器的IP地址,只需要知道微服务的名称即可。Kubernetes负责将请求路由到对应容器并在必要时进行负载均衡。
六、微服务安全
微服务应用程中通常都要存储和处理大量数据,这些数据常常都包含了各种敏感信息。例如,在eShopOnContainers中,Catalog微服务用来处理商品信息,这些信息应该对所有客户开放,因此这部分数据不是敏感数据。但是在Ordering微服务中,我们需要保存哪些客户订购了哪些商品,同时还包含客户的送货地址和付款信息,这些数据都是非常敏感的,如果一旦泄露,将会产生非常严重的后果。因此,对于那些处理敏感数据的微服务,我们必须采取有效的保护措施。
对数据进行加密
数据的加密包括几个方面,首先是数据传输过程中的加密,以防止数据被中间方监听。推荐使用业界标准的加密算法,而不要尝试自己发明新的加密算法。对于HTTP通信,可以通过配置传输层安全或者TLS(Transport Layer Security)来实现加密,使所有的请求都通过HTTPS进行访问。实现HTTPS访问需要配置SSL证书,这是一个比较复杂的过程,你需要为每一个服务申请一个证书,该证书由第三方受信任的机构颁发,证书有一定的有效期,过期之后需要更新证书。使用云托管平台可以简化这个过程,它默认就支持HTTPS访问机制。
数据的加密还包括对静态数据的加密,所谓静态数据,就是指存储在硬盘上的数据。任何保存在硬盘上的数据都应该被加密。许多云提供商都提供了标准的静态数据加密服务,所以我们无需自己花时间去配置和管理加密密钥。需要注意的一点是,不应该仅仅对正在使用的静态数据进行加密,任何备份的数据也应该被加密。
身份认证
仅仅对数据进行加密是远远不够的,我们还需要确保所有的HTTPS请求是安全的,换句话说,我们需要知道请求者是谁,他们是否获得授权以访问我们的服务。最常见的实现身份认证的方式是在每一个HTTP请求中包含一个Header,其中带有用来识别用户身份的用户名和密码,这种方式被称为基本身份认证方式(Basic authentication),例如用来实现用户登录的功能就可以使用此方法。但是这种认证方式会暴露用户的密码,而且微服务代码中如果对用户的密码处理不当,也会带来一定的安全隐患。因此这种认证方式并不推荐。
还有一种方式是为微服务的每个客户端提供自己的密钥,然后客户端在HTTP请求头中包含API密钥。但是这种方式带来了密钥管理上的问题,一旦密钥管理程序受到攻击或者密钥被泄露,后果也是非常严重的。
另外一种方式是使用客户端证书,通过使用公钥加密,证书为我们提供了一种非常安全的身份认证方式。不过,证书的安装和管理也会带来一些的问题。最好的方式是按照业界标准在微服务中实现身份认证,例如OAuth 2.0和OpenID。这通常是通过授权服务器(Authorization Server)来实现的。在eShopOnContainers中,这一部分的功能包含在Identity微服务中。首先,客户端通过发送某种凭据向授权服务器进行身份认证,如果认证通过,授权服务器返回一个有限时间的访问令牌(token)。然后,客户端将该令牌放在HTTP请求头中访问微服务,由于令牌使用了公钥加密进行签名,所以授权服务器可以验证令牌的真实性和有效性。因为OAuth 2.0和OpenID都是行业标准,有很多现成的第三方组件和服务可以直接使用,所以你不需要自己来实现,例如示例程序eShopOnContainers中使用了名为IdentityServer4的开源产品。
授权
身份认证用来告诉我们访问者是谁,而授权的作用是知道可以干什么。例如,当用户登录到eShopOnContainers时,他有权查看自己的历史订单,但无权查看其他用户的历史订单。许多Web API框架都内置了授权机制,用于检查用户的角色和权限,例如eShopOnContainers中使用的ASP.NET Core框架。
对于微服务提供的每一个API,我们都应该认证考虑哪些用户可以被访问,哪些用户不能访问。在微服务中,有一种特殊的情况我们需要特别注意。假设用户Mark通过了身份认证并成功访问了Ordering微服务的某个功能,而该功能又会调用另一个微服务,例如Payment微服务。Ordering微服务知道当前访问的用户是Mark,并知道Mark可以执行哪些操作,但是当Ordering微服务调用Payment微服务时,由于Payment微服务信任Ordering微服务,它假定所有来自Ordering微服务的请求都是安全的,所以可能会存在某种安全隐患,使得用户Mark能以某种方式欺骗Payment微服务而使用其他用户的信用卡来支付自己的订单。解决方法就是当Ordering微服务访问Payment微服务时也需要同时传入用户Mark的访问令牌,用来告诉Payment微服务最终的用户身份,如果Payment微服务识别出请求者不是Mark用户,则请求被拒绝,这样可以有效地避免Payment微服务的调用被欺骗的问题。
网络安全
我们可以使用防火墙、IP白名单和虚拟网络等网络功能来共同保护我们的微服务。下图中,我们可以看到其中有三个相互通信的微服务,如果我们将其放到虚拟网络中,那么我们就有能力拒绝任何来自虚拟网络外部的访问请求,而只允许微服务之间彼此进行通信。这种方式可以很好地保护后端服务的网络安全,这些微服务可能只需要在它们之间进行通信,而不需要从外部直接访问。但是,如果确实存在需要从外部直接访问微服务的情况呢?例如,一个单页面应用程序(SPA),它可能需要直接访问某个微服务,这是否意味着我们需要放开虚拟网络对外部请求的限制而允许访问某些微服务呢?有关这一点我们在前面“微服务通信模式”一节中已经讨论过了,一个好的实践方式是使用API网关将外部请求与虚拟网络中的微服务隔离开,这样我们就可以非常有选择性地确定哪些API可以从外部访问,哪些API不能从外部访问。而且,单一的访问入口也有利于对API网关配置防火墙,以防范DDOS攻击和SQL注入等。
通常,在微服务应用中,有一个面向公众的网站,例如eShopOnContainers网站,但同时可能还有一些其它的应用只面向一小部分用户,例如针对系统管理员访问的系统后台管理程序,或者专门针对市场部进行营销活动的页面等。对于这部分应用,我们希望只有特定的用户才能访问,而不是对公众开放。我们可以通过IP白名单的方式只允许来自特定IP地址的请求(例如系统管理员或市场部用户的IP地址),而拒绝来自其它IP地址的请求。
微服务的安全涉及到多方面的技术,而不仅仅是其中的一两个,为了保证后台服务的安全,我们应该避免仅仅依赖单个的保护机制,而应该尽可能地使用多层安全保护,这就是所谓的纵深防御机制。
纵深防御机制
前面我们谈了有关微服务安全的许多方面,包括对数据进行加密、对用户进行身份认证、授权用户只访问允许的资源、通过使用虚拟网络和IP白名单拒绝来自未经授权的网络请求等。纵深防御指的是我们不应该只依赖于其中一种技术来保护我们的应用程序和服务,这是因为一旦某一个防御被突破,那么攻击者就可以免费获取到所有数据。我们需要结合多种不同的安全措施来保证我们数据的安全性,从而尽可能地降低数据泄露的可能性。处理的数据越敏感,所需要的防护也就越多。除了上面提到的一些安全防护机制外,还有一些额外的防御措施。
黑客们会使用各种非常复杂的工具和技术来入侵和破坏我们的服务和应用,对于开发人员而言,了解这些攻击技术可以有效地采取一些防护措施来阻止攻击。我们可以安排信息安全专家对应用程序进行渗透测试,这可以知道我们的应用程序是否能够防御最先进的黑客技术,同时专家也可以提供一些有关如何提供应用程序安全性的建议。另外,创建自动化测试来验证安全性设置是否正常工作并有效,而不要仅仅假设我们已经进行了正确的网络和安全设置。可以有针对性地进行一些安全测试,例如用未经授权的用户访问API,还可以检测攻击行为何时进行,例如尝试多次重复登录和HTTP请求、对于敏感文件的网络钓鱼访问、SQL注入攻击等。所有这些攻击都可以实时检测到,我们可以进行系统报警配置,当攻击行为发生时,系统可以主动做出响应,例如阻止攻击者的IP地址请求,或者暂时关闭系统的部分功能以防止攻击等。最后,我们应该对系统中所有正在执行的操作都记录日志,这样我们才可以准确地知道谁在什么时间做了什么,当系统受到攻击或者数据发生泄露时,才能有机会了解事情是如何发生的,以及哪些数据被泄露。
七、发布微服务
自动化部署
在单体应用程序中,部署过程往往比较简单,而且通过手动就可以轻易地完成,我们只需要将所有步骤和注意事项在文档中描述清楚,然后按部就班地逐一执行就可以了。但是这种操作方式对微服务而言并不适用,因为微服务需要部署的东西往往非常多,相互之间的依赖关系也比较复杂,而且部署过程相对也较为频繁,所以我们强烈推荐通过自动化的方式来完成微服务的部署。
在微服务的部署中,这一过程被称之为CICD(Continuous Integration/Continuous Delivery),即所谓的持续集成和部署。那么如何构建一个可以持续集成和部署的自动化过程呢?持续集成从我们将代码签入到Github使开始,首先集成服务器会执行一系列的单元测试,以保证我们签入代码的质量是相对可靠的;然后通过CI管道(Pipeline)按照预先定义好的步骤部署微服务,可以将微服务部署到本地虚拟机或者云端;一旦微服务部署成功,就需要进行服务级别的集成测试;如果测试通过,接下来就可以将微服务部署到QA环境中进行整个系统级别的测试,如运行自动化的端到端的测试;在正式将微服务投入到生产环境之前,通常还需要一些额外的准备工作,如进行一些必要的手动测试、执行风险评估扫描、相关签署工作等;最后,我们将准备好的release发布到生产环境。一个完整的流程看起来像这样:
部署环境
通常,我们希望微服务能够被部署到不同的环境中。开发人员希望将微服务部署到本地虚拟机,以便在开发过程中能够在本地调试代码。同时我们还需要有一个测试环境,能够在其中对微服务进行端到端的测试,并且QA小组也希望能够在测试环境中进行一些必要的手动测试。有时,我们可能还需要专门用于渗透测试和性能测试的环境。除此之外,我们还需要生产环境,这是我们的微服务最终运行的环境,也是我们交付给客户能够真正使用的环境,不言而喻,生产环境是最重要的。在某些情况下,我们可能会有多个生产环境,比如为不同的客户准备不同的生产环境,在不同的地理区域中准备不同的生产环境等。因此,参数化我们的自动化部署脚本非常有必要,这可以使我们的部署过程尽可能简单。我们可以使用JSON或者YAML为不同的环境定义不同的参数,当然,也可以使用各种工具如Docker Compose或者Kubernetes来获得更加成熟的解决方案。
构建注册表(Artifact Registry)
将构建工件(Artifact)存储在某种注册表中可以使我们非常方便地部署微服务的任意版本,从而使得我们可以轻松地将微服务回滚到之前的某个特定版本。示例应用程序eShopOnContainers选择将每个微服务构建为Docker容器镜像,这样做有很多好处,其中之一是版本命名的标准化,并且可以存储在容器注册表中,当我们使用Docker来进行部署时,操作会变得很容易。例如名称“eshoponcontainers/orderingservice:1.3.1”代表eshop应用程序中ordering微服务的1.3.1版本。由于eShopOnContainers应用程序使用容器作为交付机制,因此它非常适合使用Kubernetes。如果我们使用Kubernetes集群,那么可以通过YAML配置文件来定义应用程序中所有的微服务。Kubernetes是基于状态策略来运行的,配置文件定义了哪些微服务需要运行以及如何进行设置,Kubernetes将配置文件中的内容与集群上的配置文件进行比较,如果不一致,则相应地添加或删除容器,直到运行的内容与所要求的内容相匹配。所以,对于微服务版本的升级,或者其它任何属性的改变(例如环境变量或者副本数量等),只需要更改配置文件即可。有关Kubernetes的详细介绍可以参考官网网站的说明。
独立升级
在前面的章节中,我们讨论过微服务之间松耦合的重要性,我们不希望单个微服务的升级同时依赖于其它的微服务。我们的自动化流程可以一次性部署所有的微服务,但是一次只能更新(或升级)一个微服务。如何对单个微服务进行升级是一个值得思考的问题。你可能会想,不就是停止服务,更新,然后再重新启动吗?如此简单的步骤还需要大费周折吗?当然,如果你不想服务被中断,这个方法当然行之有效。不过,在生产环境中,某些微服务是不允许被中断的,我们应该通过技术手段尽可能地在微服务的升级过程中减少或者避免停机时间。
一种有效的方法是同时运行新旧两个版本的微服务,然后通过负载均衡将流量从旧版本移动到新版本。这样升级过程中不会有停机时间,客户几乎不会感觉到升级过程中的任何停顿。
另一种方法是同时运行多个微服务的副本,然后逐个升级替换。例如有三个V1版本的微服务实现负载均衡,我们可以每次添加一个V2版本的新实例并同时删除一个V1版本的实例,直到所有的V1被替换成V2,并最终完成对整个微服务的升级过程,但前提是两个不同版本的微服务之间能够兼容。值得一提的是,Kubernetes集成了许多高级的升级策略,你可以查看官方文档并使用这些策略。
另外还需要考虑的一个问题是,当升级出现问题时,我们可以无缝地回滚到之前的版本。Kubernetes同样可以非常轻松地帮我们解决这一问题,回滚操作只需要更新配置文件使其指向之前版本的标签即可。
监控微服务
微服务的一大挑战是我们需要监控的东西非常多。一个微服务可能同时在多个不同的服务器上以多个不同的进程运行,所以通过手动的方式连接到这些虚拟机集群中的每个工作节点来检查和查看工作日志几乎变得不可能,而我们需要的是通过一个系统来自动监控和管理这些日志,管理员可以在一个集中的地方操作和查看这些日志。通常,这个系统会有一个称之为仪表盘(Dashboard)的界面,能够让我们实时了解系统的整体运行状况,并在出现问题时报告具体原因。通常,它分为这几个部分:
- 主机指标。包括CPU和内存使用情况。通过这些指标我们可以检测是否需要对主机容量进行扩充以满足更多的需求。许多云提供商都会提供这一类的监测数据,同时还包括警报功能,当主机运行状况超过阈值时会自动发出通知(短信、邮件或其它通知方式)。
- 应用程序级别的监测数据(Web API)。包括HTTP请求数量,以及失败的请求数量和错误代码等。例如,如果日志中有很多401 Unauthorized的响应代码,那么很可能我们的服务受到了黑客攻击,或者存在错误的配置项;如果日志中有很多500的错误代码,那么表明我们的代码中存在某种错误。如果使用消息代理,则需要跟踪并查看消息队列中是否存在大量的死消息,以表明我们的程序是否在处理消息时遇到了问题。另外,一个好的实践是让每一个微服务都支持端点检查,这是一个特定的Web API,允许定期被调用来检查微服务运行是否正常,当它被调用时,只需要回复是否OK即可,用以表明服务是否启动并工作正常,同时它还可以返回一些额外的信息,例如该微服务所依赖的下游服务是否正常工作等。
- 易于访问的日志文件。每个微服务都应该记录日志,我们需要将这些日志集中到一个地方,以方面查看和管理。使用容器的好处是可以通过一种标准化的方式来捕获日志,并将日志集中到一个统一的地方。有许多第三方的开源产品可以帮助我们实现这个功能,例如可以将日志发送到Elasticsearch,然后使用Kibana查看日志,或者在Microsoft Azure中使用Application Insights。许多云托管平台也提供了很多易于启用的监控和日志功能。
热门相关:亿万盛宠只为你 重生野性时代 回眸医笑,冷王的神秘嫡妃 前任无双 天神诀