百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

基于 haproxy 的健康检查实现 Redis 主从架构下多读实例的负载均衡

mhr18 2024-12-05 14:38 14 浏览 0 评论

概述

在 redis 高可用主从(Master-Replica)架构下, 通过 sentinel 实现 master 故障的自动切换, redis client 如何将写操作正确分发到 master 节点? 如何将读操作正确分发到多个 replica 节点以实现读负载均衡? 现提供如下基于 haproxy 自定义健康检查(tcp-check)的代理实现方案:

  • haproxy 将写命令发送到 master 节点,将读命令根据权重发送到 master 或 replica 节点实现读负载均衡
  • haproxy 自动下线异常的只读节点,待节点恢复后再重新启用
  • haproxy 端口 16379 负责写, 端口 26379 负责读负载均衡

环境规划

主机名 Ip 地址 节点类型 c2 192.168.31.12 haproxy c7 192.168.31.17 redis-server 6381 (Master), redis-sentinel(26379) c8 192.168.31.18 redis-server 6381 (Replica), redis-sentinel(26379) c9 192.168.31.19 redis-server 6381 (Replica), redis-sentinel(26379)

本方案主要涉及的技术细节:

  • Redis 主从环境
  • Redis Sentinel 哨兵集群
  • Haproxy 自定义健康检查(tcp-check)

编译安装

1、下载解压、编译安装 redis

wget http://download.redis.io/releases/redis-5.0.9.tar.gz
tar zxvf redis-5.0.9.tar.gz
# 默认 redis 相关二进制执行文件安装到 /usr/bin 目录内
cd redis-5.0.9.tar.gz && make && make install
# 创建 redis 数据及配置文件目录
mkdir -p /opt/redis/{6379,6381} && /etc/redis/{6379,6381}
cp redis.conf /etc/redis/{6379,6381}
# 创建 redis 运行用户及设置权限
useradd -r redis  && chown redis.redis -R /opt/redis/6381

2、Redis Server Systemd 启动脚本

cat > /usr/lib/systemd/system/redis.service << EOF
[Unit]
Description=Redis persistent key-value database
After=network.target

[Service]
LimitNOFILE=infinity
LimitNOFILE=102400
LimitNOFILE=102400
WorkingDirectory=/opt/redis/6381
ExecStart=/usr/bin/redis-server /etc/redis/6381/redis.conf --supervised systemd
ExecStop=/usr/bin/redis-cli shutdown
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

Redis 主从

1、Master 节点 Redis Server 配置

# cat /etc/redis/6381/redis.conf
# general config
daemonize yes
protected-mode no
port 6381
bind 0.0.0.0
databases 16
loglevel notice
pidfile "/opt/redis/6381/redis-server.pid"
logfile "/opt/redis/6381/redis-server.log"
timeout 0
maxmemory 128mb
tcp-keepalive 0
tcp-backlog 511
lua-time-limit 5000
slowlog-log-slower-than 1000
slowlog-max-len 128
requirepass "pass123"

# rdb 持久化 config
dir "/opt/redis/6381"
dbfilename "dump.rdb"
save 900 1
save 300 10
save 60 1000
rdbcompression yes
rdbchecksum yes
stop-writes-on-bgsave-error yes

# aof 持久化 config
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
aof-use-rdb-preamble yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-rewrite-incremental-fsync yes

# replication config
replica-priority 100
masterauth "pass123"
# replicaof 192.168.31.18 6381
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
repl-timeout 60
repl-backlog-ttl 600
repl-backlog-size 10mb

2、Replica(Slave) 节点 Redis Server 配置, 基于 Master 配置添加或修改如下配置项

replica-priority 80
# 指定副本连接 master 时的验证密码
masterauth "pass123"
# 指定该副本的 master ip 及 port
replicaof 192.168.31.17 6381

3、确认 Redis 主从环境运行正常

redis-cli -p 6381 -h 192.168.31.17
192.168.31.17:6381> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.31.18,port=6381,state=online,offset=39771,lag=1
slave1:ip=192.168.31.19,port=6381,state=online,offset=39771,lag=1
master_replid:b090f53b06d56ba3145709f6dde72b7e820c3935
master_replid2:f39d8c822c424308c27e2a47599ed2cb685004c4
master_repl_offset:39905
second_repl_offset:26123
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1230
repl_backlog_histlen:38676

Redis 哨兵

1、Redis Sentinel 所有节点配置

# cat /etc/redis/6379/redis.conf
port 26379
protected-mode no
dir /opt/redis/26379
logfile sentinel_26379.log
# 是否为守护进程
daemonize yes
# 至少 2 个 sentinel 节点检测到 master 失效, 才确认 master 客观下线; m1 标识一个主从复制组
# 192.168.31.17 6381 为 redis master 节点 Ip 及 Port
sentinel monitor m1 192.168.31.17 6381 2
# 指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败
sentinel failover-timeout m1 180000
# 若 redis master 在 15s 内没有回应 PONG 或者是回复了一个错误消息, 则当前 sentinel 任务该 master 挂了(SDOWN)
sentinel down-after-milliseconds m1 15000
# 指定最多可以有多少个 replica 同时对新的 master 进行同步
sentinel parallel-syncs m1 1
# sentinel 连接 redis 时的密码验证
sentinel auth-pass m1 pass123

# 发生切换之后执行的一个自定义脚本
# sentinel notification-script <master-name> <script-path>
# sentinel client-reconfig-script <master-name> <script-path>

2、设置哨兵启动脚本

# cat /usr/lib/systemd/system/redis-sentinel.service
[Unit]
Description=Redis persistent key-value database
After=network.target

[Service]
LimitNOFILE=infinity
LimitNOFILE=102400
LimitNOFILE=102400
WorkingDirectory=/opt/redis/6379
ExecStart=/usr/bin/redis-sentinel /etc/redis/6379/redis.conf --supervised systemd
ExecStop=/usr/bin/redis-cli -p 26379 shutdown
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

3、确定 Redis Sentinel 运行是否正常

# linux shell 启动 sentinel 节点
systemctl start redis-sentinel
redis-cli -p 26379
127.0.0.1:26379> info sentinel
# 输出如下信息, 标识 master 地址 192.168.31.17, 有 2 个副本, 共有三个 sentinel 节点
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=m1,status=ok,address=192.168.31.17:6381,slaves=2,sentinels=3

Redis 读写分离

1、haproxy 配置文件 haproxy_redis.cfg

global
    daemon
    maxconn 30000

defaults
    mode tcp
    log global
    option tcplog
    option dontlognull
    maxconn 30000
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

# HAProxy stats page
listen stats
    bind *:8888
    mode http
    stats enable
    # stats uri /status   # 默认路径 /haproxy?stats
    stats auth admin:admin

# redis 读负载均衡
listen redis-read
    bind *:26379
    mode tcp
    balance roundrobin
    option tcp-check
    # redis 密码认证
    tcp-check send AUTH\ pass123\r\n
    tcp-check expect string +OK
    # 读操作仅分发到 slave
    tcp-check send info\ replication\r\n
    tcp-check expect string role:slave
    # 读操作分发到 slave 和 master
    #tcp-check send PING\r\n
    #tcp-check expect string +PONG
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis-1 192.168.31.17:6381 maxconn 5000 check inter 2s
    server redis-2 192.168.31.18:6381 maxconn 5000 check inter 2s
    server redis-3 192.168.31.19:6381 maxconn 5000 check inter 2s

# redis 写流量到 redis master
frontend redis-write
    bind *:16379
    mode tcp
    option tcplog
    # 通过自己(redis-server)和 2 个 sentinel 确认本身是否为 master 节点
    use_backend redis-master-1 if { srv_is_up(redis-master-1/redis) } { nbsrv(mastercheck-redis-1) ge 2 }
    use_backend redis-master-2 if { srv_is_up(redis-master-2/redis) } { nbsrv(mastercheck-redis-2) ge 2 }
    use_backend redis-master-3 if { srv_is_up(redis-master-3/redis) } { nbsrv(mastercheck-redis-3) ge 2 }
    # 如果未找到有效的 master 时的默认后端选择
    default_backend redis-legacy

# 询问 redis-1(192.168.31.17:6381) 是否认为自己是 Master
backend redis-master-1
    mode tcp
    balance first
    option tcp-check
    #comment following 2 lines if your redis server doesn't requirepass
    tcp-check send AUTH\ pass123\r\n
    tcp-check expect string +OK
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis 192.168.31.17:6381 maxconn 5000 check inter 2s

backend redis-master-2
    mode tcp
    balance first
    option tcp-check
    tcp-check send AUTH\ pass123\r\n
    tcp-check expect string +OK
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis 192.168.31.18:6381 maxconn 5000 check inter 2s

backend redis-master-3
    mode tcp
    balance first
    option tcp-check
    tcp-check send AUTH\ pass123\r\n
    tcp-check expect string +OK
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis 192.168.31.19:6381 maxconn 5000 check inter 2s

# 询问三个 Sentinel节点当前 redis-1是否为 Master
backend mastercheck-redis-1
    mode tcp
    option tcp-check
    tcp-check send SENTINEL\ master\ m1\r\n
    tcp-check expect string 192.168.31.17
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server sentinel-1 192.168.31.17:26379 check inter 2s
    server sentinel-2 192.168.31.18:26379 check inter 2s
    server sentinel-3 192.168.31.19:26379 check inter 2s

backend mastercheck-redis-2
    mode tcp
    option tcp-check
    tcp-check send SENTINEL\ master\ m1\r\n
    tcp-check expect string 192.168.31.18
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server sentinel-1 192.168.31.17:26379 check inter 2s
    server sentinel-2 192.168.31.18:26379 check inter 2s
    server sentinel-3 192.168.31.19:26379 check inter 2s

backend mastercheck-redis-3
    mode tcp
    option tcp-check
    tcp-check send SENTINEL\ master\ m1\r\n
    tcp-check expect string 192.168.31.19
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server sentinel-1 192.168.31.17:26379 check inter 2s
    server sentinel-2 192.168.31.18:26379 check inter 2s
    server sentinel-3 192.168.31.19:26379 check inter 2s

# 通过 redis server 本身确定自己是否为 master 节点; 脑裂情况下存在多个 master 节点 该方式会导致数据混乱!!
backend redis-legacy
    mode tcp
    balance first
    option tcp-check
    tcp-check send AUTH\ pass123\r\n
    tcp-check expect string +OK
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis-1 192.168.31.17:6381 maxconn 5000 check inter 2s
    server redis-2 192.168.31.18:6381 maxconn 5000 check inter 2s
    server redis-3 192.168.31.19:6381 maxconn 5000 check inter 2s

测试验证

# 1、验证读写分离,可通过 redis server 的进程 id 可判断连接到哪个 redis 后端
redis-cli -h 192.168.31.12 -p 26379 -a pass123 info | grep process_id

# 2、通过 haproxy 状态页查看后端 redis 状态
http://192.168.31.12:8888/haproxy?stats

# 3、读写分离
haproxy 端口 16379 负责写(对应后端 master 节点), 端口 26379 负责读负载均衡(对应后端 replica 节点)

相关推荐

一文读懂Prometheus架构监控(prometheus监控哪些指标)

介绍Prometheus是一个系统监控和警报工具包。它是用Go编写的,由Soundcloud构建,并于2016年作为继Kubernetes之后的第二个托管项目加入云原生计算基金会(C...

Spring Boot 3.x 新特性详解:从基础到高级实战

1.SpringBoot3.x简介与核心特性1.1SpringBoot3.x新特性概览SpringBoot3.x是建立在SpringFramework6.0基础上的重大版...

「技术分享」猪八戒基于Quartz分布式调度平台实践

点击原文:【技术分享】猪八戒基于Quartz分布式调度平台实践点击关注“八戒技术团队”,阅读更多技术干货1.背景介绍1.1业务场景调度任务是我们日常开发中非常经典的一个场景,我们时常会需要用到一些不...

14. 常用框架与工具(使用的框架)

本章深入解析Go生态中的核心开发框架与工具链,结合性能调优与工程化实践,提供高效开发方案。14.1Web框架(Gin,Echo)14.1.1Gin高性能实践//中间件链优化router:=...

SpringBoot整合MyBatis-Plus:从入门到精通

一、MyBatis-Plus基础介绍1.1MyBatis-Plus核心概念MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提...

Seata源码—5.全局事务的创建与返回处理

大纲1.Seata开启分布式事务的流程总结2.Seata生成全局事务ID的雪花算法源码3.生成xid以及对全局事务会话进行持久化的源码4.全局事务会话数据持久化的实现源码5.SeataServer创...

Java开发200+个学习知识路线-史上最全(框架篇)

1.Spring框架深入SpringIOC容器:BeanFactory与ApplicationContextBean生命周期:实例化、属性填充、初始化、销毁依赖注入方式:构造器注入、Setter注...

OpenResty 入门指南:从基础到动态路由实战

一、引言1.1OpenResty简介OpenResty是一款基于Nginx的高性能Web平台,通过集成Lua脚本和丰富的模块,将Nginx从静态反向代理转变为可动态编程的应用平台...

你还在为 Spring Boot3 分布式锁实现发愁?一文教你轻松搞定!

作为互联网大厂后端开发人员,在项目开发过程中,你有没有遇到过这样的问题:多个服务实例同时访问共享资源,导致数据不一致、业务逻辑混乱?没错,这就是分布式环境下常见的并发问题,而分布式锁就是解决这类问题的...

近2万字详解JAVA NIO2文件操作,过瘾

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。从classpath中读取过文件的人,都知道需要写一些读取流的方法,很是繁琐。最近使用IDEA在打出.这个符号的时候,一行代...

学习MVC之租房网站(十二)-缓存和静态页面

在上一篇<学习MVC之租房网站(十一)-定时任务和云存储>学习了Quartz的使用、发邮件,并将通过UEditor上传的图片保存到云存储。在项目的最后,再学习优化网站性能的一些技术:缓存和...

Linux系统下运行c++程序(linux怎么运行c++文件)

引言为什么要在Linux下写程序?需要更多关于Linux下c++开发的资料请后台私信【架构】获取分享资料包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdf...

2022正确的java学习顺序(文末送java福利)

对于刚学习java的人来说,可能最大的问题是不知道学习方向,每天学了什么第二天就忘了,而课堂的讲解也是很片面的。今天我结合我的学习路线为大家讲解下最基础的学习路线,真心希望能帮到迷茫的小伙伴。(有很多...

一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)

前言15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易。但是要...

多商户商城系统开发全流程解析(多商户商城源码免费下载)

在数字化商业浪潮中,多商户商城系统成为众多企业拓展电商业务的关键选择。这类系统允许众多商家在同一平台销售商品,不仅丰富了商品种类,还为消费者带来更多样的购物体验。不过,开发一个多商户商城系统是个复杂的...

取消回复欢迎 发表评论: