概叙
微服务(Microservices)是一种软件架构风格,它将单一的应用程序拆分为多个小的服务,每个服务都是独立运行和部署的。每个微服务通常只专注于一个业务功能,具有自己的数据存储,并且通过轻量级的通信机制(如HTTP或消息队列)与其他服务进行交互。
微服务发展到现在,不仅仅只是服务治理,从架构设计,编码,测试,部署运维(版本管理、CICD集成发布,部署,故障跟踪、负载均衡、弹性扩展、容器化、云原生 ),服务治理(负载均衡、弹性扩展、故障跟踪、容错),再结合需求分析、产品设计,微服务基本是涵盖了软件工程的整个生命周期。
所以微服务其实是一套完善的解决方案,供大家选择。
微服务架构主要特点
-
独立开发:不同的微服务可以由不同的团队开发,每个团队可以选择最适合其服务的技术栈和开发工具。
-
独立部署:每个微服务可以独立部署,不需要影响其他服务。这使得开发和运维更加灵活,能够更快地响应业务需求的变化。
-
单一职责:每个微服务专注于完成特定的业务功能,遵循单一职责原则(SRP),这使得代码更加简洁、维护更加容易。
-
去中心化的数据管理:每个微服务可以有自己的数据库,这样可以减少服务之间的数据耦合。
-
分治原理:单个服务的吞吐始终是有限的,通过微服务拆分可以突破扩展上限,分拆流量可支撑全球化的业务,不再受机房规模甚至地域影响
-
弹性扩展:微服务架构可以按需扩展某个特定的服务,而不是整体应用。这样更能有效利用资源,提高应用的可用性和性能。
-
容错性:微服务的独立性使得某个服务的故障不会直接导致整个系统的崩溃,提高了系统的容错性。
-
风险隔离:高度自治和高度隔离,明显不让低等级的服务影响高等级
微服务的优势
-
灵活性:能够快速适应业务需求的变化,通过独立部署来实现快速迭代。
-
技术多样性:允许使用不同的技术栈来实现不同的微服务,充分利用各种技术的优势。
-
故障隔离:一个微服务的失败不会影响整个系统,增强了系统的稳定性。
微服务的不足
-
分布式系统的固有复杂性:微服务架构是基于分布式的系统,而构建分布式系统必然会带来额外的性能开销和可靠性挑战。
-
服务的依赖管理和测试:在单体应用中,通常使用集成测试来验证依赖是否正常。而在微服务架构中,单元测试和整条服务链路的可用性都需要关注。
-
有效的配置版本管理:需要引入配置的版本管理、环境管理。
-
自动化的部署流程:有效地构建自动化部署体系,配合服务网格、容器技术,是微服务面临的另一个挑战。
-
对于DevOps有更高的要求:开发者也需承担起整个服务的生命周期的责任,包括部署、链路追踪、监控。构建全功能的团队,也是一个不小的挑战。
-
运维成本飙升:运维主要包括配置、部署、监控与告警和日志收集四大方面。微服务架构中,每个微服务粒度都需要独立地配置、部署、监控和收集日志,成本呈指数级增长。服务化粒度越细,运维成本越高。
微服务架构的适用场景
微服务架构的适用场景广泛,尤其在以下情况下表现尤为突出:
1. 业务复杂,模块多且相对独立:当业务复杂到单体应用难以维护时,将应用拆分为多个微服务是一个明智的选择。每个微服务专注于一个业务领域,实现业务的高度解耦和快速迭代。
2. 团队多,管理隔离:随着公司规模的扩大,团队数量也在不断增加。每个团队都有自己的管理方式和负责的业务领域。微服务架构可以实现团队自治,提高开发效率。
3. 应用规模大,并发用户多:微服务架构可以横向分布式扩展,轻松应对应用规模的不断扩大和海量用户增长。
4. 快速迭代、持续交付:在业务需求快速变化的情况下,微服务架构可以实现快速的开发、测试和部署,支持持续交付和持续集成。
微服务的挑战
-
复杂性增加:系统由许多微服务组成,需要有效的服务管理和协调,增加了系统的复杂性。
-
性能:分布式系统是跨进程、跨网络的调用, 受网络延迟和带宽的影响。(服务拆分后必须要面对的问题。性能不够,机器来凑)
-
可靠性:由于高度依赖于网络状况, 任何一次的远程调用都有可能失败,随着服务的增多还会出现更多的潜在故障点。如何提高系统的可靠性、降低因网络引起的故障率,是系统构建的一大挑战。(相对性的4个9的可用性很难)
-
分布式系统管理:微服务架构需要处理分布式系统的一些常见问题,如网络延迟、数据一致性、服务发现等。
-
分布式通信:分布式通信大大增加了功能实现的复杂度,并且 伴随着定位难、调试难等问题
-
数据一致性:需要保证分布式系统的数据强一致性,即 在 C(一致性)A(可用性)P(分区容错性) 三者之间做出权衡。
-
-
部署和运维:需要一套完善的持续集成和持续部署(CI/CD)流程以及监控、日志系统,以确保微服务的高效运作。(增加了复杂性,运维人员要求提高了,工作量多了)
-
安全性问题:微服务架构涉及多个服务之间的网络通信,存在数据泄露、劫持等安全风险,需要实施适当的安全措施。(增加了复杂性)
微服务架构特别适用于大型、复杂且需要频繁迭代和部署的大型应用,但对中小型应用可能过于复杂。因此,选择微服务架构需要根据具体业务需求和团队能力来做出决定。
是否需要微服务
四种场景可以考虑使用微服务
-
规模大(团队超过10人,分工协作,专人专职)
-
业务复杂度高(系统超过5个子模块,单一服务不利于协作开发,也不利于系统稳定性)
-
业务需求变化快(具体需求可以拆分到某几个服务上,而不是整个系统都需要整改。例如:电商上的营销系统)
-
分布式系统的三高要求:高可并发、高可用、高性能
其他因素筛选微服务
-
软件功能变化频繁,快速迭代,缩短交付周期为核心的业务(专人专职,分工协作)。
-
模块有独立的生命周期,服务复用,降本增效,减少重复造轮子
-
有独立的隔离性需求和扩展性需求(容错,单一服务的容错性差)
-
简化的外部依赖(Facade模式(外观模式)场景:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。)
外观(Facade)模式包含以下主要角色:
1.外观(Facade)角色:为多个子系统对外提供一个共同的接口。
2.子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
3.客户(Client)角色:通过一个外观角色访问各个子系统的功能。
微服务拆分和改造
1.基于业务逻辑拆分
基于业务逻辑拆分相对好理解一点,典型的单一职责原则,我们将功能相近的业务整合到一个服务颗粒上。
业务领域模型拆分
举一个典型的电商业务例子。电商的业务体系庞大,涉及各方面的细节。但是我们大概能够根据业务的职能做一个拆分,比如阿里的电商中台业务,包含 用户账号子系统、商品子系统、订单子系统、客户子系统、物流子系统 等。
因为职能不同,这些领域之间包含清晰的界限,所以我们可以按照这个方向将服务于不同领域(商品域和订单域)的子系统拆成独立的服务颗粒。
用户群体拆分
根据用户群体做拆分,我们首先要了解自己的系统业务里的用户角色领域是否没有功能耦合,有清晰的领域界限。
比如教育信息化系统,教师的业务场景和学生的业务场景,基本比较独立,而且拆分后流量上有明显的削弱。
2.基于可扩展拆分
这个需要区分系统中变与不变的部分,不变的部分一般是成熟的、通用的服务功能,变的部分一般是改动比较多、需要不断满足业务迭代扩展性需要的功能。
根据二八原则,系统中经常变动的部分大约只占 20%(如 运营、活动),而剩下的 80% (用户信息、基本商品信息、物流信息 等模块的管理能力和视图界面)基本不变或极少变化。
3 基于可靠性拆分
核心模块拆分
我们团队在做MySQL数据库和Redis集群拆分的时候,总会把一些重要的模块独立放在一个集群上,不与其他模块混用,而这个独立的集群,服务机性能要是最好的。这样做的目的是,当重要度较低的模块发生故障时,不会影响重要度高的模块。同样的道理,我们将账号、支付等核心服务单独拆分在一个服务颗粒上,建立独立服务集群,保证资源独享,来提升可用性。
主次链路拆分
在各个业务系统中,其实都会有主次业务链路。主业务链条,完成了业务系统中最核心的那部分工作。而次链路是保证其他基础功能的稳定运行。
以电商为例子:商品搜索->商品详情页->购物车模块->订单结算->支付业务,就是一条最简单的主链路。主链路是整个系统的核心主战场,最好的资源跟火力都要放在这里,保证不失守。
4 基于性能需求拆分
根据性能需求来进行拆分。简单来说就是访问量特别大,访问频率特别高的业务,又要保证高效的响应能力,这些业务对性能的要求特别高。比如积分竞拍、低价秒杀、限量抢购。
我们要识别出某些超高并发量的业务,尽可能把这部分业务独立拆分出来。
5 微服务的无状态改造
很多应用拆分成微服务,是为了承载高并发,往往一个进程扛不住这么大的量,因而需要拆分成多组进程,每组进程承载特定的工作,根据并发的压力用多个副本公共承担流量。
将一个进程变成多组进程,每组进程多个副本,需要程序的修改支撑这种分布式的架构,如果架构不支持,仅仅在资源层创建多个副本是解决不了问题的。
很多人说,支撑双十一是靠堆机器,谁不会?真正经历过的会觉得,能够靠堆机器堆出来的,都不是问题,怕的是机器堆上去了,因为架构的问题,并发量仍然上不去。
阻碍单体架构变为分布式架构的关键点就在于状态的处理。如果状态全部保存在本地,无论是本地的内存,还是本地的硬盘,都会给架构的横向扩展带来瓶颈。
状态分为分发,处理,存储几个过程,如果对于一个用户的所有的信息都保存在一个进程中,则从分发阶段,就必须将这个用户分发到这个进程,否则无法对这个用户进行处理,然而当一个进程压力很大的时候,根本无法扩容,新启动的进程根本无法处理那些保存在原来进程的用户的数据,不能分担压力。
所以要讲整个架构分成两个部分,无状态部分和有状态部分,而业务逻辑的部分往往作为无状态的部分,而将状态保存在有状态的中间件中,如缓存,数据库,对象存储,大数据平台,消息队列等。
这样无状态的部分可以很容易的横向扩展,在用户分发的时候,可以很容易分发到新的进程进行处理,而状态保存到后端。而后端的中间件是有状态的,这些中间件设计之初,就考虑了扩容的时候,状态的迁移,复制,同步等机制,不用业务层关心。
微服务的无状态化主要指的是微服务不维护任何状态,也不在请求之间存储会话特定的数据。每个请求被无状态微服务独立处理,不依赖于之前的交互。这种特性使得无状态微服务可以在多个实例之间进行水平扩展,而不会影响其功能。无状态微服务的这种特性允许它们根据负载动态地创建和销毁实例,从而有效地处理可预测和不可预测的流量模式。
无状态微服务的实现关键在于数据的分发、处理和存储。对于保存在内存里的数据,例如会话数据,可以放在外部统一的缓存中,如Redis,以实现无状态化。对于业务相关的数据,则应该保存在统一的数据库中,如果性能不足,可以采用读写分离或分布式数据库的方式来处理。对于文件、照片之类的数据,应该存放在统一的对象存储里面,通过CDN进行预加载,以实现无状态微服务的架构优化。
如图所示,将架构分为两层,无状态和有状态。
容器和微服务是双胞胎,因为微服务会将单体应用拆分成很多小的应用,因而运维和持续集成会工作量变大,而容器技术能很好的解决这个问题。然而在微服务化之前,建议先进行容器化,在容器化之前,建议先无状态化,当整个流程容器化了,以后的微服务拆分才会水到渠成。
微服务的无状态改造,目的就是为了容器化。
无状态化的几个要点
前面说对于任何状态,需要考虑它的分发,处理,存储。
对于数据的存储,主要包含几类数据:
-
会话数据等,主要保存在内存中。
-
结构化数据,主要是业务逻辑相关
-
文件图片数据,比较大,往往通过CDN下发
-
非结构化数据,例如文本,评论等
如果这些数据都保存在本地,和业务逻辑耦合在一起,就需要在数据分发的时候,将同一个用户分到同一个进程,这样就会影响架构的横向扩展。
对于保存在内存里的数据,例如Session,可以放在外部统一的缓存中。
如果性能还是抗住不,则可以使用分布式数据库。
对于业务相关的数据,则应该保存在统一的数据库中,如果性能扛不住,可以进行读写分离,如文章微服务化的数据库设计与读写分离。
对于非结构化数据,可以存在在统一的搜索引擎里面,例如ElasticSearch。
如果所有的数据都放在外部的统一存储上,则应用就成了仅仅包含业务逻辑的无状态应用,可以进行平滑的横向扩展。
而所有的外部统一存储,无论是缓存,数据库,对象存储,搜索引擎,都有自身的分布式横向扩展机制。
在实行了无状态化之后,就可以将有状态的集群集中到一起,进行跨机房的部署,实现跨机房的高可用性。而无状态的部分可以通过Dubbo自动发现,当进程挂掉的时候,自动重启,自动修复,也可以进行多机房的部署。
6.微服务接口的幂等设计
但是还有一个遗留的问题,就是已经分发,正在处理,但是尚未存储的数据,肯定会在内存中有一些,在进程重启的时候,数据还是会丢一些的,那这部分数据怎么办呢?
这部分就需要通过重试进行解决,当本次调用过程中失败之后,前序的进程会进行重试,例如Dubbo就有重试机制;spring-retry提供的@Retryable注解。
既然重试,就需要接口是幂等的,也即同一次交易,调用两次转账1元,不能最终转走2元。
接口分为查询,插入,更新,删除等操作。
对于查询接口来讲,本身就是幂等的,不用做特殊的判断。
对于插入接口来讲,如果每一个数据都有唯一的主键,也能保证插入的唯一性,一旦不唯一,则会报错。
对于更新操作来讲,则比较复杂,分几种情况。
一种情况是同一个接口,前后调用多次的幂等性。另一种情况是同一个接口,并发环境下调用多次的正确性。
为了保持幂等性,往往要有一个幂等表,通过传入幂等参数匹配幂等表中ID的方式,保证每个操作只被执行一次,而且在实行最终一致性的时候,可以通过不断重试,保证最终接口调用的成功。
对于并发条件下,谁先调用,谁后调用,需要通过分布式锁如Redis,Zookeeper等来实现同一个时刻只有一个请求被执行,如何保证多次执行结果仍然一致呢?
则往往需要通过状态机,每个状态只流转一次。还有就是乐观锁,也即分布式的CAS操作,将状态的判断、更新整合在一条语句中,可以保证状态流转的原子性。乐观锁并不保证更新一定成功,需要有对应的机制来应对更新失败。
幂等是为了解决在分布式系统中,由于网络故障、系统错误或用户重复操作导致系统状态的不一致问题。
幂等性是一个数学与计算机科学中的概念,它指的是一个操作,无论执行一次还是多次,产生的结果都是相同的。在分布式系统中,由于存在多个节点或服务可能同时访问和修改共享资源,因此需要确保某些操作(如数据更新、状态变更等)的幂等性,以防止因重复执行而导致的系统状态不一致。
幂等性的实现可以通过多种方式,包括但不限于:
- 数据库唯一主键:通过数据库中的唯一主键约束,确保在插入或更新操作时,只有满足特定条件的记录会被处理,避免重复插入或更新相同的数据。
- 分布式锁:通过分布式锁机制,确保在同一时间只有一个节点可以执行特定的业务逻辑,从而避免多个节点同时修改同一资源。
- 缓存机制:利用缓存来检查请求是否已经处理过,如果请求已经处理过(例如,缓存中存在相应的key),则直接返回处理结果,避免重复处理。
- 令牌机制:通过生成令牌并验证令牌的有效性来控制业务的执行,确保只有有效的令牌才能触发业务逻辑。
这些方法各有优缺点,选择哪种方法取决于具体的业务场景和需求。例如,数据库唯一主键适用于确保插入操作的幂等性,而分布式锁和缓存机制则更适合于需要控制并发访问的场景。通过这些方法,可以有效地解决分布式系统中由于并发访问和修改共享资源导致的问题,保证系统的正确性和一致性
7.微服务的容器化
无状态化之后,实行容器化就十分顺畅了,容器的不可改变基础设施,以及容器基于容器平台的挂掉自动重启,自动修复,都因为无状态顺畅无比。
无状态化之后,实行容器化就十分顺畅了,容器的不可改变基础设施,以及容器基于容器平台的挂掉自动重启,自动修复,都因为无状态顺畅无比。
关键技术一:Dockerfile
为什么一定要用Dockerfile,而不建议通过保存镜像的方式来生成镜像呢?
这样才能实现环境配置和环境部署代码化 ,将Dockerfile维护在Git里面,有版本控制,并且通过自动化的build的过程来生成镜像,而镜像中就是环境的配置和环境的部署,要修改环境应先通过Git上面修改Dockerfile的方式进行,这就是IaC。
Dockerfile的主要作用是定义和构建Docker镜像。
它是一个文本文件,包含了一系列指令和参数,这些指令指导Docker如何构建一个定制的镜像。以下是Dockerfile的一些关键作用:
- 定义镜像构建过程:通过在Dockerfile中编写指令,如
FROM
、RUN
、CMD
等,可以精确控制镜像的构建过程,包括安装软件、配置环境、设置启动命令等。12 - 实现自动化构建:由于Dockerfile将镜像的构建过程编码为文本文件,可以实现自动化构建和发布,提高构建效率和可重复性。5
- 标准化和最佳实践:通过使用Dockerfile,团队成员可以按照统一的规范构建镜像,实现标准化和最佳实践,确保研发、测试和运维使用相同的环境和配置。
- 易于版本管理:由于Dockerfile是文本文件,可以方便地存放在代码仓库中进行版本管理,便于追踪变更历史。7
- 无需考虑环境差异:使用Dockerfile构建镜像时,无需担心构建环境的不同,因为基于同一个Dockerfile在任何地方构建的结果都是一致的。
总之,Dockerfile是Docker生态系统中一个核心组件,它使得镜像的构建、分享和部署变得更加标准化、自动化和可重复。
Dockerfile详细解读如下:
一、 变量变量用 $variable_name 或者 ${variable_name} 表示。${variable:-word} 表示如果 variable 设置,则结果将是该值。如果 variable 未设置,word 则将是结果。
${variable:+word} 表示如果 variable 设置则为 word 结果,否则为空字符串。变量前加 \ 可以转义成普通字符串:\$foo or \${foo},表示转换为 $foo 和 ${foo} 文字。二、FROM初始化一个新的构建阶段,并设置基础镜像:FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]单个 Dockfile 可以多次出现 FROM,以使用之前的构建阶段作为另一个构建阶段的依赖项
AS name 表示为构建阶段命名,在后续 FROM 和 COPY --from=<name> 说明中可以使用这个名词,引用此阶段构建的映像
digest 其实就是就是根据镜像内容产生的一个 ID,只要镜像的内容不变 digest 也不会变
tag 或 digest 值是可选的。如果您省略其中任何一个,构建器默认使用一个 latest 标签。如果找不到该 tag 值,构建器将返回错误。
--platform 标志可用于在 FROM 引用多平台镜像的情况下指定平台。例如,linux/amd64、linux/arm64、 或 windows/amd64。三、RUN将在当前镜像之上的新层中执行命令,在 docker build时运行。RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
RUN 有两种形式:RUN<command>(shell 形式,命令在 shell 中运行,默认 /bin/sh -c 在 Linux 或 cmd /S /CWindows 上)
RUN ["executable", "param1", "param2"](执行形式)说明:可以使用 \(反斜杠)将单个 RUN 指令延续到下一行
RUN 在下一次构建期间,指令缓存不会自动失效。可以使用 --no-cache 标志使指令缓存无效
Dockerfile 的指令每执行一次都会在 Docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大,可以使用 && 符号连接命令,这样执行后,只会创建 1 层镜像四、CMD运行程序,在 docker run 时运行,但是和 run 命令不同,RUN 是在 docker build 时运行。FROM ubuntu
CMD ["/usr/bin/wc","--help"]
支持三种格式:CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数。指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。五、LABEL添加元数据:LABEL multi.label1="value1" \multi.label2="value2" \other="value3"
六、EXPOSEEXPOSE <port> [<port>/<protocol>...]
Docker 容器在运行时侦听指定的网络端口。可以指定端口是监听TCP还是UDP,如果不指定协议,默认为TCP。该 EXPOSE 指令实际上并未发布端口。要在运行容器时实际发布端口,docker run -P 来发布和映射一个或多个端口。默认情况下,EXPOSE 假定 TCP。您还可以指定 UDP:EXPOSE 80/udp
七、ENV设置环境变量:ENV <key>=<value> ...
设置的环境变量将持续存在,您可以使用 docker inspect 来查看。使用 docker run --env <key>=<value> 来更改环境变量的值。如果环境变量只在构建期间需要,请考虑为单个命令设置一个值:RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
或者使用 ARG,它不会保留在最终镜像中:ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
八、ADD复制新文件、目录或远程文件 URL <src> ,并将它们添加到 <dest> 中。<src> 可以指定多个资源,但如果它们是文件或目录,则它们的路径被解释为相对于构建上下文的源,也就是 WORKDIR。每个都 <src> 可能包含通配符,匹配将使用 Go 的 filepath.Match 规则。例如:添加所有以“hom”开头的文件:ADD hom* /mydir/
在下面的示例中,? 被替换为任何单个字符,例如“home.txt”。ADD hom?.txt /mydir/
<dest> 是一个绝对路径,或相对 WORKDIR 的相对路径。九、COPY语法同ADD一致,复制拷贝文件。COPY 指令和 ADD 指令的唯一区别在于:是否支持从远程URL获取资源。COPY 指令只能从执行 docker build 所在的主机上读取资源并复制到镜像中。而 ADD 指令还支持通过 URL 从远程服务器读取资源并复制到镜像中。相同需求时,推荐使用 COPY 指令。ADD 指令更擅长读取本地tar文件并解压缩。十、ENTRYPOINTENTRYPOINT 和 CMD 一样,都是在指定容器启动程序及参数,不过它不会被 docker run 的命令行参数指定的指令所覆盖。如果要覆盖的话,需要通过 docker run --entrypoint 来指定。它有2种格式:ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
指定了 ENTRYPOINT 后, CMD 的内容作为参数传给 ENTRYPOINT 指令,实际执行时,将变为:<ENTRYPOINT> <CMD>
十一、VOLUME创建一个具有指定名称的挂载数据卷。VOLUME ["/var/log/"]
VOLUME /var/log
它的主要作用是:避免重要的数据,因容器重启而丢失
避免容器不断变大十二、ARG定义变量,与 ENV 作用相同,不过 ARG 变量不会像 ENV 变量那样持久化到构建好的镜像中。ARG <name>[=<default value>]
Docker 有一组预定义的 ARG 变量,您可以在 Dockerfile 中没有相应指令的情况下使用这些变量。HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy要使用这些,请使用 --build-arg 标志在命令行上传递它们,例如:docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
十三、ONBUILD将一个触发指令添加到镜像中,以便稍后在该镜像用作另一个构建的基础时执行。也就是另外一个 dockerfile FROM 了这个镜像的时候执行。ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
十四、STOPSIGNAL设置将发送到容器退出的系统调用信号。该信号可以是与内核系统调用表中的位置匹配的有效无符号数,例如 9,或格式为 SIGNAME 的信号名称,例如 SIGKILL。STOPSIGNAL signal
默认的 stop-signal 是 SIGTERM,在 docker stop 的时候会给容器内 PID 为 1 的进程发送这个 signal,通过 --stop-signal 可以设置自己需要的 signal,主要目的是为了让容器内的应用程序在接收到 signal 之后可以先处理一些事物,实现容器的平滑退出,如果不做任何处理,容器将在一段时间之后强制退出,会造成业务的强制中断,默认时间是 10s。十五、HEALTHCHECK用于指定某个程序或者指令来监控 Docker 容器服务的运行状态。该 HEALTHCHECK 指令有两种形式:HEALTHCHECK [OPTIONS] CMD command(通过在容器内运行命令来检查容器健康状况)
HEALTHCHECK NONE(禁用从基础镜像继承的任何健康检查)十六、SHELL覆盖用于命令的 shell 形式的默认 shell。Linux 上的默认 shell 是 ["/bin/sh", "-c"],Windows 上是 ["cmd", "/S", "/C"]。SHELL ["executable", "parameters"]
该 SHELL 指令在 Windows 上特别有用,因为 Windows 有两种常用且截然不同的本机 SHELL:cmd 和 powershell,以及可用的备用 shell,包括 sh。该 SHELL 指令可以出现多次。每条 SHELL 指令都会覆盖所有先前的 SHELL 指令,并影响所有后续指令。十七、WORKDIR工作目录,如果 WORKDIR 不存在,即使它没有在后续 Dockerfile 指令中使用,它也会被创建。docker build 构建镜像过程中,每一个 RUN 命令都会新建一层。只有通过 WORKDIR 创建的目录才会一直存在。可以设置多个 WORKDIR,如果提供了相对路径,它将相对于前一条 WORKDIR 指令的路径。例如:WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最终 pwd 命令的输出是 /a/b/c。该 WORKDIR 指令可以解析先前使用 ENV,例如:ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
最终 pwd 命令的输出是 /path/$DIRNAME。十八、USER设置用户名(或 UID)和可选的用户组(或 GID)。USER <user>[:<group>]
USER <UID>[:<GID>]
关键技术二:容器镜像
通过Dockerfile可以生成容器镜像,容器的镜像是分层保存,对于Dockerfile中的每一个语句,生成一层容器镜像,如此叠加,每一层都有UUID。
容器镜像可以打一个版本号,放入统一的镜像仓库。
关键技术三:容器运行时
容器运行时,是将容器镜像之上加一层可写入层,为容器运行时所看到的文件系统。
容器运行时使用了两种隔离的技术。
一种是看起来是隔离的技术,称为namespace,也即每个namespace中的应用看到的是不同的IP地址、用户空间、程号等。
的技术,称为cgroup,也即明明整台机器有很多的CPU、内存,而一个应用只能用其中的一部分。
微服务技术和微服务框架
微服务技术
上面两图中基本包含了微服务中的技术栈。
科普文:微服务技术栈梳理-CSDN博客
负载均衡器Load Balancer
Nginx作为整个架构的流量入口,可以理解为一个外部的网关,它承担着请求的路由转发、负载均衡、动静分离等功能。作为一个核心入口点,Nginx肯定要采用多节点部署,同时通过keepalived来实现高可用,从而保障整个平台的高可用。
科普文:深入理解负载均衡(四层负载均衡、七层负载均衡)_第七层负载均衡-CSDN博客
Traefik是一种开源的负载均衡方法,它是一个现代化的反向代理和负载均衡器,专为微服务架构设计。Traefik可以自动地为容器化的应用程序提供负载均衡和动态路由功能。
Traefik适用于各种场景,特别是在容器化的微服务架构中,可以帮助实现服务的负载均衡、动态路由和高可用性。它可以与多种云原生技术和工具集成,如Docker、Kubernetes、Consul等。
Seesaw:Google的网络负载均衡器神器 是Google开源的一个网络负载均衡器,它设计用于大规模、高性能的云环境。
Seesaw基于Linux虚拟服务器(LVS)构建,利用Netlink接口与内核通信,提供高效的连接分发。它支持四层(TCP/UDP)和七层(HTTP及其他应用协议)负载均衡,确保了各种服务的高效运行。
微服务网关
网关是在Nginx(负载均衡器)后的另外一个核心组件。它承担着请求鉴权,路由转发,协议转换,流量监控等一系列功能。
spring Cloud Gateway来实现业务网关的功能,在网关选型中,我们还有其他的选择,比如Zuul1,Zuul2,Kong等等,这些方案都有自己的优势和局限性,我们可以根据自己他们的特点来抉择到底选用哪一个方案。对于网关的深入了解,可以参见之前的系列文章网关那点事,这里不做赘述。
Spring Cloud Gateway下面有jwt和OAuth2,其实这两个就是基于token的认证鉴权,一般互联网项目中,在登录模块都是支持微信或者qq登录,这就是用到OAuth2的授权登录。
微服务业务组件
网关之后就是我们的业务组件了,可以理解就是拆分之后的微服务了,比如电商平台常见的账号服务、订单服务、发票服务、收银台服务等等。
服务组件之间通过Feign来进行http调用,Feign集成Ribbon来实现客户端侧负载均衡。
具体的服务领域划分,服务限界上下文的设定,这就另外的知识了,如果想做好服务划分,DDD领域驱动设计这块可以深入了解下。
微服务注册中心
不管是基于Dubbo实现的SOA,还是基于Spring Cloud拆分的微服务架构,服务注册中心都是必须的,我们把所有的服务组件都注册到注册中心,进而实现服务的动态调用。常见能实现注册中心功能的有Zookeeper,Eureka,Nacos,Zookeeper在Dubbo中使用比较多,目前公司服务微服务架构是基于Eureka的,Eureka好像目前不维护了。一般新的平台建议直接集成Nacos,Nacos除了能做注册中心来使用,也可以作为分布式配置中心来使用,比Sping Cloud Config更好使。
Spring Cloud Netflix Eureka:目前还是有很多系统在用Eureka做服务注册中心。就像美帝的*富还有很多jsp页面。毕竟Spring Cloud Netflix是早期很流行的微服务框架(2018年之前)。
实战:Eureka的概念作用以及用法详解-CSDN博客
Spring Cloud Alibaba Nacos:Spring Cloud Alibaba提供的注册中心和配置中心工具。
微服务配置中心
微服务意味着要将单体应用中的业务拆分成一个个子服务 ,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。如果我们每个微服务自己带着一个application.yml配置文件,上百个配置文件的管理,我们肯定会特别抓狂的,所以SpringCloud提供了ConfigServer(分布式配置中心)来解决这个问题。
Spring Cloud Config是Spring Cloud微服务体系中的配置中心,是一个集中化外部配置的分布式系统,由服务端和客户端组成,其不依赖于注册中心,是一个独立的配置中心,支持多种存储配置信息形式,目前主要有jdbc、value、native、svn、git,其中默认是git。
重点讨论功能有如下两个方面:
- 将程序中配置的各种功能开关、参数配置、服务器地址------>修改后实时生效
- 灰度发布、分环境、分集群管理配置--------->全面集中化管理
Spring Cloud Alibaba Nacos:Spring Cloud Alibaba提供的注册中心和配置中心工具。
科普文:微服务之Apollo配置中心_apollo配置+微服务-CSDN博客
缓存和分布式锁(图中未画出)
我们可以把Redis作为缓存来使用,把一些查询慢,使用率高的热点数据做缓存处理,能快速提高接口响应时间。同时redis在微服务中的一大使用场景就是分布式锁,传统的Sychronized和显示Lock锁显然是不能解决分布式并发问题。
为了保障Redis的高可用,可以采用哨兵部署,不是三个redis节点,一主二从,同时部署三个哨兵节点,来实现故障转移,避免单点问题,如果Redis存储的数据量很大,达到了单节点的Redis的性能瓶颈,我们也可以用Redis集群模式来实现分布式存储。
实战:ZooKeeper实现分布式锁-CSDN博客
科普文: Etcd实现高可用AP+强一致性CP的分布式锁_etcd高可用-CSDN博客
科普文:从Redis1.0到Redis7.0的发展历程来理解为什么Redis钟爱单线程_redis各个版本发布时间-CSDN博客
科普文:深入理解Redis-CSDN博客
实战:搞懂Redisson、分布式锁、限流器_redisson分布式限流-CSDN博客
数据持久层
不管单体服务,还是微服务,数据持久层都是必须的,我们是选用互联网项目经常使用的mysql作为DB,为了保证服务读写效率以及高可用性,我们主从分离模式,同时实现读写分离,来保障mysql的读写性能。
随着业务量增长,单表的数据量达到性能瓶颈之后,我们就要采用分库分表来对数据库表进行水平拆分和垂直拆分了,具体如何进行合理的拆分,以及技术选型,这些和项目现有的表结构设计是息息相关的,要考虑后续的可拓展性,不能短期拆了一时爽,后续业务量增暴涨之后,服务器的性能不足以维持数据库的性能时,这时候要拆分服务器部署了。当然,一般企业的数据量级达不到那样的量级。
结构型数据存储
mysql比较擅长存储关系型数据,项目中有需要存储结构性数据的场景,比如存储JSON字符串,这种场景通过mysql来存储显然事不合适的。一般我们会采用Elasticsearch或者MangoDB来进行存储,如果业务中需要检索功能,更建议使用Elasticsearch。Elasticsearch支持DSL,有比较丰富查询检索功能,甚至能实现GIS空间检索功能。
消息中间件
前面说到,微服务架构中,服务之间同步调用是通过Feign来实现的,那服务间的异步解耦就要通过MQ来实现了。虽然我们可以通过多线程来实现异步调用,但是这种异步调用不支持持久化,可能会造成消息丢失,所以一般都集成RabbitMq或者RocketMq。
科普文:万字详解Kafka基本原理和应用-CSDN博客
科普文:万字梳理高性能 Kafka快的8个原因_kafka 性能-CSDN博客
微服务日志收集和链路跟踪
在微服务架构中,通过一个组件,比如说订单服务都是多节点分布式部署,每个节点的log日志都是存储在节点本地,如果要查询日志,我们难道要登录到各个节点找到对应的日志信息?这种查看日志肯定是不行的。所以一般会引入ELK来做日志收集,和可视化展示查询。
-
Logstash 用来做日志收集工作,通常在Logstash前会加一个Filebeat,由Filebeat来收集日志,Logstash做数据转换工作。
-
Elasticsearch做数据存储,以及生成索引数据,便于Kibana做检索。
-
Kibana做数据的展示,以及查询检索功能,我们通过检索关键词就能快速的查询到想要日志信息。
分布式任务调度中心
项目中经常会用到定时功能,单体应用中,我们使用sping自带的Schedule,或者使用Quartz即可,在分布式应用中,我们就要集成分布式定时器,比如Quartz(Quartz配合数据库表也是支持分布式定时任务的),还有Elastic-Job、XXL-JOB等等。
Elastic-job 当当网基于quartz 二次开发的弹性分布式任务调度系统,功能丰富强大,采用zookeeper实现分布式协调,实现任务高可用以及分片。Elastic-Job是一个分布式调度的解决方案,由当当网开源,它由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成,使用Elastic-Job可以快速实现分布式任务调度。
XXL-JOB 是一个分布式任务调度平台(XXL是作者徐雪里姓名拼音的首字母),其核心设计目标是开发迅速、学习简单、轻量级、易扩展。将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。将任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。因此,“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性。
分布式对象存储
项目中经常会有文件上传功能,比如图片,音频视频。在分布式架构中,我们将文件存储在节点服务器上显然是不行的,这时候,我们就需要引入分布式文件存储。常见方案有MinIo、阿里的OSS(收费),阿里FastDFS等等。
MinIO 是一款基于Go语言发开的高性能、分布式的对象存储系统。客户端支持Java、Net、Python、Javacript、Golang语言。
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
其他技术
容器化、授权认证就先不提了。
微服务框架
Spring Cloud Netflix
是 Spring Cloud 生态系统中最早期的一个子项目,它为 Spring Cloud 提供了一套基于 Netflix OSS 组件实现的服务治理方案。
包含了如 Eureka(服务注册与发现)、Hystrix(熔断器)、Zuul(API 网关)、Ribbon(客户端负载均衡器)和 Turbine(监控仪表盘)等组件。
自2018年12月12日起,Netflix 公司宣布停止对其内部开源组件的维护,转而推荐社区自行维护。这意味着 Spring Cloud Netflix 中的部分组件将不再得到官方更新和支持。
Spring Cloud Netflix
Spring Cloud
是一个基于 Spring Boot 实现的微服务解决方案框架,它提供了一套微服务开发所需的规范和标准。
定义了一系列开发微服务应用的模式和组件,比如服务注册与发现、配置中心、负载均衡、熔断器、API 网关等,但并不直接提供具体实现。
Spring Cloud
Spring Cloud Alibaba
是 Spring Cloud 的一个中国化增强版本,也是 Spring Cloud 生态的重要组成部分,旨在提供一站式的微服务解决方案。
针对中国开发者的需求,提供了阿里巴巴集团多个开源组件的整合,例如 Nacos(服务注册与发现、配置管理)、Sentinel(流量控制、熔断降级)、RocketMQ(消息队列)、Dubbo(RPC 框架)、Seata(分布式事务解决方案)等。
相较于 Spring Cloud Netflix,Spring Cloud Alibaba 使用的是更活跃且持续维护的组件,并针对云原生场景做了优化,更适合当前微服务架构的发展趋势。
Spring Cloud Alibaba
Spring Cloud Alibaba 是什么 - Spring Cloud Alibaba官网
三者直接得组件对比
Spring Cloud Netflix | Spring Cloud | Spring Cloud Alibaba | |
注册中心 | Netflix Eureka | Netflix Eureka | Alibaba Nacos |
配置中心 | Spring Cloud Config | Alibaba Nacos | |
服务调用 | Netflix Fegin | Spring Cloud OpenFegin | Spring Cloud OpenFegin ,Alibaba Dubbo |
负载均衡 | Netflix Ribbon | Netflix Ribbon | Netflix Ribbon |
服务容错 | Netflix Hystrix | Spring Cloud Hystrix | Alibaba Sentinel |
API网关 | zuul | spring Cloud Gateway | spring Cloud Gateway |
消息驱动 | Kafka | Spring Cloud Stream | Alibaba RocketMQ |
事件总线 | Spring Cloud Bus | Alibaba RocketMQ | |
链路跟踪 | Spring Cloud Sleuth | ||
分布式事务 | Alibaba Seate | ||
分布式调度 | Alibaba SchedulerX |
Spring Cloud 微服务体系
Spring Cloud 是分布式微服务架构的一站式解决方案,它提供了一套简单易用的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务系统的构建。 Spring Cloud 提供以微服务为核心的分布式系统构建标准。
Spring Cloud 本身并不是一个开箱即用的框架,它是一套微服务规范,共有两代实现。
- Spring Cloud Netflix 是 Spring Cloud 的第一代实现,主要由 Eureka、Ribbon、Feign、Hystrix 等组件组成。
- Spring Cloud Alibaba 是 Spring Cloud 的第二代实现,主要由 Nacos、Sentinel、Seata 等组件组成。
Spring Cloud Alibaba 定位
Spring Cloud Alibaba 是阿里巴巴结合自身丰富的微服务实践而推出的微服务开发的一站式解决方案,是 Spring Cloud 第二代实现的主要组成部分。吸收了 Spring Cloud Netflix 微服务框架的核心架构思想,并进行了高性能改进。自 Spring Cloud Netflix 进入停更维护后,Spring Cloud Alibaba 逐渐代替它成为主流的微服务框架。
同时 Spring Cloud Alibaba 也是国内首个进入 Spring 社区的开源项目。2018 年 7 月,Spring Cloud Alibaba 正式开源,并进入 Spring Cloud 孵化器中孵化;2019 年 7 月,Spring Cloud 官方宣布 Spring Cloud Alibaba 毕业,并将仓库迁移到 Alibaba Github OSS 下。