微服务架构原理及原理实践
架构及介绍
由于需求复杂性的多样化,在互联网发展和硬件设施发展,开发人员的急剧增加,计算机理论的急速发展所诞生的一种新型架构。
架构一览:
组成:
模拟说明:
将HDFS模拟成只有一个NameNode实例(NameNode Service层)和多个DataNode实例(DataNode Service层)的微服务架构。 具体来说,NameNode是负责管理文件系统命名空间和元数据的主节点,而DataNode则负责存储和处理实际的文件块数据。
这种架构存在的缺点:
- 单点故障:由于只有一个NameNode实例,如果该实例发生故障,则整个系统将无法继续提供服务。
- 性能瓶颈:在这种架构下,所有的文件操作都需要经过NameNode。这意味着所有的元数据操作(如创建、删除、重命名等)都会产生对NameNode的请求,这可能会导致性能瓶颈。
- 扩展性受限:由于只有一个NameNode实例,其所能处理的文件系统大小和文件数目有限。一旦达到了极限,就无法再扩展文件系统的容量和性能。
因而一般的微服务架构具有多个实例。
通信对比
在单体架构中,所有的功能模块都运行在同一个应用程序中,它们可以直接调用彼此的函数。这种内部调用方式非常高效和简单,因为函数之间的调用是在内存中完成的,没有网络开销。
相比之下,在微服务架构中,不同的功能模块被拆分成独立的服务,并以分布式的方式部署在不同的主机上。因此,微服务之间的通信需要通过网络进行,而不再是简单的函数调用。(通信方式以Http协议和RPC为常见
场景模拟
调用HTTP服务时动态获取目标服务的地址 hardcode(硬编码的方式): client:grpc.NewClient("10.23..45.67.8080"),因为是固定的地址(写死)而服务的地址是动态的,不支持动态的配置管理 DNS(Domain Name System)(动态解析获取) net.Dial("b.service.org:8080")
-
本地的DNS存在缓存,将保存解析的结果,用户获得结果会是缓存的旧结果
-
实例负载不均衡问题,
-
不支持服务实例的探活检查,即使某个服务实例已经停止运行或不可用,DNS仍然会返回它的IP地址,客户端无法直接感知到服务的实际可用性。
-
域名无法配置端口,域名解析只返回IP地址,而无法直接指定端口号
解决思路:
- 添加服务注册中心用于记录各个服务实例的信息,包括服务名称、IP地址和端口号等。服务实例启动时,将自己的信息注册到服务注册中心,以便其他服务或客户端能够发现和调用它。
- 当需要调用某个服务时,可以通过调用服务注册中心的接口进行查询。使用
addrs = svc_reg.find("service.b")的方式获取服务B的地址列表。 在获取到服务地址列表后,使用随机算法从中选择一个地址。使用addrs[random(n)]的方式随机选择一个地址,其中n为地址列表的长度。 - 进行服务调用:选定了服务地址后,使用网络库(如
net.Dial())建立与该地址的连接,并进行服务调用。使用net.Dial(addrs[random(n)])的方式建立与选定地址的连接。
服务实例的上线和下线,下线会首先去服务状态中心删除记录; 上线添加实例,会进行health check发送请求测试,确认该实例能够正常运作,再在服务注册中心进行注册
流量特征:
- 负载均衡:将流量均匀地分发到多个服务实例上,以提高系统的可扩展性和稳定性。
- 网关:位于客户端和服务端之间的中间层,它提供一些共享的功能,如路由、鉴权、限流、日志记录等。网关可以集中处理流量,并将请求转发给相应的服务。
- 内网通信采用RPC:在微服务架构中,服务之间的通信通常采用远程过程调用(RPC)方式。RPC允许服务之间进行函数调用,使得服务能够在分布式环境下进行协同工作。内网通信多数采用RPC可以提供更高的性能和效率,同时也能够支持各种编程语言和协议。
- 网状调用链路:一个服务可能会依赖多个其他服务,而这些服务又可能依赖其他服务,形成了复杂的服务依赖关系,网状调用链路可以监控和追踪整个调用链上的请求流动,帮助发现和解决潜在的问题和性能瓶颈。
核心服务治理:
服务发布(deployment):服务升级运行新代码的过程
要想实现服务发布,就得克服以下难点:
服务不可用(对来自a服务的请求,b服务不响应了,用不了了),服务抖动(不稳定处理,能跑但是跑的不好的情况),服务回滚(新版本bug了,切换旧版本,记忆回溯)
克服途径:
-
蓝绿部署(Blue-Green Deployment):蓝绿部署是指在生产环境中同时维护两个版本的服务实例,一个是当前稳定版本(蓝色),另一个是新版本(绿色)。在流量低的时候,可以将一部分流量切换到新版本上进行测试和验证。具体特点如下:
- 简单稳定:蓝绿部署相对简单,只需要两个完全独立的环境来支撑两个版本的服务,并且在切换过程中不会影响用户的访问。
- 需要两倍资源:由于需要同时维护两个版本的服务实例,因此蓝绿部署需要消耗两倍的资源,包括服务器、网络等。
-
灰度发布(Canary Release):灰度发布是指逐步将新版本的服务实例引入线上环境,逐渐将流量切换到新版本上,以进行测试和验证。具体特点如下:
- 经济化节省资源:相比蓝绿部署,灰度发布只需要逐渐引入新版本的服务实例,可以根据流量来动态分配资源,节省了资源的使用。
- 缓慢但安全:由于逐个节点添加和调试新版本,灰度发布相对比较缓慢,但能够有效控制风险,并及时发现和解决问题,确保新版本的稳定性。
总之就是蓝绿部署适用于对服务的稳定性要求较高,且具备足够的资源支持的场景,毕竟成本高但是收益也是在那里的,而灰度发布适用于经济化利用的场景,测试虽然慢但是稳定。
流量治理: 可以基于地区集群实例请求等维度,对端到端流量的路由路径进行调配 例如:
- 繁荣地区与次繁荣地区,
- 稳定的服务和测试中的服务,
- 新的运行实体和旧的运行实体,
- 寻常的请求(大量)和测试的请求(少量)
负载均衡 负责分配请求在每个下有实例的分布,目的就是不能给一个实例过多压力,而其他实例反而没用上,强调资源的利用
常见的策略
(阿巴阿巴)
- 轮询(Round Robin):按照顺序依次分配给后端服务器。
- 随机(Random):随机地分配给后端服务器。
- 哈希(Hash)(
又是你哈希):根据请求的某个属性(如客户端IP地址或URL)计算哈希值,并将请求分发到对应的后端服务器。 - 最少连接(Least Connection):将请求分配给当前连接数最少的后端服务器。
稳定性治理
(意外太多还是太难维持线上服务了)
解决方式:
- 限流(设置请求次数限制)
- 熔断(响应端出了点问题,先断掉,过段时间再看看)
- 过载保护(压力过大,先断掉请求,喘口气)
- 降级(过载了,砍掉一些业务,但还是要完成主要业务)
治理实践
重试的意义
检测本地函数会出现的异常:
- 非法参数异常。
- 内存溢出异常
- 空指针异常
- 边界异常
- 系统崩溃、死循环与程序异常退出
检测远程函数的异常(由于服务器的不可抗力因素,也许下一次就会成功):
- 抖动
- 超载,
- 宕机,
- 调度超时
- 熔断,
- 限流
通过解决这些异常情况,减低错误率,减低长尾延时,容忍暂时性错误,避开下游故障实例
常见问题
- 幂等性,指对同一操作的多次执行所产生的影响与执行一次的影响相同。例如,在数据库写操作中使用唯一键或乐观锁来确保幂等性。这样即使请求出现重复或重试,也不会对系统产生副作用。(必须要确保的性质,多次重试带来的副作用会宕机的~)
- 重试风暴(雪崩效应),指的是分布式系统中的一个故障扩散现象,当某个服务故障时,其上游服务会发起大量重试请求,导致上游服务负载过高、不可用,最终形成级联故障。
是指数级增长警告 - 超时设置:在远程函数调用中,超时设置是为了避免请求无限等待或占用系统资源。通过设置适当的超时时间,可以在一定时间内得到响应,或者在超时后进行相应的错误处理。
解决方式
- 限制重试比例:可以设置一个阈值,确保重试请求的比例不超过该阈值。例如,可以配置一个百分比,表示每个请求中最多允许的重试比例。如果超过了这个比例,可以拒绝继续进行重试,或者采取其他处理方式,如返回错误信息或进行回退操作。
- 防止链路重试:可以针对每一层或每个服务设置重试次数的上限,以避免整个链路出现重试风暴。当达到重试次数上限时,可以快速失败,并告知上游服务请求失败,从而减轻链路上的重试压力。
- Hedged Requests(并行请求):对于可能超时的请求,可以同时向多个下游发送相同的请求,并等待先到达的响应。这样可以提高整体的请求成功率,避免单个请求的超时影响整体性能。当第一个响应到达时,可以取消其他请求。
- 重试效果验证:可以使用for循环来实现重试逻辑,并结合接入重试组件,例如断路器(Circuit Breaker)、熔断器(Fallback)等。通过不断重试和监控重试结果,可以验证重试机制的有效性和性能。
总结:
微服务架构通过将一个大型应用拆分成一系列更小、独立的服务,每个服务专注于特定的业务功能。这种方式使得每个服务能够独立运行、部署和扩展,从而提高系统的可伸缩性和灵活性。
通过微服务架构,可以方便地根据流量需求来进行服务的分配与负载均衡,从而实现利用率的提升。例如,可以通过服务注册与发现机制(如Consul、Eureka)来动态地将请求流量分配到不同的服务实例上,根据实时的负载情况来进行动态调整,确保资源的合理利用。
此外,重试机制在微服务架构中也扮演着重要的角色。由于每个服务都是独立部署和运行的,可能会面临网络延迟、故障或其他异常情况。通过重试机制,当请求遇到错误或超时时,可以自动地进行重试,以期最终获得成功的响应。同时,重试机制还能帮助排查服务运行中出现的异常情况,通过记录和监控重试请求的结果,可以及时发现并修复问题,提高系统的稳定性和可靠性。
总之,微服务架构的部署独立性、流量分配灵活性以及重试机制的使用,确实为系统的可扩展性、稳定性和故障排查提供了方便和有效的手段。但同时也需要注意合理设置重试策略,避免过多的重试导致资源浪费或性能下降。