一、客户端缓存优化
1、添加CDN缓存
CDN 缓存也叫作网络访问的“第一跳”,用户请求先到达的是互联网网络服务商的机房。在机房里面部署 CDN 服务器,提供缓存服务。缓存了一些静态资源。如果存在用户请求的内容,直接通过CDN进行返回;没有的话继续向下请求
2、正向代理缓存
正向代理缓存保存在客户端,代理客户端访问互联网,比如访问谷歌,直接访问不到,我们就可以使用一个代理服务器,将请求转发给代理服务器,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们。
既然正向代理服务器可以取得谷歌的数据,他就可以缓存这些数据同时记录下客户端身份,当下次请求的时候通过客户端身份验证,就可以直接找到这些缓存的数据了。
3、反向代理缓存
反向代理是在服务器这一端的,用户通过互联网连接到数据中心的时候,连接的通常是一个反向代理服务器,反向代理服务器根据用户的请求,在本地的反向代理缓存中查找是否有用户请求的数据,如果有就直接返回这个数据,如果没有再把这个请求向下继续转发。
二、服务端优化
1、应用程序分布式部署
分布式部署的意思是同一个应用程序分布在不同的服务器上,一块对外提供服务。他们之间的等位相等。
(1)负载均衡算法访问不同应用程序,避免单一应用程序的压力
客户端通过反向代理执行负载均衡算法去访问不同的应用程序
①随机均衡算法 ②多权重负载 ③session 粘连
(2)不同的应用程序其实是同一个,如何解决session问题
注意,不同功能的应用程序也要解决session功能,
第一种:粘性session
原理:粘性Session是指将用户锁定到某一个服务器上,比如,用户第一次请求时,负载均衡器将用户的请求转发到了A服务器上,第二次访问时,反向代理根据sessionID通过hash算法,转发到A服务器上。
缺点:缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的session信息都将失效。
第二种:session同步
原理:任何一个服务器上的session发生改变(增删改),会广播给所有其它节点,不管其他服务器需不需要session,以此来保证Session同步。
缺点:会对网络负荷造成一定压力,如果session量大的话可能会造成网络堵塞,拖慢服务器性能。
第三种:持久化到缓存数据库
原理:拿出一个缓存数据库,存储session信息,并设置相应的失效时间。每次访问的时候不管是负载到了哪一个服务器都会先从缓存数据库里面查询session
2、应用程序拆分成微服务
(1)微服务的注册与发现Eureka
既然是微服务,就要有一个中心去管理,有了中心之后,我们就可以将各种服务往里面注册使得各个服务可以相互感知到。
Springcloud有一个Eureka注册与发现中心,保存了各个微服务的名称、IP地址、端口号等信息。这个时候,服务之间进行交流与交互。
一般注册中心会采取集群的策略,进行容错;
(2)微服务的通信
①Ribbon
本质是一个带有负载均衡功能的http客户端,在每次请求的时候会通过负载均衡算法Round Robin轮询算法选择一台机器,均匀的把请求分发到各台机器上。这是从每一个微服务搭建成了分布式的系统来考虑的。
实现原理:Ribbon会从 Eureka Client里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口号;然后Ribbon就可以使用默认的Round Robin算法,从中选择一台机器。
②Feign
两个不同的微服务就可以使用Feign来通信,Feign集成了Ribbon。底层使用的是动态代理的功能:
Feign的工作原理:
第一:对某个接口定义了@FeignClient注解,Feign就会针对这个接口创建一个动态代理;
第二:接着你要是调用那个接口,本质就是会调用 Feign创建的动态代理
第三:Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址;
第四:Feign中的Ribbon拿到这个地址,通过负载均衡算法调用相应的微服务的相应方法;
(3)微服务的容错机制Hystrix
这个功能的微服务即使是搭建了集群可能也会出错,从而造成服务雪崩;Springcloud提供了熔断和服务降级的策略进行容错;
①降级
如果调用的远端服务出现问题(超时或异常),则返回一个结果,用于提示。一般都是返回一个默认值;
②熔断
如果调用的远端服务出现问题,则在一段时间之内直接返回提示信息(不再调远端的服务),一段时间后陆续调用远端服务,如果不再出现问题,则恢复正常调用远端服务。
(4)微服务的网关Zuul
这么多微服务部署在不同的服务器上地址是不一样的,可以通过网关Zuul把所有的服务接口统一起来暴露出去给客户用。在这里我们可以过滤用户的一些信息或者是做权限验证等等工作;
(5)微服务的属性配置SpringCloud Config
服务一多,改配置太麻烦了,需要有个东西来管理,最好还能在线修改配置,这时候分布式配置中心(Spring Cloud Config)就出现了,它实现了将所有服务的配置文件都抽取到一个统一的地方
(6)微服务消息总线Springcloud Bus
想要更改配置的时候,服务能够知道并且热更新配置,那么就需要一个消息传递工具——消息总线(Spring Cloud Bus),通过这个总线向其他服务传递消息
(7)微服务的链路追踪
我们想要知道各个服务之间的调用关系间接得到服务之间的依赖,那么就需要服务追踪组件(zipkin ,SpringCloud Sleuth集成了zipkin)了。
3、微服务会出现的问题
(1)Session问题:解决方式同上
(2)大规模的增删改查,如何抵抗住压力
①采用消息队列对用户访问进行削峰处理
②服务的熔断和降级策略
(3)分布式事务
比如:例如在下单场景下,库存和订单如果不在同一个节点上,涉及分布式事务。
分布式事务的最主要特点是需要跨节点进行通信,解决办法如下:
首先基于CAP理论,保证BASE特性。接下来看看如何解决:
①在设计微服务架构的时候尽可能的保证微服务的独立性。
②2pc协议模型
引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;
第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。
2pc的缺点:
(1)第一阶段到第二阶段有一个时间差,时间太久了之后,第一节点的某些参与者可能不能成功执行事务了,但是之前告诉协调者说我可以成功发送,这就造成了错误。
(2)协调者在整个两阶段提交过程中扮演着举足轻重的作用,一旦协调者所在服务器宕机,那么就会影响整个数据库集群的正常运行,比如在第二阶段中,如果协调者因为故障不能正常发送事务提交或回滚通知,那么参与者们将一直处于阻塞状态,整个数据库集群将无法提供服务。
(3)同步阻塞:两阶段提交执行过程中,所有的参与者都需要听从协调者的统一调度,期间处于阻塞状态而不能从事其他操作,这样效率及其低下。
③基于消息队列的最终一致性方案
消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
基本思路如下:
(1)将事务操作进行封装
(2)上游业务(订单服务)先执行事务操作,成功之后将数据发送到消息队列
(3)下游服务(库存服务)监听消息队列,然后去执行,执行成功或者是失败都通过ACK告诉上游业务,上游业务根据这个ACK信息决定是否回滚或提交。
但是可能会出现异常的情况:
(1)直接无法到达消息队列
网络断了,抛出异常,上游业务直接回滚即可。
(2)消息已经到达消息队列,但返回的时候出现异常
MQ提供了确认ack机制,可以用来确认消息是否有返回。因此我们可以在发送前在数据库中先存一下消息,如果ack异常则进行重发
(3)消息送达后,消息服务自己挂了
先操作数据库,然后再往消息队列发送
(4)未送达消费者
消息队列收到消息后,消费者去消费,此时消息队列会处于"UNACK"的状态,直到客户端确认消息
(5)确认消息丢失
消息返回时假设确认消息丢失了,那么消息队列认为消息没有到达消费者会重发消息。
(6)消费者业务处理异常
消费者接受消息并处理,假设抛异常了,先重试,重试到一定的次数之后进行返回事务执行失败。
(7)分布式锁
关于分布式锁的设计,从以下四个角度考虑:
第一:互斥性。在任意时刻,只有一个客户端能持有锁。
第二:不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
第三:具有容错性。只要大部分的节点正常运行,客户端就可以加锁和解锁。
第四:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
常见的,分布式锁有三种实现方式:
第一:数据库乐观锁;
利用表的唯一索引行级锁进行加解锁,加锁:
加锁:insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
解锁:delete from methodLock where method_name ='method_nam
第二:基于Redis的分布式锁;
jedis.setnx(String key, String value, String nxxx, String expx, int time)
第三:基于ZooKeeper的分布式锁
zk 是一种提供配置管理、分布式协同以及命名的中心化服务,用于集群配置中心管理,服务的注册监听等。zookeeper 分布式锁的实现利用zookeeper 管理配置中心的watcher机制(观察者模式),对竞争分布式锁的客户端维护了一张临时顺序表。表中每个节点代表一个客户端。