单体架构相信大家都很熟悉,它所对应的是早期的大型机时代,当时我们期望通过一台性能非常好的机器来承载程序中的所有部分,比如数据库、业务逻辑等。 但这种架构不仅代码复杂,相互之间的调用也很复杂,还很难进行修改。
后来为了方便功能之间的交互,分层架构被提出。这种架构中代码被分为了表现层、业务逻辑层、数据持久层以及数据库层。
之后又发展出了面向服务的SOA架构。所谓服务指的是一个单独的功能单元,可以远程访问并独立执行和更新,例如在线检索信用卡账单。这套架构的基本原则是独立于厂商、产品和技术,并通过网络上的通信协议,应用程序组件将服务提供给其他组件。SOA在技术架构上有局限性,虽然所有的服务都已解耦,但是部署还是在一起,维护起来依然很麻烦。
接下来出现的就是大家很熟悉的微服务架构,它将单体应用程序划分成一组小的服务,服务之间松耦合,使用轻量级通信协议(RPC/REST)进行服务间通信。微服务架构与SOA架构最大的不同在于可以独立部署。基于这些特性所带来的好处不仅是可以更快上线,并且由于服务间使用通信协议交流,所以能够技术异构。另外从应用架构角度来看它的可扩展性和伸缩性也很好,同时它还有一个很好的特性API first,这使得架构对mobile应用很友好。
注:除了这些典型架构外,还有微内核架构以及事件驱动架构,这里不再介绍,读者可以查看Oreilly的《软件架构模式》。
基础设施演进
在基础设施演进的早期阶段,最先使用的是大型机,再往后是x86小型机服务器,一直到这一阶段基础设施的成本还是很高,所以后续才会发展出虚拟化和云化。云化使得基础设施被外包出去,成本得以降低,可维护性得以保障,同时,云服务通常具备可编程性,更容易自动化,很多服务还有着弹性和自恢复的特性。容器化有着平台无关,标准化的特性,可以无缝的在云平台之间切换,由于对资源的利用率高,所以更适合部署微服务,同时还有易伸缩的特性。
基于架构、基础设施、工程实践以及组织结构这四个角度,云原生的概念被提出来,它主要是为了让服务或组织更适应云上运行。在架构层面上微服务是云原生推荐的标准架构,以及12factors应用的实践,基础设施层面主要涉及的是云化和容器化,工程实践层面上则是使用DevOps理念让交付流程更顺畅,组织结构上是去中心化,让团队自治。
不管是云化还是微服务化都不是能够简单完成的,还要面临诸多挑战。首先是云化的挑战,主要有两点,一是如何将遗留系统改造的更适合上云,二是如何更加有效的利用已有的云服务。
微服务化的挑战有以下四点,一是较高的存量遗留系统改造成本,单就从微服务拆分角度来看,业务的复杂度越高拆分难度越大。二是微服务化开发框架方面的问题,目前的微服务框架仅有Java的Spring Cloud和GO的ServiceComb等少量几个,另一方面这些框架虽然能够提供熔断和服务治理方面的能力,但是提高了团队的学习成本和升级维护成本。三是微服务治理功能不完善。四是关于微服务的监控和运维,比如日志、调用等。
云设计模式
云设计模式由微软Azure架构中心推出,提供了32个云设计模式,是可重复的上云方案。我们认为设计模式解决软件的重复性,而云设计模式解决云化的重复性。
云设计模式的分类和内容
Azure从可用性、数据管理、设计实现、消息、管理和监控、性能和可扩展性、弹性、安全等角度抽象出了不同的云设计模式,对于每个模式的描述都遵循模式描述、背景和问题、解决方案、问题和注意事项、何时使用、案例、相关模式和指南这样的流程。下面将对其中的一些设计模式进行具体介绍。
可用性模式:健康端点监控模式
我们一般采用的服务监控方式,不太适用在微服务化场景,一方面是时间滞后,另一方面则是准确性不够。假设这样一个场景,A服务依赖于B服务和某个数据库,某一时刻收到了关于A服务响应慢的警告,这时我们可能无法判断问题是出在B服务上还是数据库上,只能通过查看日志系统才能发现问题。
针对上面的问题健康端点监控系统给出了解决方案,它在应用程序内部实现了检查的功能,外部工具可以定期通过暴露出去的端点访问,以帮助验证应用程序和服务是否正常运行。
数据管理模式:静态内容托管模式
静态内容托管模式是将静态内容部署到云存储服务,然后直接返回给浏览器(前后端分离就是使用这样的模式),当要进行请求的时候,使用CDN来提升性能。这样带来的好处就是完全不用维护,因为这是一个托管服务,也不需要使用工具做监控,同时这样对基础设施的开销也更低。
设计和实现——绞杀者模式
绞杀者模式是服务解耦常用的一种模式,简单来说就是用新的应用程序和服务逐步替换特定的功能。 创建一个外层来拦截请求前往后端旧版系统。 外层可将这些请求路由到旧版应用程序或新服务。 现有功能可逐步迁移到新系统,使用者可继续使用相同的接口,他们并不知道迁移已发生。
设计和实现——网关聚合模式
前后端分离中前端会请求很多的API数据,这不仅造成了网络和性能的损耗,同时也会给服务器带压力。使用网关能够减少客户端与服务之间的通信频率。 网关会接收客户端请求,将请求分派到不同的后端系统,然后聚合结果并将其返回给请求客户端。
设计和实现——网关路由模式
网关路由模式类似Nginx反向代理,在一组应用程序、服务或部署前放置网关。 使用应用层 7 路由将请求路由到相应实例。使用此模式,客户端应用程序只需了解单个终结点并与之通信。 如果服务进行合并或分解,客户端不一定需要更新。 它可以继续向网关发出请求,只有路由会更改。
Service Mesh
Service Mesh是用来处理服务间通信的基础设施层,在服务拓扑间实现请求可靠传递,通常实现为一组和应用程序部署在一起的轻量级的网络代理,但对应用程序来说是透明的。Service Mesh的网络代理能够实现很多功能,比如服务发现、负载均衡等,对比微服务框架它让开发者能够更专注于业务开发。而之所以被叫做服务网格,是由于sidecar(单个服务调用)连接起来类似网格。
Istio
Istio是Service Mesh的一个实现,它分为控制面和数据面。数据面就是代理,通过sidecar挎斗模式接入服务,由于和服务本身没有关系,所以可以使用HTTP2、gRPC不同协议等。控制面分为Pilot、Mixer、Istio-Auth三个部分,Pilot包含服务发现、路由、负载均衡等功能,Mixer可以做代理的访问控制、遥测、监控、指标收集、限流等,Istio-Auth负责身份验证、秘钥管理、审计等。
Service Mesh带来的好处显而易见,首先是能够0侵入接入遗留系统或者无微服务框架系统,其次应用层只需要关注业务逻辑,还解决了微服务监控、运维、治理等问题,最后Service Mesh本身的升级和运维是独立的。
目前Service Mesh还是一个比较新的概念,所以还没有能够完全在生产环境中使用。它在数据面工具还算比较多,有Linkerd、Nginx、Envoy、Mesher、Conduit,控制面则寥寥无几,现阶段只有有Istio和CSE。这些工具由于都是容器化的,所以大部分都是运行在k8s上。
注:本话题分享时,Istio还处于比较早期的版本,在0.6以后,我们尝试使用Istio,在生产环境上部署流量较少,不那么重要的服务。从实际的使用过程来看,它在如下方面带来的极大的好处:
1)开发可以专注业务代码开发,服务治理的任务交给Istio来完成。
2)可以根据服务特性,采用合适的语言开发,不用担心服务注册发现客户端引入的问题。
3)使用很小的成本就可以实现0宕机时间的自动化部署以及灰度发布。
在目前的使用中,我们使用Istio遇到的问题主要是Kubenetes的维护上,以及Istio本身还不够成熟,遇到的bug通常只能通过升级、重启去解决,团队缺乏Istio的经验。当然这些随着Istio本身的成熟以及团队成员进一步的总结学习,是可以解决的。