序言:
文章内容输出来源:拉勾教育Java高薪训练营。
本篇文章是学习课程中的一部分课后笔记
一、一致性Hash问题及解决方案
1、Hash算法应⽤场景
-
请求的负载均衡(⽐如nginx的ip_hash策略)
1) Nginx的IP_hash策略可以在客户端ip不变的情况下,将其发出的请求始终路由到同⼀个⽬标服务器上,实现会话粘滞,避免处理session共享问题。
2) 如果没有IP_hash策略,那么如何实现会话粘滞?
可以维护⼀张映射表,存储客户端IP或者sessionid与具体⽬标服务器的映射关系 <ip,tomcat1>- 缺点
1)在客户端很多的情况下,映射表⾮常⼤,浪费内存空间
2)客户端上下线,⽬标服务器上下线,都会导致重新维护映射表,映射表维护成本很⼤。
如果使⽤哈希算法,事情就简单很多,我们可以对 ip地址或者sessionid进⾏计算哈希值,哈希值与服务器数量进⾏取模运算 ,得到的值就是当前请求应该被路由到的服务器编号,如此,同⼀个客户端ip发送过来的请求就可以路由到同⼀个⽬标服务器,实现会话粘滞。
- 缺点
分布式存储
以分布式内存数据库Redis为例,集群中有redis1,redis2,redis3 三台Redis服务器那么,在进⾏数据存储时,<key1,value1>数据存储到哪个服务器当中呢?
针对key进⾏hash处理hash(key1)%3=index, 使⽤余数index锁定存储的具体服务器节点
2、普通Hash算法存在的问题
以ip_hash为例,假定下载⽤户ip固定没有发⽣改变,现在tomcat3出现了问题,down机了,服务器数量由3个变为了2个,之前所有的求模都需要重新计算。
如果在真实⽣产情况下,后台服务器很多台,客户端也有很多,那么影响是很⼤的,缩容和扩容都会存在这样的问题,⼤量⽤户的请求会被路由到其他的⽬标服务器处理,⽤户在原来服务器中的会话都会丢失。
3、⼀致性Hash算法
⾸先有⼀条直线,直线开头和结尾分别定为1和2的32次⽅(int最大值)减1,这相当于⼀个地址,对于这样⼀条线,弯过来构成⼀个圆环形成闭环,这样的⼀个圆环称为hash环。
我们把服务器的ip或者主机名求hash值然后对应到hash环上,那么针对客户端ip进⾏hash求值,也对应到环上某个位置;
然后按照顺时针⽅向找最近的服务器节点
- 缩容
假如服务器3下线后,原来路由到3的客户端重新路由到服务器4,对于其他客户端没有影响只是这⼀⼩部分受影响
(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免了⼤量请求迁移 )
- 扩容
增加服务器5之后,原来路由到3的部分客户端路由到新增服务器5上,对于其他客户端没有影响只是这⼀⼩部分受影响
-
数据(请求)倾斜问题
1)⼀致性哈希算法在服务节点太少时,容易因为节点分部不均匀⽽造成数据倾斜问题。
例如系统中只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端请求落在了节点1上。2)为了解决数据倾斜问题,⼀致性哈希算法引⼊了虚拟节机制,即对每⼀个服务节点计算多个哈希,每个计算结果位置都放置⼀个此服务节点,称为虚拟节点。
二、集群时钟同步配置
1、时钟不同步导致的问题
时钟此处指服务器时间,如果集群中各个服务器时钟不⼀致势必导致⼀系列问题。
电商并发下订单情况:
2、集群时钟同步配置
-
1) 分布式集群中各个服务器节点都可以连接互联⽹
操作⽅式:
windows有计划任务
Linux也有定时任务,crond
可以使⽤linux的定时任务,每隔10分钟执⾏⼀次ntpdate命令
ntpdate -u ntp.api.bz
使⽤ ntpdate ⽹络时间同步命令
从⼀个时间服务器同步时间
-
2) 分布式集群中某⼀个服务器节点可以访问互联⽹或者所有节点都不能够访问互联⽹
操作⽅式:
- 选取集群中的⼀个服务器节点A(172.17.0.17)作为时间服务器(整个集群时间从这台服务器同步,如果这台服务器能够访问互联⽹,可以让这台服务器和⽹络时间保持同步,如果不能就⼿动设置⼀个时间)
- ⾸先设置好A的时间
- 把A配置为时间服务器(修改/etc/ntp.conf⽂件)
1、如果有 restrict default ignore,注释掉它 2、添加如下⼏⾏内容 restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap # 放开局域⽹同步功能,172.17.0.0是你的局域⽹⽹段 server 127.127.1.0 # local clock fudge 127.127.1.0 stratum 10 3、重启⽣效并配置ntpd服务开机⾃启动 service ntpd restart chkconfig ntpd on
- 集群中其他节点就可以从A服务器同步时间了
ntpdate 172.17.0.17
- 选取集群中的⼀个服务器节点A(172.17.0.17)作为时间服务器(整个集群时间从这台服务器同步,如果这台服务器能够访问互联⽹,可以让这台服务器和⽹络时间保持同步,如果不能就⼿动设置⼀个时间)
三、分布式ID解决⽅案
-
分布式集群环境下的全局唯⼀ID
分布式全局唯一id方式:
1、UUID
UUID 是指Universally Unique Identifier,翻译为中⽂是通⽤唯⼀识别码,产⽣重复 UUID 并造成错误的情况⾮常低,是故⼤可不必考虑此问题。
Java中得到⼀个UUID,可以使⽤java.util包提供的⽅法
public class MyTest {
public static void main(String[] args) {
System.out.println(java.util.UUID.randomUUID().toString());
}
}
2、独⽴数据库的⾃增ID
单独的创建⼀个Mysql数据库,创建⼀张表,这张表的ID设置为⾃增,其他地⽅需要全局唯⼀ID的时候,就模拟向这个Mysql数据库的这张表中模拟插⼊⼀条记录,此时ID会⾃增,然后通过Mysql的select last_insert_id()获取到刚刚这张表中⾃增⽣成的ID。
insert into DISTRIBUTE_ID(createtime) values(NOW());
select LAST_INSERT_ID();
3、SnowFlake 雪花算法
雪花算法是Twitter推出的⼀个⽤于⽣成分布式ID的策略。
⽣成的ID是⼀个long型,那么在Java中⼀个long型是8个字节,算下来是64bit。
4、Redis的Incr命令获取全局唯⼀ID
Redis Incr 命令将 key 中储存的数字值增⼀。
如果 key 不存在,那么 key 的值会先被初始化为 0,然后再执⾏ INCR 操作。
四、分布式调度问题
调度—>定时任务。Quartz
分布式调度—>在分布式集群环境下定时任务这件事。
Elastic-job(当当⽹开源的分布式调度框架)
1、什么是分布式调度
- 1)运⾏在分布式集群环境下的调度任务(同⼀个定时任务程序部署多份,只应该有⼀个定时任务在执⾏)
- 2)分布式调度—>定时任务的分布式—>定时任务的拆分(即为把⼀个⼤的作业任务拆分为多个⼩的作业任务,同时执⾏)
2、定时任务与消息队列的区别
1 )共同点
- 异步处理
- ⽐如注册、下单事件
- 应⽤解耦
- 流量削峰
双⼗⼀的时候,任务作业和MQ都可以⽤来扛流量,后端系统根据服务能⼒定时处理订单或者从MQ抓取订单抓取到⼀个订单到来事件的话触发处理,对于前端⽤户来说看到的结果是已经
下单成功了,下单是不受任何影响的。
2)本质不同
定时任务作业是时间驱动,⽽MQ是事件驱动;
时间驱动是不可代替的,⽐如⾦融系统每⽇的利息结算,不是说利息来⼀条(利息到来事件)就算⼀下,⽽往往是通过定时任务批量计算;
所以,定时任务作业更倾向于批处理,MQ倾向于逐条处理。
3、分布式调度框架Elastic-Job
Elastic-Job-Lite 轻量级⽆中⼼化解决⽅案,使⽤Jar包的形式提供分布式任务的协调服务。
Elastic-Job-Cloud ⼦项⽬需要结合Mesos以及Docker在云环境下使⽤。
1、主要功能
分布式调度协调
在分布式环境中,任务能够按指定的调度策略执⾏,并且能够避免同⼀任务多实例重复执⾏丰富的调度策略
基于成熟的定时任务作业框架Quartz cron表达式执⾏定时任务弹性扩容缩容
当集群中增加某⼀个实例,它应当也能够被选举并执⾏任务;当集群减少⼀个实例时,它所执⾏的任务能被转移到别的实例来执⾏。失效转移
某实例在任务执⾏失败后,会被转移到其他实例执⾏错过执⾏作业重触发
若因某种原因导致作业错过执⾏,⾃动记录错过执⾏的作业,并在上次作业完成后⾃动触发。⽀持并⾏调度
⽀持任务分⽚,任务分⽚是指将⼀个任务分为多个⼩任务项在多个实例同时执⾏。作业分⽚⼀致性
当任务被分⽚后,保证同⼀分⽚在分布式环境中仅⼀个执⾏实例。
2、 Elastic-Job-Lite轻量级去中⼼化的特点
3、任务分⽚
⼀个⼤的⾮常耗时的作业Job,
⽐如:⼀次要处理⼀亿的数据,那这⼀亿的数据存储在数据库中,如果⽤⼀个作业节点处理⼀亿数据要很久,在互联⽹领域是不太能接受的,互联⽹领域更希望机器的增加去横向扩展处理能⼒。
所以,ElasticJob 可以把作业分为多个的task(每⼀个task就是⼀个任务分⽚),每⼀个task交给具体的⼀个机器实例去处理(⼀个机器实例是可以处理多个task的)
分片部分代码:
4、任务分⽚扩容
新增加⼀个运⾏实例app3,它会⾃动注册到注册中⼼,注册中⼼发现新的服务上线,注册中⼼会通知ElasticJob 进⾏重新分⽚。
注意:
1)分⽚项也是⼀个JOB配置,修改配置,重新分⽚,在下⼀次定时运⾏之前会重新调⽤分⽚算法,那么这个分⽚算法的结果就是:哪台机器运⾏哪⼀个⼀⽚,这个结果存储到zk中的,主节点会把分⽚给分好放到注册中⼼去,然后执⾏节点从注册中⼼获取信息(执⾏节点在定时任务开启的时候获取相应的分⽚)。
2)如果所有的节点挂掉值剩下⼀个节点,所有分⽚都会指向剩下的⼀个节点,这也是ElasticJob的⾼可⽤。
五 、Session共享问题
- Session共享及Session保持或者叫做Session⼀致性
1、问题
Http协议是⽆状态的协议,客户端和服务端在某次会话中产
⽣的数据不会被保留下来,所以第⼆次请求服务端⽆法认识到你曾经来过。
2、解决Session⼀致性的⽅案
1) Nginx的 IP_Hash 策略
同⼀个客户端IP的请求都会被路由到同⼀个⽬标服务器,也叫做会话粘滞
- 优点:
配置简单,不⼊侵应⽤,不需要额外修改代码 - 缺点:
服务器重启Session丢失
存在单点负载⾼的⻛险
单点故障问题
2)Session复制
-
多个tomcat之间通过修改配置⽂件,达到Session之间的复制
- 优点:
不⼊侵应⽤
便于服务器⽔平扩展
能适应各种负载均衡策略
服务器重启或者宕机不会造成Session丢失 - 缺点:
性能低
内存消耗
不能存储太多数据,否则数据越多越影响性能
延迟性
- 优点:
3) redis共享
- 优点:
能适应各种负载均衡策略
服务器重启或者宕机不会造成Session丢失
扩展能⼒强
适合⼤集群数量使⽤ - 缺点:
对应⽤有⼊侵,引⼊了和Redis的交互代码
Spring Session使得基于Redis的Session共享应⽤起来⾮常之简单
引入jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>