Redis主从高可用缓存

nopCommerce 3.9 大波浪系列 之 使用Redis主从高可用缓存

 

一.概述

    nop支持Redis作为缓存,Redis出众的性能在企业中得到了广泛的应用。Redis支持主从复制,HA,集群。

    一般来说,只有一台Redis是不可行的,原因如下:

  1. 单台Redis服务器会发生单点故障,并且单服务器需要处理所有的请求会导致压力较大。
  2. 单台Redis服务器内存容量有限,不易扩展。

    第一个问题可以通过Redis主从模式实现单节点的高可用(HA)。

  • 从节点(slave)是主节点(master)副本,当主节点(master)宕机后,Redis 哨兵(Sentinel)会自动将从节点(slave)提升为主节点。
  • 由于一个master可以有多个slave,这样就可以实现读写分离,master负责写,slave负责读取。
  • slave同步master数据分为完整同步和部分重同步,首次启动或slave长时间离线后重启后会完整同步,当slave短时间离线会执行部分重同步,除非slave出现异常会执行完整同步。所以建议单台redis不要存储太大的数据,数据太大同步时间也会很长。

    第二个问题可以通过Redis-cluster集群解决。(本篇不介绍)

    本篇介绍的是第一个问题的解决方案,即主从模式及哨兵。

二.前期准备

测试环境

    使用Docker部署Redis环境。由于本机操作系统windows 10 专业版,所以使用 docker for windows(下载

  • windows 10 (部署 Web服务器)
  • docker for windows (部署 Redis)

架构

  • 1台 web服务器,部署nopCommerce
  • 1台 Master 主服务器
  • 1台 Slave 从服务器
  • 3台 Sentinel 哨兵服务器,用于检测master状态,负责主备切换。  

    架构图如下:

image_thumb10

软件版本

  • nopCommerce 3.9
  • redis 4.0

三.Docker for Windows  搭建Docker环境

    Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署。这里就不多介绍了,总之很方便。

    由于大波浪操作系统是Win 10 所以使用Docker for Windows (下载

    Docker for Windows 安装需要满足以下条件

  • 64位Windows 10 Pro、Enterprise或者Education版本(Build 10586以上版本)
  • 系统启用Hyper-V。如果没有启用,Docker for Windows在安装过程中会自动启用Hyper-V(这个过程需要重启系统)
  • 如果不是使用的Windows 10,也没有关系,可以使用Docker Toolbox作为替代方案。

    安装成功后任务栏会出现image_thumb11小鲸鱼的图标,打开Hyper-V 管理器发现多出一个虚拟机,Docker就是在这个虚拟机中。

image_thumb13[1]

     右键点击小鲸鱼,Settings菜单用于配置Docker,Kitematic菜单用于打开Kitematic(一款可视化Docker交互容器)。

image_thumb14

     Settings配置

image_thumb16

     首先设置共享目录(Shared Drivers),为Docker容器与宿主机实现目录共享,Docker中叫做volume。

image_thumb18

    然后设置Docker Hub 镜像站,我用的是阿里云。如何配置参考我的另一篇博客

image_thumb20

     打开PowerShell输入docker version 出现下图信息恭喜你安装成功。

image_thumb23

    如果你安装了kitematic,打开后出现下图。

image_thumb26

  Docker的三个基本概念
  • 镜像(Image)

Docker 镜像(Image)就是一个只读的模板。
例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Redis 或用户需要的其它应用程序。
镜像可以用来创建 Docker 容器。
Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以 直接从其他人那里下载一个已经做好的镜像来直接使用。

  • 容器 (Container)

Docker 利用容器(Container)来运行应用,比如 本篇Redis主从,哨兵都是部署在不同的容器中。
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是 相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的 linux 环境(包括root用户权限、进程空间、用户 空间和网络空间等)和运行在其中的应用程序。
*注:镜像是只读的,容器在启动的时候创建一层可写层作为最上层。

  • 仓库(Repository)

仓库(Repository)是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务 器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着 多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。
国内的公开仓库包括 时速云 、网易云 等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库(参考本文“私有仓库”部分)。
当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓 库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来 就可以了。
*注:Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服 务。

四.Redis主从及哨兵配置

    Redis slave 从服务器配置

    只需要在从服务器的redis.conf文件中找到

    # slaveof <masterip> <masterport>,   masterip是主服务器ip,masterport为主服务器端口。

    试验中将slave的配置文件修改成 修改成  slaveof redis-master 6379

    Redis sentinel 哨兵服务器配置

复制代码
  1 #常规配置:
  2 port 26379
  3 daemonize yes
  4 logfile "/var/log/redis/sentinel.log"
  5 
  6 sentinel monitor mymaster redis-master 6379 2       #配置master名、ip、port、需要多少个sentinel才能判断[客观下线]
  7 sentinel down-after-milliseconds mymaster 30000     #配置sentinel向master发出ping,最大响应时间、超过则认为主观下线
  8 sentinel parallel-syncs mymaster  1                 #配置在进行故障转移时,运行多少个slave进行数据备份同步(越少速度越快)
  9 sentinel failover-timeout mymaster  180000          #配置当出现failover时下一个sentinel与上一个sentinel对[同一个master监测的时间间隔](最后设置为客观下线)
复制代码

五.Docker 中部署 Redis

    Docker可以通过run命令创建容器,可以通过Dockerfile文件创建自定义的image(镜像),也可以通过docker-compose通过模板快速部署多个容器。由于我们需要构建5个docker容器我们使用docker-compose方式。

    使用docker-compose需要一个docker-compose.yml(基于YUML语法)配置文件。首先先看下我们的目录结构。

image_thumb27

  • redis 为根目录
    • docker-compose.yml 部署配置文件
    • sentinel  文件夹用于放置 sentinel镜像相关文件
      • Dockerfile  sentinel镜像文件
      • sentinel-entrypoint.sh    sentinel容器启动时执行的命令
      • sentinel.conf    redis-sentinel的配置文件,用于配置哨兵服务。

dos2unix.exe  用于将windows 脚本 转换成 unix 下的脚本。主要转换sentinel-entrypoint.sh文件用的。

    首先看下docker-compose.yml 文件

复制代码
  1 master:
  2   image: redis:4
  3   ports:
  4      - 7010:6379
  5 slave:
  6   image: redis:4
  7   command: redis-server --slaveof redis-master 6379
  8   ports:
  9      - 7011:6379
 10   links:
 11     - master:redis-master
 12 sentinel:
 13   build: sentinel
 14   environment:
 15     - SENTINEL_QUORUM =2
 16     - SENTINEL_DOWN_AFTER=5000
 17     - SENTINEL_FAILOVER=5000
 18   links:
 19     - master:redis-master
 20     - slave
复制代码

image_thumb29

  1. 服务名称这里配置了 master,slave ,sentinel三个服务(后续 docker-compose scale 命令可以用到)。
  2. image  指定为镜像名称或镜像 ID,这里使用  redis 4.0版本
  3. ports  设置端口映射 7010:6379 代表外部7010端口映射到容器内部6379端口
  4. command 覆盖容器启动后默认执行的命令,这里是当slave 容器启动时关联主服务器 redis-master
  5. links  链接到其它服务中的容器。使用服务名称(同时作为别名)或服务名称:服务别名 (SERVICE:ALIAS) 格式都可以。这里关联master服务并且使用别名redis-master。
  6. build  指定 Dockerfile 所在文件夹的路径。 Compose 将会利用它自动构建这个镜像,然后使用这个镜像。这里指定当前目录下的sentinel文件夹
  7. environment  设置环境变量。你可以使用数组或字典两种格式。只给定名称的变量会自动获取它在 Compose 主机上的值,可以用来防止泄露不必要的数据。这里环境变量用于配置sentinel.conf的参数。

docker-compose.yml 文件定义了三个服务,master 为主服务,slave为从服务,sentinel为哨兵服务,都是基于redis 4.0镜像,

master:redis 主服务,对外公开的端口为7010 如果你在外部使用 Redis Dasktop这种工具通过7010端口就可以连接了。

slave:redis 从服务,对外公开端口为7011.同时通过links链接到 master容器实现容器间的通信。

sentinel:哨兵服务,使用Dockfile方式构建镜像,同时链接 master 和 slave容器。

    接下来我们看下sentinel文件夹下的Dockefile文件,该文件用于构建哨兵镜像

 DockerFile

image_thumb33

  1. FROM 定义基于redis 4.0 镜像
  2. MAINTAINER  镜像创建者
  3. EXPOSE  容器内部服务开启的端口,哨兵服务默认端口是 26379.
  4. ADD  复制本地sentinel.conf 哨兵配置文件 拷贝到 容器的 /etc/redis/sentinel.conf
  5. RUN 容器中运行命令 chown redis:redis /etc/redis/sentinel.conf
  6. ENV  创建的时候给容器中加上个需要的环境变量。指定一个值,为后续的RUN指令服务。
  7. COPY 复制本地sentinel-entrypoint.sh文件到容器中/usr/local/bin/目录下
  8. RUN  为7中复制的脚本文件赋予执行权限。
  9. ENTRYPOINT  配置容器启动后执行的命令,并且不可被docker run 提供的参数覆盖,本例中启动后执行 7中复制的脚本。

    sentinel.conf文件为哨兵文件。

 sentinel.conf

    sentinel-entrypoint.sh 脚本通过配置的ENV环境变量替换sentinel.conf中的参数,最后通过exec 命令启动哨兵服务。

复制代码
  1 #!/bin/sh
  2 
  3 sed -i "s/$SENTINEL_QUORUM/$SENTINEL_QUORUM/g" /etc/redis/sentinel.conf
  4 sed -i "s/$SENTINEL_DOWN_AFTER/$SENTINEL_DOWN_AFTER/g" /etc/redis/sentinel.conf
  5 sed -i "s/$SENTINEL_FAILOVER/$SENTINEL_FAILOVER/g" /etc/redis/sentinel.conf
  6 
  7 exec docker-entrypoint.sh redis-server /etc/redis/sentinel.conf --sentinel
复制代码

    通过以上配置完整的docker-compose.yml 模板就制作完成了,别急还差最后一步。

    PowerShell 中进入放置docker-compose.yml的目录redis,输入build命令 重建镜像。

   docker-compose build

image_thumb35

    最后输入up命令生成容器。

   docker-compose up -d

image_thumb37

    打开kitematic 或者 输入 docker ps –a 看到我们生成的容器了吧。

image_thumb39

image_thumb43

     点击redis_master_1查看主服务状态,对外的接口也是7010.

image_thumb48

image_thumb45

    我们通过Redis Manager 就可以链接了。

image_thumb51

   我们再看下我们的redis_slave_1从服务器。我们发现不仅对外暴露了7011端口,同时容器内部也关联了主服务。

image_thumb54

    接下来我们测试下主从是否成功。链接主服务器,输入 set master ok 命令插入一条记录。

image_thumb57

    从服务器 输入 get master 获取值为ok,主从复制成功。

image_thumb60

主从配置成功了,我们发现只创建了一个redis_sentinel_1 哨兵容器,我们至少需要3台才能完成切换。

    我们通过 docker-compose scale sentinel=3 命令创建 3个sentinel容器。

image_thumb63

   添加成功后我们发现3个哨兵服务器,并且哨兵容器也记录了其他的哨兵信息。

image_thumb66

    我们在从服务器中输入info Replication 命令查看到 role:slave 并且master主机ip为 172.17.0.2(docker内部ip)

image_thumb70

   现在模拟master主服务器宕机,选中redis_master_1点击STOP按钮,日志中我们看到 在08:53:15时刻关闭主服务器

image_thumb76

    点击哨兵服务器,我们发现08:53:20时发现master服务器宕机,间隔是5秒,为什么是5秒?因为哨兵服务器配置中我们设置的5秒钟,通过另外两台哨兵服务器投票超过了2票最终确认master服务器宕机。然后把slave主机上升为master主机,这里需要注意如果刚才宕机的服务器又正常启动了,这时候该服务器会变为slave服务器,并不会恢复原来的master身份。

image_thumb79

image_thumb82

     我们在salve中输入info Replication命令,查看下该服务是不是上升为master服务了.

     role:master,同时connected_saves:0 说明有0个从服务器。

image_thumb87

   接下来我们把刚才停掉的容器恢复,我们发现connected_saves:1说明宕机的主机恢复后会降为slave服务器。

image_thumb90

  好了,这样我们就完成了主从复制,和哨兵的配置了,是不是使用docker配置环境很简单。

如果部署中遇到问题,又修改了,记得先用docker-compose build 命令 再使用docker-compose up命令

六.nopCommerce 使用Redis作为缓存服务

     Redis环境已经搭建好了,接下来我们修改nopCommerce中的代码来使用Redis.

  • nop使用StackExchange.Redis开源项目作为Redis 客户端   
  • 使用RedLock为 Redis 分布式锁

     首先修改Web.config文件 将 RedisCaching 节点 Enabled设置为True。

     ConnectionString设置我们上边的master(localhost:7010)和slave(localhost:7011)两台服务器。

<RedisCaching Enabled="true" ConnectionString="localhost:7010,localhost:7011" />

image_thumb92

    在Nop.Core项目Caching下拷贝RedisCacheManager.cs 和 RedisConnectionWrapper.cs,

并重命名为RedisMSCacheManager.cs,RedisMSConnectionWrapper.cs。

image_thumb1

   RedisMSCacheManager类中修改RemoveByPattern 和 Clear 方法,添加  if (server.IsConnected == false || server.IsSlave == true) continue;代码判断服务器是否连接是否是从服务。

image_thumb3

 RedisMSCacheManager 完整代码

    RedisMSConnectionWrapper中修改GetConnection()方法

image_thumb5

   修改PerformActionWithLock方法 我们将RedLock实现的分布式锁改成StackExchange.Redis的LockTake来实现。

image_thumb7

 RedisMSConnectionWrapper 完整代码

最后在Nop.Web.Framework项目DependencyRegistrar中修改依赖注入项。

if (config.RedisCachingEnabled)
             {
               //  builder.RegisterType<RedisConnectionWrapper>().As<IRedisConnectionWrapper>().SingleInstance();
                 builder.RegisterType<RedisMSConnectionWrapper>().As<IRedisConnectionWrapper>().SingleInstance();
                 builder.RegisterType<RedisMSCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").InstancePerLifetimeScope();
             }

image_thumb9

   重新编译项目再启动就可以了。

   为了方便测试在Nop.Core.Tests项目添加测试类RedisMSCacheManagerTests.cs,同时添加App.config配置文件

image_thumb13

 RedisMSCacheManagerTests 完整代码
 App.config 完整代码

   运行Set_Cache_Test(),并在过程中关闭docker的主容器,然后再恢复容器。

image_thumb15

     下图为测试中结果,当关闭redis 主服务器时出现异常,然后下一秒就恢复了,证明主备切换正常。

image_thumb17

好了就先到这里吧。

本文地址:http://www.cnblogs.com/yaoshangjin/p/7456378.html 

本文为大波浪原创、转载请注明出处。

原文地址:https://www.cnblogs.com/Leo_wl/p/7456908.html