服务的拆分
1,抽取公共的基础服务(短信,邮件,文件管理) 2,以业务为边界,拆分服务 3,管理多个服务-- 注册中心 4,服务之间需要通信--通信的方式 1.同步调用 Dubbo, RPC的方式,底层采用Netty来实现,基于TCP建立的是长连接 注意: BIO, NIO只是一种网络通信模式 BIO:为每个连接创理个纯程 NIO: -个往程服务多个连接,有弊端,当连接数太多,性能下降 Netty:封装了NIO, 在它的基础上,添加主从处理组,BossGraup, WorkerGroup 编程模型: Reactor (反应堆)思想,NIO.Netty是 种实现 SpringCloud, restul http的方式 短链接的方式 性能角度:Dubbo > SpringCloud 调用方需要等待执行方的处理结果 2.异步通知 MQ,调用方无需等待执行方的处理结果
简述ZAB协议ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议,实现分布式数据一致性 。所有客户端的请求都是写入到Leader进程中,然后,由Leader同步到其他节点,称为Follower。在集群数据同 步的过程中,如果出现Follower节点崩溃或者Leader进程崩溃时,都会通过Zab协议来保证数据一致性。
ZAB协议包括两种基本的模式:崩溃恢复和消息广播。
消息广播: 集群中所有的事务请求都由Leader节点来处理,其他服务器为Follower, Leader将客户端的事务请求转换为事 务Proposal,并且将Proposal分发给集群中其他所有的Follower. 完成广播之后,Leader 等待Follwer 反馈,当有过半数的Follower反馈信息后,Leader 将再次向集群内 Follower广播Commit信息,Commit 信息就是确认将之前的Proposal 提交。
Leader节点的写入是一个两步操作, 第-步是广播事务操作,第二步是广播提交操作,其中过半数指的是反馈的 节点数>=N/2+1, N是全部的Follower节点数量。
崩溃恢复:
●初始化集群,刚刚启动的时候
●Leader 崩溃,因为故障宕机
●Leader 失去了半数的机器支持,与集群中超过-半的节点断连
此时开启新-轮Leader选举,选举产生的Leader会与过半的Follower进行同步,使数据一致, 当与过半的机器 同步完成后,就退出恢复模式,然后进入消息广播模式
整个ZooKeeper集群的一致性保证就是在上面两个状态之前切换,当Leader服务正常时,就是正常的消息广播 模式;当Leader不可用时,则进入崩溃恢复模式,崩溃恢复阶段会进行数据同步,完成以后,重新进入消息广播阶段。.
Zxid是Zab协议的一个事务编号,Zxid 是一个64位的数字,其中低32位是一个简单的单调递增计数器,针对客户端每一个事务请求, 计数器加1;而高32位则代表Leader周期年代的编号。 Leader周期( epoch),可以理解为当前集群所处的年代或者周期,每当有一个 新的Leader选举出现时,就会从这个Leader服务器上取出其本地日志中最大事务的Zxid,并从中读取epoch值,然后加1,以此作为新的周期ID。高32位代表了每代Leader的唯一性, 低32位则代表了每代Leader中事务的唯一性。
zab节点的三种状态:
- following:服从leader的命令
- leading:负责协调事务
- election/looking:选举状态
znode树状结构、watch机制、本地缓存服务列表
zk的数据模型和节点类型数据模型:树形结构
zk维护的数据主要有:客户端的会话(session) 状态及数据节点(dataNode) 信息。
zk在内存中构造了个DataTree的数据结构,维护着path到dataNode的映射以及dataNode间的树状层级关系。 为了提高读取性能,集群中每个服务节点都是将数据全量存储在内存中。所以, zk最适于读多写少且轻量级数据的应用场景。
数据仅存储在内存是很不安全的,zk采用事务日志文件及快照文件的方案来落盘数据,保障数据在不丢失的情况下能快速恢复。
树中的每个节点被称为一Znode
Znode兼具文件和目录两种特点。可以做路径标识,也可以存储数据,并可以具有子Znode。具有增、删、改、查等操作。
Znode具有原子性操作,读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一 个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的 操作
Znode存储数据大小有限制。每个Znode的数据大小至多1M,常规使用中应该远小于此值。(因为zk主要做服务协调,不会太多,也能节省带宽,保持轻量及效率)
Znode通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的, 也就是说每一个路径只有一 个表示, 因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息, 比如关键配额信息 。
持久节点: 一旦创建、该数据节点会一直存储在zk服务器 上、即使创建该节点的客户端与服务端的会话关闭了、该节点也不会被删除
临时节点:当创建该节点的客户端会话因超时或发生异常而关闭时、该节点也相应的在zk上被删除。(分布式锁广泛使用:解决死锁问题,数据库唯一索引也可以解决但是客户端挂掉的话此锁不能及时删除,定时删除操作成本高。zk中临时节点zk发现客户端不在了会主动删除,不会出现死锁问题)
有序节点:不是一种单独种类的节 点而是在持久节点和临时节点的基础上、增加了一个节点有序的性质。
简述zk的命名服务、配置管理、集群管理命名服务: 通过指定的名字来获取资源或者服务地址(注册中心)。Zookeeper可以创建一 个全局唯一 的路径, 这个路径就可以作为一个 名字。被命名的实体可以是集群中的机器,服务的地址,或者是远程的对象等。一些分布式服务框架 (RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据特定的名字来获取资源的实体、服务地址和提供者信息等
配置管理: 实际项目开发中,经常使用.properties或者xml需要配置很多信息, 如数据库连接信息、fpst地址端口等等。 程序分布式部署时,如果把程序的这些配置信息保存在zk的znode节点下,当你要修改配置,即znode会发生变化时,可以通过改变zk中某个目录节点的内容,利用watcher通知给各个客户端,从而更改配置。
集群管理: 集群管理包括集群监控和集群控制,就是监控集群机器状态,剔除机器和加入机器。zookeeper可以方 便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与zk断开连接,对应的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似。
Zookeeper watch机制客户端,可以通过在znode.上设置watch,实现实时监听znode的变化Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端(发生了什么变化是需要去查看的)
- 父节点的创建,修改,删除都会触发Watcher事件。
- 子节点的创建,删除会触发Watcher事件。
一次性: - 旦被触发就会移除 (减少不必要的通知),再次使用需要重新注册, 因为每次变动都需要通知所有客户端,一 -次性可以减轻压力,3.6.0默认持久递归,可以触发多次
轻量:只通知发生了事件,不会告知事件内容,减轻服务器和带宽压力
Watcher机制包括三个角色:客户端线程、客户端的WatchManager以及ZooKeeper服务器
- 客户端向ZooKeeper服务器注册一个Watcher监听,
- 把这个监听信息存储到客户端的WatchManager中(把watcher对象存起,监听信息还会往zkServer上注册类似map的节点)
- 当ZooKeeper中的节点发生变化时,会通知客户端,客户端会调用相应Watcher对象中的回调方法。watch回调是串行同步的
分布式容错框架
- 阻止故障的连锁反应,实现熔断
- 快速失败,实现优雅降级.
- 提供实时的监控和告警
资源隔离:线程隔离,信号暈隔离
- 线程隔离: Hystrix会给每一 个Command分配一个单独的线程池,这样在进行单个服务调用的时候,就可以在独立的线程池里面进行,而不会对其他线程池造成影响
- 信号量隔离:客户端需向依赖服务发起请求时,首先要获取一 个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超过信号量个数时,后续的请求都会直接拒绝,进入fallback流程。 信号量隔离主要是通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的。
熔断和降级:调用服务失败后快速失败
熔断是为了防止异常不扩散,保证系统的稳定性
降级:编写好调用失败的补救逻辑,然后对服务直接停止运行,这样这些接口就无法正常调用,但又不至于直接报 错,只是服务水平下降
- 通过HystrixCommand或者HystrixObservableCommand将所有的外部系统(或者称为依赖)包装起来,整个包装对象是单独运行在一个线程之中 (这是典型的命令模式)。
- 超时请求应该超过你定义的阈值
- 为每个依赖关系维护一个小的线程池(或信号量) ; 如果它变满了,那么依赖关系的请求将立即被拒绝(fallback),而不 是排队等待。
- 统计成功,失败(由客户端抛出的异常),超时和线程拒绝。
- 打开断路器可以在一段时间内停 止对特定服务的所有请求,如果服务的错误百分比通过阈值,手动或自动的关 闭断路器。
- 当请求被拒绝、连接超时或者断路器打开,I直接执行fallback逻辑。
- 近乎实时监控指标和配置变化。
Eureka:服务注册与发现
注册:每个服务都向Eureka登记自己提供服务的元数据,包括服务的ip地址、端口号、版本号、通信协议等。 eureka将各个服务维护在了一个服务清单中(双层Map, 第一层key是服务名, 第二_层key是实例名,value是服务地址加端口)。同时对服务维持心跳,剔除不可用的服务,eureka集群 各节点相互注册每个实例中都有一样的服务清单。
发现: eureka注册的服务之间调用不需要指定服务地址,而是通过服务名向注册中心咨询,并获取所有服务实例清单(缓存到本地),然后实现服务的请求访问。
Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台(被调用方的服务 地址有多个),Ribbon也是 通过发起http请求,来进行的调用,只不过是通过调用服务名的地址来实现的。虽然说Ribbon不用去具体请求服务实例的ip地址或域名了,但是每调用一个接口都还要 手动去发起Http请求
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/ribbon-consumer ")
pub1ic string he11oConsumler (){
return
restTemp1ate . getForEntity("http://exampleservice/index" ,String . class). getBody();}}
Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求,简化服务间的调 用,在Ribbon的基础上进行了进-步的封装。单独抽出了-个组件, 就是Spring Cloud Feign。在引|入Spring Cloud Feign后,我们只需要创建一个接口并用注解的方 式来配置它,即可完成对服务提供方的接口绑定。调用远程就像调用本地服务一样
@RestController
pub1ic class UserController {
@GetMapping("/getUser")
public string getUser(){
List list = new ArrayList<>O;
1ist . add("张三");
string json = JSON . toJsonstring(1ist);
return json;
}
}
@FeignClient(name = "user")
pub1ic interface Userclient {
@GetMapping("/getUser")
String getUser();
@RestController
public c1ass TestController {
@Resource
userclient userclient ;
@RequestMapping(" /test")
public string testO){
string user = userclient. getuser();
return user ;
}
Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,通过统计接口超时次数返回默认值,实现服务熔断和降级(A服务调B,B调C,假如A的并发量很大请求超时会把整个并发量向后传导,导致BC的并发量也很大,就极有可能因为A这一个接口把整条链路全部压垮,或者后面的资源处理有问题导致A无法继续,Hystrix就起到熔断作用保护现有健康服务 )对于没有即时处理的请求先响应并记录日志,后续读取日志记录异步处理。
Zuul:如果前端、移动端要调用后端系统,统-从Zuul网关进入, 由Zuu网关转发请求给对应的服务,通过与Eureka进行整合,将自身注册为Eureka'下的应用,从Eureka 下获取所有服务的实例,来进行服务的路由。Zuul还提供了一套过滤器机制, 开发者可以自己指定哪些规则的请求需要执行校验逻辑,只有通过校验逻辑的请求才会被路由到具体服务实例上,否则返回错误提示。也可选Gateway
分布式id生成方案
uuid .
- 当前日期和时间 时间戳
- 时钟序列。 计数器
- 全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
优点:代码简单,性能好(本地生成,没有网络消耗),保证唯一。(相对而言,重复概率极低可以忽略)
缺点:
- 每次生成的ID都是无序的,而且不是全数字,且无法保证趋势递增对数据库聚簇索引不友好。
- UUID生成的是字符串,字符串存储性能差,查询效率慢,写的时候由于不能产生顺序的append操作,需要进行insert操作,导致频繁的页分裂,这种操作在记录占用空间比较大的情况下,性能下降比较大,还会增加读取磁盘次数
- UUID长度过长,不适用于存储,耗费数据库性能。ID无-定业务含义,可读性差。
- 有信息安全问题,有可能泄露mac地址
数据库自增序列
单机模式:
优点:
- 实现简单,依靠数据库即可,成本小。
- ID数字化,单调白增,满足数据库存储和查询性能。
- 具有一定的业务可读性。(结 合业务code)
缺点:
- 强依赖DB,存在单点问题,如果数据库宕机,则业务不可用。
- DB生成ID性能有限,单点数据库压力大,无法扛高并发场景。
- 信息安全问题,比如暴露订单量,ur1查询改一下id查到别人的订单
数据库高可用:多主模式做负载,基于序列的起始值和步长设置,不同的初始值,相同的步长,步长大于节点数
优点: 解决了ID生成的单点问题,同时平衡了负裁。
缺点:
- 系统扩容困难:系统定义好步长之后,增加机器之后调整步长困难。
- 数据库压力大:每次获取一- 个ID都必须读写-次数据库。
- 主从同步的时候:电商下单->支付insert master db select数据 ,因为数据同步延迟导致查不到这个数措。加cache (不是最好的解决方式)数据要求比较严连的话查master主库。
雪花算法
生成:一个64bit的整性数字,第一位符号位固定为0,41位时间戳,10位workId(机器id), 12位序列号(自增长的)位数可以有不同实现
优点:
每个毫秒值包含的ID值很多,不够可以变动位数来增加,性能佳(依赖workId的实现)。 时间戳值在高位,中问是固定的机器码,自增的序列在低位,整个ID是趋势递增的。能够根据业务场景数据库节点布置灵活挑战bit位划分,灵活度商。
缺点:
强依赖于机器时钟,如果时钟回拨,会导致重复的ID生成,所以一般基于此的算法发现时钟回拨,都会抛异常处理,阻止ID生成,这可能导致服务不可用。
分布式锁保证锁是共享的第三方资源 需要这个锁独立于每一个服务之外, 而不是在服务里面。 上锁1 解锁 0
数据库的方式:利用主键冲突控制一次只有一个线程能获取锁,非阻塞需要自己实现等待、不可重入(每次都要获取锁)、单点、失效时间(数据库不好支持key的失效时间) t_ lock id lock(0) 数据库是磁盘操作(IO)效率低
- 上锁:将数值修改为1
- 解锁:将数值修改为0
Redis方式: 内存操作单线程处理网络请求,不需要考忠并发安全性(适合做分布式锁也是现在用的较多的一种)所有服务节点设置相同的key,返回为0、则锁获取失败 setnx: key不存在,就设置成功,否则,设置失败 不适合集群模式(AP的对一致性欠缺,主从数据同步不及时)
- 上锁:成功执行setnx key value
- 解锁: delete key
- 避免死锁:需要设置过期时间expire key timeout
- 注意需要将两个操作(设置锁和锁的有效期)变成一个原子操作(4.0之前Redis + lua脚本。lua脚本是帮助我们扩充redis指令的;4.0之后直接使用现成的指令,在设置数据的时候同时设置时间)
- 避免无锁:(上锁并设置了过期时间后业务逻辑执行时间过长,锁已过期被删除,下一个服务上锁被之前执行完的服务释放,导致无锁)---释放锁的时候检查这把锁是否当前线程的(可以将锁的值设置为UUID)
问题:
- 早期版本没有超时多数,需要单独设置(expire命令),存在死锁问题(中途宕机)
- 后期版本提供加锁与没置时间原子操作(set(NX,time)),但是存在任务超时,锁自动释放,导致并发问题,加锁与释放锁不是同一线程问题 (可以set的时候将value存为当前线程唯一标识)
删除锁:判断线程唯一标志,再删除
可重入性及锁续期没有实现,通过redisson解决(类似AQS的实现,看门狗监听机制给key设置一个监听器,监听这个任务,判断是否执行完,没有执行完就把key的过期时间延长)
redlock:意思的机制都只操作单节点、即使Redis通过sentinel保证高可用, 如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况(redis同步 设置可能数据丢失)。redlock从多个节点申请锁, 当一半以上节点获取成功、锁才算获取成功,redission有相应的实现
zookeeper: zk通过临时节点,解决了死锁的问题,一 旦客户端获取到馈之后实然挂掉(session连接断开),那么这个临时节点就公自动删除掉(不会造成死锁了),其他客户端自动获取锁。临时颗序节点解决惊群效应。以节点作为锁,创建成功,表示获取锁成功 集群的话zk方式更可靠
- 上锁:创建节点lock
- 解锁:删除节点lock
- 避免死锁:节点为临时节点类型,客户端如果挂掉,会自动删除
- 羊群(惊群)效应: 一旦锁失效后假设有上百个客户端都争抢这把锁,这时候称为羊群效应,其实只让一个客户端抢到即可。用时序节点,节点有序号,按序号获取锁
五个角色:
- 注册中心registry:服务注册与发现
- 服务提供者provider:暴露服务
- 服务消费者consumer:调用远程服务
- 监控中心monitor:统计服务的调用次数和调用时间
- 容器container:服务允许容器 (启动的时候加载及运行provider)
调用流程:
- container容器负责启动、 加载、运行provider
- provider在启动时,向regisitry中心注册自 己提供的服务 (以接口的维度提供服务)
- consumer在启动时,向regisitry中心订阅自己所需的服务
- regisitry返回服务提供者列表给consumer,如果有变更,registry将基于长连接推送变更数据给consumer (regisitry与提供方维护的长连接主要是心跳机制,发现有服务长时间没有心跳则会删除,与消费者也是长连接维护,主要是发送服务端的列表,客户端调用服务端是基于负载均衡算法的)
- consumer调用provider服务,基于负载均衡算法进行调用
- consumer调用provider的统计, 基于短链接定时每分钟- -次统计到monitor
分层:
- 接口服务层( Service)应用层 :面向开发者,业务代码、接口、实现等
- 配置层( Config) :对外配置接口,以ServiceConfig和ReferenceConfig为中心,一般用zk来实现
- 服务代理层( Proxy) :对生产者和消费者、dubbo都会产生一个代理类封装调用细节, 业务层对远程调用无感
- 服务注册层( Registry) : 封装服务地址的注册和发现,以服务URL为中心 也可用zk
- 路由层( Cluster) :封装多个提供者的路由和负载均衡,并桥接注册中心
- 监控层( Monitor) : RPC 调用次数和调用时间监控
- 远程调用层( Protocal) :封装RPC调用
- 信息交换层( Exchange) :封装请求响应模式, 同步转异步
- 网络传输层( Transport) :抽象mina 和netty 为统-接口,统一网络传输接口
- 数据序列化层( Serialize) :数据传输的序列化和反序列化
Dubbo的底层网络通信:是基于Netty(主从线程组)网络架框架,而Netty是对NIO的封装,多路复用
SpringCloud 和 Dubbo对比- Dubbe, RPC的性能比HTTP的性能更好,并发能力更强,经过深度优化的RPC服务框架,性能和并发能力更好
- Spring Cloud走HTTP协议,性能相比Dubbo的RPC性能差一些;
- Spring Cloud主打的是微服务架构全家桶,组件齐全,功能齐全,Dubbo最初定位的是- -个RPC框架, 很多的组件需要自己再做整合
- 融合, Spring Cloud Alibaba,阿里技术将会融入Spring Cloud里面去,最终汇集
- 注册中心: Spring Cloud使用的eureka,dubbo推荐使用zookeeper
- 模型定义: dubbo 将一个接口定义为-个服务,SpringCloud 则是将一个应用定义为一个服务
SpringCloud是一个生态, 而Dubbo是 SpringCloud生态中关于服务调用一种解决方案(服务治理)
负载均衡算法、类型1.轮询法
将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器, 而不关心服务器实际的连接数和当前的系统负载。
2、随机法
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
3、源地址哈希法
源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同- IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一-台后端服务器进行访问。
4、加权轮询法
不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载, 加权轮询能很好地处理这一问题, 并将请求顺序且按照权重分配到后端。
5、加权随机法
与加权轮询法一样, 加权随机法也根据后端机器的配置,系统的负载分配不同的权車。不同的是,它是按照权重随 机请求后端服务器,而非顺序。
6.最小连接数法
最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务 器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。.
类型: DNS(域名解析)方式实现负载均衡 解析的时间损耗低,效率高
硬件负载均衡: F5 和A10
软件负载均衡:
Nginx、HAproxy、 LVS。 其中的区别:
- Nginx:七层负载均衡,支持HTTP. E-mail协议,同时也支持4负载均衡;
- HAproxy: 支持七层规则的,性能也很不错。OpenStack默认使用的负载均衡软件就是HAproxy;
七层(在应用层做负载均衡根据URL的规则进行路由到service层,要建立连接客户端和服务端都要维护一个长连接导致性能低一些)
- LVS:运行在内核态,性能是软件负载均衡中最高的,严格来说工作在三层(传输层做负载均衡,直接更改ip,效率也比较高),所以更通用一些,适用各种应用服务。
日标:完成用户的状态认证
单机版: session中, 服务器自动ioloiesesiondn--abced)
集群版或者分布式: 有状态的方式、无状态的方式: .
有状态 服务器依然要存储用户的信息,存储有效凭证 生成cookie(user. tokenuuid),从而来帮助我们确定存储在redis中的用户凭证信息。此处uuid等向于之前的sessionld.但是有状态的方式需要耗费一定的内存空间来存储用户信息服务端只需要给客户端一个凭证信息, 比如uuid即可
无状态 服务器不需要保存用户的状态信息 但是需要耗费一定的CPU资源来计算。 基于一种算法的方式JWT的方式token(用户本身的信息+时效信息) 依赖于解析的结果,如果发生异常。说明凭证已经时效,如果没有异常,说明状态正常
方式:
1.采用无状态服务,抛弃session(比如采用JWT json web token 生成凭证:基于算法生成令牌;验证凭证:基于算法核对令牌,令牌被篡改或过期都会抛异常)
2.存入cookie (cookie是存在客户端(浏览器 )的,有安全风险)
3、服务器之间进行Session同步,这样可以保证每个服务器上都有全部的Session信息,不过当服务器数量比较 多的时候,同步是会有延迟甚至同步失败而且占资源;
4、IP 绑定策略 使用Nginx (或其他复 杂均衡软硬件)中的IP绑定策略,同-个IP只能在指定的同-个机器访问,但是这样做失去了负载均衡的意义。当挂掉一台服务器的时候,会影响一批用户的使用,风险很大;
5、使用Redis存储(使用广泛) 把Session放到Redis中存储,虽然架构上变得复杂,并且需要多访问一次Redis ,但是这种方案带来的好处也是很大的:
- 实现了Session共享;
- 可以水平扩展(增加Redis服务器) ; .
- 服务器重启Session不失(不过也要注意Session在Redis中的刷新/失效机制) ;
- 不仅可以跨服务器Session共享,甚至可以跨平台(例如网页端和APP端)。



