微服务是一种软件开发架构模式,它将一个复杂的应用程序拆分成一系列更小、更独立的服务。每个服务都担当自己特定的业务功能,可以独立开发、部署和扩展。这些服务之间通过轻量级的通信机制进行交互,如HTTP/REST或消息队列。
微服务架构的主要思想是将应用程序分解为一组自治的服务,每个服务都具有自己的数据库和逻辑,可以独立地进行部署和扩展。这种分解带来了许多好处,包括:
当然,微服务架构也存在一些挑战,例如服务之间的通信开销、数据一致性的管理、服务的发现和治理等问题需要额外的关注和解决。因此,在设计和实施微服务架构时,需要综合考虑各种因素,并根据具体情况进行权衡和抉择。
1.1 单体架构及其存在的不足
在软件设计中,经常提及和使用经典的3层模型,即表示层、业务逻辑层和数据访问层。
在一个小型应用的初始阶段,访问量较小,应用只需要一台服务器就能够部署所有的资源,例如将应用程序、数据库、文件资源等部署在同一台服务器上。最典型的就是LAMP系统,即服务器采用Linux系统,开发应用程序的语言为PHP,部署在Apache服务器上,采用MySQL数据库。在应用程序的初始阶段,采用这种架构的性价比是非常高的,开发速度快,开发成本低,只需要一台廉价的服务器。此时的服务器架构如图1-2所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/344d7c488dc845768d47cc60c4ef812e.png)
单体架构是传统的软件开发架构模式,它将整个应用程序作为一个单一的单元进行开发、部署和运行。在单体架构中,所有的功能模块都耦合在一起,共享相同的数据库和代码库。
1.1.2 单体架构存在的不足
然而,单体架构存在一些不足之处,包括以下几点:
可扩展性限制:由于整个应用程序作为一个单一单元进行开发和部署,当应用程序的规模增大,访问量增加时,很难对其进行水平扩展。这可能导致性能瓶颈和可用性问题。
部署复杂性:由于所有的功能模块都集成在一个单体应用中,每次更新或部署都需要重新构建整个应用程序。这导致部署过程变得复杂和耗时,并且可能引入新的错误。
技术栈限制:在单体架构中,所有的功能模块使用相同的技术栈和开发语言。这限制了团队在选择最适合特定任务的技术和工具时的灵活性。
可靠性和容错性:由于单体应用的高度耦合性,一个模块的故障可能会影响整个应用程序的稳定性和可用性。难以实现针对故障模块的局部修复和容错机制。
开发效率下降:在单体架构中,所有开发人员共享同一个代码库。这可能导致协同开发困难,合并代码冲突频繁,影响开发效率。
系统复杂性增加:随着应用程序的增长,单体架构可能变得庞大而复杂。这会导致理解和维护代码的困难,并且使整个系统更加脆弱。
开发效率下降:在单体架构中,所有开发人员共享同一个代码库。这可能导致协同开发困难,合并代码冲突频繁,影响开发效率。
为了解决单体架构的不足,微服务架构应运而生。微服务架构通过将应用程序拆分为一组独立的服务来提供更好的可扩展性、部署灵活性、技术栈多样性和容错性。每个服务都可以独立开发、部署和扩展,提高了开发效率和整体系统的可维护性。然而,微服务架构也需要更多的管理和治理复杂性,并且在选择合适的架构模式时需要综合考虑多种因素。
1.1.3 单体架构使用服务器集群及存在的不足
在单体架构中,可以通过使用服务器集群来实现高可用性和负载均衡。服务器集群是将多台服务器组成一个逻辑单元,它们共同为单个应用程序提供服务。每个服务器都具有相同的代码和数据副本,以实现冗余和故障恢复。
然而,单体架构使用服务器集群也存在一些不足之处:
部署和维护复杂性:在单体架构中,服务器集群需要保持一致的状态,包括代码、配置和数据。这增加了部署和维护的复杂性,需要确保所有服务器都具有相同的版本和配置,并且需要进行协调和同步。
资源利用率低:服务器集群中的每个节点都必须具有足够的资源来处理最大负载的情况,这可能导致资源利用率低下。例如,在低负载时,一些节点可能处于空闲状态,而其他节点则过载,导致资源浪费。
水平扩展限制:虽然服务器集群可以通过增加节点数目来实现水平扩展,但在单体架构中,应用程序的规模和复杂性可能会限制其水平扩展的能力。因为单体应用程序具有高度耦合性,必须将整个应用程序的副本复制到每个节点上。
故障隔离和恢复困难:在单体架构中,一个模块的故障可能会影响整个应用程序的可用性。虽然服务器集群可以提供冗余和容错能力,但故障隔离和恢复仍然可能是挑战,特别是涉及到共享的数据库和状态。
扩展性限制:由于服务器集群仍然是在单个应用程序的范围内操作,因此扩展性仍受到该应用程序设计以及单体架构本身的限制。当应用程序变得更大和更复杂时,继续添加节点可能不再有效,并且可能需要重新评估整体架构。
综上所述,尽管使用服务器集群可以改善单体架构的一些问题,但仍然存在一z些困难和限制,这是微服务架构等其他架构模式出现的原因。
微服务是一种软件架构模式,它将应用程序拆分为一组小型、独立的服务,每个服务专注于执行特定的业务功能。每个微服务运行在自己的进程中,并使用轻量级通信机制(如HTTP或消息队列)进行相互之间的通信。
微服务架构的主要原则包括:
单一责任原则:每个微服务只负责一个明确定义的业务功能,它们之间通过明确定义的接口进行通信。
松耦合:微服务之间相互独立,可以独立开发、部署和扩展。一个服务的变更不会直接影响其他服务。
拆分按需:应用程序按照业务边界进行拆分,每个微服务只包含必要的功能和数据,避免过度设计和冗余。
独立部署:每个微服务可以独立地进行部署,这样可以实现快速迭代和持续交付。每个服务团队可以独立地选择适合他们的技术栈和开发工具。
去中心化治理:微服务架构中的每个服务都有自己的数据存储和业务逻辑,没有单一的中央数据库或共享状态。这减少了共享资源的竞争和复杂性。
微服务架构的优势包括:
高可扩展性:每个微服务可以独立地进行水平扩展,根据需要增加或减少实例数量,以满足不同的负载需求。
独立开发和部署:每个微服务可以由一个小团队独立开发和部署,加快了开发速度和持续交付的能力。
容错性和可恢复性:一个微服务的故障不会影响整个应用程序,其他服务仍然可用。故障服务可以快速恢复或被替换。
技术多样性:不同的微服务可以使用适合自己需求的技术栈和开发语言,提供灵活性和创新性。
然而,微服务架构也带来了一些挑战,例如分布式系统的复杂性、服务间通信的开销、数据一致性等,需要仔细考虑和解决。
微服务的自动化部署是指通过使用自动化工具和流程,实现对微服务应用的快速、可靠和一致的部署过程。它能够显著提高开发团队的效率,减少人为出错和手动操作的风险。
下面是一些常见的微服务自动化部署的方法和工具:
容器化:使用容器技术(如Docker)将每个微服务打包成一个独立的容器,包含其运行时环境和依赖项。然后使用容器编排工具(如Kubernetes)来管理和编排这些容器。
持续集成/持续部署(CI/CD):通过持续集成和持续部署流水线,自动执行构建、测试、部署等步骤。常见的工具有Jenkins、GitLab CI/CD、Travis CI等,它们可以与代码仓库和部署环境集成,实现自动触发和部署。
基础设施即代码(IaC):使用IaC工具(如Terraform、Ansible)定义和管理云基础设施资源,包括虚拟机、网络、存储等。通过IaC,可以实现对微服务部署环境的自动创建和配置。
配置管理:使用配置管理工具(如Puppet、Chef)管理微服务的配置信息,包括环境变量、数据库连接等。通过配置管理工具,可以实现对不同环境的自动化配置。
自动化测试:在部署过程中,可以通过自动化测试工具(如Selenium、JUnit)执行各种类型的测试,包括单元测试、集成测试和端到端测试。这样可以确保每个微服务在部署后正常运行。
监控和日志:在部署完成后,需要建立监控和日志系统,实时监测微服务的性能、健康状态和异常情况。可以使用监控工具(如Prometheus)和日志管理工具(如ELK Stack)进行集中管理和分析。
综上所述,微服务的自动化部署可以通过容器化、持续集成/持续部署、基础设施即代码、配置管理、自动化测试、监控和日志等方式实现。这些方法可以帮助开发团队快速部署和管理微服务应用,提高整体的开发效率和质量。
5.服务集中化管理
微服务系统是按业务单元来划分服务的,服务数量越多,管理起来就越复杂,因此微服务必须使用集中化管理。目前流行的微服务框架中,例如Spring
Cloud支持使用Eureka、Zookeeper和Consul来注册服务和发现服务,另外,Etcd和Nacos等都是非常优秀的服务注册与发现组件。
6.分布式架构
分布式系统是集群部署的,由很多计算机相互协作共同构成,它能够处理海量的用户请求。当分布式系统对外提供服务时,用户是毫不知情的,还以为是一台服务器在提供服务。分布式系统的复杂任务通过计算机之间的相互协作来完成,当然简单的任务也可以在一台计算机上完成。分布式系统通过网络协议来通信,所以分布式系统在空间上没有任何限制,即分布式服务器可以部署不同的机房和不同的地区。微服务架构是分布式架构,分布式系统比单体系统更加复杂,主要体现在服务的独立性和服务相互调用的可靠性,以及分布式事务、全局锁、全局Id等,而单体系统不需要考虑这些复杂性。另外,分布式系统的应用都是集群化部署,会给数据一致性带来困难。分布式系统中的服务通信依赖于网络,网络不好,必然会对分布式系统带来很大的影响。在分布式系统中,服务之间相互依赖,如果一个服务出现了故障或者是网络延迟,在高并发的情况下,会导致线程阻塞,在很短的时间内该服务的线程资源会消耗殆尽,最终使得该服务不可用。由于服务的相互依赖,可能会导致整个系统的不可用,这就是“雪崩效应”。为了防止此类事件的发生,分布式系统必然要采取相应的措施,例如“熔断机制”。
7.熔断机制
熔断机制(CircuitBreaker)是一种用于微服务架构中的故障保护和容错机制。它的主要目的是在服务之间的通信发生故障时,防止故障的扩散,保护系统的可用性和稳定性。
熔断机制的原理类似于电力系统中的保险丝。当系统中的某个服务或组件发生故障或异常时,熔断器会迅速中断对该服务的调用,并在一段时间内拒绝对该服务的请求。这样可以减少对不可用服务的访问,避免资源浪费和雪崩效应。
以下是熔断机制的几个关键概念和工作流程:
状态切换:熔断机制有三个状态:关闭(Closed)、打开(Open)和半开(Half-Open)。初始状态为关闭,允许正常的服务调用通过。当服务调用失败率超过设定的阈值时,熔断器进入打开状态,拒绝对服务的请求。在一定时间后,熔断器将进入半开状态,允许部分请求通过,以检测服务是否恢复正常。
故障计数器:熔断器会记录最近一段时间内服务调用失败的次数,根据故障计数器的值来判断是否触发熔断。
熔断时间窗口:当熔断器进入打开状态后,在一定时间内,所有的请求都将被快速失败,并且熔断器不会尝试调用被熔断的服务。这个时间窗口是为了避免频繁尝试失败的请求对整个系统的影响。
半开状态:在熔断时间窗口结束后,熔断器将进入半开状态。在半开状态下,允许一部分请求通过到被熔断的服务。如果这些请求成功,熔断器将切换回关闭状态。如果有请求失败,熔断器将重新进入打开状态。
熔断机制的好处在于它能够避免对已经出现故障的服务继续发起请求,从而减少了对不可用服务的资源消耗和等待时间。另外,熔断机制还可以提供故障监控和报警功能,及时发现和处理故障,提高系统的稳定性和可用性。
1.3 微服务的不足
尽管微服务架构有许多优势,但也存在一些不足之处,包括以下几点:
分布式系统复杂性:微服务架构将应用程序拆分为多个服务,每个服务都是独立的,这导致了系统变得更加复杂。在一个分布式系统中,需要考虑服务之间的通信、数据一致性、事务管理、故障处理等问题,这增加了开发、测试和部署的复杂性。
系统运维成本:由于微服务架构中有多个独立的服务,每个服务都需要独立进行部署、监控和维护。这需要额外的运维工作,包括配置管理、日志收集、故障排查等。在大规模的微服务系统中,运维成本可能会显著增加。
服务间通信开销:微服务架构中的服务需要通过网络进行通信,这导致了额外的延迟和开销。当服务之间的依赖关系较多时,可能会出现较大的网络流量和性能瓶颈。此外,网络通信的不稳定性也可能引发故障和错误处理的挑战。
数据一致性:微服务架构中的数据管理变得更加复杂。由于数据分散在多个服务中,确保数据的一致性变得更加困难。跨服务的事务管理和数据复制可能需要额外的工作,以确保数据的正确性和可靠性。
分布式系统的故障处理:在微服务架构中,服务之间的故障是常见的情况。如果一个或多个服务出现故障,可能会影响整个系统的功能和性能。因此,需要有强大的故障处理机制和监控工具来及时检测、处理和修复故障。
开发人员技术挑战:微服务架构要求开发人员具备分布式系统设计和开发的技能。开发人员需要了解服务间通信、负载均衡、容错机制等概念和技术,并且需要在不同的服务中进行跨团队协作。这带来了技术挑战和学习成本。
总的来说,微服务架构的不足之处主要集中在分布式系统的复杂性、运维成本和服务间通信开销上。此外,数据一致性、故障处理和开发人员技术要求也是需要考虑的问题。在采用微服务架构时,需要权衡这些不足与优势,并根据具体需求和团队技术能力做出决策。
1.3.2 分布式事务
分布式事务是指在分布式系统中涉及多个独立的服务或数据库的一组操作,要么全部成功提交,要么全部回滚。它的目标是保证数据的一致性和可靠性。
在分布式系统中,由于数据被拆分到不同的服务或数据库中,跨服务或跨数据库的事务操作可能存在并发冲突、网络故障等问题。为了解决这些问题,通常采用以下几种分布式事务的解决方案:
两阶段提交(Two-Phase Commit,
2PC):这是一种经典的分布式事务协议。它包含两个阶段:准备阶段和提交阶段。在准备阶段,协调者询问所有参与者是否可以提交事务,参与者会将自己的决策(是/否)发送给协调者。在提交阶段,如果所有参与者都同意提交,则协调者通知所有参与者提交事务,否则通知回滚事务。尽管2PC保证了分布式事务的原子性,但它的缺点是存在阻塞、单点故障和数据不一致等问题。补偿事务(Compensating
Transaction):补偿事务模式通过在每个服务中定义相应的补偿操作来处理分布式事务。当某个服务执行失败时,可以触发相应的补偿操作来回滚之前的操作。补偿事务模式可以保证系统的最终一致性,但实现起来相对复杂,需要开发人员设计和实现补偿操作。最大努力通知(Best Effort
Delivery):在某些情况下,为了提高系统的吞吐量和性能,可以使用最大努力通知的方式处理分布式事务。这种方式下,事务提交后,系统会尽力将结果通知给其他相关服务,但无法保证确保每个参与者都成功接收到通知。这种方式可能会产生一定程度的数据不一致性,适用于某些业务场景要求不高的情况。Saga 模式:Saga
是一种更加灵活的分布式事务处理模式,在此模式下,将一个大的事务拆分为多个小的本地事务,每个本地事务负责自己的业务操作,并通过发送消息来与其他服务进行协调。每个本地事务都有相应的补偿操作,当出现失败时,可通过执行补偿操作来回滚之前的操作。Saga
模式相对于2PC和补偿事务模式更具可伸缩性和容错性。选择适合的分布式事务解决方案需要综合考虑业务需求、性能要求和开发复杂性。在实际应用中,可能需要结合多种解决方案,并根据具体情况进行设计和实施
。
2.1.1 服务的注册与发现
服务的注册与发现是在分布式系统中用于管理和追踪各个服务实例的一种机制。在分布式系统中,由于服务数量庞大、动态变化和部署在不同的主机上,需要一种机制来帮助服务实例进行自我注册和告知,以便其他组件或服务能够发现并使用这些服务。
下面是一个简单的服务注册与发现的过程:
注册:当一个服务实例启动时,它会向服务注册中心发送注册请求,包含自己的信息(如IP地址、端口号、服务接口等)。服务注册中心通常是一个集中化的组件或服务,负责维护服务实例的注册信息。
发现:其他组件或服务需要使用某个服务时,会向服务注册中心发送查询请求,获取所需服务的注册信息。服务注册中心会返回符合条件的服务实例列表。
负载均衡:在从服务注册中心获取到服务实例列表后,需要进行负载均衡策略的选择,以决定从哪个服务实例获取服务。常见的负载均衡策略包括随机选择、轮询、权重等。
服务调用:通过获取到的服务实例的信息(如IP地址和端口号),可以建立网络连接并进行服务调用。服务调用过程中,可以利用各种通信协议和技术实现远程调用,如HTTP、RPC等。
健康检查和下线:服务注册中心会周期性地对已注册的服务实例进行健康检查,以确保服务实例的可用性。当一个服务实例需要下线时,它会发送下线请求给服务注册中心,使其从注册表中移除。
常见的服务注册与发现的解决方案包括ZooKeeper、Consul、Etcd等。它们提供了高可用性、可靠性和弹性扩展的服务注册与发现功能,帮助构建可靠的分布式系统。
在分布式系统中,服务的容错是指在面对各种故障和异常情况时,保持系统的可用性和可靠性的能力。以下是一些常见的服务容错机制:
超时和重试:当发起服务调用时,可以设置一个合理的超时时间。如果在指定时间内未收到响应,则可以进行重试。重试可以帮助处理短暂的网络故障或服务不可用的情况。
限流和熔断:为了保护系统免受过载的影响,可以实施限流和熔断策略。限流可以控制并发请求的数量,以避免资源耗尽。熔断是一种自动切断对某个服务的请求,并返回预定义的错误响应,当检测到该服务出现故障或性能下降时触发。
快速失败:当发生服务调用失败时,可以立即返回错误响应,而不是继续等待或重试。快速失败可以减少系统的等待时间,并快速通知调用方出现问题。
服务降级:当系统压力过大或某个关键服务不可用时,可以通过服务降级来保证核心功能的可用性。服务降级是指提供一种简化版本或备用方案,以满足基本需求,并减少对失败服务的依赖。
事务回滚和补偿:在分布式事务中,如果某个操作失败,可以进行事务回滚,将数据恢复到之前的状态。同时,在某些情况下可以采用补偿操作来处理失败的事务,通过执行一系列逆操作或回滚操作来修复数据一致性。
快速恢复和备份:当出现故障时,应该尽快进行故障排查和修复,以减少系统的不可用时间。同时,定期进行系统备份和数据备份,以便在发生灾难性故障时能够快速恢复。
监控和报警:建立有效的监控体系,实时监测系统的各个组件和指标,及时发现潜在的故障和性能问题。同时,设置合理的报警规则,以便在发生异常情况时及时通知相关人员进行处理。
以上是一些常见的服务容错机制,它们可以单独或结合使用,以提高分布式系统的可靠性和容错性。具体的容错策略需要根据系统的需求和业务场景进行选择和实施。
服务网关(Service Gateway)是一种在分布式系统架构中用于管理和统一对外提供服务的组件。它作为系统的门面或入口,承担了多种功能,并提供了一系列的服务治理和安全控制特性。
以下是一些常见的服务网关功能和特点:
路由与负载均衡:服务网关可以根据请求的路由规则将请求分发到相应的后端服务实例。通过集成负载均衡算法,可以有效地平衡流量,提高系统的性能和可扩展性。
API 管理:服务网关可以提供全面的 API 管理功能,包括 API 的发布、订阅、版本控制、文档生成等。它可以帮助开发者和消费者更好地理解和使用提供的 API,并提供统一的接口访问方式。
安全与认证:服务网关可以实施安全措施,如认证和授权,以确保只有经过验证的用户才能访问受保护的服务。它可以集成各种认证机制,如基于令牌的身份验证、OAuth、JWT 等。
日志和监控:服务网关可以对进出的请求进行日志记录和监控,包括请求响应时间、错误率、流量等指标。这些信息可以帮助系统管理员和开发人员进行故障排查和性能调优。
缓存和性能优化:服务网关可以提供缓存机制,缓存一些频繁请求的结果,以减少对后端服务的访问,提高系统的响应速度和吞吐量。
限流和熔断:为了保护后端服务免受过载的影响,服务网关可以实施限流和熔断策略。限流可以控制并发请求的数量,熔断是一种自动切断对某个服务的请求,并返回预定义的错误响应。
服务治理:服务网关可以集成服务注册与发现机制,帮助自动化地管理和追踪服务实例。它可以与服务注册中心协同工作,实现动态路由和服务的自动发现。
常见的服务网关包括Nginx、Spring Cloud
Gateway、Kong等。它们提供了丰富的功能和可插拔的插件机制,可根据具体需求进行配置和扩展。通过使用服务网关,可以提高系统的可维护性、可扩展性和安全性,并简化分布式系统的整体架构。
双机热备
双机热备(Dual Machine Hot Backup)是一种容错机制,用于提高系统的可用性和容灾能力。它通过在主备两台服务器之间实时同步数据,使得备用服务器能够立即接管主服务器的工作,以确保系统在主服务器发生故障或不可用时仍能正常运行。
以下是双机热备的基本原理和关键特点:
主备关系:双机热备需要配置一对主备服务器,其中主服务器负责处理实际的请求和业务操作,备服务器处于待命状态,等待接管主服务器的工作。
数据同步:主备服务器之间通过某种机制实时同步数据,以保持数据的一致性。常见的同步方式包括基于数据库的主从复制、文件系统级别的同步或存储级别的镜像。
心跳检测:主备服务器之间会通过心跳检测机制保持通信,定期发送心跳信号来确认对方的存活状态。如果主服务器的心跳信号中断,备服务器会立即感知到,并触发接管操作。
故障切换:当主服务器发生故障或不可用时,备服务器会接管主服务器的IP地址或服务端口,并开始处理请求。这个过程通常是自动进行的,可以快速恢复系统的运行状态。
监控和报警:双机热备系统通常配备监控和报警机制,以便实时监测主备服务器的状态。当主服务器发生故障时,会触发报警通知管理员进行故障排查和处理。
扩展性:双机热备可以通过添加更多备用服务器来实现水平扩展,提高系统的可靠性和容量。备用服务器可以分布在不同的物理位置或数据中心,以防止单点故障。
双机热备是一种常见且有效的容错机制,常用于关键业务系统、数据库、网络设备等场景。它能够大幅度减少系统的停机时间和数据丢失风险,提供高可用性和灾备能力,从而满足企业对系统连续性和可靠性的要求。
网关层具有很重要的意义,具体体现在以下方面。
•网关将所有服务的API接口资源统一聚合,对外统一暴露,外界系统调用的API接口都是网关对外暴露的API接口。外界系统不需要知道微服务架构中各服务相互调用的复杂性,微服务系统也保护了其内部微服务单元的API接口,防止被外界直接调用以及服务的敏感信息对外暴露。
•网关可以做一些用户身份认证、权限认证,防止非法请求操作API接口,对内部服务起到保护作用。
•网关可以实现监控功能,实时日志输出,对请求进行记录。 •网关可以用来做流量监控,在高流量的情况下,对服务进行降级。
•API接口从内部服务分离出来,方便做测试。当然,网关实现这些功能,需要做高可用,否则网关很可能成为架构中的瓶颈。最常用的网关组件有Zuul和Nginx等。
2.1.5 服务配置的统一管理
在分布式系统中,统一管理服务配置是确保系统的一致性和可维护性的重要组成部分。通过集中管理和动态更新服务配置,可以降低配置错误的风险、提高系统的灵活性,并简化配置管理的过程。
以下是一些常见的方法和实践来实现服务配置的统一管理:
配置中心:使用配置中心作为集中存储和管理服务配置的基础设施。配置中心可以提供用户友好的界面或 API,用于创建、更新和查询配置项。它还可以支持配置版本控制、权限管理和审计功能。
配置文件管理:将配置信息存储在配置文件中,并将这些文件存储在版本控制系统中,如Git。通过合理的目录结构和文件命名规范,可以便于对配置文件进行组织和管理,并通过版本控制工具来进行历史记录和版本控制。
环境分离:将不同环境(如开发、测试、生产)的配置项分开管理,确保每个环境只加载相应的配置。这样可以防止环境间的配置混淆,并减少人为错误的可能性。
动态配置更新:配置中心应当支持动态更新配置,以及将最新的配置推送到服务实例。服务实例应具备能力接收并应用新的配置,以充分利用动态配置的优势。这可以通过配置中心提供的通知机制、轮询或者其他推送方式实现。
配置验证和校验:在配置中心或配置文件中,可以添加验证规则和格式限制,确保配置的正确性和合法性。例如,对于数值型配置项,可以验证其取值范围;对于字符串类型配置项,可以验证其格式和长度等。
安全和权限控制:配置中心应具备安全机制,确保只有授权用户能够访问和修改配置。可以通过认证和授权机制实现对配置的细粒度权限控制,以保护敏感配置信息。
配置的监控与审计:配置中心和服务实例应提供监控和审计功能,记录配置读取和更新的操作日志,以便及时发现问题并追踪配置变更的历史。
以上方法和实践可以根据具体的系统架构和需求做适当调整和扩展。统一管理服务配置是分布式系统中一项重要的任务,它可以提升系统的可靠性、一致性和可维护性,并为系统的持续集成和部署提供了基础支持。
•首先,Config Server(配置服务)读取配置文件仓库的配置信息,其中配置文件仓库可以存放在配置服务的本地仓库,也可以放在远程的Git仓库(例如GitHub、Coding等)。
•配置服务启动后,读取配置文件信息,读取完成的配置信息存放在配置服务的内存中。
•当启动服务A、B时,由于服务A、B指定了向配置服务读取配置信息,服务A、B向配置服务读取配置信息。
•当服务的配置信息需要修改且修改完成后,向配置服务发送Post请求进行刷新,这时服务A、B会向配置服务重写读取配置文件。
对于集群化的服务,可以通过使用消息总线来刷新多个服务实例。如果服务数量较多,对配置中心需要考虑集群化部署,从而使配置中心高可用,做分布式集群。
2.1.6 服务链路追踪
微服务系统是一个分布式架构的系统,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元。由于服务单元数量很多且业务复杂,服务与服务之间的调用有可能非常复杂,一旦出现了异常和错误,就会很难去定位。所以在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而使每个请求链路清晰可见,出了问题很快就能定位。
服务链路追踪(Service Tracing)是一种用于分布式系统中跟踪和监控服务间调用关系的技术。它通过在请求经过的每个服务组件上添加唯一标识符,并记录相关的信息,如处理时间、错误信息等,以实现对整个调用链路的可视化和分析。
以下是服务链路追踪的一般工作流程:
标识请求:当一个请求进入系统时,给该请求分配一个全局唯一的标识符(例如,Trace ID)。这个标识符会被传递到请求经过的每个服务组件。
注入标识符:在请求所经过的每个服务组件中,将标识符注入到请求的上下文中。这可以通过添加相关的数据字段或者将标识符放置在请求头中来实现。
记录信息:在服务组件中,记录关键的信息和指标。这可能包括请求到达的时间、处理时间、错误信息、调用的下游服务等。这些信息可以用来分析性能问题、定位错误和优化服务调用。
传递标识符:在服务组件间的服务调用过程中,将标识符传递给下一个服务。这样每一个服务都能够将自己的处理信息和指标与整个调用链路关联起来。
数据存储:将收集到的调用链路信息和指标存储在可查询和分析的存储系统中,如加粗样式Elasticsearch、Zipkin、Jaeger等。这些工具可以提供丰富的查询和可视化功能,方便开发人员和运维人员对系统进行监控和故障排查。
分析和可视化:使用链路追踪工具来分析和可视化收集到的调用链路信息。这可以帮助我们理解系统中的服务依赖关系、性能瓶颈以及各组件之间的交互情况。
通过服务链路追踪,我们可以全面了解分布式系统中的服务间调用关系和性能情况。它可以帮助我们定位问题、优化性能,以及实现系统的可观测性和故障排查能力。在微服务架构和分布式系统中,服务链路追踪已经成为了一项重要的技术和工具。
2.2.2 常用组件
(1)服务注册和发现组件Eureka
利用Eureka组件可以很轻松地实现服务的注册和发现的功能。Eureka组件提供了服务的健康监测,以及界面友好的UI。通过Eureka组件提供的UI,Eureka组件可以让开发人员随时了解服务单元的运行情况。另外Spring
Cloud也支持Consul和Zookeeper,用于注册和发现服务。
(2)熔断组件Hystrix
Hystrix是一个熔断组件,它除了有一些基本的熔断器功能外,还能够实现服务降级、服务限流的功能。另外Hystrix提供了熔断器的健康监测,以及熔断器健康数据的API接口。Hystrix
Dashboard组件提供了单个服务熔断器的健康状态数据的界面展示功能,Hystrix
Turbine组件提供了多个服务的熔断器的健康状态数据的界面展示功能。
(3)负载均衡组件Ribbon
Ribbon是一个负载均衡组件,它通常和Eureka、Zuul、RestTemplate、Feign配合使用。Ribbon和Zuul配合,很容易做到负载均衡,将请求根据负载均衡策略分配到不同的服务实例中。Ribbon和RestTemplate、Feign配合,在消费服务时能够做到负载均衡。
(4)路由网关Zuul
路由网关Zuul有智能路由和过滤的功能。内部服务的API接口通过Zuul网关统一对外暴露,内部服务的API接口不直接暴露,防止了内部服务敏感信息对外暴露。在默认的情况下,Zuul和Ribbon相结合,能够做到负载均衡、智能路由。Zuul的过滤功能是通过拦截请求来实现的,可以对一些用户的角色和权限进行判断,起到安全验证的作用,同时也可以用于输出实时的请求日志。
*上述的4个组件都来自于Netflix的公司,统一称为Spring Cloud Netflix。
*
(5)Spring Cloud Config
Spring Cloud Config组件提供了配置文件统一管理的功能。Spring Cloud
Config包括Server端和Client端,Server端读取本地仓库或者远程仓库的配置文件,所有的Client向Server读取配置信息,从而达到配置文件统一管理的目的。通常情况下,Spring
Cloud Config和Spring Cloud Bus相互配合刷新指定Client或所有Client的配置文件。
(6)Spring Cloud Security
Spring Cloud Security是对Spring Security组件的封装,Spring Cloud Security向服务单元提供了用户验证和权限认证。一般来说,单独在微服务系统中使用Spring Cloud Security是很少见的,一般它会配合Spring Security
OAuth2组件一起使用,通过搭建授权服务,验证Token或者JWT这种形式对整个微服务系统进行安全验证。
(7)Spring Cloud Sleuth
Spring Cloud Sleuth是一个分布式链路追踪组件,它封装了Dapper、Zipkin和Kibana等组件,通过它可以知道服务之间的相互依赖关系,并实时观察链路的调用情况。
(8)Spring Cloud Stream
Spring Cloud Stream是Spring Cloud框架的数据流操作包,可以封装RabbitMQ、ActiveMQ、Kafka、Redis等消息组件,利用Spring Cloud Stream可以实现消息的接收和发送。
上述列举了一些常用的Spring Cloud组件。一个简单的由Spring Cloud构建的微服务系统,通常由服务注册中心Eureka、网关Zuul、配置中心Config和授权服务Auth构成,架构如图2-9所示。
2.3 Dubbo简介
Dubbo是一款高性能、轻量级的开源分布式服务框架,由阿里巴巴集团开发和维护。它致力于提供可靠的RPC(远程过程调用)通信和服务治理的解决方案。
Dubbo的主要特点包括:
NIO(非阻塞式输入输出)通信是一种基于Java NIO库的网络通信模型,与传统的阻塞式IO(就是java.io包中的流)相比,具有更高的效率和并发处理能力。
在传统的阻塞IO模型中,当一个线程在等待网络数据时,它会被阻塞,无法同时处理其他任务。这种模型在并发量较低的情况下可以工作,但是当并发量增加时,每个请求都需要创建一个线程来处理,线程数量可能会非常庞大,从而导致线程资源的浪费和系统性能的下降
而NIO模型则引入了选择器(Selector)和通道(Channel)的概念,使得可以使用较少的线程处理多个连接。NIO的主要特点如下:
非阻塞:NIO采用非阻塞的方式进行IO操作,当没有数据可读取时,线程不会被阻塞,而是可以继续处理其他任务。
选择器:选择器是NIO的核心组件,它可以同时管理多个网络连接,并通过事件机制告知哪些连接有数据可读或可写。
通道:通道是NIO中与数据源进行交互的对象,可以用于读取和写入数据。通道提供了灵活的缓冲区(Buffer)管理,有效地减少了内存拷贝的次数。
使用NIO通信模型,可以实现高并发的网络通信。当有新的连接请求到来时,可以通过选择器注册该连接,并且在就绪状态(如可读、可写)时进行处理,而不需要为每个连接创建一个线程。这种方式可以大大提高系统的并发处理能力。
需要注意的是,NIO通信模型相对于传统的阻塞式IO来说,编程模型更加复杂一些,需要开发人员熟悉选择器、通道和缓冲区等概念。但是,它具有更好的可扩展性和性能表现,因此在需要高并发处理的分布式系统中广泛应用。
服务治理:Dubbo提供了丰富的服务治理功能,包括负载均衡、集群容错、服务降级、动态配置等,可以实现对服务的可靠调用和监控。
可扩展性:Dubbo支持横向扩展和纵向扩展,可以根据需求灵活地增加或减少服务提供者和消费者。
配置简单:Dubbo使用注解和XML配置相结合的方式进行配置,使得服务的发布和调用变得简单和灵活。
安全性:Dubbo提供了多层次的安全认证和访问控制机制,保障服务的安全性和可靠性。
Dubbo的架构由三个核心组件组成:
服务提供者(Provider):将自己提供的服务注册到注册中心,等待消费者调用。
注册中心(Registry):负责服务的注册与发现,维护服务提供者和消费者的关系。
服务消费者(Consumer):从注册中心获取服务提供者的地址,发起远程调用。
Dubbo还提供了丰富的生态系统,包括监控中心、容器、集成测试等辅助工具,以满足各种分布式场景和需求。
总之,Dubbo是一款功能强大、性能优越的分布式服务框架,广泛应用于众多大型互联网企业和项目中,为分布式系统的开发和运维带来了很大的便利。
Dubbo是一个非常优秀的服务治理框架,在国内互联网公司应用广泛,它具有以下特性。
•连通性:注册中心负责服务的注册;监控中心负责收集调用次数、调用时间;注册中心、服务提供者、服务消费者为长连接。
•健壮性:监控中心宕机不影响其他服务的使用;注册中心集群,任意一个实例宕机自动切换到另一个注册中心实例;服务实例集群,任意一个实例宕机,自动切换到另一个可用的实例。
•伸缩性:可以动态增减注册中心和服务的实例数量。
•升级性:服务集群升级,不会对现有架构造成压力。
Spring Cloud和Dubbo都是用于构建分布式系统的框架,但它们在设计理念、功能特点和生态系统上有所不同。
Spring Cloud:Spring Cloud基于Spring生态系统构建,注重提供开箱即用的微服务解决方案。它倡导使用轻量级的RESTful API实现服务间的通信,支持多种协议和通信方式。
Dubbo:Dubbo注重高性能和可靠性,采用了RPC(远程过程调用)的方式进行服务间的通信。它强调服务治理和服务间的直连通信模式。
2. 功能特点:
Spring Cloud:Spring Cloud提供了丰富的功能组件,包括服务注册与发现、负载均衡、配置中心、断路器、网关等。它还集成了其他Spring项目,如Spring Boot和Spring Data,简化了微服务的开发和部署。
Dubbo:Dubbo提供了强大的服务治理功能,包括服务注册与发现、负载均衡、故障转移、服务降级等。它还支持多种序列化协议和通信方式,并提供了详细的监控和管理工具。
3. 生态系统:
Spring Cloud:Spring Cloud是构建在Spring生态系统之上的,可以无缝集成Spring框架的各个组件和第三方扩展,如Spring Boot、Spring Data、Netflix OSS、Zuul等。它拥有庞大的社区支持和丰富的教程文档。
Dubbo:Dubbo同样也有一定的生态系统,提供了监控中心、分布式调用链追踪、容器集成等扩展。但相对于Spring Cloud来说,Dubbo的生态系统规模相对小一些。
综上所述,Spring
Cloud注重提供全面的微服务解决方案,适合构建轻量级、灵活的分布式系统;而Dubbo则注重高性能和可靠性,更适合构建大规模、复杂的分布式系统。选择使用哪个框架取决于具体的需求和项目规模。在实际应用中,也可以将二者结合起来,利用各自的优势来构建强大的分布式系统。
在计算机系统中,消息通信是不同进程或线程之间进行信息交换的一种方式。它是一种异步的通信模型,其中发送方将消息发送到接收方,并且接收方可以在适当的时间接收和处理该消息。
message通信方式
在消息通信中,有两种常见的通信方式:
队列(Queue)方式:队列是一种通过先进先出(FIFO)原则来存储和传递消息的数据结构。发送方将消息放入队列的末尾,接收方从队列的开头获取消息。这种方式可以实现发送方和接收方的解耦,并且能够实现可靠的消息传递。常见的队列通信技术包括消息队列(Message Queue,如RabbitMQ、Apache Kafka)和任务队列(Task Queue,如Celery)等。
发布/订阅(Publish/Subscribe)方式:发布/订阅是一种广播式的消息通信方式,其中发送方将消息发布到频道(Channel),而多个接收方可以订阅并接收该频道上的消息。每个接收方都可以独立地接收和处理消息,而发送方并不关心消息是否被接收。常见的发布/订阅通信技术包括消息代理(MessageBroker,如Redis Pub/Sub、Apache ActiveMQ)和事件总线(Event Bus,如Spring
Events、Google Guava EventBus)等。
消息通信方式具有以下特点:
异步性:发送方和接收方之间的消息传递是异步的,发送方可以继续执行其他任务而无需等待接收方的响应。
解耦性:消息通信可以实现发送方和接收方的解耦,它们可以独立地进行开发和部署。
可扩展性:通过使用消息队列或发布/订阅方式,可以很容易地扩展系统,增加新的发送方或接收方。
消息通信在分布式系统、微服务架构、事件驱动架构等场景中广泛应用,它能够提供高可靠性、松耦合和可伸缩性的通信方式。
HTTP(HyperText Transfer Protocol)是一种应用层协议,用于在Web浏览器和Web服务器之间进行通信。它是一种无状态协议,即每个请求之间相互独立,不会保留任何信息。
HTTP通信遵循客户机-服务器模式,其中客户机向服务器发送请求,服务器响应请求,并返回所需的数据。HTTP通信的主要特点如下:
基于请求/响应模型:客户机发送HTTP请求,服务器响应该请求并返回所需的资源。
使用URI(Uniform Resource Identifier)来标识资源:在HTTP通信中,使用URL(Uniform Resource Locator)来定位资源的位置。
支持多种请求方法:HTTP支持多种请求方法,包括GET、POST、PUT、DELETE等,每种请求方法有不同的语义和用途。
状态码:HTTP响应会包含一个状态码,用于表示服务器对请求的处理结果,包括响应的成功或失败以及错误类型等。
无状态性:HTTP是一种无状态协议,即每个请求之间相互独立,不会保留任何信息。
支持缓存:HTTP支持使用缓存技术来减少网络传输量,提高效率。
消息头:消息头包含了HTTP请求或响应的元数据,包括请求/响应的类型、长度、编码方式以及请求/响应的日期等信息。
HTTP通信是Web应用程序中最常用的通信方式之一,它具有简单、灵活和可扩展的特点。通过使用HTTP,我们可以轻松地实现Web API、Web服务、RESTful服务和Web页面等应用。同时,由于HTTP具有无状态性和支持缓存等特性,可以提高Web应用的性能和效率。
RPC(Remote Procedure Call)远程过程调用是一种通信协议,用于实现分布式系统中不同节点之间的远程调用。它使得开发人员可以像调用本地函数一样调用位于远程节点上的函数或方法。
使用RPC进行远程调用的过程如下:
定义接口:首先,在RPC中需要定义接口或服务契约,明确远程节点上提供的函数或方法及其参数和返回类型。这个接口可以使用IDL(Interface Definition Language)来编写,并且应该被所有参与远程调用的节点共享。
生成代理类:在客户端上,根据接口定义生成代理类(Proxy),代理类可以模拟本地调用而实际上将调用发送到远程节点。
序列化和传输:当客户端调用代理类的方法时,参数数据需要进行序列化,即将其转换为字节流进行传输。一般情况下,使用类似JSON、XML或二进制等格式进行序列化。
网络传输:通过网络将序列化后的请求发送到远程节点。通常使用TCP/IP协议进行通信,当然也可以使用其他传输协议。
接收和反序列化:远程节点接收到请求后,将其反序列化为对应的参数对象。
调用方法:在远程节点上,根据请求中指定的接口和方法信息,调用相应的函数或方法,并传递参数。
执行结果和序列化:远程节点执行完方法后,将结果返回给客户端。同样需要将结果进行序列化。
网络传输和反序列化:客户端接收到响应后,将其进行反序列化为对应的结果对象。
返回结果:最后,客户端将结果返回给调用方。
RPC的优点包括:
提供了一种简单、易用的方式来实现远程调用,使得分布式系统的开发更加方便。
隐藏了网络通信的复杂性,使得开发人员可以像调用本地函数一样调用远程函数。 支持多种编程语言和平台之间的通信,提供了跨语言的能力。
可以实现高效的网络传输和序列化机制,提高了性能和效率。 常见的RPC框架包括 gRPC、Apache Dubbo、Thrift 和 Apache Avro 等。这些框架提供了自动生成代理类、网络传输、序列化和反序列化的功能,简化了RPC的开发过程。
2.5 Kubernetes简介
Kubernetes(通常简称为K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它最初是由Google设计和开发,后来成为了Cloud Native Computing Foundation(CNCF)的一个重要项目。
Kubernetes的设计目标是简化容器化应用程序的部署和管理。它提供了一种可扩展的架构,可以在集群中运行数千个容器,并使用自动化的方式进行负载均衡、容错处理和水平扩展等操作。
Kubernetes的主要特点如下:
自动化容器部署和管理:Kubernetes能够自动化地在集群中部署和管理容器化应用程序。它可以根据用户定义的规则和策略来调度容器,确保它们按照所需的状态进行部署和运行。
容器编排和调度:Kubernetes提供了强大的容器编排和调度功能。它可以根据资源需求、可用性和健康状况等因素,将容器调度到合适的节点上,并实现负载均衡。
服务发现和负载均衡:Kubernetes提供了内置的服务发现和负载均衡机制,使得容器之间可以通过名称进行通信,并且能够自动将流量分发到可用的实例上。
自我修复和健康检查:Kubernetes具有自我修复和健康检查的能力。它可以监测容器的状态和健康状况,并在发现故障或异常时自动进行恢复和重启操作。
水平扩展:Kubernetes支持应用程序的水平扩展,可以根据负载情况自动调整应用程序的副本数量,以满足需求。
配置管理:Kubernetes提供了一种集中化的配置管理机制,可以帮助用户管理和更新应用程序的配置信息。
安全性:Kubernetes提供了多层次的安全机制,包括身份认证、授权和访问控制等功能,以确保集群的安全性和稳定性。
Kubernetes已经成为了容器编排和管理领域的事实标准,并且得到了广泛的应用和支持。它适用于各种云平台和虚拟化环境,可以帮助开发人员和运维团队更好地管理和扩展容器化应用程序。
4.4 运行状态监控Actuator
运行状态监控是Spring Boot Actuator 提供的一个功能,用于获取应用程序的运行时信息和指标数据。通过运行状态监控,开发人员可以实时查看应用程序的健康状况、性能指标和其他相关信息,以便进行故障排查和性能优化。
在Spring Boot中启用运行状态监控非常简单,只需完成以下几个步骤:
添加依赖:在项目的构建配置文件(如pom.xml)中添加Spring Boot Actuator的依赖,例如:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置端点:默认情况下,Spring Boot Actuator会提供一些默认的端点,如/actuator/health和/actuator/info等。你可以根据需要选择启用或禁用特定的端点。在application.properties或application.yml文件中进行配置,例如:
properties
management.endpoints.web.exposure.include=health,info
访问端点:启动应用程序后,你可以通过HTTP请求访问相应的端点来获取信息。例如,使用浏览器或curl命令访问/actuator/health端点:
http://localhost:8080/actuator/health
返回的结果将包含应用程序的健康状态信息。
除了默认的健康端点,Spring Boot Actuator还提供了许多其他的端点,包括:
/actuator/info:提供有关应用程序的信息。
/actuator/metrics:提供应用程序的各种指标数据,如内存使用、线程池状态等。
/actuator/env:提供应用程序的环境变量信息。
/actuator/loggers:用于查看和修改日志记录器的配置。
/actuator/trace:提供应用程序的请求跟踪信息。
你可以根据自己的需求选择启用或禁用这些端点,并通过HTTP请求访问相应的URL来获取信息。
运行状态监控是Spring Boot Actuator的一个重要功能,它提供了实时监控和管理应用程序的能力,帮助开发人员更好地理解和调试应用程序。
4.4.1 查看运行程序的健康状态
查看运行状态
/health 查看状态
如图所示:
4.4.2 查看运行程序的Bean
/Beans
在返回的信息中包含了Bean的以下4类信息。
•bean:Spring应用程序上下文中的Bean名称或Id。
•resource:class文件的物理位置,通常是一个Url,指向构建出的Jar文件的路径。 •scope:Bean的作用域(通常是单例singleton,也可以是ptototype、request和session)。
•type:Bean的类型。
4.4.3 使用Actuator关闭应用程序
/shutdown
配置端点:在application.properties或application.yml文件中进行配置,启用/actuator/shutdown端点,例如:
management.endpoints.web.exposure.include=health,info,shutdown
关闭应用程序:启动应用程序后,你可以通过HTTP POST请求访问/actuator/shutdown端点来关闭应用程序。
例如,使用curl命令发送POST请求:
curl -X POST http://localhost:8080/actuator/shutdown
4.4.4 使用shell连接Actuator
通过REST API这种方式,开发人员通过Actuator可以很方便地了解运行中的程序的监控信息。Actuator也可以通过shell的方式连接,需要注意的是,在Spring Boot1.5之后的版本已经将此废弃掉了。通过shell连接Actuator,需要在工程的pom文件加上shell的起步依赖spring-boot-starter-remote-shell,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell
</artifactId>
</dependency>
在程序的pom文件加上spring-boot-starter-remote-shell起步依赖后,启动Spring Boot应用程序,在程序的控制台会输出连接shell的密码,密码是随机的,每次都不一样,大概格式如下:
Using default password for shell access: 45f17018-5839-478e-a1a1-06de4cc82d4f
与密码相对应的用户名是user,可以通过远程SSH连接shell,它的端口是2000,这是固定的。如果你是用Mac系统,这时可以用终端连接shell,在终端输入连接命令,命令如下:
ssh user@localhost -p 2000
Password authentication
Password:
PTY allocation request failed on channel 0
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)
Eureka是Netflix开源的一款服务注册与发现框架,用于构建分布式系统中的服务注册和发现功能。它可以让微服务应用程序进行自动的服务注册和发现,以实现服务之间的通信和协作。
Eureka由两个主要组件组成:Eureka Server和Eureka Client。
Eureka Server:
Eureka Server作为服务注册中心,负责管理和维护微服务应用程序的注册信息。
微服务应用程序通过向Eureka Server发送注册请求来将自身的信息注册到注册中心上。
Eureka Server还会定期从微服务应用程序接收心跳消息,以检测服务的状态。
Eureka Client:
Eureka Client是微服务应用程序中的组件,用于向Eureka Server注册服务并获取其他服务的信息。
微服务应用程序启动后,会将自身的信息注册到Eureka Server上。
Eureka Client会周期性地向Eureka Server发送心跳信息,以保持服务的健康状态。
客户端应用程序可以从Eureka Server获取注册在其中的其他微服务的信息,并使用它们进行服务调用。
使用Eureka,你可以轻松构建具有弹性和可伸缩性的分布式系统。当微服务应用程序需要与其他服务进行通信时,它只需查询Eureka Server获取目标服务的信息,而不需要硬编码目标服务的URL地址。
除了Eureka,还有其他的服务注册与发现框架可供选择,例如Consul、ZooKeeper和Etcd等。这些框架都提供了类似的功能,可以根据你的具体需求和技术栈选择适合的框架。
eureka配置
以下是一个示例的Eureka Server配置文件application.yml:
<parent>
<groupId>com.forezp</groupId>
<artifactId>chapter5-2</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
server:
port: 9901
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false # 是否将自己注册到 Eureka Server,默认为 true
fetchRegistry: false # 是否从 Eureka Server 获取注册信息,默认为 true
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
通过以上配置,你可以创建一个简单的Eureka Server实例,并指定它的端口号为8761。此外,你还需要设置Eureka
Client的属性,包括是否向Eureka Server注册本身,是否从Eureka Server获取注册信息等。
以下是一个示例的Eureka Client配置文件application.yml:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9901/eureka/
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
到目前为止,Eureka Server的所有搭建工作已经完成。启动程序启动类EurekaServerApplication的main方法,启动程序。在浏览器上访问Eureka Server的主界面http://localhost:9901,在界面上的Instances currently registered with Eureka这一项上没有任何注册的实例,没有是正常的,因为还没有Eureka Client向注册中心Eureka
Server注册实例。Eureka Server的主界面如
通过以上配置,你可以创建一个Eureka Client实例,并指定它要连接的Eureka Server的地址和端口号。在应用程序中,你还需要添加@EnableDiscoveryClient注解,以启用Eureka Client的自动服务注册和发现功能。
除了以上的基本配置,你还可以根据具体的需求进行更高级的配置,例如安全认证、负载均衡、健康检查等。关于更高级的配置信息,请参考Spring Cloud官方文档。
5.3 编写Eureka Client
编写起步依赖
<parent>
<groupId>com.forezp</groupId>
<artifactId>chapter5-2</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml配置如下
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9901/eureka/ #注册地址
server:
port: 9902
spring:
application:
name: eureka-client
注释
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
启动Eureka Client工程,启动成功之后,在控制台会打印出如下信息:
com.netflix.discovery.DiscoveryClient : DiscoveryClient_EUREKA-CLIENT/bogon:eureka-client:8762 - registration status: 204
以上的日志信息说明Eureka Client已经向Eureka Server注册了。在浏览器上打开Eureka Server主页http://localhost:8761,主页显示如图5-2所示。
5.4.1 Eureka的一些概念
(1)Register——服务注册当Eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址、端口、运行状况指标的Url、主页地址等信息。
(2)Renew——服务续约Eureka Client在默认的情况下会每隔30秒发送一次心跳来进行服务续约。通过服务续约来告知Eureka Server该Eureka Client仍然可用,没有出现故障。在正常情况下,如果Eureka Server在90秒内没有收到Eureka Client的心跳,Eureka Server会将Eureka Client实例从注册列表中删除。注意:官网建议不要更改服务续约的间隔时间。
(3)Fetch Registries——获取服务注册列表信息Eureka Client从Eureka Server获取服务注册表信息,并将其缓存在本地。Eureka Client会使用服务注册列表信息查找其他服务的信息,从而进行远程调用。该注册列表信息定时(每30秒)更新一次,每次返回注册列表信息可能与Eureka Client的缓存信息不同,Eureka Client会自己处理这些信息。如果某种原因导致注册列表信息不能及时匹配,Eureka Client会重新获取整个注册表信息。Eureka Server缓存了所有的服务注册列表信息,并将整个注册列表以及每个应用程序的信息压缩,压缩内容和没有压缩的内容完全相同。Eureka Client和Eureka Server可以使用JSON和XML数据格式进行通信。在默认的情况下,Eureka Client使用JSON格式的方式来获取服务注册列表的信息。
(4)Cancel——服务下线Eureka Client在程序关闭时可以向Eureka Server发送下线请求。发送请求后,该客户端的实例信息将从Eureka Server的服务注册列表中删除。该下线请求不会自动完成,需要在程序关闭时调用以下代码:
(5)Eviction——服务剔除在默认情况下,当Eureka Client连续90秒没有向Eureka Server发送服务续约(即心跳)时,Eureka Server会将该服务实例从服务注册列表删除,即服务剔除。
5.4.2 Eureka的高可用架构
从图5-3中可知,在这个架构中有两个角色,即Eureka Server和Eureka Client。而Eureka Client又分为Applicaton Service和Application Client,即服务提供者和服务消费者。每个区域有一个Eureka集群,并且每个区域至少有一个Eureka Server可以处理区域故障,以防服务器瘫痪。Eureka Client向Eureka Server注册,将自己的客户端信息提交给Eureka Server。然后,Eureka Client通过向Eureka Server发送心跳(每30秒一次)来续约服务。如果某个客户端不能持续续约,那么Eureka Server断定该客户端不可用,该不可用的客户端将在大约90秒后从Eureka Server服务注册列表中删除。服务注册列表信息和服务续约信息会被复制到集群中的每个Eureka Server节点。来自任何区域的Eureka Client都可以获取整个系统的服务注册列表信息。根据这些注册列表信息,Application Client可以远程调用Applicaton Service来消费服务。
为了实现Eureka的高可用架构,你可以将Eureka Server部署在多个实例上,并通过互相注册来实现相互备份和负载均衡。下面是一种常见的Eureka高可用架构示例:
创建多个Eureka Server实例:你可以在不同的服务器上创建多个Eureka Server实例,每个实例都运行在不同的端口上,并且它们之间相互独立。
实例互相注册:将每个Eureka Server实例都配置为相互注册,这样它们就会互相引用和备份彼此的注册信息。
客户端配置多个Eureka Server:在客户端应用程序的配置中,将多个Eureka Server的地址配置到eureka.client.serviceUrl.defaultZone属性中,以便客户端可以从多个Eureka Server获取服务注册信息。
负载均衡:使用负载均衡器(如Nginx)将客户端请求分发到不同的Eureka Server实例上,以实现负载均衡和故障转移。负载均衡器能够根据当前实例的健康状态和负载情况分配请求。
心跳和健康检查:每个Eureka Server实例都会周期性地向其他实例发送心跳信息,以检测它们的健康状态。如果某个实例长时间未发送心跳,其他实例将把该实例从注册表中剔除,从而保证服务的可用性。
通过以上的架构设计,即使其中一个Eureka Server实例出现故障,其他实例仍然能够提供服务注册和发现功能,确保系统的高可用性。
需要注意的是,为了实现真正的高可用性,你应该将Eureka Server实例部署在不同的服务器上,并使用合适的监控和故障转移机制来处理故障情况。此外,还应该进行适当的容量规划和性能测试,以确保Eureka Server能够处理预期的负载和流量。
5.4.3 Register服务注册
服务续约和服务注册非常相似,通过前文中的分析可以知道,服务注册在Eureka
Client程序启动之后开启,并同时开启服务续约的定时任务。在eureka-client-1.9.8.jar的DiscoveryClient的类下有renew()方法,其代码如下:
/**
* Renew with the eureka service by making the appropriate REST call
*/
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instance
Info.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug("{} - Heartbeat status: {}", PREFIX + appPathIdentifier,
httpResponse.getStatusCode());
if (httpResponse.getStatusCode() == 404) {
REREGISTER_COUNTER.increment();
logger.info("{} - Re-registering apps/{}", PREFIX + appPathIdentifier, instanceInfo.getAppName());
return register();
}
return httpResponse.getStatusCode() == 200;
} catch (Throwable e) {
logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e);
return false;
}
}
另外,Eureka
Server的续约接口在eureka-core:1.9.8.jar的com.netflix.eureka包下的InstanceResource类下,接口方法为renewLease(),它是一个RESTful
API接口。为了减少本章的篇幅,省略了大部分代码的展示。其中有一个registry.renew()方法,即服务续约,代码如下:
@PUT
public Response renewLease(…省略参数){
…//省略代码
boolean isSuccess=registry.renew(app.getName(),id, isFromReplicaNode);
…//省略代码
}
读者可以跟踪registry.renew的代码继续深入研究,和追踪服务注册的源码类似,在此不再赘述。另外服务续约有两个参数是可以配置的,即Eureka
Client发送续约心跳的时间参数和Eureka
Server在多长时间内没有收到心跳将实例剔除的时间参数。在默认情况下,这两个参数分别为30秒和90秒,官方的建议是不要修改,如果有特殊需求还是可以调整的,只需要分别在Eureka
Client和Eureka Server的配置文件application.yml中加以下的配置:
eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationDurationInSeconds
最后,有关服务注册列表的获取、服务下线和服务剔除的源码不在这里进行跟踪解读,因为与服务注册和续约类似,有兴趣的读者可以自行研究。总的来说,通过阅读源码可以发现,整体架构与5.4.2节的Eureka的高可用架构图完全一致。
5.4.5 为什么Eureka Client获取服务实例这么慢
(1)Eureka Client的注册延迟Eureka Client启动之后,不是立即向Eureka Server注册的,而是有一个延迟向服务端注册的时间。通过跟踪源码,可以发现默认的延迟时间为40秒,源码在eureka-client-1.9.8.jar的DefaultEurekaClientConfig类中,代码如下:
public int getInitialInstanceInfoReplicationIntervalSeconds() {
return configInstance.getIntProperty(
namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40).get();
}
(2)Eureka Server的响应缓存Eureka Server维护每30秒更新一次响应缓存,可通过更改配置eureka.server.responseCache UpdateIntervalMs来修改。所以即使是刚刚注册的实例,也不会立即出现在服务注册列表中。
(3)Eureka Client的缓存Eureka Client保留注册表信息的缓存。该缓存每30秒更新一次(如前所述)。因此,Eureka Client刷新本地缓存并发现其他新注册的实例可能需要30秒。
(4)LoadBalancer的缓存Ribbon的负载平衡器从本地的Eureka Client获取服务注册列表信息。Ribbon本身还维护了缓存,以避免每个请求都需要从Eureka Client获取服务注册列表。此缓存每30秒刷新一次(可由ribbon.ServerListRefreshInterval配置),所以可能至少需要30秒的时间才能使用新注册的实例。
综上因素,一个新注册的实例,默认延迟40秒向服务注册中心注册,所以不能马上被Eureka Server发现。另外,刚注册的Eureka
Client也不能立即被其他服务调用,原因是调用方由于各种缓存没有及时获取到最新的服务注册列表信息。
5.4.6 Eureka的自我保护模式
当有一个新的Eureka Server出现时,它尝试从相邻Peer节点获取所有服务实例注册表信息。如果从相邻的Peer节点获取信息时出现了故障,Eureka Server会尝试其他的Peer节点。如果Eureka Server能够成功获取所有的服务实例信息,则根据配置信息设置服务续约的阈值。在任何时间,如果Eureka Server接收到的服务续约低于为该值配置的百分比(默认为15分钟内低于85%),则服务器开启自我保护模式,即不再剔除注册列表的信息。
这样做的好处在于,如果是Eureka Server自身的网络问题而导致Eureka Client无法续约,Eureka Client的注册列表信息不再被删除,也就是Eureka Client还可以被其他服务消费。
在默认情况下,Eureka Server的自我保护模式是开启的,如果需要关闭,则在配置文件添加以下代码:
eureka:
server:
enable-self-preservation: false
5.5 构建高可用的Eureka Server集群
要构建高可用的Eureka Server集群,必须满足以下两个关键要素:
Eureka Server集群节点之间需要相互注册:在Eureka Server集群中,每个节点都需要知道其他节点的位置和状态。因此,在搭建Eureka Server集群时,需要在每个节点的配置文件中明确指定其他节点的地址和端口。
Eureka Server集群节点之间需要进行心跳检测:为了保证每个节点都能及时掌握其他节点的状态变化,Eureka Server会周期性地发送心跳请求,并接收其他节点的心跳响应。如果一个节点长时间无法收到其他节点的心跳响应,就会认为该节点已经离线,从而将其从服务注册表中删除。
下面是一些构建高可用的Eureka Server集群的具体步骤:
准备多台服务器:至少需要准备两台服务器来搭建Eureka Server集群。建议每台服务器配置相同。
部署Eureka Server:在每台服务器上部署相同版本的Eureka Server,可以通过Docker容器或手动安装的方式来完成。
配置Eureka Server:在每个Eureka Server节点的配置文件中设置 eureka.client.service-url.defaultZone 属性,指定其他节点的地址和端口。例如,http://node1:8761/eureka/,http://node2:8762/eureka/ 将指定两个节点的位置。
启动Eureka Server:在所有节点上启动Eureka Server,确保它们之间能够相互注册,并能够正常工作。
验证Eureka Server集群:可以通过浏览器或Postman等工具访问任意一个Eureka Server节点的服务注册表页面,查看所有已经注册的服务实例是否都能正常显示。
注意,为了保证Eureka Server集群的高可用性,每个节点应该至少部署在两台不同的物理服务器上,以防止单点故障造成整个集群的崩溃。此外,建议使用Zookeeper或Consul等外部系统来管理Eureka Server节点状态,从而提高整个集群的可靠性和稳定性。
具体配置
---
spring:
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2:8762/eureka/
---
spring:
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/
6.1 RestTemplate简介
RestTemplate是Spring框架提供的一个用于进行HTTP请求和响应的客户端工具类。它封装了常见的HTTP操作,为开发者提供了一种便捷的方式来访问RESTful风格的Web服务。
RestTemplate可以发送各种类型的HTTP请求,例如GET、POST、PUT、DELETE等,并能够接收和处理HTTP响应。它支持传递路径参数、查询参数、请求头、请求体等信息,并能够将响应转换为不同的Java对象,包括原始数据、JSON、XML等。
以下是RestTemplate的一些主要特性:
简化的API:RestTemplate提供了一组简化的方法来执行各种类型的HTTP请求,无需手动处理HTTP连接、输入输出流等底层细节,使得代码编写更加简洁和易读。
内置的序列化和反序列化支持:RestTemplate可以自动将Java对象转换为JSON或XML格式,并将响应转换回Java对象。这个过程可以通过配置消息转换器来实现,Spring框架内置了多种消息转换器。
错误处理和异常处理:RestTemplate提供了丰富的错误处理和异常处理机制,可以捕获HTTP请求过程中可能出现的异常,并提供相应的错误处理策略。
拦截器支持:RestTemplate允许通过拦截器对请求进行增强或修改,例如添加自定义的请求头、记录请求日志等。这样可以实现更细粒度的控制和定制。
异步请求支持:除了同步请求外,RestTemplate还提供了异步请求的支持,通过使用ListenableFuture或CompletableFuture等异步结果对象,可以在发送请求后继续执行其他任务,待响应返回后再进行处理。
使用RestTemplate,可以方便地与各种RESTful风格的Web服务进行通信,例如调用第三方API、访问远程服务等。它是一个成熟且功能强大的HTTP客户端工具,在Spring框架中被广泛使用。但需要注意的是,在最新的Spring版本中,推荐使用WebClient代替RestTemplate来进行HTTP请求操作。
@RestController
public class RestTestController {
@GetMapping("/testRest")
public String testRest(){
RestTemplate restTemplate=new RestTemplate();
return
restTemplate.getForObject("https://www.baidu.com/",String.class);
}
}
User user=restTemplate.getForObject("https://www.~~~.com/",User.class);
WebClient
WebClient是Spring框架提供的非阻塞、响应式的Web客户端工具类。它是在Spring 5以及Spring Boot 2中引入的,用于替代传统的RestTemplate来进行HTTP请求和响应处理。
WebClient基于Reactive Streams规范,通过使用响应式编程方式,可以实现异步、非阻塞的HTTP通信,更好地适应高并发场景和处理大量的并发请求。
以下是WebClient的一些主要特点:
响应式:WebClient基于Reactor库,采用响应式编程风格,支持使用Flux和Mono来处理流式数据。可以利用异步事件驱动模型处理请求和响应,实现高效的异步非阻塞操作。
函数式API:WebClient提供了一组简洁、易用的函数式API,封装了常见的HTTP操作,例如GET、POST、PUT、DELETE等。可以通过链式调用的方式构建请求,并使用丰富的操作符对请求进行转换和处理。
内置的序列化和反序列化支持:WebClient内置了多种消息编解码器,可以方便地进行对象的序列化和反序列化,支持JSON、XML等格式的数据交互。
拦截器支持:WebClient允许通过拦截器对请求进行增强或修改,例如添加自定义的请求头、记录请求日志等。这样可以实现更细粒度的控制和定制。
WebClientExchangeFilterFunction:通过使用WebClientExchangeFilterFunction,可以对请求和响应进行全局的过滤和操作。这包括添加通用的请求头、修改请求参数、记录请求日志等。
支持连接池:WebClient内置了连接池,可以提高连接的复用性和效率。通过合理配置连接池大小和超时时间,可以更好地管理连接资源,减少资源的浪费。
WebClient的使用方法与RestTemplate类似,但更适合于构建响应式的、非阻塞的应用程序。它在处理大量并发请求时表现出色,并且能够利用响应式编程的特性来简化异步编程。如果使用Spring 5或Spring Boot 2及以上版本,推荐使用WebClient作为HTTP客户端工具类。