微服务之基础与重要部件

客服端如何访问这些服务


传统的开发方式,所有的服务都是本地的,UI可以直接调用,现在按功能拆分成独立的服务,
跑在独立的运行环境中;客户端UI如何访问他的?后台有N个服务,前台就需要记住管理N个服务,
一个服务线下/更新/升级,前台就要重新部署,这明显不满足我们拆分的理念,特别是前台是
移动应用的时候,通常业务变化的节奏快,另外N个小服务的调用也是一个不小的网络开销,
还有一般微服务在系统内部,通常是无状态的,用户登录信息和权限管理最好有一个统一的地方维护管理

所以,一般在后台N个服务和UI之间一般会有一个代理或者叫API Gateway他的作用包括
1.提供统一的服务入口,让微服务对前台透明
2.聚合后台的服务,节省流量,提升性能
3.提供安全,过滤,流控等API管理功能
我的理解其实这个API Gateway可以有很广义的实现方法,可以是一个软件,比如kong,也可以是swoole的服务端自己编写的程序,
他们最重要的作用是为前台(通常是移动应用)提供后台服务的聚合,提供一个统一的服务出口,解除他们之间的耦合,
不过API Gateway也有可能成为单点故障点或者性能的瓶颈;

MyAnswer博客

服务之间如何通信?

因为所有的微服务都是独立的进程跑在独立的虚拟机上,所有服务间的通信就是IPC,已经有很多成熟的方案,
现在基本最通用的两种方式;
RESTFUL
一般同步调用比较简单,一致性强,但是容易出调用问题,性能体验上也会差些,特别是调用层次多的时候,RESful和PRC
的比较也是一个很有意思的话题,一般RESTful基于HTTP,更容易实现,更容易被接受,服务端实现技术也更灵活些,
各个语言都能支持,同时能跨客服端,对客服端没有特殊要求,只要封装了Http的SDK就能调用,所有相对使用的广一些
RPC
RPC也有自己的优点,传输协议更高效,安全更可控,特别再一个公司内部,如果有统一个的开发规范和统一的服务框架时,
他的开发效率优势更明显些,就看各自的技术积累实际条件,自己的选择了;


这么多服务怎么查找?

在微服务架构中,一般每一个服务都是有多个拷贝,来做负载均衡,一个服务随时可能下线,也可能应对临时访问
压力增加的服务节点,服务之间如何相互感知?服务如何管理?这就是服务发现的问题了,一般有两类做法,也各
有优缺点,基本是通过consul等类似技术做服务注册信息的分布式管理,当服务上线时,服务提供者将自己的
服务信息注册到consul中,并且通过心跳检测健康状态,实时更新链接信息,服务调用通过consul寻址,根据
可定制算法,找到一个服务,还可以将服务信息缓存在本地以提高性能,当服务下线时,consul会发通知给服务客服端

客户端做:优点是架构简单,扩展灵活,只对服务注册器依赖,缺点是客服端要维护所有的调用服务地址,有技术难度
一般大公司都有成熟的内部框架支持,比如Dubbo
服务端做:所有服务对前台调用方透明,一般在小公司在云服务上部署的应用采用的比较多

MyAnswer博客


服务挂了怎么办?

分布式最大的特性就是网络是不可靠的,通过微服务拆分能降低这个风险,不过如果没有特别的保障,结局肯定是噩梦,
比如一个线上故障就是一个很不起眼的数据修改功能,在访问量上升时,导致数据库负载(load)飙高,
影响了所在应用的性能,从而影响所有调用这个应用服务的前台应用,所以当我们的系统是由于一系列的服务调用链组成的时候,
我们必须确保任一环节出问题都不至于影响整体链路,相应的手段有很多
1.重试机制
2.限流
3.熔断机制
4.负载均衡
5.降级(本地缓存)


服务注册中心

服务之间需要创建一种服务发现机制,用于帮助服务之间互相感知彼此存在,服务启动时会将自身的服务信息
注册到注册中心,并订阅自己需要消费的服务;
服务注册中心是服务发现的核心,它保存了各个可用服务实例的网络地址(IPAddress和Port),服务注册中心必须要有
高可用性和实时更新功能;
上面提到的consul就是一个服务注册中心,它提供了服务注册和查询服务信息的REST API
服务通过使用POST请求注册自己的IPAddress和Port,每30秒发送一个PUT请求刷新注册信息,通过DELETE请求注销服务
客户端通过GET请求获取可用的实例信息,常见的服务注册中心的有:
1.etcd ---高可用,分布式,强一致性的,key-value,Kubernetes和cloud foundry都是使用了etcd
2.consul --一个用于disconving和configuring的工具,他提供了允许客户端和发现服务的Api,Consul可以进行服务健康检查,
以确定服务的可用性
3.zookeeper -- 在分布式应用中被广泛使用,高性能的协调服务

负载均衡

服务高可用的保证方式,为了保证高可用,每一个微服务都需要部署多个服务实例来提供服务,此时客户端进行服务的负载均衡
负载均衡常见的策略
1.随机-->把来自网络的请求随机分配给内部的多个服务器;
2.轮询-->每一个来自网络的请求,轮流分配给内部的服务器,从1到N然后重新开始,此种负载均衡算法适合服务器组内部的
服务器都具有相同的配置并且平均服务请求相对均衡的情况;
3.加权轮询-->根据服务器的不同处理能力,给每个服务器分配的不同权值,使其能够接受相应权值数的服务请求,例如:
服务器A的权值被设计成1,B的权值是3,C的权值是6,则服务器A,B,C将分别接受到10%,30%,60%的服务器请求,此种均衡算法
能确保高性能的服务器得到更多的使用率,避免低性能的服务器负载过重;
4.IP Hash-->这种方式通过生成请求源IP的哈希值,并通过这个哈希值来找到正确的真实服务,这意味着对于同一主机来说
他的服务器总是相同的,使用这种方式,你不需要保存任何源IP,但是需要注意,这种方式可能导致服务器负载不平衡;
5.最少连接数-->客户端的每一次请求服务在服务器停留的时间可能会有较大的差异,随着工作时间的加长,如果采用简单的
轮询或随机均衡算法,每一台服务器上的连接进程可能会差生极大的不同,并没有达到真正的负载均衡,最少连接数均衡算法
对内部中需负载的每一台服务器都有一个数据记录,记录当前该服务器正在处理的连接数量,当有新的服务连接时,将把
当前请求分配给连接数最少的服务器,使均衡更加符合情况,负载更加均衡,此种算法适合长时间处理的请求服务,如FTP

容错

容错,这个词的理解,直面意思就是可以容下错误,不让错误再次扩张,让这个错误产生在影响在一个固定的边界内,
千里之堤毁于蚁穴,我们用容错的方式就是让这种蚁穴不要变大,那么我们常见的降级,限流,熔断器,超时重试等等都是容错的方法
在调用服务集群时,如果一个微服务调用异常,如超时,连接异常,网络异常等,则根据容错策略进行服务容错,目前支持的
服务容错策略有快速失败,失败切换,如果连续失败多次则直接熔断,不再发起调用,这样可以避免一个服务异常拖垮所有
依赖于他的服务
容错策略
1.快速失败 -->服务只发起一次待用,失败立即报错
2.失效切换 -->服务发起调用,当出现失败后,重试其他服务器,通常用于读操作,但重试会带来更长时间的延迟,重试的
次数通常是可以设置的
3.失败安全 -->失败安全,当服务调用出现异常时,直接忽略,通常用于写入日志等操作
4.失败自动恢复 -->当服务调用出现异常,记录失败请求,定时重发,通常用于消息通知
5.广播调用 -->广播调用所有提供者,逐个调用,任何一台失败则失败,通常用于通知所有提供者更新缓存或日志等本地资源信息

熔断

熔断技术可以说是一种智能化的容错,当调用满足失败次数,失败比例就会触发熔断器的打开,有程序自动切断当前的RPC的调用
来防止错误进一步扩大,实现一个熔断器主要是考虑三种模式,关闭,打开,半开
我们在处理异常的时候,要根据业务情况来决定处理方式,比如我们调用商品接口,对方只是临时做了降级处理,那么作为网关
调用就要切到可替换的服务上来执行或者获取拖低数据,给用户友好提示,还有要区分异常的类型,比如依赖的服务崩溃了,这个可能
需要花费比较久的时间来解决,也可能是由于服务器负载临时过高导致超时,作为熔断器应该能够甄别这种异常类型,从而根据具体的
错误类型调整熔断策略,增加手动设置,在失败的服务恢复时间不确定的情况下,管理员可以手动强制切换熔断的状态,最后,熔断器
使用常见是调用可能失败的远程服务程序或者共享资源,如果是本地缓存私有资源,使用熔断器则会增加系统的额外开销,还要注意,
熔断器不能作为应用程序中业务逻辑的异常处理替代品
有一些异常比较顽固,突然发送,无法预测,而且很难恢复,并且还会导致级联失败
举个例子,假设一个服务集群的负载非常高,如果这时候集群的一部分挂掉了,还占了很大一部分资源,整个集群都有可能遭殃;
如果我们这时还不断进行重试的话,结果大多都是失败的,因此,此时我们的应用需要立即进入失败状态,并采取合适的方法进行恢复

我们可以用状态机来实现CircuitBreaker,它有以下三种状态:
1. 关闭(Closed):默认情况下状态是关闭的,此时允许操作执行,CircuitBreaker内部记录着最近失败次数,如果对应的操作执行失败,
次数就会续一次,如果在某个时间段内,失败次数(或者失败比率)达到阈值,CircuitBreaker会转换到开启(Open)状态,
在开启状态中,CircuitBreaker会启用一个超时倒计时,设这个计时器的目的是给集群相应的时间来恢复,当倒计时间
到的时候,CircuitBreaker会转换到半开启(Half-Open)状态
2. 开启(Open):在此状态下,执行对应的操作将会立即失败并且立即抛出异常
3. 半开启(Half-Open):在此状态下,Circuit Breaker会允许执行一定数量的操作,如果所以操作全部成功,CircuitBreaker就会
假定故障已经恢复,它就会转换到关闭状态,并且重置失败次数,如果其中,任意一次操作失败了,Circuit Breaker就会认为故障
任然存在,所以它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)

MyAnswer博客

限流和降级

保证核心服务的稳定性,为了保证核心服务的稳定性,随着访问量的不断增加,需要为系统能够处理的服务数量设置一个极限阈值,
超过这个阈值的请求则直接拒接,同时,为了保证核心服务的可用,可以对某系非核心服务进行降级,通过限制服务的最大访问量进行
限流,通过管理控制台对单个微服务进行人工降级
关于降级限流的方法业界都已经有很成熟的方法了,比如FAILBACK机制,限流的方法(令牌桶,漏桶,信号量)等,这里谈一下
我们的一些经验,降级一般都是由统一配置中心的降级开关来实现的,那么当有很多接口来自同一个提供方,这个提供方的系统
或者这机器所在的机房网络出现了问题,我们就要有一个统一的降级开关,不然就要一个接口一个接口的来降级,也就是对业务型有
一个大闸刀,还有就是降级切记暴力降级,什么是暴力降级,比如论坛功能降调,结果用户显示一个大白板,我们要实现缓存住一些数据
也就是托底数据,限流一般分为分布式限流和单机限流,如果实现分布式限流的话就要一个公共的后端存储服务比如redis,在
nginx节点上利用lua读取redis配置信息


API网关

这是说的网关是指API网关,直面意思是将所有API调用统一接入到API网关层,有网关层统一接入和数据,一个网关的基本功能有:
统一接入,安全防护,协议适配,流量管控,长短链接支持,容错能力,有了网关之后,各个API服务提供团队可以专注于自己的
业务逻辑处理,而API网关更专注于安全,限流,路由等问题;

超时和重试

超时与重试机制也是容错的一种方法,凡是发生RPC调用的地方,比如读取reids,db,mq等,因为网络故障或者是所依赖的服务故障,
长时间不能返回结果,就会导致线程增加,加大cpu负载,甚至导致雪崩,所以对每一个RPC调用都要设置超时时间
对于强依赖RPC调用资源的情况,还要有重试机制,但是重试的次数建议1-2次,另外如果有重试,那么超时时间就要相应调小;
这里也再谈一下一次RPC调用的时间都消耗在那些环节,一次正常的调用统计的耗时主要包括
1.调用端RPC框架执行时间
2.网络发送时间
3.服务端RPC框架执行时间
4.服务端业务代码时间
调用方和服务方都有各自的性能监控,比如调用方TP99是500ms,服务方TP99是100ms,找了网络组的同事确认网络没有问题
那么时间都花在什么地方了呢,两种原因,客户端调用方,还有一个原因是网络发送TCP重传


MyAnswer博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论