Redis 主从模式安装配置开发Idea 实战

规划

Centos7

 

 

1、创建目录:

mkdir 638{5,6,7}

[root@machine138 redis-stack]# mkdir masterSlave
[root@machine138 redis-stack]# cd masterSlave/
[root@machine138 masterSlave]# vi redis.conf
[root@machine138 masterSlave]# rm -rf 638{5,6,7}
[root@machine138 masterSlave]# mkdir 638{5,6,7}

[root@machine138 masterSlave]# cd ..
[root@machine138 redis-stack]# echo 6385 6386 6387 |xargs -t -n 1 cp masterSlave/redis.conf 
cp masterSlave/redis.conf 6385 
cp masterSlave/redis.conf 6386 
cp masterSlave/redis.conf 6387 
[root@machine138 redis-stack]# 

redis.conf(这是最简单的主从模式的 redis.conf配置文件内容。)

port 6385
daemonize no
appendonly no
dir ./

protected-mode no
bind 0.0.0.0
tcp-backlog 511  #没有这个会报错:redis Non blocking connect for SYNC fired the event

 

批量修改配置文件内容

 
[root@machine138 redis-stack]# sed -i   -e 's/6385/6385/g' -e 's/dir .\//dir \/opt\/module\/redis-stack\/6385\//g' 6385/redis.conf
[root@machine138 redis-stack]# sed -i -e 's/6385/6386/g' -e 's/dir .\//dir \/opt\/module\/redis-stack\/6386\//g' 6386/redis.conf
[root@machine138 redis-stack]# sed -i -e 's/6385/6387/g' -e 's/dir .\//dir \/opt\/module\/redis-stack\/6387\//g' 6387/redis.conf
[root@machine138 redis-stack]# cat 6387/redis.conf
port 6387 daemonize no appendonly no dir /opt/module/redis-stack/6387/ [root@machine138 redis-stack]# cat 6386/redis.conf port 6386 daemonize no appendonly no dir /opt/module/redis-stack/6386/ [root@machine138 redis-stack]#

在每个配置文件追加声明IP配置

 

sed -i '1a replica-announce-ip 192.168.1.138' 6385/redis.conf
sed -i '1a replica-announce-ip 192.168.1.138' 6386/redis.conf
sed -i '1a replica-announce-ip 192.168.1.138' 6387/redis.conf
或者整合上面三个命令
printf '%s\n' 6385 6386 6387 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.1.138' {}/redis.conf

 

xargs命令插曲

[root@machine138 redis-stack]# xargs ls #回车
6386#输入要ls 的目录 回车
6385.conf  redis.conf #回车 就可以看到目录的内容了
[root@machine138 redis-stack]# 


-p 确认要执行的命令

使用xargs命令之后,由于存在着参数转换的过程,所以要对执行的命令进行确认。

-p 参数就是用来对要执行的命令进行确认的

$ echo "dir1 dir2 dir3" | xargs -p mkdir
执行之后会在终端显示出要执行的命令,然后是让用户确认,输入 y (大小写均可)才会继续执行命令。
-t 显示要执行的命令

-t 选项是用来显示要执行的命令的,和-p选项不同的是,它不需要用户进行确认

$ echo "dir1 dir2 dir3" | xargs -t mkdir
会直接显示

mkdir dir1 dir2 dir3
$ find . -name "*.txt" | xargs grep "abc"
上面命令找出所有 TXT 文件以后,对每个文件搜索一次是否包含字符串abc。
[root@machine138 redis-stack]# find -name *.conf
./6379/6379.conf
./6379/nodes-6379.conf
./6380/6380.conf
./6380/nodes-6380.conf
./etc/redis-stack-service.conf
./etc/redis-stack.conf
./6385/6385.conf
./6385/redis.conf
./6386/6385.conf
./6386/redis.conf
./6387/6385.conf
./6387/redis.conf
./masterSlave/redis.conf
[root@machine138 redis-stack]#  xargs -n 1 find -name
*.conf
./6379/6379.conf
./6379/nodes-6379.conf
./6380/6380.conf
./6380/nodes-6380.conf
./etc/redis-stack-service.conf
./etc/redis-stack.conf
./6385/6385.conf
./6385/redis.conf
./6386/6385.conf
./6386/redis.conf
./6387/6385.conf
./6387/redis.conf
./masterSlave/redis.conf

 

-n参数指定每次将多少项,作为命令行参数。

$ xargs -n 1 find -name
上面命令指定将每一项(-n 1)标准输入作为命令行参数,分别执行一次命令(find -name)。

 

 

-I 选项

如果xargs要将命令行参数传给多个命令,可以使用-I参数。

-I 指定每一项命令行参数的替代字符串。

$ cat jiyik.txt jiyik_one jiyik_two jiyik_three
$ cat jiyik.txt | xargs -I file sh -c 'echo file; mkdir file' jiyik_one jiyik_two jiyik_three
查看当前目录下已经生成了三个目录

$ ls jiyik_one  jiyik_three  jiyik_two
上面代码中,jiyik.txt是一个包含三行的文本文件。我们希望对每一项命令行参数,执行两个命令(echo和mkdir),使用-I file表示file是命令行参数的替代字符串。执行命令时,具体的参数会替代掉echo file; mkdir file里面的两个file。

 

 

--max-procs 参数

xargs默认只用一个进程执行命令。如果命令要执行多次,必须等上一次执行完,才能执行下一次。

--max-procs参数指定同时用多少个进程并行执行命令。--max-procs 2表示同时最多使用两个进程,--max-procs 0表示不限制进程数。

$ docker ps -q | xargs -n 1 --max-procs 0 docker kill

 

例如;

printf '%s\n' 6385 6386 6387 | xargs -I{} -t sed -i '1a protected-mode no' {}/redis.conf  #第一行append  protected-mode no
printf '%s\n' 6385 6386 6387 | xargs -I{} -t sed -i '1a bind 0.0.0.0' {}/redis.conf #第一行append  bind 0.0.0.0
printf '%s\n' 6385 6386 6387 | xargs -I{} -t sed -i -e 's/master_sync_in_progress 1/tcp-backlog 511/g' {}/redis.conf  #替换 master_sync_in_progress 1 为 tcp-backlog 511

 

批量操作xshell

在打开的窗口 右键鼠标 复制会话

 

 

 

需要几个复制几个

然后 进行窗口排布

 

 

 可以对每个窗口 右键 重命名

 

 

 

 

 

 

进行同步控制

 

 

 

如果相对某个窗口进行单独控制,只要把窗口右上角的 on-->OFF

 

 

 

 

配置主从(master ,salve)

两种方式:

  1、修改配置文件 方式

    slaveof <masterIP> <masterPORT>

  2、命令行方式:

    用redis-cli 链接redis server ,在客户端中执行 slaveof <masterIP> <masterPORT> 把当前连接的redis server 设置为 slave

  现在把 6385 作为master ,让 6386 6387 作为 6385 的slave 

 

 

 

  同样的操作完成 6387 slaveof 6385  

 

 

 注意:命令 :replicaof <masteriIP> <masterPORT> 与 slaveof 相同

登录master 查看信息

[root@machine138 redis-stack]# bin/redis-cli -p 6385
127.0.0.1:6385> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.138,port=6386,state=online,offset=882,lag=1
slave1:ip=192.168.1.138,port=6387,state=online,offset=882,lag=1
master_failover_state:no-failover
master_replid:8fe2fe374bfb887c4dd2a54b4b4a06fdb2fc30e9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:882
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:882
127.0.0.1:6385> 

 

登陆到slave 看 info relication

127.0.0.1:6386> info replication
# Replication
role:slave
master_host:192.168.1.138
master_port:6385
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:1429
slave_repl_offset:1429
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8fe2fe374bfb887c4dd2a54b4b4a06fdb2fc30e9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1429
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1429
127.0.0.1:6386> 

6387

[root@machine138 redis-stack]# bin/redis-cli -p 6387
127.0.0.1:6387> info replication
# Replication
role:slave
master_host:192.168.1.138
master_port:6385
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:1611
slave_repl_offset:1611
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8fe2fe374bfb887c4dd2a54b4b4a06fdb2fc30e9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1611
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:519
repl_backlog_histlen:1093
127.0.0.1:6387> 

 

 

 

测试:

主从

1、读写分离,

2、数据同步

 

127.0.0.1:6385> set number 123
OK
127.0.0.1:6385> 
[root@machine138 redis-stack]# bin/redis-cli -p 6386
127.0.0.1:6386> ping 
PONG
127.0.0.1:6386> get number
"123"
127.0.0.1:6386> set name lihui
(error) READONLY You can't write against a read only replica.
127.0.0.1:6386> 

 

主从数据同步机制

 

 

 

 

 

 

 

 

主从模式优化:

1、无写磁盘同步

# With slow disks and fast (large bandwidth) networks, diskless replication
# works better.
repl-diskless-sync yes

 

2、尽快修复故障的slave

3、如果slave节点过多可以使用 master-->slave1-->slave2  模式,替代 master-->slave1,master-->slave2

 

 

 

在idea中利用第三方可以完成对 master slave 进行读 写操作  redisson

 

POM.XML 

    <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.19.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

 

创建@Bean

    @Bean
    public Redisson redisson()
    {

        String masterIP="";
        masterIP="192.168.1.138:6385";
        Set<String> slavelist=new HashSet<>();
        slavelist.add("redis://192.168.1.138:6386");
        slavelist.add("redis://192.168.1.138:6387");


        Config config=new Config();
        //此为单机 模式
        //config.useSingleServer().setAddress("redis://"+masterIP).setDatabase(0).setPassword("123456");
        config.useMasterSlaveServers().setDatabase(0).setMasterAddress("redis://"+masterIP).setSlaveAddresses(slavelist);
        return (Redisson) Redisson.create(config);
    }

 yml

注意:这里的 host,和 port 要与 上面 的Bean 中master 一样,共同指向 master redis      !!!!

  redis:
#    sentinel:
#      master: mymaster
#      nodes:
#        - 192.168.1.138:27001
#        - 192.168.1.138:27002
#        - 192.168.1.138:27003
    host: 192.168.1.138 # 192.168.1.136
    port: 6385 #6479
    #password: 123456
    lettuce:
      pool:
        max-active: 10
        max-idle: 10
        min-idle: 1
        time-between-eviction-runs: 10s

 

 

controller:

@RestController
@RequestMapping("/stock")
public class RedisLockTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private Redisson redisson;

    @RequestMapping("init")
    public Result addStock(@RequestParam("count") String count) {

        String tipmsg = "";
        String stockkey = "stock:101";

        synchronized (this) { //synchronized 单线程锁 不能解决分布式并发问题
            if (StrUtil.isBlank(count))
                count = "1000";
            stringRedisTemplate.opsForValue().set(stockkey, count);
            String redisStock = stringRedisTemplate.opsForValue().get(stockkey);
            return Result.ok("库存初始化成功:" + redisStock);
        }
    }


    @RequestMapping("/substock")
    public Result substock() {
        String tipmsg = "";
        String stockkey = "stock:101";
        String stockLockkey = "stockLock:101";
        String clientThreadID = UUID.randomUUID().toString().replace("-", "");//用于标识当前进程,redis 锁删除时判断是当前进程的锁


        //synchronized (this){  //synchronized 单线程锁 不能解决分布式并发问题
        // ctrl+alt +L 格式化代码
        //利用 redisson 工具 创建一个锁对象
        RLock redissonLock = redisson.getLock(stockLockkey);
        redissonLock.lock();//默认30s 每隔10秒检测一下主线程没有完成则续命锁 tryLockInnerAsync  lua scripts
        try {

            //redis 分布式锁实现
            //设置有效期
          /*  boolean isredisLock = stringRedisTemplate.opsForValue().setIfAbsent(stockLockkey,"stockLockValue", 10, TimeUnit.SECONDS);//redis setnx(key,value)

            if (!isredisLock) {
                return Result.fail(stockLockkey + "获取失败。");

            }*/


            String stock = stringRedisTemplate.opsForValue().get(stockkey);
            System.out.println(stockkey + "执行了。");
            if (stock == "null" || StrUtil.isBlank(stock))//isEmpty() 只是对字符串长度为 0 进行判断,没有对null 进行判断,所以isEmpty()可能 有空指针的情况
            {
                tipmsg = "当前库存记录不存在。key:" + stockkey;
                //return Result.fail(tipmsg);
            } else {

                int stockInt = Integer.parseInt(stock);//redis.get(stockkey)
                if (stockInt > 0) {
                    int realStock = stockInt - 1;
                    //redis 字符 串操作 所以key value都必须是string
                    stringRedisTemplate.opsForValue().set(stockkey, StrUtil.toString(realStock));//jedis.set(key,value)
                    tipmsg = "库存扣减成功,剩余库存:" + realStock;
                    //return Result.ok(tipmsg);
                } else {

                    tipmsg = "库存不足:" + stockkey;
                    //return Result.fail(tipmsg);
                }
            }

            return Result.ok(tipmsg);

        } finally {
            //try catch 解决异常导致的死锁问题
         /*   if(clientThreadID.equals(stringRedisTemplate.opsForValue().get(stockLockkey))) {//判断是否为自已的锁
                //删除redis 分步式锁
                //TODO 但是此时如果锁的有效期 失效了。。此时再另有一个进程重新进行了加锁,那么此时删除的锁已经是别的锁了,因不自已的锁已经到期自动释放了
                //TODO 这个问题可以通过 锁续命来解决 就是在主线程之外增加一个分线程,只要主线程不结束 就自动完成锁有效期的延长 redisson已经封装好了
                stringRedisTemplate.delete(stockLockkey);
            }*/
            redissonLock.unlock();//解锁
        }


    }
}

Idea日志

                        3.4.3 
2023-03-01 16:41:27.860  INFO 53820 --- [           main] org.redisson.Version                     : Redisson 3.19.3
2023-03-01 16:41:28.321  INFO 53820 --- [isson-netty-4-6] o.r.c.pool.MasterPubSubConnectionPool    : 1 connections initialized for 192.168.1.138/192.168.1.138:6387
2023-03-01 16:41:28.358  INFO 53820 --- [sson-netty-4-19] o.r.c.pool.MasterConnectionPool          : 24 connections initialized for 192.168.1.138/192.168.1.138:6387
2023-03-01 16:41:28.364  INFO 53820 --- [isson-netty-4-2] o.r.c.pool.PubSubConnectionPool          : 1 connections initialized for 192.168.1.138/192.168.1.138:6386
2023-03-01 16:41:28.364  INFO 53820 --- [isson-netty-4-3] o.r.c.pool.PubSubConnectionPool          : 1 connections initialized for 192.168.1.138/192.168.1.138:6385
2023-03-01 16:41:28.394  INFO 53820 --- [sson-netty-4-22] o.r.connection.pool.SlaveConnectionPool  : 24 connections initialized for 192.168.1.138/192.168.1.138:6386
2023-03-01 16:41:28.401  INFO 53820 --- [sson-netty-4-25] o.r.connection.pool.SlaveConnectionPool  : 24 connections initialized for 192.168.1.138/192.168.1.138:6385
2023-03-01 16:41:29.177  INFO 53820 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2023-03-01 16:41:29.186  INFO 53820 --- [           main] com.hmdp.HmDianPingApplication           : Started HmDianPingApplication in 5.764 seconds (JVM running for 6.94)

 

 运行效果

 

 

 

全网最简单的主从配置

port 6386
tcp-backlog 511
bind 0.0.0.0
protected-mode no
replica-announce-ip 192.168.1.138
daemonize no
appendonly no
dir /opt/module/redis-stack/6386/

 

 

 

结束

 

posted on 2023-03-01 15:01  hztech  阅读(191)  评论(0编辑  收藏  举报

导航