本系列博客旨在搭建一套能用于实际开发使用的spring cloud多模块微服务项目框架,并不是一个spring cloud的demo而已,提供系统的开发规范限制,微服务注册中心,配置中心,负载均衡,熔断,redis缓存,分布式事务,kafka服务消息通信,系统安全(sql注入攻击,xxs攻击等等),多数据源切换,全局异常处理等等。
目录顺风车:
spring cloud多模块项目框架搭建 :https://blog.csdn.net/lingyancangqiong/article/details/109841353
本章接着上章《spring cloud多模块项目框架搭建-集成Redis连接客户端及连接池Lettuce》来说,搭建Redis-Cluster模式高可用集群示例,并在本系统中用Lettuce连接池连接。
redis主从集群分为一主一从,一主多从,级联三种模式,级联就是一主(master)多从(slave),从节点再有多个从节点,主节点可以有多个从节点,但从节点只能有一个主节点。主要实现读写分离,容灾恢复。一台服务器通过执行slaveof命令或设置slaveof选项,复制同步到另一个服务器,就形成了主从集群,被复制的叫做master主节点,复制的成为slave从节点。问题是当master节点挂了,整个集群就无法写入了,但还是能slave节点读取,这时需要手动将一台slave节点使用slaveof no one提升为master。
实现原理步骤:
上面说到主从模式当主节点挂了之后,需要人为的切换,这就无法达到高可用;于是哨兵模式(Sentinel)应运而生,可以抠字眼,当主节点挂了,由Sentinel像个哨兵一样自动的发现并根据配置规则提升一个slave节点为新的master节点,是redis主从的模式的高可用解决方案,一般的中小型系统中对内存存储数量不多的情况下使用较多,但是Sentinel本身也可能挂掉,实际使用中Sentinel也会集群。
实现原理步骤:
3.什么是Redis-Cluster模式?
高可用Redis(十二):Redis Cluster,什么是Redis-Cluster模式及实现原理?可以看看这位大神的这篇讲解,讲的非常到位。总结来讲,Redis-Cluster翻译就是redis集群嘛,实现了去中心化,不在依赖于主节点,而是将存储的key通过hash值运算平均分配已知的节点上,每个节点的权重是一样的,同时每个节点都至少有一个备用节点(主节点、备份节点由redis-cluster集群确定),当主节点挂了,备份节点之一自动补上,你在使用时也无需去寻找你的key存在哪个节点上,你只要查询集群其中一个节点,Redis-Cluster会自动去寻找在哪个节点并返回数据。Redis-Cluster模式主要用于分布式和对内存容量有要求的大型系统中,在我看来他还是主从,只是拆成了多个组,高可用实现的优雅了许多,可以理解为主从是普通版,哨兵为plus版,Cluster模式为pro版。
集群中至少应该有2n+1(n不为0)奇数个节点,所以至少有三个节点,每个节点至少有一个备份节点,所以下面使用6节点(主节点、备份节点由redis-cluster集群确定),由于没有6台服务器,我们示例就在一台服务器上开6个端口,作为集群的6个节点。为了方便日常开发,文末也提供了windows版本的集群下载。
1. 下载redis,并上传到服务器,编译安装,我下载的是最新的redis
[root@VM-0-15-centos redis-3.2.0]# tar xzf redis-6.0.9.tar.gz
[root@VM-0-15-centos redis-3.2.0]# cd redis-6.0.9
[root@VM-0-15-centos redis-3.2.0]# make
[root@VM-0-15-centos redis01]# make install PREFIX=/usr/andy/redis-cluster
make 这里可能报错:erver.c:4511:19: error: ‘struct redisServer’ has no member named ‘stat_total_writes_processed’,参考这位老兄的博客解决:https://blog.csdn.net/qq_45069833/article/details/108762535
2. 在/usr/andy/redis-cluster下 ,创建目录7000 7001 7002 7003 7004 7005共6个目录,复制解压目录下的redis.conf配置文件到/usr/andy/redis-cluster下,再复制到刚才创建的这六个文件下,并在最后追加如下内容,把原内容的protected-mode yes改为protected-mode no(在没有密码的情况下,关闭保护模式),把daemonize no改为daemonize yes (是否为进程守护,关闭ssh窗口后即是否在后台继续运行),注释掉bind 127.0.0.1 (取消绑定本地地址)或者改成bind 0.0.0.0:
daemonize yes #后台启动
port 7000 #修改端口号,从7000到7005
cluster-enabled yes #开启cluster,去掉注释
cluster-config-file nodes.conf #自动生成
cluster-node-timeout 15000 #节点通信时间
appendonly yes #持久化方式
这里中文注释可能报错,去掉即可,这里命令就是基本的vi,就不写了,不会的同学自行问度娘。
3.复制redis解压文件src下的redis-trib.rb文件到/usr/andy/redis-cluster目录并安装gem
[root@VM-0-15-centos redis-cluster]# gem install redis
这里可能报这两个错:1. bash: gem: command not found 和 2. ERROR: Could not find a valid gem 'redis' (>= 0) in any repository ,这是因为ruby 版本太低,遇到错,就要勇于解决,不要害怕,恐惧报错,这也是我近两年才学会,以前一报错就是求爹爹告奶奶,或者逃避去选择其他的实现方法,现在养成解决bug的习惯后,除了一些问题很耗时间外,其他一般的问题好好思考,勇于解决还是不难的。
解决办法:
[root@VM-0-15-centos redis-cluster]# yum install -y ruby
[root@VM-0-15-centos redis-cluster]# yum install -y rubygems
[root@VM-0-15-centos redis-cluster]# echo "source $HOME/.rvm/scripts/rvm" >> ~/.bash_profile
[root@VM-0-15-centos redis-cluster]#
[root@VM-0-15-centos redis-cluster]#
再次执行gem又报错:Error installing redis: redis requires Ruby version >= 2.3.0 ,解决方案:先安装rvm,再把ruby版本提升至2.6.5
[root@VM-0-15-centos redis-cluster]# yum install curl
[root@VM-0-15-centos redis-cluster]# curl -L get.rvm.io | bash -s stable
[root@VM-0-15-centos redis-cluster]#
[root@VM-0-15-centos redis-cluster]#
到这步有可能报错,第一次在腾讯云安然无事,这是我第二次在阿里云搭的时候报的:curl: (7) Failed connect to raw.githubusercontent.com:443; Connection refused ,原因是被墙了,解决如下:
修改hosts
[root@VM-0-15-centos redis-cluster]# vi /etc/hosts
末尾添加如下内容:
199.232.28.133 raw.githubusercontent.com
然后接着上面来再次执行
[root@VM-0-15-centos redis-cluster]# curl -L get.rvm.io | bash -s stable
这里注意看提示,它有可能像下面这样提示你用密钥下载
这里先执行下它提示的这个密钥命令,再把上面的curl命令执行下,就会发现执行成功了。
[root@VM-0-15-centos redis-cluster]# curl -L get.rvm.io | bash -s stable
[root@VM-0-15-centos redis-cluster]# source /usr/local/rvm/scripts/rvm
查看ruby版本,有很多版本
[root@VM-0-15-centos redis-cluster]# rvm list known
安装一个ruby版本,我安装的2.6.5,这里安装要很久,慢慢等着吧
[root@VM-0-15-centos redis-cluster]# rvm install 2.6.5
使用一个ruby版本
[root@VM-0-15-centos redis-cluster]# rvm use 2.6.5
设置默认版本
[root@VM-0-15-centos redis-cluster]# ruby --version
卸载一个已知版本
[root@VM-0-15-centos redis-cluster]# rvm remove 2.0.0
再次gem安装,ok了
[root@VM-0-15-centos redis-cluster]# gem install redis
Successfully installed redis-4.2.5
Parsing documentation for redis-4.2.5
Done installing documentation for redis after 0 seconds
1 gem installed
[root@VM-0-15-centos redis-cluster]#
4. 在/usr/andy/redis-cluster下创建并编写启动文件start.sh,内容如下:
cd 7000
redis-server redis.conf
cd ..
cd 7001
redis-server redis.conf
cd ..
cd 7002
redis-server redis.conf
cd ..
cd 7003
redis-server redis.conf
cd ..
cd 7004
redis-server redis.conf
cd ..
cd 7005
redis-server redis.conf
cd ..
5. 设置权限,并启动:chmod 777是设置所有人可以读 + 写 + 执行。
[root@VM-0-15-centos redis-cluster]# chmod 777 start.sh
[root@VM-0-15-centos redis-cluster]# sh start-all.sh
这里可能会报错,原因:这是因为在系统的/usr/bin目录下没有命令文件
解决办法:将redis-cluster/bin目录下的redis-server、redis-cli、redis-benchmark、redis-check-aof、redis-check-rdb、redis-sentinel这些可执行文件复制到/usr/bin下
[root@VM-0-15-centos redis-cluster]# ls
7000 7001 7002 7003 7004 7005 bin redis.conf redis-trib.rb start.sh
[root@VM-0-15-centos redis-cluster]# cd bin
[root@VM-0-15-centos bin]# ls
redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@VM-0-15-centos bin]# cp redis-benchmark /usr/bin
[root@VM-0-15-centos bin]# cp redis-check-aof /usr/bin/
[root@VM-0-15-centos bin]# cp redis-check-rdb /usr/bin/
[root@VM-0-15-centos bin]# cp redis-cli /usr/bin/
[root@VM-0-15-centos bin]# cp redis-sentinel /usr/bin/
[root@VM-0-15-centos bin]# cp redis-server /usr/bin/
[root@VM-0-15-centos bin]#
6. 再次执行sh脚本
[root@VM-0-15-centos bin]# cd ..
[root@VM-0-15-centos redis-cluster]# ls
7000 7001 7002 7003 7004 7005 bin redis.conf redis-trib.rb start.sh
[root@VM-0-15-centos redis-cluster]# sh start.sh
7. 查看redis启动的情况,可以看到redis的6个节点已经启动成功
[root@VM-0-15-centos redis-cluster]# ps -ef|grep cluster
root 54956 1 0 19:17 ? 00:00:00 redis-server *:7000 [cluster]
root 54961 1 0 19:17 ? 00:00:00 redis-server *:7001 [cluster]
root 54966 1 0 19:17 ? 00:00:00 redis-server *:7002 [cluster]
root 54971 1 0 19:17 ? 00:00:00 redis-server *:7003 [cluster]
root 54976 1 0 19:17 ? 00:00:00 redis-server *:7004 [cluster]
root 54981 1 0 19:17 ? 00:00:00 redis-server *:7005 [cluster]
root 55071 24089 0 19:24 pts/0 00:00:00 grep --color=auto cluster
8. 使用redis-cli命令创建集群,以前使用的./redis-trib.rb create已经废弃了,这里ip 127.0.0.1最好是改成你的服务器ip
[root@VM-0-15-centos redis-cluster]# redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7000
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 2730
Master[1] -> Slots 2731 - 5460
Master[2] -> Slots 5461 - 8191
Master[3] -> Slots 8192 - 10922
Master[4] -> Slots 10923 - 13652
Master[5] -> Slots 13653 - 16383
M: ff5bae64ce3aa0ee89f3d8813b155b45ff97788a 127.0.0.1:7001
slots:[0-2730] (2731 slots) master
M: 634c92d72d4a657102583f6ff81e46e9aaf8bc6f 127.0.0.1:7002
slots:[2731-5460] (2730 slots) master
M: 691743965bd6a300ffe84740077797cefc403f67 127.0.0.1:7003
slots:[5461-8191] (2731 slots) master
M: 0a099c363e9e9cadb83f1215658ee01b2645c7dc 127.0.0.1:7004
slots:[8192-10922] (2731 slots) master
M: ea521ddcdef20d0fc924d4e862035a7232456c75 127.0.0.1:7000
slots:[10923-13652] (2730 slots) master
M: 8386f6314894649b15463c72d44f2b05d752f798 127.0.0.1:7005
slots:[13653-16383] (2731 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: ff5bae64ce3aa0ee89f3d8813b155b45ff97788a 127.0.0.1:7001
slots:[0-2730] (2731 slots) master
M: 0a099c363e9e9cadb83f1215658ee01b2645c7dc 127.0.0.1:7004
slots:[8192-10922] (2731 slots) master
M: 8386f6314894649b15463c72d44f2b05d752f798 127.0.0.1:7000
slots:[13653-16383] (2731 slots) master
M: 634c92d72d4a657102583f6ff81e46e9aaf8bc6f 127.0.0.1:7002
slots:[2731-5460] (2730 slots) master
M: 691743965bd6a300ffe84740077797cefc403f67 127.0.0.1:7003
slots:[5461-8191] (2731 slots) master
M: ea521ddcdef20d0fc924d4e862035a7232456c75 127.0.0.1:7005
slots:[10923-13652] (2730 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
至此集群就搭建好了,测试下
[root@VM-0-15-centos redis-cluster]# redis-cli -c -p 7001
127.0.0.1:7001> set name 11
-> Redirected to slot [5798] located at 127.0.0.1:7003
OK
127.0.0.1:7003> get name
"11"
127.0.0.1:7003>
1. 我们还是在第八章《spring cloud多模块项目框架搭建-集成Redis连接客户端及连接池Lettuce》的代码基础上进行集成,将dream.order.web和dream.activity.web下的application.yml里的redis的配置改成如下即可.
redis:
database: 2
cluster:
refresh:
adaptive: true
period: 30000
nodes:
- 192.168.10.185:7001
- 192.168.10.185:7002
- 192.168.10.185:7003
- 192.168.10.185:7004
- 192.168.10.185:7005
- 192.168.10.185:7000
max-redirects: 3 # 获取失败 最大重定向次数
timeout: 6000ms # 连接超时时长(毫秒)
lettuce:
pool:
max-active: 100 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
timeout: 6000ms
#设置lettuce日志输出级别,输出的日志太多了
logging:
config: classpath:log4j2.xml
level:
io.lettuce.core: info
2. 将dream父模块的jar 包版本升级为如下,之前的低版本使用的Lettuce会出现连接池拓扑图不自动刷新,连接超时问题。
org.springframework.boot
spring-boot-starter-parent
2.3.7.RELEASE
1.8
UTF-8
2.3.7.RELEASE
2.3.7.RELEASE
2.3.7.RELEASE
2.2.5.RELEASE
8.0.19
3.4.0
1.18.16
1.2.3
2.3.5.RELEASE
2.0.0-alpha1
2.4.0
2.8.1
3. 将dream.order.common和dream.activity.common下的RedisConfig分别改成如下:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* redis配置
*
* @AutoConfigureAfter(RedisAutoConfiguration.class) 是让我们这个配置类在内置的配置类之后在配置,
*
* 这样就保证我们的配置类生效,并且不会被覆盖配置 选择性配置
* @Author: renkj
* @Version 1.0.0
*/
@Component
@Configuration
@EnableCaching
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 配置自定义redisTemplate
*
* @return
*/
@Bean
RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
4. 用第八章redis的测试代码测试下,完美的取到了值,第一次请求会有点慢,应该是进行初始化,第二次之后就很快了。
到此Redis-Cluster集群搭建及系统集成就完成了,为了方便在本地开发使用,我也搭建了一套windows版的Redis-Cluster集群,包括启动脚本都放在csdn资源(https://download.csdn.net/download/lingyancangqiong/13701164)了,下载下来按使用说明修改下启动脚本盘符路径,双击启动脚本就能使用了。本章更新的有些久了,原因是两周前我的电脑硬盘坏了,在jd买的才用了五个月,这两周一直在扯皮和维权,后面实在等不了,自己买了个固态换上。坏掉的固态客服连型号都不知道,拆下一看生产厂家,批次,日期,型号啥也没有,京东处理的也比较消极,到今日也没个初步处理结果,每次都是升级专员处理,京东再也不是当年那个绝无假货的京东了。
本章代码我放在了蓝奏云,可以下载下来对照对照: (https://wws.lanzous.com/b01hweach 密码:2vx3 )
上面所写内容如有不足和纰漏,欢迎留言或私聊指正批评。如果需要转载,也是欢迎,不甚荣幸,但请把《spring cloud多模块项目框架搭建》这一系列博客全部一起转载,这一系列博客毕竟是个整体教程,如果别人只看到一部分,那就是个残次品,谢谢,鞠躬。