在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来说就是体积小、著名的"2 pizza 团队"很好的诠释了这一解释(2 pizza 团队最先是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,全部参与人从设计、开发、测试、运维全部人加起来 只须要2个披萨就够了 )。 而所谓服务,必定要区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户能够感知最小功能集。java
微服务最先由Martin Fowler与James Lewis于2014年共同提出,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每一个服务运行在本身的进程中,并使用轻量级机制通讯,一般是HTTP API,这些服务基于业务能力构建,并可以经过自动化部署机制来独立部署,这些服务使用不一样的编程语言实现,以及不一样数据存储技术,并保持最低限度的集中式管理。node
在传统的IT行业软件大多都是各类独立系统的堆砌,这些系统的问题总结来讲就是扩展性差,可靠性不高,维护成本高。到后面引入了SOA服务化,可是,因为 SOA 早期均使用了总线模式,这种总线模式是与某种技术栈强绑定的,好比:J2EE。这致使不少企业的遗留系统很难对接,切换时间太长,成本过高,新系统稳定性的收敛也须要一些时间。最终 SOA 看起来很美,但却成为了企业级奢侈品,中小公司都望而生畏。mysql
单体架构在规模比较小的状况下工做状况良好,可是随着系统规模的扩大,它暴露出来的问题也愈来愈多,主要有如下几点:nginx
1.复杂性逐渐变高git
好比有的项目有几十万行代码,各个模块之间区别比较模糊,逻辑比较混乱,代码越多复杂性越高,越难解决遇到的问题。github
2.技术债务逐渐上升web
公司的人员流动是再正常不过的事情,有的员工在离职以前,疏于代码质量的自我管束,致使留下来不少坑,因为单体项目代码量庞大的惊人,留下的坑很难被发觉,这就给新来的员工带来很大的烦恼,人员流动越大所留下的坑越多,也就是所谓的技术债务愈来愈多。redis
3.部署速度逐渐变慢算法
这个就很好理解了,单体架构模块很是多,代码量很是庞大,致使部署项目所花费的时间愈来愈多,曾经有的项目启动就要一二十分钟,这是多么恐怖的事情啊,启动几回项目一天的时间就过去了,留给开发者开发的时间就很是少了。spring
4.阻碍技术创新
好比之前的某个项目使用struts2写的,因为各个模块之间有着千丝万缕的联系,代码量大,逻辑不够清楚,若是如今想用spring mvc来重构这个项目将是很是困难的,付出的成本将很是大,因此更多的时候公司不得不硬着头皮继续使用老的struts架构,这就阻碍了技术的创新。
5.没法按需伸缩
好比说电影模块是CPU密集型的模块,而订单模块是IO密集型的模块,假如咱们要提高订单模块的性能,好比加大内存、增长硬盘,可是因为全部的模块都在一个架构下,所以咱们在扩展订单模块的性能时不得不考虑其它模块的因素,由于咱们不能由于扩展某个模块的性能而损害其它模块的性能,从而没法按需进行伸缩。
单体架构全部的模块全都耦合在一块,代码量大,维护困难,微服务每一个模块就至关于一个单独的项目,代码量明显减小,遇到问题也相对来讲比较好解决。
单体架构全部的模块都共用一个数据库,存储方式比较单一,微服务每一个模块均可以使用不一样的存储方式(好比有的用redis,有的用mysql等),数据库也是单个模块对应本身的数据库。
单体架构全部的模块开发所使用的技术同样,微服务每一个模块均可以使用不一样的开发技术,开发模式更灵活。
微服务,从本质意义上看,仍是 SOA 架构。但内涵有所不一样,微服务并不绑定某种特殊的技术,在一个微服务的系统中,能够有 Java 编写的服务,也能够有 Python编写的服务,他们是靠Restful架构风格统一成一个系统的。因此微服务自己与具体技术实现无关,扩展性强。
微服务,关键其实不只仅是微服务自己,而是系统要提供一套基础的架构,这种架构使得微服务能够独立的部署、运行、升级,不只如此,这个系统架构还让微服务与微服务之间在结构上“松耦合”,而在功能上则表现为一个统一的总体。这种所谓的“统一的总体”表现出来的是统一风格的界面,统一的权限管理,统一的安全策略,统一的上线过程,统一的日志和审计方法,统一的调度方式,统一的访问入口等等。
微服务的目的是有效的拆分应用,实现敏捷开发和部署 。
微服务提倡的理念团队间应该是 inter-operate, not integrate 。inter-operate是定义好系统的边界和接口,在一个团队内全栈,让团队自治,缘由就是由于若是团队按照这样的方式组建,将沟通的成本维持在系统内部,每一个子系统就会更加内聚,彼此的依赖耦合能变弱,跨系统的沟通成本也就能下降。
微服务能够按照业务功能自己的独立性来划分,若是系统提供的业务是很是底层的,如:操做系统内核、存储系统、网络系统、数据库系统等等,这类系统都偏底层,功能和功能之间有着紧密的配合关系,若是强制拆分为较小的服务单元,会让集成工做量急剧上升,而且这种人为的切割没法带来业务上的真正的隔离,因此没法作到独立部署和运行,也就不适合作成微服务了。
能不能作成微服务,取决于四个要素:
小:微服务体积小,2 pizza 团队。
独:可以独立的部署和运行。
轻:使用轻量级的通讯机制和架构。
松:为服务之间是松耦合的。
从单体式结构转向微服务架构中会持续碰到服务边界划分的问题:好比,咱们有user 服务来提供用户的基础信息,那么用户的头像和图片等是应该单独划分为一个新的service更好仍是应该合并到user服务里呢?若是服务的粒度划分的过粗,那就回到了单体式的老路;若是过细,那服务间调用的开销就变得不可忽视了,管理难度也会指数级增长。目前为止尚未一个能够称之为服务边界划分的标准,只能根据不一样的业务系统加以调节
拆分的大原则是当一块业务不依赖或极少依赖其它服务,有独立的业务语义,为超过2个的其余服务或客户端提供数据,那么它就应该被拆分红一个独立的服务模块。
单一职责原则
意思是每一个微服务只须要实现本身的业务逻辑就能够了,好比订单管理模块,它只须要处理订单的业务逻辑就能够了,其它的没必要考虑。
服务自治原则
意思是每一个微服务从开发、测试、运维等都是独立的,包括存储的数据库也都是独立的,本身就有一套完整的流程,咱们彻底能够把它当成一个项目来对待。没必要依赖于其它模块。
轻量级通讯原则
首先是通讯的语言很是的轻量,第二,该通讯方式须要是跨语言、跨平台的,之因此要跨平台、跨语言就是为了让每一个微服务都有足够的独立性,能够不受技术的钳制。
接口明确原则
因为微服务之间可能存在着调用关系,为了尽可能避免之后因为某个微服务的接口变化而致使其它微服务都作调整,在设计之初就要考虑到全部状况,让接口尽可能作的更通用,更灵活,从而尽可能避免其它模块也作调整。
每一个微服务可独立运行在本身的进程里;
一系列独立运行的微服务共同构建起了整个系统;
每一个服务为独立的业务开发,一个微服务通常完成某个特定的功能,好比:订单管理,用户管理等;
微服务之间经过一些轻量级的通讯机制进行通讯,例如经过REST API或者RPC的方式进行调用。
易于开发和维护
因为微服务单个模块就至关于一个项目,开发这个模块咱们就只需关心这个模块的逻辑便可,代码量和逻辑复杂度都会下降,从而易于开发和维护。
启动较快
这是相对单个微服务来说的,相比于启动单体架构的整个项目,启动某个模块的服务速度明显是要快不少的。
局部修改容易部署
在开发中发现了一个问题,若是是单体架构的话,咱们就须要从新发布并启动整个项目,很是耗时间,可是微服务则不一样,哪一个模块出现了bug咱们只须要解决那个模块的bug就能够了,解决完bug以后,咱们只须要重启这个模块的服务便可,部署相对简单,没必要重启整个项目从而大大节约时间。
技术栈不受限
好比订单微服务和电影微服务原来都是用java写的,如今咱们想把电影微服务改为nodeJs技术,这是彻底能够的,并且因为所关注的只是电影的逻辑而已,所以技术更换的成本也就会少不少。
按需伸缩
咱们上面说了单体架构在想扩展某个模块的性能时不得不考虑到其它模块的性能会不会受影响,对于咱们微服务来说,彻底不是问题,电影模块经过什么方式来提高性能没必要考虑其它模块的状况。
运维要求较高
对于单体架构来说,咱们只须要维护好这一个项目就能够了,可是对于微服务架构来说,因为项目是由多个微服务构成的,每一个模块出现问题都会形成整个项目运行出现异常,想要知道是哪一个模块形成的问题每每是不容易的,由于咱们没法一步一步经过debug的方式来跟踪,这就对运维人员提出了很高的要求。
分布式的复杂性
对于单体架构来说,咱们能够不使用分布式,可是对于微服务架构来讲,分布式几乎是必会用的技术,因为分布式自己的复杂性,致使微服务架构也变得复杂起来。
接口调整成本高
好比,用户微服务是要被订单微服务和电影微服务所调用的,一旦用户微服务的接口发生大的变更,那么全部依赖它的微服务都要作相应的调整,因为微服务可能很是多,那么调整接口所形成的成本将会明显提升。
重复劳动
对于单体架构来说,若是某段业务被多个模块所共同使用,咱们即可以抽象成一个工具类,被全部模块直接调用,可是微服务却没法这样作,由于这个微服务的工具类是不能被其它微服务所直接调用的,从而咱们便不得不在每一个微服务上都建这么一个工具类,从而致使代码的重复。
目前微服务的开发框架,最经常使用的有如下四个:
Spring Cloud:http://projects.spring.io/spring-cloud(如今很是流行的微服务架构)
Dubbo:http://dubbo.io
Dropwizard:http://www.dropwizard.io (关注单个微服务的开发)
Consul、etcd&etc.(微服务的模块)
Spring Boot:
旨在简化建立产品级的Spring应用和服务,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用微服务功能,能够和spring cloud联合部署。
Spring Cloud:
微服务工具包,为开发者提供了在分布式系统的配置管理、服务发现、断路器、智能路由、微代理、控制总线等开发工具包。
传统的开发方式,全部的服务都是本地的,UI能够直接调用,如今按功能拆分红独立的服务,跑在独立的通常都在独立的虚拟机上的 Java进程了。客户端UI如何访问他的?后台有N个服务,前台就须要记住管理N个服务,一个服务下线/更新/升级,前台就要从新部署,这明显不服务咱们 拆分的理念,特别当前台是移动应用的时候,一般业务变化的节奏更快。另外,N个小服务的调用也是一个不小的网络开销。还有通常微服务在系统内部,一般是无状态的,用户登陆信息和权限管理最好有一个统一的地方维护管理(OAuth)。
因此,通常在后台N个服务和UI之间通常会一个代理或者叫API Gateway,他的做用包括
提供统一服务入口,让微服务对前台透明
聚合后台的服务,节省流量,提高性能
提供安全,过滤,流控等API管理功能
个人理解其实这个API Gateway能够有不少广义的实现办法,能够是一个软硬一体的盒子,也能够是一个简单的MVC框架,甚至是一个Node.js的服务端。他们最重要的做用是为前台(一般是移动应用)提供后台服务的聚合,提供一个统一的服务出口,解除他们之间的耦合,不过API Gateway也有可能成为单点故障点或者性能的瓶颈。
由于全部的微服务都是独立的Java进程跑在独立的虚拟机上,因此服务间的通行就是IPC(inter process communication),已经有不少成熟的方案。如今基本最通用的有两种方式。这几种方式,展开来说均可以写本书,并且你们通常都比较熟悉细节了, 就不展开讲了。
REST(JAX-RS,Spring Boot)
RPC(Thrift, Dubbo)
异步消息调用(Kafka, Notify)
通常同步调用比较简单,一致性强,可是容易出调用问题,性能体验上也会差些,特别是调用层次多的时候。RESTful和RPC的比较也是一个颇有意 思的话题。通常REST基于HTTP,更容易实现,更容易被接受,服务端实现技术也更灵活些,各个语言都能支持,同时能跨客户端,对客户端没有特殊的要 求,只要封装了HTTP的SDK就能调用,因此相对使用的广一些。RPC也有本身的优势,传输协议更高效,安全更可控,特别在一个公司内部,若是有统一个的开发规范和统一的服务框架时,他的开发效率优点更明显些。就看各自的技术积累实际条件,本身的选择了。
而异步消息的方式在分布式系统中有特别普遍的应用,他既能减低调用服务之间的耦合,又能成为调用之间的缓冲,确保消息积压不会冲垮被调用方,同时能 保证调用方的服务体验,继续干本身该干的活,不至于被后台性能拖慢。不过须要付出的代价是一致性的减弱,须要接受数据最终一致性;还有就是后台服务通常要 实现幂等性,由于消息发送出于性能的考虑通常会有重复(保证消息的被收到且仅收到一次对性能是很大的考验);最后就是必须引入一个独立的broker,如 果公司内部没有技术积累,对broker分布式管理也是一个很大的挑战。
在微服务架构中,通常每个服务都是有多个拷贝,来作负载均衡。一个服务随时可能下线,也可能应对临时访问压力增长新的服务节点。服务之间如何相互 感知?服务如何管理?这就是服务发现的问题了。通常有两类作法,也各有优缺点。基本都是经过zookeeper等相似技术作服务注册信息的分布式管理。当 服务上线时,服务提供者将本身的服务信息注册到ZK(或相似框架),并经过心跳维持长连接,实时更新连接信息。服务调用者经过ZK寻址,根据可定制算法,找到一个服务,还能够将服务信息缓存在本地以提升性能。当服务下线时,ZK会发通知给服务客户端。
客户端作:优势是架构简单,扩展灵活,只对服务注册器依赖。缺点是客户端要维护全部调用服务的地址,有技术难度,通常大公司都有成熟的内部框架支持,好比Dubbo。
服务端作:优势是简单,全部服务对于前台调用方透明,通常在小公司在云服务上部署的应用采用的比较多。
分布式最大的特性就是网络是不可靠 的。经过微服务拆分能下降这个风险,不过若是没有特别的保障,结局确定是噩梦。咱们刚遇到一个线上故障就是一个很不起眼的SQL计数功能,在访问量上升 时,致使数据库load彪高,影响了所在应用的性能,从而影响全部调用这个应用服务的前台应用。因此当咱们的系统是由一系列的服务调用链组成的时候,咱们必须确保任一环节出问题都不至于影响总体链路。相应的手段有不少:
重试机制
限流
熔断机制
负载均衡
降级(本地缓存) 这些方法基本上都很明确通用,就不详细说明了。好比Netflix的Hystrix:https://github.com/Netflix/Hystrix
这里有一个图很是好的总结微服务架构须要考虑的问题,包括
API Gateway
服务间调用
服务发现
服务容错
服务部署
数据调用
服务之间须要建立一种服务发现机制,用于帮助服务之间互相感知彼此的存在。服务启动时会将自身的服务信息注册到注册中心,并订阅本身须要消费的服务。
服务注册中心是服务发现的核心。它保存了各个可用服务实例的网络地址(IPAddress和Port)。服务注册中心必需要有高可用性和实时更新功能。上面提到的 Netflix Eureka 就是一个服务注册中心。它提供了服务注册和查询服务信息的REST API。服务经过使用POST请求注册本身的IPAddress和Port。每30秒发送一个PUT请求刷新注册信息。经过DELETE请求注销服务。客户端经过GET请求获取可用的服务实例信息。 Netflix的高可用(Netflix achieves high availability )是经过在Amazon EC2运行多个实例来实现的,每个Eureka服务都有一个弹性IP Address。当Eureka服务启动时,有DNS服务器动态的分配。Eureka客户端经过查询 DNS来获取Eureka的网络地址(IP Address和Port)。通常状况下,都是返回和客户端在同一个可用区Eureka服务器地址。 其余可以做为服务注册中心的有:
etcd —– 高可用,分布式,强一致性的,key-value,Kubernetes和Cloud Foundry都是使用了etcd。
consul —–一个用于discovering和configuring的工具。它提供了容许客户端注册和发现服务的API。Consul能够进行服务健康检查,以肯定服务的可用性。
zookeeper —— 在分布式应用中被普遍使用,高性能的协调服务。 Apache Zookeeper 最初为Hadoop的一个子项目,但如今是一个顶级项目。
简单来说,zookeeper能够充当一个服务注册表(Service Registry),让多个服务提供者造成一个集群,让服务消费者经过服务注册表获取具体的服务访问地址(ip+端口)去访问具体的服务提供者。以下图所示:
具体来讲,zookeeper就是个分布式文件系统,每当一个服务提供者部署后都要将本身的服务注册到zookeeper的某一路径上: /{service}/{version}/{ip:port}, 好比咱们的HelloWorldService部署到两台机器,那么zookeeper上就会建立两条目录:分别为/HelloWorldService/1.0.0/100.19.20.01:16888 /HelloWorldService/1.0.0/100.19.20.02:16888。
zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上创建的是一个 socket 长链接),若是长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除,好比100.19.20.02这台机器若是宕机了,那么zookeeper上的路径就会只剩/HelloWorldService/1.0.0/100.19.20.01:16888。
服务消费者会去监听相应路径(/HelloWorldService/1.0.0),一旦路径上的数据有任务变化(增长或减小),zookeeper都会通知服务消费方服务提供者地址列表已经发生改变,从而进行更新。
更为重要的是zookeeper 与生俱来的容错容灾能力(好比leader选举),能够确保服务注册表的高可用性。
服务高可用的保证手段,为了保证高可用,每个微服务都须要部署多个服务实例来提供服务。此时客户端进行服务的负载均衡。
把来自网络的请求随机分配给内部中的多个服务器。
每个来自网络中的请求,轮流分配给内部的服务器,从1到N而后从新开始。此种负载均衡算法适合服务器组内部的服务器都具备相同的配置而且平均服务请求相对均衡的状况。
根据服务器的不一样处理能力,给每一个服务器分配不一样的权值,使其可以接受相应权值数的服务请求。例如:服务器A的权值被设计成1,B的权值是3,C的权值是6,则服务器A、B、C将分别接受到10%、30%、60%的服务请求。此种均衡算法能确保高性能的服务器获得更多的使用率,避免低性能的服务器负载太重。
这种方式经过生成请求源IP的哈希值,并经过这个哈希值来找到正确的真实服务器。这意味着对于同一主机来讲他对应的服务器老是相同。使用这种方式,你不须要保存任何源IP。可是须要注意,这种方式可能致使服务器负载不平衡。
客户端的每一次请求服务在服务器停留的时间可能会有较大的差别,随着工做时间加长,若是采用简单的轮循或随机均衡算法,每一台服务器上的链接进程可能会产生极大的不一样,并无达到真正的负载均衡。最少链接数均衡算法对内部中需负载的每一台服务器都有一个数据记录,记录当前该服务器正在处理的链接数量,当有新的服务链接请求时,将把当前请求分配给链接数最少的服务器,使均衡更加符合实际状况,负载更加均衡。此种均衡算法适合长时处理的请求服务,如FTP。
容错,这个词的理解,直面意思就是能够容下错误,不让错误再次扩张,让这个错误产生的影响在一个固定的边界以内,“千里之堤毁于蚁穴”咱们用容错的方式就是让这种蚁穴不要变大。那么咱们常见的降级,限流,熔断器,超时重试等等都是容错的方法。
在调用服务集群时,若是一个微服务调用异常,如超时,链接异常,网络异常等,则根据容错策略进行服务容错。目前支持的服务容错策略有快速失败,失效切换。若是连续失败屡次则直接熔断,再也不发起调用。这样能够避免一个服务异常拖垮全部依赖于他的服务。
服务只发起一次待用,失败当即报错。一般用于非幂等下性的写操做
服务发起调用,当出现失败后,重试其余服务器。一般用于读操做,但重试会带来更长时间的延迟。重试的次数一般是能够设置的
失败安全, 当服务调用出现异常时,直接忽略。一般用于写入日志等操做。
当服务调用出现异常时,记录失败请求,定时重发。一般用于消息通知。
并行调用多个服务器,只要有一个成功,即返回。一般用于实时性较高的读操做。能够经过forks=n来设置最大并行数。
广播调用全部提供者,逐个调用,任何一台失败则失败。一般用于通知全部提供者更新缓存或日志等本地资源信息。
熔断技术能够说是一种“智能化的容错”,当调用知足失败次数,失败比例就会触发熔断器打开,有程序自动切断当前的RPC调用,来防止错误进一步扩大。实现一个熔断器主要是考虑三种模式,关闭,打开,半开。各个状态的转换以下图。
咱们在处理异常的时候,要根据具体的业务状况来决定处理方式,好比咱们调用商品接口,对方只是临时作了降级处理,那么做为网关调用就要切到可替换的服务上来执行或者获取托底数据,给用户友好提示。还有要区分异常的类型,好比依赖的服务崩溃了,这个可能须要花费比较久的时间来解决。也多是因为服务器负载临时太高致使超时。做为熔断器应该可以甄别这种异常类型,从而根据具体的错误类型调整熔断策略。增长手动设置,在失败的服务恢复时间不肯定的状况下,管理员能够手动强制切换熔断状态。最后,熔断器的使用场景是调用可能失败的远程服务程序或者共享资源。若是是本地缓存本地私有资源,使用熔断器则会增长系统的额外开销。还要注意,熔断器不能做为应用程序中业务逻辑的异常处理替代品。
有一些异常比较顽固,忽然发生,没法预测,并且很难恢复,而且还会致使级联失败(举个例子,假设一个服务集群的负载很是高,若是这时候集群的一部分挂掉了,还占了很大一部分资源,整个集群都有可能遭殃)。若是咱们这时仍是不断进行重试的话,结果大多都是失败的。所以,此时咱们的应用须要当即进入失败状态(fast-fail),并采起合适的方法进行恢复。
咱们能够用状态机来实现CircuitBreaker,它有如下三种状态:
关闭( Closed ):默认状况下Circuit Breaker是关闭的,此时容许操做执行。CircuitBreaker内部记录着最近失败的次数,若是对应的操做执行失败,次数就会续一次。若是在某个时间段内,失败次数(或者失败比率)达到阈值,CircuitBreaker会转换到开启( Open )状态。在开启状态中,Circuit Breaker会启用一个超时计时器,设这个计时器的目的是给集群相应的时间来恢复故障。当计时器时间到的时候,CircuitBreaker会转换到半开启( Half-Open )状态。
开启( Open ):在此状态下,执行对应的操做将会当即失败而且当即抛出异常。
半开启( Half-Open ):在此状态下,Circuit Breaker会容许执行必定数量的操做。若是全部操做所有成功,CircuitBreaker就会假定故障已经恢复,它就会转换到关闭状态,而且重置失败次数。若是其中 任意一次 操做失败了,Circuit Breaker就会认为故障仍然存在,因此它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)
保证核心服务的稳定性。为了保证核心服务的稳定性,随着访问量的不断增长,须要为系统可以处理的服务数量设置一个极限阀值,超过这个阀值的请求则直接拒绝。同时,为了保证核心服务的可用,能够对否些非核心服务进行降级,经过限制服务的最大访问量进行限流,经过管理控制台对单个微服务进行人工降级
SLA:Service-LevelAgreement的缩写,意思是服务等级协议。 是关于网络服务供应商和客户间的一份合同,其中定义了服务类型、服务质量和客户付款等术语。 典型的SLA包括如下项目:
分配给客户的最小带宽;
客户带宽极限;
能同时服务的客户数目;
在可能影响用户行为的网络变化以前的通知安排;
拨入访问可用性;
运用统计学;
服务供应商支持的最小网络利用性能,如99.9%有效工做时间或天天最多为1分钟的停机时间;
各种客户的流量优先权;
客户技术支持和服务;
惩罚规定,为服务供应商不能知足 SLA需求所指定。
这里说的网关是指API网关,直面意思是将全部API调用统一接入到API网关层,有网关层统一接入和输出。一个网关的基本功能有:统一接入、安全防御、协议适配、流量管控、长短连接支持、容错能力。有了网关以后,各个API服务提供团队能够专一于本身的的业务逻辑处理,而API网关更专一于安全、流量、路由等问题。
最简单的缓存就是查一次数据库而后将数据写入缓存好比redis中并设置过时时间。由于有过时失效所以咱们要关注下缓存的穿透率,这个穿透率的计算公式,好比查询方法queryOrder(调用次数1000/1s)里面嵌套查询DB方法queryProductFromDb(调用次数300/s),那么redis的穿透率就是300/1000,在这种使用缓存的方式下,是要重视穿透率的,穿透率大了说明缓存的效果很差。还有一种使用缓存的方式就是将缓存持久化,也就是不设置过时时间,这个就会面临一个数据更新的问题。通常有两种办法,一个是利用时间戳,查询默认以redis为主,每次设置数据的时候放入一个时间戳,每次读取数据的时候用系统当前时间和上次设置的这个时间戳作对比,好比超过5分钟,那么就再查一次数据库。这样能够保证redis里面永远有数据,通常是对DB的一种容错方法。还有一个就是真正的让redis作为DB使用。就是图里面画的经过订阅数据库的binlog经过数据异构系统将数据推送给缓存,同时将将缓存设置为多级。能够经过使用jvmcache做为应用内的一级缓存,通常是体积小,访问频率大的更适合这种jvmcache方式,将一套redis做为二级remote缓存,另外最外层三级redis做为持久化缓存。
超时与重试机制也是容错的一种方法,凡是发生RPC调用的地方,好比读取redis,db,mq等,由于网络故障或者是所依赖的服务故障,长时间不能返回结果,就会致使线程增长,加大cpu负载,甚至致使雪崩。因此对每个RPC调用都要设置超时时间。对于强依赖RPC调用资源的状况,还要有重试机制,可是重试的次数建议1-2次,另外若是有重试,那么超时时间就要相应的调小,好比重试1次,那么一共是发生2次调用。若是超时时间配置的是2s,那么客户端就要等待4s才能返回。所以重试+超时的方式,超时时间要调小。这里也再谈一下一次PRC调用的时间都消耗在哪些环节,一次正常的调用统计的耗时主要包括: ①调用端RPC框架执行时间 + ②网络发送时间 + ③服务端RPC框架执行时间 + ④服务端业务代码时间。调用方和服务方都有各自的性能监控,好比调用方tp99是500ms,服务方tp99是100ms,找了网络组的同事确认网络没有问题。那么时间都花在什么地方了呢,两种缘由,客户端调用方,还有一个缘由是网络发生TCP重传。因此要注意这两点。
在抗量这个环节,Servlet3异步的时候,有提到过线程隔离。线程隔离的之间优点就是防止级联故障,甚至是雪崩。当网关调用N多个接口服务的时候,咱们要对每一个接口进行线程隔离。好比,咱们有调用订单、商品、用户。那么订单的业务不可以影响到商品和用户的请求处理。若是不作线程隔离,当访问订单服务出现网络故障致使延时,线程积压最终致使整个服务CPU负载满。就是咱们说的服务所有不可用了,有多少机器都会被此刻的请求塞满。那么有了线程隔离就会使得咱们的网关能保证局部问题不会影响全局。
关于降级限流的方法业界都已经有很成熟的方法了,好比FAILBACK机制,限流的方法令牌桶,漏桶,信号量等。这里谈一下咱们的一些经验,降级通常都是由统一配置中心的降级开关来实现的,那么当有不少个接口来自同一个提供方,这个提供方的系统或这机器所在机房网络出现了问题,咱们就要有一个统一的降级开关,否则就要一个接口一个接口的来降级。也就是要对业务类型有一个大闸刀。还有就是 降级切记暴力降级,什么是暴力降级的,好比把论坛功能降调,结果用户显示一个大白板,咱们要实现缓存住一些数据,也就是有托底数据。限流通常分为分布式限流和单机限流,若是实现分布式限流的话就要一个公共的后端存储服务好比redis,在大nginx节点上利用lua读取redis配置信息。咱们如今的限流都是单机限流,并无实施分布式限流。
API网关是一个串行的调用,那么每一步发生的异常要记录下来,统一存储到一个地方好比elasticserach中,便于后续对调用异常的分析。鉴于公司docker申请都是统一分配,并且分配以前docker上已经存在3个agent了,再也不容许增长。咱们本身实现了一个agent程序,来负责采集服务器上面的日志输出,而后发送到kafka集群,再消费到elasticserach中,经过web查询。如今作的追踪功能还比较简单,这块还须要继续丰富。