面试:微服务
微服务的好坏处
微服务(Microservices)是一种软件架构方法,它将大型的应用程序拆分成一组小的、独立的、松散耦合的服务。每个服务都运行在其自己的进程中,并通过轻量级通信机制(如 REST API、消息队列等)与其他服务通信。微服务架构的主要目标是提高系统的可伸缩性、可维护性和灵活性。
以下是微服务架构的好处和坏处:
好处:
- 独立部署和扩展:每个微服务都可以独立地部署和扩展,这使得团队能够更快速地响应需求变化,而不需要重新部署整个应用程序。
- 技术选型灵活性:每个微服务可以使用不同的编程语言、框架和数据库,这使得团队能够选择最适合其需求的技术栈。
- 故障隔离:由于微服务是独立的,一个服务的故障不会影响到其他服务,从而提高了整个系统的容错能力。
- 可伸缩性:可以根据每个微服务的负载情况独立地进行伸缩,从而实现更高效的资源利用。
- 团队独立性和并行开发:微服务架构允许团队独立地开发、测试和部署服务,从而提高了开发速度和并行性。
- 更小的变更影响面:由于每个微服务的功能范围相对较小,因此更改一个服务通常只会影响到一小部分功能,降低了变更的风险。
坏处:
- 复杂性增加:微服务架构增加了系统的复杂性,因为需要管理更多的服务、数据库和网络连接。这可能导致配置和部署变得更加复杂。
- 分布式系统的挑战:微服务之间的通信通常是异步的,需要处理延迟、事务和一致性问题。此外,分布式系统还面临网络分区和故障转移等挑战。
- 数据一致性:在微服务架构中,数据通常被拆分到多个服务中,这可能导致数据一致性问题。需要采用复杂的数据一致性策略和机制(如分布式事务或事件驱动架构)来解决这些问题。
- 测试困难:由于微服务之间的依赖关系,测试变得更加复杂。需要进行集成测试、端到端测试和性能测试来确保整个系统的稳定性和性能。
- 运维挑战:微服务架构需要更复杂的运维策略,包括服务发现、负载均衡、监控和日志管理等。此外,由于服务数量众多,更新和维护也变得更加困难。
- 版本管理:在微服务架构中,不同服务可能使用不同的版本,这可能导致版本冲突和兼容性问题。需要采用有效的版本管理策略和机制来解决这些问题。
常见的微服务框架
-
gRPC:
- gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。
- 它由 Google 开发并开源,基于 Protocol Buffers 序列化协议开发,且支持多种开发语言。
- gRPC 提供了诸如双向流、流控制、头部压缩、单 TCP 连接上的多复用请求等现代通信所需的功能。
-
Thrift:
- Thrift 是一个可扩展的跨语言服务开发框架,用于定义和创建服务。
- 它具有一个完整的堆栈,包括用于定义数据类型和服务的接口定义语言 (IDL),以及用于在多种语言中生成代码的编译器。
-
Istio(服务网格):
- Istio 是一个开源的服务网格框架,可以管理、连接和保护微服务之间的通信。
- 它提供了流量管理、安全、监控等功能,有助于更好地管理微服务应用。
-
Dubbo:
- Dubbo 是由阿里巴巴开发的开源式的分布式服务化治理框架,通过 RPC 请求方式访问。
- 它致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。
- Dubbo 在阿里巴巴的电商平台中逐渐探索演进形成,并经历过复杂业务的高并发挑战。
-
Spring Cloud:
- Spring Cloud 是一个微服务架构技术生态链的集合,基于 HTTP (s) 的 REST 服务构建服务体系。
- 它提供了一整套解决方案,包括服务注册与发现、负载均衡、断路器、分布式配置等功能。
- 利用 Spring Cloud 可以快速构建可扩展和独立部署的微服务应用。
-
Go-kit
- Go-kit 不是一个完整的微服务框架,而是一套微服务工具集,它提供了一套用于构建微服务的基础库和模式。
- 包含日志记录、跟踪、限流和熔断等库,帮助开发人员提高微服务架构的性能和稳定性。
-
Kratos(奎托斯)
-
Kratos 是一套轻量级的 Go 微服务框架,包含大量微服务相关功能及工具。
-
作为一个轻量级的框架,它可能更侧重于提供核心功能和灵活性,让开发者能够根据自己的需求进行定制。
-
RPC
- RPC 代指远程过程调用(Remote Procedure Call)
- 包含了传输协议和编码(对象序列号)协议
- 允许运行于一台计算机的程序调用另一台计算机的子程序
RPC 对于开发人员来说是透明的,他们只需要关注服务之间的接口定义和数据传输格式,而无需关心底层网络通信的细节。
RPC 通常使用轻量级的通信协议,如 HTTP/2、Protocol Buffers 等,以提高通信效率和性能。
gRPC
- gRPC 是一个高性能、开源、通用的 RPC 框架
- 基于 HTTP2.0 协议标准设计开发
- 支持多语言,默认采用 Protocol Buffers 数据序列化协议
gRPC 调用流程
gRPC 的通信协议和编码协议
通信协议:gRPC 使用 HTTP/2 作为其通信协议。
相较于 HTTP/1.x,HTTP/2 具有许多优势,包括:
- 多路复用:允许通过同一连接发送多个并行请求,提高了网络连接利用率。
- 双向全双工通信:支持客户端和服务端同时发送和接收数据,为实时通信和低延迟场景提供了支持。
- 头部压缩:减少了网络使用率,提高了通信效率。
- 内置流式处理:支持对大型数据集进行异步流式处理的请求和响应。
编码协议:gRPC 默认使用 Protocol Buffers 作为其编码协议。
Protobuf 具有以下特点:
- 轻量级且高效:Protobuf 是一种语言无关的高性能序列化框架,序列化后的数据更加紧凑,相较于 XML 和 JSON 等文本格式,在解析速度和性能方面都有很大的优势。
- 跨语言支持:Protobuf 支持多种编程语言,包括 C++、Java、Python 等,使得 gRPC 能够轻松地实现跨语言的服务调用。
- 自动生成代码:基于 IDL(接口定义语言)文件定义服务,通过 Protobuf 编译器工具(如 protoc)自动生成指定语言的数据结构、服务端接口以及客户端 Stub,大大简化了开发流程。
gPRC 的原理
gRPC 实现调用本地函数的方式来进行远程函数调用,主要依赖于 RPC(Remote Procedure Call,远程过程调用)的概念和 gRPC 框架的特性。以下是 gRPC 实现远程函数调用的关键步骤和原理:
-
定义服务接口:
-
使用 Protocol Buffers(protobuf)的
.proto
文件定义服务接口和消息类型。例如,定义一个ProductService
服务,该服务包含一个GetProductStock
方法,用于获取产品的库存量。 -
在
.proto
文件中,定义请求和响应的消息类型,如ProductRequest
和ProductResponse
。相当于
.proto
文件是一个标准,客户端和服务端都按照这个标准来,客户端按照这个标准来调用远程的函数,服务端按照这个标准来实现函数的内容。
-
-
生成代码:
-
使用 protobuf 编译器
protoc
和 gRPC 插件生成服务端和客户端的代码。编译器会根据.proto
文件生成xxx.pb.go
和xxx_grpc.pb.go
文件。 -
xxx.pb.go
文件包含消息类型的 Go 语言实现。 -
xxx_grpc.pb.go
文件包含服务接口的定义和用于序列化和反序列化的代码。生成的这两个文件会在客户端和服务端都保存一份。
客户端:
先创建一个与服务端的连接;然后使用
xxx_grpc.pb.go
文件中的NewAddServiceClient(conn)
创建一个处于连接状态的客户端client
;然后client
调用远程函数,会自动进行 RPC 请求,得到返回值。服务端:
先对某端口进行监听,创建一个
listener
,然后使用grpc.NewServer()
创建一个grpc
服务器,并将其注册到grpc
服务中,最后启动grpc
服务。
-
-
服务端实现:
- 服务端实现
xxx_grpc.pb.go
中定义的服务接口。这通常包括继承或实现接口中定义的方法。 - 服务端启动 gRPC 服务器,监听指定端口,并注册服务实现。当客户端发起请求时,服务器会调用相应的服务实现来处理请求。
- 服务端实现
-
客户端调用:
- 客户端使用
xxx_grpc.pb.go
中定义的服务接口来构造请求,并通过 gRPC 客户端将请求发送给服务端。 - 客户端调用远程函数时,就像调用本地函数一样。这是因为 gRPC 框架封装了网络访问的细节,使远程调用看起来就像本地函数调用一样。
- 客户端使用
-
序列化和反序列化:
- gRPC 使用 protobuf 作为序列化和反序列化的格式。在发送请求之前,客户端将请求消息序列化为二进制数据,并通过网络发送给服务端。
- 服务端收到请求后,将其反序列化为消息对象,然后调用相应的服务实现来处理请求。
- 处理完成后,服务端将响应消息序列化为二进制数据,并发送给客户端。客户端收到响应后,将其反序列化为消息对象。
-
传输层:
- gRPC 基于 HTTP/2 协议进行传输,支持多路复用和流控制,提高了通信的效率和可靠性。
总结:gRPC 通过定义服务接口、生成代码、服务端实现、客户端调用、序列化和反序列化以及基于 HTTP/2 的传输层等步骤,实现了调用本地函数的方式来进行远程函数调用。这种方式简化了分布式应用和服务开发的复杂性,使开发者能够像调用本地函数一样调用远程函数。
gRPC 编写微服务代码的步骤
1. 定义 Protocol Buffers 文件
创建一个 .proto
文件来定义你的服务和消息。这个文件描述了 gRPC 服务的接口、消息类型以及服务方法。
|
|
2. 使用 Protocol Buffers 编译器生成代码
使用 Protocol Buffers 编译器 protoc
将 .proto
文件编译成 Go 语言代码。在 Go 中,你可以使用 protoc-gen-go
插件和 protoc-gen-go-grpc
插件生成 gRPC 和 Protocol Buffers 的代码。确保安装了 Go 的 Protocol Buffers 的插件:
|
|
然后运行以下命令:
|
|
这将生成一个名为 your_proto_file.pb.go
的文件,其中包含 gRPC 服务的 Go 语言代码。
.pb.go 和 grpc.pb.go 两个文件
xxxx.pb.go
和 xxxx_grpc.pb.go
这两个文件分别由 Protocol Buffers 编译器的 protoc-gen-go
插件和 protoc-gen-go-grpc
插件生成,用于支持 Protocol Buffers 和 gRPC 的使用。它们有以下区别和各自的作用:
-
xxxx.pb.go:
-
作用:包含与 Protocol Buffers 相关的代码,用于序列化和反序列化消息。这个文件中定义了由 Protocol Buffers 文件中的消息类型生成的 Go 结构体,以及用于消息的编码和解码的方法。
-
典型内容:包含 Protocol Buffers 消息类型的 Go 结构体定义、编码和解码的方法、默认值等。
-
命名约定:通常以
xxxx.pb.go
的形式命名,其中xxxx
是与 Protocol Buffers 文件名相同的基本文件名。
-
-
xxxx_grpc.pb.go:
-
作用:包含与 gRPC 相关的代码,用于实现 gRPC 服务和客户端。这个文件中定义了由 Protocol Buffers 文件中的服务接口生成的 gRPC 服务的实现,以及用于构建 gRPC 客户端的接口和实现。
-
典型内容:包含 gRPC 服务接口的实现、服务注册代码、与服务通信的客户端接口和实现等。
-
命名约定:通常以
xxxx_grpc.pb.go
的形式命名,其中xxxx
是与 Protocol Buffers 文件名相同的基本文件名。
-
综合来说,xxxx.pb.go
主要关注 Protocol Buffers 数据结构的编码和解码,而 xxxx_grpc.pb.go
主要关注 gRPC 服务的实现和与服务进行通信的客户端。在使用 gRPC 进行开发时,通常需要同时使用这两个文件,它们协同工作以提供完整的 Protocol Buffers 和 gRPC 功能。
3. 实现 gRPC 服务接口
创建一个 Go 文件,实现由 Protocol Buffers 生成的服务接口的具体逻辑。
|
|
4. 启动 gRPC 服务
在 main
函数中创建 gRPC 服务器并注册你实现的服务,然后监听指定的端口。
构建和运行:使用 go build
构建你的 Go 项目,然后运行生成的可执行文件。
|
|
5. 编写 gRPC 客户端
如果需要编写 gRPC 客户端,可以导入生成的客户端代码并使用它来调用 gRPC 服务。
|
|
这是一个基本的 gRPC 服务和客户端的例子。你可以根据实际需求修改消息类型、服务方法和逻辑。记得处理错误,以及进行适当的上下文管理。
gRPC 请求流程

常见组件
一个微服务系统通常包含以下关键组件:
- 服务(Services):
- 定义:微服务系统中的基本构建块,每个服务都是一个独立的、可独立部署的软件单元,通常实现某个具体的业务功能。
- 特点:每个服务都有自己的代码、数据和资源,可以独立部署、扩展和升级。服务之间通过轻量级的通信机制进行交互和协作。
- 服务注册与发现(Service Registry and Discovery):
- 定义:允许服务实例在启动时将自己的位置(通常是 IP 地址和端口号)注册到一个中心位置,同时服务消费者可以从注册中心查询到提供特定功能的服务实例的位置信息。
- 常见选型:Eureka、Consul、Zookeeper、Nacos 等。
- 配置管理(Configuration Management):
- 定义:提供了一个集中的地方来管理所有服务的配置,使得跨不同环境(开发、测试、生产等)的配置变更和推送变得更加容易,同时也支持动态配置的更新,无需重启服务。
- 常见选型:Nacos、Spring Cloud Config、Apollo、Consul 等。
- API 网关(API Gateway):
- 定义:微服务架构中的一个入口点,所有外部请求都通过它进入系统。它可以处理跨多个服务的请求路由、聚合和组合,同时提供认证、授权、限流、日志记录等功能。
- 常见选型:Spring Cloud Gateway、Kong、Amazon API Gateway、Zuul 等。
- 负载均衡(Load Balancing):
- 定义:确保请求均匀分布在所有可用的服务实例上,从而提高性能和可靠性。
- 常见选型:Ingress、Spring Cloud LoadBalancer、Consul、Ribbon 等。
- 熔断器(Circuit Breaker):
- 定义:一种防止系统过载的模式。它允许服务在遇到错误时暂时停止发送请求,从而避免系统崩溃。
- 常见选型:Spring Cloud Hystrix 等。
- 链路追踪(Distributed Tracing):
- 定义:帮助开发者跟踪请求在多个服务间的流动路径,对于调试和监控微服务系统中的性能问题至关重要。
- 常见选型:Zipkin、Jaeger 等。
- 服务网格(Service Mesh):
- 定义:一个专门的基础设施层,它提供了服务发现、负载均衡、故障恢复、度量和监控等特性,而无需对服务代码进行任何更改。
- 示例:Istio 等。
- 数据库和存储:
- 定义:每个微服务通常有自己的数据库或数据存储,用于持久化其业务数据。
- 特点:不同的微服务可能会选择不同的数据库类型,以适应其特定的数据需求和访问模式。
- 监控和日志:
- 定义:微服务系统需要良好的监控和日志功能,以便及时发现和解决问题。
- 常见工具:Prometheus、Grafana、ELK Stack(Elasticsearch、Logstash、Kibana)等。
这些组件共同构成了微服务系统的核心基础设施,支持服务的注册、发现、配置、路由、负载均衡、容错处理、监控和日志记录等功能,从而确保微服务系统的可靠运行和高效扩展。
DDD
在微服务领域中,DDD(Domain-Driven Design,领域驱动设计)是一种软件开发方法论,旨在帮助开发人员更好地理解和建模复杂的业务领域。以下是 DDD 的清晰解释和关键点归纳:
-
定义:DDD 是一种软件开发方法论,它将软件系统的设计与领域模型紧密结合,以尽量准确地反映业务需求和业务规则。
真正决定软件复杂性的是设计方法。
-
核心思想:
- 领域模型:DDD 强调以领域模型为核心,通过领域专家与开发人员的紧密合作来构建和演化领域模型。领域模型是对业务领域的抽象和建模,它将业务实体、值对象、聚合、领域服务等概念组织起来,形成一个相对独立且可复用的模型。
- 战略设计:主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文。限界上下文作为微服务设计的参考边界,有助于确保每个微服务都专注于其特定的业务领域。
- 战术设计:则从技术视角出发,侧重于领域模型的技术实现,包括聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。
-
关键概念:
- 领域(Domain):有着自己特定业务领域知识和规则的部分,如电商领域、外卖配餐领域等。
- 子域(Subdomain):领域的细分,如电商领域的订单、商品、物流等子域。子域根据重要性和功能划分为核心域、通用域和支撑域。
- 通用语言(Ubiquitous Language):通过团队交流达成共识的,能够简单、清晰、准确描述业务涵义和规则的语言。通用语言会贯穿整个项目设计过程,并体现在代码命名中。
- 限界上下文(Bounded Context):用来封装通用语言和领域对象,提供上下文环境,确保在领域之内的一些术语、业务相关对象等有一个确切的含义,没有二义性。
-
作用:DDD 有助于降低业务理解和系统实现的复杂度,通过领域细分和构建合适的领域模型,使微服务能够更好地反映业务需求和业务规则。同时,DDD 的分层架构和解耦的设计原则可以提高系统的可维护性和可扩展性。
有助于指导我们确定系统边界;能够聚焦在系统核心元素上;帮助我们拆分系统。
总之,DDD 在微服务领域中是一种重要的软件开发方法论,它通过将软件系统的设计与领域模型紧密结合,帮助开发人员更好地理解和建模复杂的业务领域,从而提高软件的质量和可维护性。
康威定理
核心观点是:“设计系统的架构受制于产生这些设计的组织的沟通结构”。换句话说,产品(系统)必然是其人员组织沟通结构的缩影。
它提醒我们在设计系统时需要考虑组织的沟通结构和方式,以确保系统能够高效、灵活地满足业务需求。
微服务架构

consul
介绍
一、概述
Consul 是 HashiCorp 公司推出的一款开源工具,用于实现分布式系统的服务发现、配置管理以及健康检查等功能。Consul 以其分布式、高可用性、可横向扩展的特性,在微服务架构中得到了广泛应用。
二、主要功能
- 服务发现:
- Consul 通过 DNS 或 HTTP 接口,使得服务消费者能够轻松发现服务。
- 应用程序可以通过 Consul 找到所依赖的服务,无论是内部服务还是外部服务(如 SaaS 提供的服务)。
- Consul 支持多数据中心,无需复杂的配置即可支持任意数量的区域。
- 健康检查:
- Consul 可以对服务进行健康检查,快速告警集群中的问题。
- 与服务发现的集成可以防止服务转发到故障的服务,从而提高系统的健壮性和稳定性。
- 配置管理:
- Consul 提供了一个简单的 HTTP 接口,用于存储和检索动态配置。
- 这使得配置管理变得简单和集中,方便应用程序在运行时获取最新的配置信息。
三、架构与角色
- 节点类型:
- Consul 的节点分为 Server 和 Client 两种类型。
- Server 节点负责存储集群的状态信息,执行健康检查,并协调服务注册和发现。
- Client 节点负责转发外部请求,并将所有注册到当前节点的服务转发到 Server 节点进行处理。
- 关键组件:
- Agent:Consul 集群中长时间运行的守护进程,负责运行时检查和保持服务同步。
- Datacenter:数据中心,多数据中心联合工作保证数据存储安全快捷。
- Consensus:一致性协议使用的是 Raft Protocol,确保数据在多个 Server 节点之间保持一致。
- Gossip:基于 Serf 实现的 gossip 协议,负责成员、失败探测、事件广播等。
四、使用场景
- 服务发现:Consul 作为注册中心,服务地址被注册到 Consul 中以后,可以使用 Consul 提供的 DNS、HTTP 接口查询。
- 服务隔离:Consul 支持以服务为单位设置访问策略,能同时支持经典的平台和新兴的平台,支持 TLS 证书分发,service-to-service 加密。
- 服务配置:Consul 提供 key-value 数据存储功能,并且能将变动迅速地通知出去,通过工具 consul-template 可以更方便地实时渲染配置文件。
五、优势
- Consul 使用 Raft 算法来保证一致性,比复杂的 Paxos 算法更直接。
- Consul 支持多数据中心,内外网的服务可以采用不同的端口进行监听。
- Consul 的开源特性使得其易于集成和扩展,满足各种复杂的业务需求。
Consul 注册中心
Consul 注册中心是一个由 HashiCorp 公司推出的开源工具,用于分布式系统的服务发现、服务隔离、服务配置等关键功能。以下是对 Consul 注册中心的详细解释:
- 定义与特性:
- Consul 是一个开源的、分布式的、高可用的服务发现和配置管理工具。
- 它使用 Go 语言开发,具有跨平台、运行高效等特点,易于部署和维护。
- Consul 支持多数据中心,内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储等功能。
- 主要功能:
- 服务发现:Consul 允许服务的客户端注册自己,如 API 或数据库服务,其他客户端可以使用 Consul 来发现特定服务的提供者。通过 DNS 或 HTTP,应用程序可以很容易地找到它们所依赖的服务。
- 健康检查:Consul 支持客户端提供任何数量的健康检查,这些检查可以与给定的服务相关联或与本地节点相关联。健康检查信息可以被运维人员用来监控集群的健康状况,并用于服务发现组件来路由流量。
- KV 存储:Consul 提供了一个层级化的 Key/Value 存储,应用程序可以利用这个存储来实现动态配置、功能标记、协调、领导者选举等。
- 安全服务通信:Consul 可以为服务生成和分发 TLS 证书,以建立相互的 TLS 连接。此外,它还可以使用 Intention 来定义哪些服务被允许进行通信,实现服务隔离。
- 架构与角色:
- Consul 分为 Client 和 Server 两种节点(所有的节点也被称为 Agent)。
- Server 节点保存配置信息,并参与 Raft 选举、维护集群状态、响应 RPC 查询等任务。在生产环境中,每个数据中心的 Server 节点数量推荐为 3 个或 5 个以实现高可用。
- Client 节点无状态,负责将 HTTP 和 DNS 接口请求转发给局域网内的 Server 节点集群,起到代理的作用。
- 集成与扩展:
- Consul 具有强大的扩展性和集成能力,可以与 Docker 等轻量级容器无缝配合,也可以与各种监控、日志等系统集成。
- Consul 的 Web 管理界面提供了直观的集群状态监控和配置管理功能。