微服务
Last updated
Was this helpful?
Last updated
Was this helpful?
常见的软件架构有单体应用架构、主从架构等,微服务架构也是一种软件架构。
强模块化边界
可独立部署
技术多样性
分布式复杂性
当设计了上百个微服务时,要理解整个系统是如何工作的就变得非常复杂。
数据一致性
由于数据也是分散管理的,每个小组团队都有自己的数据拷贝,比如A团队有订单数据,B团队也有订单数据的拷贝,那么A团队对订单数据修改之后,需要同步给B团队,在同步的过程中也就会出现数据不一致的问题,在要求强一致性的业务场景是不可接受的。
分布式事务
运维复杂性
测试复杂性
服务消费方访问后台服务的时候,需要使用域名,通过DNS服务器进行域名解析,解析到LB负载均衡器上(如:nginx)。
后台服务会部署多份,请求通过负载均衡器后,会打到其中一个上面。
优点:最简单实现。
缺点:这个机制需要一个集中式的LB,存在单点故障问题。
把LB移到应用进程内,服务提供方通过服务注册方式(服务注册表),并且定期发送心跳,告诉服务注册表我还活着。
客户端拉取注册表中的服务,实现负载均衡功能。
优点:没有单点故障问题。
缺点:客户端需要实现负载均衡功能,如果有多种语言的客户端,升级成本会比较高。
在前面2个基础做个折中,每台服务器上部署一个LB。
服务生产者会自动注册到我们的服务注册表中,并且定期的发送心跳。消费者端的LB也会定期的去同步主机服务。
当消费者去调用后台服务的时候,直接调用本机的LB进程。
优点:没有单点故障问题;不需要每个应用去实现负载均衡功能。
缺点:运维成本比较高。
如果是采用容器的部署方式,那么这个LB是部署在容器内被独享呢,还是部署在容器的宿主机中被多个容器中的微服务共享呢?
容器部署的话,建议每个容器部署一个独立进程LB(或者说Service Mesh Proxy),这样隔离性更好,容器内的LB挂了,只影响那个容器,主机上其它容器不受影响。如果容器共享主机上的独立进程LB的话,则如果主机上的LB挂了,则整个主机上的容器全部受影响。
API 网关(API Gateway)是系统的门面。
当整个服务需要制定反爬虫策略时,如果向外暴露了多个服务,而使用的编程语言又不一样,那么就需要多个服务同时去实现反爬虫策略。而有了入口网关之后,作为整个服务的统一入口,只需要在入口网关实现反爬虫就可以了。
它提供客户端一个统一的接入地址,API 网关可以将用户的请求动态路由到不同的业务服务上,并且做一些必要的协议转换工作。在你的系统中,你部署的微服务对外暴露的协议可能不同:有些提供的是 HTTP 服务;有些已经完成 RPC 改造,对外暴露 RPC 服务;有些遗留系统可能还暴露的是 Web Service 服务。API 网关可以对客户端屏蔽这些服务的部署地址,以及协议的细节,给客户端的调用带来很大的便捷。
可以植入一些服务治理的策略,比如服务的熔断、降级,流量控制和分流等等。
客户端的认证和授权的实现,也可以放在 API 网关中(不同类型的客户端使用的认证方式是不同的)。手机 APP 使用 Oauth 协议认证,HTML5 端和 Web 端使用 Cookie 认证,内部服务使用自研的 Token 认证方式。这些认证方式在 API 网关上,可以得到统一处理,应用服务不需要了解认证的细节。
设置黑白名单,比如针对设备 ID、用户 IP、用户 ID 等维度的黑白名单。
日志记录,比如记录 HTTP 请求的访问日志。分布式链路追踪标记一次请求的 requestId,也可以在网关中来生成。
我们在系统开发中,会依赖很多外部的第三方系统,比如典型的例子:第三方账户登录、使用第三方工具支付等等。我们可以在应用服务器和第三方系统之间,部署出口网关,在出口网关中,对调用外部的 API 做统一的认证、授权,审计以及访问控制。
服务降级是从服务的整体负载考虑,通过限制次要服务的访问,只运作核心服务,就不会浪费负载能力在次要服务上。
服务熔断类似于家庭用电的保险丝,当使用电流过高达到一定的数值的时候,就会熔断保险丝来保护家里的电器。
当某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,再恢复调用链路。
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流。
一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。
当进入下一个时间窗口之后,计数器就清零重新计数。这个方案无法应对两个时间窗口临界时间内的突发流量。 比如:限定每秒钟不能超过 100 次接口请求,第一个时间窗口内的流量集中在最后的10ms,100次,第二个时间窗口内的流量集中在开头的10ms,100次。 虽然两个时间窗口内流量都符合限流要求(≤100 个请求),但在两个时间窗口临界的 20ms 内,会集中有 200 次接口请求。
假设限流的规则是,在任意 1s 内,接口的请求次数都不能大于 K 次。我们就维护一个大小为 K+1 的循环队列,用来记录 1s 内到来的请求。 注意,这里循环队列的大小等于限流次数加一,因为循环队列存储数据时会浪费一个存储单元 当有新的请求到来时,我们将与这个新请求的时间间隔超过 1s 的请求,从队列中删除。然后,我们再来看循环队列中是否有空闲位置。 如果有,则把新请求存储在队列尾部(tail 指针所指的位置);如果没有,则说明这 1 秒内的请求次数已经超过了限流值 K,所以这个请求被拒绝服务。
令牌桶算法以一个设定的速率产生令牌并放入令牌桶,每次用户请求都得申请令牌,如果令牌不足,则拒绝请求。令牌的发送速率可以设置,从而可以对突发的出口流量进行有效的应对。 令牌的数量与时间和发放速率强相关,时间流逝的时间越长,会不断往桶里加入越多的令牌,如果令牌发放的速度比申请速度快,令牌最终会占满整个令牌桶;如果令牌的发放速度,慢于请求到来速度,桶内就无牌可领,请求就会被拒绝。 相较于上述算法,这是更加平滑的限流算法,可以防止在细时间粒度上访问过于集中的问题。
所谓蓝绿部署,是指同时运行两个版本的应用,如上图所示,蓝绿部署的时候,并不停止掉老版本,而是直接部署一套新版本,等新版本运行起来后,再将流量切换到新版本上。但是蓝绿部署要求在升级过程中,同时运行两套程序,对硬件的要求就是日常所需的二倍,比如日常运行时,需要10台服务器支撑业务,那么使用蓝绿部署,你就需要购置二十台服务器。
灰度发布也叫金丝雀发布,起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高。
在灰度发布开始后,先启动一个新版本应用,但是并不直接将流量切过来,而是测试人员对新版本进行线上测试,启动的这个新版本应用,就是我们的金丝雀。如果没有问题,那么可以将少量的用户流量导入到新版本上,然后再对新版本做运行状态观察,收集各种运行时数据,如果此时对新旧版本做各种数据对比,就是所谓的A/B测试。
当确认新版本运行良好后,再逐步将更多的流量导入到新版本上,在此期间,还可以不断地调整新旧两个版本的运行的服务器副本数量,以使得新版本能够承受越来越大的流量压力。直到将100%的流量都切换到新版本上,最后关闭剩下的老版本服务,完成灰度发布。
如果在灰度发布过程中(灰度期)发现了新版本有问题,就应该立即将流量切回老版本上,这样,就会将负面影响控制在最小范围内。
常见的权限控制有:功能级权限控制、URL级、数据表的列级、数据表的行级等。
行级权限需要以列级权限为基准,行级权限是对列级权限更进一步的限制,即只有行级权限是查看不到表数据的。
举个例子,假设有商品基本信息、商品参数这两个关联性比较高但又被拆分了的微服务,商品服务的部分表结构如下:
1
iPhone X
2
iPhone XS
3
iPad
4
Mac
商品参数部分表如下:
1
2
2
3
3
1
4
3
假设有一个数据行级控制的需求,比如:限制zhangsan这个用户,不允许他访问goods_param_id为2的商品。
现在商品基本信息、商品参数这2个服务已经被拆分成了2个相对独立的微服务,要怎么做呢?
你可能会想到,先查商品参数服务,再取出goods_id来查商品基本信息服务,但是如果服务还有多个拆分出来的服务要怎么做呢?你可能会想到,总有一个逻辑能排出优先级来处理,但实际上却是做不到的。比如还有一个商品活动服务,部分表结构如下:
1
3
2
1
3
2
4
1
现在要求限制zhangsan这个用户,不允许他访问promotion_id为2或者goods_param_id为2的商品。
如果把请求分别打到如上3个微服务,那么在商品参数服务查询到的结果集goods_id为2,3,4,商品活动服务查询到的结果集goods_id为1,2,4,显然,由于把一个并列的筛选条件变成单个分开执行,结果不符合期望。
你可能又会想到,在聚合层服务操作,取交集就可以了,取交集不就可以取到目标goods_id为2,4的记录了吗?实际上这是一个典型的错误思路。如果在商品活动服务中promotion_id!=2的第一页记录恰好在商品参数服务中全是goods_param_id=2怎么办呢?这样的话,取这2个微服务第一页的交集,就是空,而实际上,如果服务没有做拆分,表数据都在一起,显然是能查出数据的。
结论:把一条业务线拆分的太细的微服务,对于整条业务线的数据行级权限控制,在拆分后的微服务下是无法实现的,一定要控制好服务拆分的粒度。
持续集成、持续交付和持续部署。
一旦开发人员将改动的代码合并到主分支,系统就会通过自动构建应用,并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。
CI 在完成了构建、单元测试和集成测试这些自动化流程后,持续交付可以自动把已验证的代码保存为新版本。
持续交付旨在建立一个可随时将开发环境的功能部署到生产环境的代码库。
自动将应用发布到生产环境。
对接口调用的频率进行限制,。
、