Bootstrap

SpringBoot整合SSS框架+sessionId已一致性

分布式主要内容

第⼀部分:⼀致性Hash算法
第⼆部分:集群时钟同步问题
第三部分:分布式ID解决⽅案
数据表A(ID),A的数据量很⼤的情况下,我们会进⾏分表操作,A(ID)表拆分成了A1表
(ID)+A2表(ID),需要⼀种在分布式集群架构中能够产⽣全局唯⼀ID的⽅案
第四部分:分布式调度问题(定时任务的分布式)
第五部分:Session共享(⼀致性)问题
浏览器—>Nginx—>Tomcat1(Session中记录⽤户信息)
—>Tomcat2
—>Tomcat3

分布式⼀定是集群,但是集群不⼀定是分布式


1、一致性HASH算法

1、hash介绍

​ 查询速度快,如果设计OK的话,时间复杂度接近于O(1) ->(一次拿到,比如:“直接寻址法“,缺点,浪费空间)。

​ 涉及扩展:做顺序查找法,二分查找,除留余数法(容易冲突),开放寻址法(了解)

开放寻址法:1放进去了,6再来的时候,向前或者向后找空闲位置存放,不好的地⽅,如果数组⻓度定
义好了⽐如10,⻓度不能扩展,来了11个数据,不管Hash冲突不冲突,肯定存不下这么多数据

**拉链法:**数据⻓度定义好了,怎么存储更多内容呢,算好Hash值,在数组元素存储位置放了⼀个链表

Hash表的查询效率⾼不⾼取决于Hash算法,

除留余数法 3%5
线性构造Hash算法
直接寻址法也是⼀种构造Hash的⽅式,只不过更简单,表达式:H(key)=key
⽐如H(key)=a*key + b(a,b是常量)
hashcode其实也是通过⼀个Hash算法得来的
2、场景
1、请求的负载均衡:比如 nginx的ip_hash

​ nginx的ip_hash可以在客户端IP不冲突的情况下,将请求始终路由到同一个目标服务,实现会话粘带,避免处理 session共享问题

​ 如果没有ip_hash策略,维护会话粘带,则需要维护一张表,,存储客户端IP或者sessionid与具体⽬标服务器的 映射关系<ip,tomcat1>

​ 缺点:1、表可能会很大,2、目标的上下限,导致映射表重新维护,维护成本大。

​ 解决:使用hash算法,以对ip地址或者sessionid进⾏计算哈希值, 哈希值与服务器数量进⾏取模运算,得到 的值就是当前请求应该被路由到的服务器编号,

2、分布式存储

​ 以分布式内存数据库Redis为例,集群中有redis1,redis2,redis3 三台Redis服务器。那么,在进⾏数据存储时,数 据存储到哪个服务器当中呢?针对key进⾏hash处理。hash(key1)%3=index, 使⽤余数index锁定存储的具体服 务器节点

3、普通HASH算法存在的问题

​ 普通Hash算法存在⼀个问题,以ip_hash为例,假定下载⽤户ip固定没有发⽣改变,现在tomcat3出现
​ 了问题,down机了,服务器数量由3个变为了2个,之前所有的求模都需要重新计算。缩容和扩容都会存在这样 的问题,

4、一致性算法

​ hash环。我们把服务器的ip或者主机名求hash值然后对应到hash环上

​ 假如将服务器3下线,服务器3下线后,原来路由到3的客户端重新路由到服务器4,对于其他客户端没有影响。 只是这⼀⼩部分受影响(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免了大量请求 迁移 )

​ 但是,⼀致性哈希算法在服务节点太少时,容易因为节点分部不均匀⽽造成数据倾斜问题。例如系统中
​ 只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端
​ 请求落在了节点1上,这就是数据(请求)倾斜问题

在这里插入图片描述

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tTk2Lstf-1602604472444)(C:\Users\wangqingli\Desktop\hash一致性虚拟节点.jpg)]

解决方案:

​ 为了解决这种数据倾斜问题,⼀致性哈希算法引⼊了虚拟节点机制,即对每⼀个服务节点计算多个哈希,每个 计算结果位置都放置⼀个此服务节点,称为虚拟节点。

具体做法可以在服务器ip或主机名的后⾯增加编号来实现。⽐如,可以为每台服务器计算三个虚拟节
点,于是可以分别计算 “节点1的ip#1”、“节点1的ip#2”、“节点1的ip#3”、“节点2的ip#1”、“节点2的
ip#2”、“节点2的ip#3”的哈希值,于是形成六个虚拟节点,当客户端被路由到虚拟节点的时候其实是被
路由到该虚拟节点所对应的真实节点

​ nginx 去ip前三段 计算hash(计算网段的)

​ 考虑权重 等等

普通hash

/**
* 普通Hash算法实现
*/
public class GeneralHash {
   
  public static void main(String[] args) {
   
 // 定义客户端IP
     String[] clients = new String[]{
   "10.78.12.3","113.25.63.1","126.12.3.8"};
     // 定义服务器数量
     int serverCount = 5;// (编号对应0,1,2)
     // hash(ip)%node_counts=index
     //根据index锁定应该路由到的tomcat服务器
     for(String client: clients) {
   
     	int hash = Math.abs(client.hashCode());
    	 int index = hash%serverCount;
     	System.out.println("客户端:" + client + " 被路由到服务器编号为:" + index);
     }
  }
}

一致性:不含虚拟节点

import java.util.SortedMap;
import java.util.TreeMap;
public class ConsistentHashNoVirtual {
   
 public static void main(String[] args) {
   
     //step1 初始化:把服务器节点IP的哈希值对应到哈希环上
     // 定义服务器ip
     String[] tomcatServers = new String[]
    		{
   "123.111.0.0","123.101.3.1","111.20.35.2","123.98.26.3"};
     	SortedMap<Integer,String> hashServerMap = new TreeMap<>();
    	for(String tomcatServer: tomcatServers) {
   
     // 求出每⼀个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
     	int serverHash = Math.abs(tomcatServer.hashCode());
     // 存储hash值与ip的对应关系
    	hashServerMap.put(serverHash,tomcatServer);
     }
     //step2 针对客户端IP求出hash值
     // 定义客户端IP
     String[] clients = new String[]
   				 {
   "10.78.12.3","113.25.63.1","126.12.3.8"};
     for(String client : clients) {
   
    	 int clientHash = Math.abs(client.hashCode());
     //step3 针对客户端,找到能够处理当前客户端请求的服务器(哈希环上顺时针最近)
     // 根据客户端ip的哈希值去找出哪⼀个服务器节点能够处理()
    	 SortedMap<Integer, String> integerStringSortedMap =
         hashServerMap.tailMap(clientHash);
       if(integerStringSortedMap.isEmpty()) {
   
     		// 取哈希环上的顺时针第⼀台服务器
     		 Integer firstKey = hashServerMap.firstKey();
    		 System.out.println("==========>>>>客户端:" + client + " 被
    		//路由到服务器:" + hashServerMap.get(firstKey));
    	 }else{
   
   	 		 Integer firstKey = integerStringSortedMap.firstKey<
;