Redis+Twemproxy+HAProxy集群

Redis主从模式
Redis数据库与传统数据库属于并行关系,也就是说传统的关系型数据库保存的是结构化数据,而Redis保存的是一些所谓的 临时 数据,因为Redis具备一项很强的功能 持久化数据 ,发现Redis好像也可以做一些传统数据库的开发。但是现在Redis除了可以进行数据的存储之外,实际上也可以在一些系统的架构设计之中作为数据的缓冲点: 
如果要想实现主从模式的配置,首先一定要准备出三台Redis实例,本次为了方便将在一台主机上进行模拟,也就是说在这一台主机上将准备出三个Redis实例,分别对应的端口为:6379、6380、6381,其中6379运行的Redis服务为主服务,而其它两个端口运行的服务为从服务。

1.如果要想进行主从的模式匹配,主服务器上不需要做出任何的变化,也就是说主服务器根本就不关心是否有从服务器;

2.所有的从服务的配置文件redis-6380.conf、redis-6381.conf必须要求明确的设置出它对应的主服务器。

编辑redis-6380.conf配置文件:vim /usr/local/redis/conf/redis-6380.conf; 
配置主服务器的IP地址:slaveof 192.168.125.161 6379 
设置主服务器的密码:masterauth mldnjava
随后redis-6381.conf配置文件采用与之一样的方式完成处理;
3.启动所有的Redis数据服务:

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6379.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6380.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6381.conf

4.登录6379端口的Redis服务主服务查看所有的副本信息:

/usr/local/redis/bin/redis-cli -h 192.168.125.161 -p 6379 -a mldnjava info replication
connected_slaves:2 
slave0:ip=192.168.125.161,port=6380,state=online,offset=99,lag=0 slave1:ip=192.168.125.161,port=6381,state=online,offset=99,lag=0
如果可以发现以上的信息就表示现在6379下有两个从节点。

5.操作主节点6379的数据:set mldn java

随后退出此客户端,随意登录6380或6381的主机:get mldn; 
这个时候一定是通过主节点进行数据的设置,而后自动同步到所有的从节点上,这样的好处是可以进行数据的备份处理,如果你现在直接在从节点上操作,则会出现如下错误提示: (error) READONLY You can't write against a read only slave. 。
备注: 
主从设计的最大好处在于:可以自动对数据做备份; 主从设计模式的最大缺点在于:只能够做备份,而出现灾难之后无法立即恢复。

Redis哨兵机制
哨兵机制原理
只要是进行高可用的架构部署,那么就必须保证多节点,在Redis里面使用了主从模式可以实现多节点配置,但是传统的主从模式的设计有一个缺陷:一旦Master主机出现了问题之后,两台Slave主机将无法提供正常的工作支持,例如:slave主机为只读主机,而且如果要想继续提供支持,那么你至少应该通过剩余的几台slave里面去推选出一个新的master,并且最为重要的是,这个新的master还必须能够被用户的程序找到。 
 
哨兵机制(Sentinel) 
Redis哨兵是redis官方推荐的redis高可用(HA)解决方案之一;

sentinel的功能: 
监控(Monitoring),sentinel时刻监控着redis master-slave 是否正常运行; 
通知(Notification),sentinel 可以通过api来通知管理员,被监控的 redis master-slave 出现了问题; 
自动故障转移(Automatic failover),当redis master出现故障不可用状态,sentinel 会开始一次故障转移,将其中一个slave 提升为新的 master ,将其他的 slave 将重新配置使用新的 master 同步,并使用redis 的服务器应用程序在连接时受到使用新的地址连接; 
配置提供者(Configuration provider),sentinel 作为在集群中的权威来源,客户端连接到 sentinel 来获取某个服务的当前 redis 主服务器的地址和其他信息。当前故障转移发生时,sentinel 会报告新地址。

哨兵机制实现
1.如果要想进行哨兵配置及使用,请确保你的主机上已经准备好了Redis服务,本次将在一台主机上模拟哨兵机制,现在的实现原则:一台主机运行三个哨兵,并且该哨兵运行端口不同,但是这三个哨兵都要去监控同一个master的地址。

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6379.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6380.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6381.conf
2.通过redis源代码拷贝出哨兵运行程序: 
cp /usr/local/src/redis-3.2.9/src/redis-sentinel /usr/local/redis/bin/

3.所有的哨兵如果要想运行一定要准备出一个配置文件:sentinel.conf;

对于此配置文件已经给出了一个参考模版:/usr/local/src/redis-3.2.9/sentinel.conf;
4.建立sentinel-26379.conf配置文件:vim /usr/local/redis/conf/sentinel-26379.conf;

建立一个哨兵的配置文件目录:mkdir -p /usr/data/redis/{sentinel-26379,sentinel-26380,sentinel-26381};
配置哨兵监听端口:port 26379 
配置哨兵的工作目录:dir /usr/data/redis/sentinel-26379 
设置监控的master:sentinel monitor mymaster 192.168.125.161 6379 2

备注: 
设置的mymaster只是一个代表名称,如果你一个哨兵监控多个master,则这个名称一定要有所不同,而后 2 表示如果有两个哨兵认为你出现了问题,则你应该下线选举出新的master

设置master的认证信息:sentinel auth-pass mymaster mldnjava 
设置master不活跃的时间:sentinel down-after-milliseconds mymaster 30000 
选举新的master失败时间:sentinel failover-timeout mymaster 180000 
只有一个master同步:sentinel parallel-syncs mymaster 1 
撤销Redis保护模式:protected-mode no

随后按照这样的配置分别建立sentinel-26380.conf、sentinel-26381.conf两个配置文件。

cp /usr/local/redis/conf/sentinel-26379.conf /usr/local/redis/conf/sentinel-26380.conf
cp /usr/local/redis/conf/sentinel-26379.conf /usr/local/redis/conf/sentinel-26381.conf
1
2
建议将此时的配置做一个副本保留一下:cp /usr/local/redis/conf/* /usr/data/redis/back/

5.启动三个哨兵进程:

/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26379.conf 
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26380.conf 
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26381.conf
1
2
3
通过哨兵的信息输出可以发现如下特点:

+slave :当一个哨兵启动之后如果已经确定可以连接到了master节点,则自动追加所有的slave节点;
+sentinel :每当启动一个新的哨兵进程后会自动进行哨兵增加的信息提示;
6.直接kill掉当前监控的master主机,随后会发现有如下提示信息:

+sdown master mymaster 192.168.125.161 6379 :当前的master主机已经下线了;
+vote-for-leader :进行重新的投票选举;
+slave-reconf-sent slave 192.168.125.161:6381 :从主机会自动修改redis.conf配置文件;
+switch-master mymaster 192.168.125.161 6379 192.168.125.161 6380 :6380为新的master;
7.如果此时的6379的进程又重新启动成功了,那么这个时候可以考虑通过命令做一个从的设置,一定要求设置好redis-6379.conf配置文件中的masterauth属性,如果不进行此项配置则无法连接到master主机。

同时也可以发现所有哨兵文件对应的配置内容已经发生了改变,证明在整个哨兵的运行机制里面,所有的配置文件都有可能出现更改。
Jedis访问哨兵机制
现在为止已经成功的实现了哨兵处理机制,但是对于程序的编写依然需要注意一点,如果要进行哨兵的处理操作,那么一定要求通过哨兵来取得可用的master地址。

package cn.mldn.jedis;

import java.util.HashSet;
import java.util.Set;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

public class JedisSentinel {
    public static final String MASTER_NAME = "mymaster" ;   // 定义哨兵的Master配置名称
    public static final int TIMEOUT = 2000 ;    // 连接超时时间
    public static final String REDIS_AUTH = "mldnjava" ;    // 认证密码
    public static final int MAX_TOTAL = 1000 ;  // 设置最大连接数
    public static final int MAX_IDLE = 200 ;    // 设置最小维持连接数
    public static final int MAX_WAIT_MILLIS = 1000 ;    // 设置最大等待时间
    public static void main(String[] args) {
        // 如果要通过哨兵机制进行Redis访问,那么必须要明确的设置出所有可以使用的哨兵的地址与端口
        Set<String> sentinels = new HashSet<String>() ; // 设置所有的哨兵的处理地址信息
        sentinels.add("192.168.125.161:26379") ;    // 哨兵的地址
        sentinels.add("192.168.125.161:26380") ;    // 哨兵的地址
        sentinels.add("192.168.125.161:26381") ;    // 哨兵的地址
        // 首先如果要想使用Jedis连接池,则必须有一个类可以保存所有连接池相关属性的配置项
        JedisPoolConfig poolConfig = new JedisPoolConfig() ;
        poolConfig.setMaxTotal(MAX_TOTAL);  // 设置最大连接数
        poolConfig.setMaxIdle(MAX_IDLE);    // 设置空闲的连接数
        poolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);// 最大等待时间
        // 此时所有的连接应该通过哨兵机制取得,所以这个时候应该使用JedisSentinelPool对象
        JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels,
                poolConfig);    // 建立一个哨兵的连接池
        Jedis jedis = pool.getResource() ;  // 通过连接池获取连接对象
        jedis.auth(REDIS_AUTH) ;
        System.out.println(jedis);
        jedis.set("mldn", "www.mldn.cn") ;
        jedis.close();
        pool.close();   // 关闭连接池
    }
}

SpringData访问哨兵
在之前使用的SpringData进行Redis访问的时候采用的是一个Spring内部支持的连接池,并且只有一个连接地址,但是如果要进行哨兵的配置,则需要将所有的哨兵地址都进行配置。

1.修改redis.properties配置文件,追加所有的哨兵配置地址:

# 追加所有的哨兵的访问处理地址以及对应的端口号 
redis.sentinel-1.host=192.168.68.165 
redis.sentinel-2.host=192.168.68.165 
redis.sentinel-3.host=192.168.68.165 
redis.sentinel-1.port=26379 
redis.sentinel-2.port=26380 
redis.sentinel-3.port=26381 
# 定义哨兵的master的名称 
redis.sentinel.master.name=mymaster 
# Redis的认证信息,认证信息密码 
redis.password=mldnjava 
# Redis连接的超时时间 
redis.timeout=2000 
# 设置最大的可用连接数 
redis.pool.maxTotal=100 
# 最小维持的可用连接数 
redis.pool.maxIdle=20 
# 最大等待时间 
redis.pool.maxWaitMillis=2000

2.修改spring-redis.xml配置文件,在这个配置文件里面需要将之前的连接池做一些更改:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <!-- 如果要进行Redis处理用户应该不去关注具体的序列化或反序列化操作,这一切都应该交给SpringData处理 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>    <!-- 定义Redis连接工厂 -->
        <property name="keySerializer"> <!-- 定义序列化Key的程序处理类 -->
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">   <!-- 处理value数据的操作 -->
            <!-- 明确表示如果要进行value数据保存的时候,保存的对象一定要使用JDK提供的序列化处理类 -->
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">   <!-- 处理hash数据的保存 -->
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
    </bean>
    <!-- 进行所有的哨兵地址的配置项 -->
    <bean id="sentinelsConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master"><!-- 配置master的节点名称 -->
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <!-- 通过资源文件读取出master的名称进行配置 -->
                <property name="name" value="${redis.sentinel.master.name}"/>
            </bean>
        </property>
        <!-- 配置所有哨兵的连接地址信息 -->
        <property name="sentinels">
            <set>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel-1.host}"/>
                    <constructor-arg name="port" value="${redis.sentinel-1.port}"/>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel-2.host}"/>
                    <constructor-arg name="port" value="${redis.sentinel-2.port}"/>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel-3.host}"/>
                    <constructor-arg name="port" value="${redis.sentinel-3.port}"/>
                </bean>
            </set>
        </property>
    </bean>
    <!-- 首先进行Jedis连接池的相关配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.pool.maxTotal}"/> <!-- 最大可用连接数 -->
        <property name="maxIdle" value="${redis.pool.maxIdle}"/>   <!-- 最小维持连接数 -->
        <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>   <!-- 最大等待时间 -->
    </bean>
    <!-- 进行ConnectionFactory的配置 -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <constructor-arg name="sentinelConfig" ref="sentinelsConfiguration"/>
        <property name="poolConfig" ref="jedisPoolConfig"/> <!-- 引用进行连接池的配置项 -->
        <property name="password" value="${redis.password}"/>  <!-- 定义的是连接密码,认证密码 -->
    </bean>
</beans>

twemproxy代理机制
不管你现在电脑性能有多好,只要你运行了Redis,那么就有可能造成一种非常可怕局面:你电脑的内存将立刻被占满,而且一台Redis数据库的性能终归是有限制的,那么现在如果要求保证用户的执行速度快,就需要使用集群的设计。而对于集群的设计主要的问题就是解决单实例Redis的性能瓶颈。 


twemproxy代理概述
Twemproxy是一个专门为了这种nosql数据库设计的一款代理工具软件,这个工具软件最大的特征是可以实现数据的分片处理。所谓的分片指的是根据一定的算法将要保存的数据保存到不同的节点之中。 
有了分片之后数据的保存节点就可能有无限多个,但是理论上如果要真进行集群的搭建,往往要求三台节点起步。

Twemproxy

Twemproxy,也叫 nutcraker。是一个Twitter开源的一个redis和memcache快速/轻量级代理服务器;Twemproxy是一个快速的单线程代理程序,支持Memcached ASCII协议和更新的redis协议;
Twemproxy通过引入一个代理层,可以将其后端的多台redis或memcached实例进行统一管理与分配,使应用程序只需要在Twemproxy 上进行操作,而不用关系后面具体有多少个真实的redis或memcached存储;
github地址:https://github.com/twitter/twemproxy
Twemproxy 的特性

支持失败节点自动删除 
可以设置重新连接该节点的时间
可以设置连接多少次之后删除该节点
支持设置HashTag 
通过HashTag可以自己设定将两个key哈希到同一个实例上去
减少与redis的直接连接数 
保持与redis的长连接
减少了客户端直接与服务器连接的链接数量
自动分片到后端多个redis实例上 
多种hash算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
多种分片算法:ketama(一致性hash算法的一种实现)、modular、random
可以设置后端实例的权重
避免单节点问题 
可以平行部署多个代理层,通过HAProxy做负载均衡,将redis的读写分散到多个twemproxy上。
支持状态监控 
可设置状态监控IP和端口,访问IP和端口可以得到一个json格式的状态信息串
可设置监控信息刷新间隔时间
使用pipelining处理请求和响应 
连接复用,内存服用
将多个连接请求,组成redis pipelining统一redis请求
并不是支持所有redis命令 
不支持redis的事务操作
使用SIDFF,SDIFFSTORE,SINTER,SINTERSTORE,SMOVE,SUNION and SUNIONSTORE 命令需要保证key都在同一个分片上。
最主要功能:用户不再直接操作真正的Redis,而且支持高性能的数据访问,而且支持分片处理,可以操作Redis集群。

配置Redis集群主机
本次预计使用三台Redis服务器,并且考虑到实际的应用环境,这三台的Master分别有各自的两个slave主机。于是现在给出本次使用的主机列表:

主机名称    IP地址    描述
tw-redis-server-a    192.168.125.162    Reids master数据服务:6379 Reids slave数据服务:6380 Reids slave 数据服务:6381
tw-redis-server-b    192.168.125.163    Reids master 数据服务:6379 Reids slave 数据服务:6380 Reids slave 数据服务:6381
tw-redis-server-c    192.168.125.164    Reids master 数据服务:6379 Reids slave 数据服务:6380 Reids slave 数据服务:6381
1.tw-redis-server-*考虑到主机的通用性,所有的主机都建议修改主机名称以及对应的hosts文件:

修改hostname:vim /etc/hostname,修改完成之后重新启动;
修改hosts:vim /etc/hosts,做好ip地址与主机名称的映射;
2.tw-redis-server-a本次主机通过之前的配置得来里面已经实现了基本的配置环境,但是依然需要由用户自己来配置主从关系:

修改6380端口配置:vim /usr/local/redis/conf/redis-6380.conf
slaveof 192.168.125.162 6379 
masterauth mldnjava

修改6381端口配置:vim /usr/local/redis/conf/redis-6381.conf
slaveof 192.168.125.162 6379 
masterauth mldnjava

3.tw-redis-server-*启动所有Redis数据库的实例

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6379.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6380.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6381.conf

此时就相当于有就台Redis数据库,而后有三台是Master节点,六台为Slave节点。

4.tw-redis-server-*查看从节点状态:

/usr/local/redis/bin/redis-cli -h 192.168.125.162 -p 6379 -a mldnjava info replication

一台主机的redis客户端,只要你连接的ip、端口、密码正确,那么都可以连接到任意的主机上。

编译与配置twemproxy
现在关键的问题就在于如何进行Redis分片处理,而分片处理的关键工具就是twemproxy工具,但是此工具给出的是一个源代码,所以使用之前一定要先进行编译处理。

1.tw-proxy-server-a将twemproxy-0.4.1.tar.gz源代码开发包上传到Linux系统之中,随后将其解压缩到源代码目录:

tar xzvf /srv/ftp/twemproxy-0.4.1.tar.gz -C /usr/local/src/

2.tw-proxy-server-a进入到源代码所在的目录:cd /usr/local/src/twemproxy-0.4.1/

首先要使用autoreconf工具生成一些编译的程序文件:autoreconf -fvi;
建立一个twemproxy编译后的工作目录:mkdir -p /usr/local/twemproxy;
进行编译的目录配置:./configure --prefix=/usr/local/twemproxy;
进行源代码的编译与安装:make && make install;
3.tw-proxy-server-a随后需要配置一个twemproxy的配置文件,这个配置文件考虑到随后与其它机制的整合,名称一定要设置为 redis_master.conf :

首先建立一个保存配置文件的目录:mkdir -p /usr/local/twemproxy/conf;
拷贝文件:cp /usr/local/src/twemproxy-0.4.1/conf/nutcracker.yml /usr/local/twemproxy/conf/redis_master.conf
4.tw-proxy-server-a编辑redis_master.conf文件:vim /usr/local/twemproxy/conf/redis_master.conf

redis_master:
  listen: 0.0.0.0:22121
  hash: fnv1a_64
  distribution: ketama
  auto_eject_hosts: true
  redis: true
  redis_auth: mldnjava
  server_retry_timeout: 2000
  server_failure_limit: 1
  servers:
    - 192.168.125.162:6379:1
    - 192.168.125.163:6379:1
    - 192.168.125.164:6379:1

以上配置的 redis_master 的名称与配置文件是一样的,而且必须一样,同时在本文件里面配置了redis访问密码,以及twemproxy所有可能代理到的redis服务器master节点。

5.tw-proxy-server-a{坑}twemproxy本身自带有一个配置文件的检测工具:

/usr/local/twemproxy/sbin/nutcracker -t /usr/local/twemproxy/conf/redis_master.conf

6.tw-proxy-server-a启动twemproxy的服务:

首先为了启动方便一定要设置一些数据的保存目录:mkdir -p /usr/local/twemproxy/{pid,logs}
/usr/local/twemproxy/sbin/nutcracker -c /usr/local/twemproxy/conf/redis_master.conf -p /usr/local/twemproxy/pid/redis_master.pid -o /usr/local/twemproxy/logs/redis_master.log -d

7.tw-proxy-server-a这个时候一旦twemproxy启动之后就表示该进程是一个redis的代理进程, 所有的服务可以通过twemproxy访问,而它的地址是:22121,随意找到一个redis客户端即可。 
/usr/local/redis/bin/redis-cli -h 192.168.68.170 -p 22121 -a mldnjava 
现在连接的直接为代理,随后找到各自的redis数据库的服务。 
/usr/local/redis/bin/redis-cli -h 192.168.68.167 -p 6379 -a mldnjava

8.编写java程序通过jedis访问:

package cn.mldn.jedis; 
import redis.clients.jedis.Jedis; 
import redis.clients.jedis.JedisPool; 
import redis.clients.jedis.JedisPoolConfig; 
public class TwemproxyDemo { 
    public static final String REDIS_HOST = "192.168.68.170" ; // 主机地址 
    public static final int REDIS_PORT = 22121 ; // 端口号 
    public static final int TIMEOUT = 2000 ; // 连接超时时间 
    public static final String REDIS_AUTH = "mldnjava" ; // 认证密码 
    public static final int MAX_TOTAL = 1000 ; // 设置最大连接数 
    public static final int MAX_IDLE = 200 ; // 设置最小维持连接数 
    public static final int MAX_WAIT_MILLIS = 1000 ; // 设置最大等待时间 
    public static final boolean TEST_ON_BORROW = true ; // 是否进行可用测试 
    public static void main(String[] args) {
        // 首先如果要想使用Jedis连接池,则必须有一个类可以保存所有连接池相关属性的配置项 
        JedisPoolConfig poolConfig = new JedisPoolConfig() ; 
        poolConfig.setMaxTotal(MAX_TOTAL); // 设置最大连接数 
        poolConfig.setMaxIdle(MAX_IDLE); // 设置空闲的连接数 
        poolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);// 最大等待时间 
        poolConfig.setTestOnBorrow(TEST_ON_BORROW); // 是否要进行连接测试,以保证返回的连接为可用连接 
        JedisPool pool = new JedisPool(poolConfig,REDIS_HOST,REDIS_PORT,TIMEOUT,REDIS_AUTH) ; 
        Jedis jedis = pool.getResource() ; // 通过连接池获取连接对象 
        for (int x = 0 ; x < 1000 ; x ++) { 
            jedis.set("mldn-" + x, "www.mldn.cn") ; 
        } 
        jedis.close(); 
        pool.close(); // 关闭连接池 
    } 
}

Twemproxy与Sentinel集成
Twemproxy如果要与Redis集成使用的是Redis的Master节点,因为只有Master节点才具备有写功能,而所有的Slave节点只具备有只读的数据功能,现在的思考点暂时不放在twemproxy上,而现在的问题集中在后端的所有Redis节点之中,因为Redis存在有主从关系,那么一旦某一个Redis的Master被干掉了,则一定要重新选举出一个新的Master节点,但是这个时候会出现有一个问题:twemproxy所使用的配置文件是单独存在的:

redis_master:
  listen: 0.0.0.0:22121
  hash: fnv1a_64
  distribution: ketama
  auto_eject_hosts: true
  redis: true
  redis_auth: mldnjava
  server_retry_timeout: 2000
  server_failure_limit: 1
  servers:
    - 192.168.125.162:6379:1
    - 192.168.125.163:6379:1
    - 192.168.125.164:6379:1

Redis集群中进行哨兵配置
如果要进行哨兵机制运行至少需要有三台或以上的主机,本次已经有了三台Redis集群,所以为了方便,在之前的三台主机上进行Sentinel配置,现在主机所运行的进程关系如下:

主机名称    IP地址    描述
tw-redis-server-a    192.168.125.162    Redis服务6379、6380、6381 Sentinel服务 26379
tw-redis-server-b    192.168.125.163    Redis服务6379、6380、6381 Sentinel服务 26379
tw-redis-server-c    192.168.125.164    Redis服务6379、6380、6381 Sentinel服务 26379
tw-proxy-server-a    192.168.125.165    Twemproxy代理服务 Sentinel服务26379
1.tw-redis-server-*在所有的主机上建立哨兵机制的保存数据目录:mkdir -p /usr/data/redis/sentinel

2.tw-redis-server-a进行哨兵配置文件的编写:vim /usr/local/redis/conf/sentinel.conf

port 26379 
dir /usr/data/redis/sentinel 
protected-mode no 

sentinel monitor redis_master_group1 192.168.125.162 6379 2 
sentinel auth-pass redis_master_group1 mldnjava 
sentinel down-after-milliseconds redis_master_group1 10000 
sentinel failover-timeout redis_master_group1 10000 
sentinel parallel-syncs redis_master_group1 1 

sentinel monitor redis_master_group2 192.168.125.163 6379 2 
sentinel auth-pass redis_master_group2 mldnjava 
sentinel down-after-milliseconds redis_master_group2 10000 
sentinel failover-timeout redis_master_group2 10000 
sentinel parallel-syncs redis_master_group2 1 

sentinel monitor redis_master_group3 192.168.125.164 6379 2 
sentinel auth-pass redis_master_group3 mldnjava 
sentinel down-after-milliseconds redis_master_group3 10000 
sentinel failover-timeout redis_master_group3 10000 
sentinel parallel-syncs redis_master_group3 1

3.tw-redis-server-a将哨兵的配置文件发送到其它主机上: 
拷贝到 tw-redis-server-b 主机:scp /usr/local/redis/conf/sentinel.conf 192.168.125.163:/usr/local/redis/conf/ 
拷贝到 tw-redis-server-c 主机:scp /usr/local/redis/conf/sentinel.conf 192.168.125.164:/usr/local/redis/conf/

4.tw-redis-server-*考虑到哨兵机制运行之后会进行配置文件的变更,那么最好的做法是将这些配置文件做一个副本,这样测试起来会比较方便:cp /usr/local/redis/conf/* /usr/data/redis/back/

5.tw-redis-server-*启动哨兵进程:/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel.conf

twemproxy整合哨兵机制
如果要想在twemproxy之中与哨兵整合,并且实现twemproxy进程的重新启动,那么有一个前提必须有保证:你的twemproxy运行的主机一定要提供有哨兵机制,目的是为了与其它的哨兵进行整合处理。

1.tw-redis-server-a将哨兵的配置文件拷贝到tw-proxy-server-a主机上;

scp /usr/data/redis/back/sentinel.conf 192.168.68.170:/usr/local/redis/conf; 

2.tw-proxy-server-a通过源代码文件拷贝出哨兵进程的启动项:cp /usr/local/src/redis-3.2.9/src/redis-sentinel /usr/local/redis/bin/

3.tw-proxy-server-a启动哨兵机制,这个哨兵机制是为了让Shell脚本可以整合到所有的哨兵机制里;

/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel.conf

4.tw-proxy-server-a为了方便进行shell脚本的保存在twemproxy程序目录中创建有一个sh的目录:mkdir -p /usr/local/twemproxy/sh

5.tw-proxy-server-a建立一个可以进行哨兵重启twemproxy进程的shell脚本: 
编辑命令:vim /usr/local/twemproxy/sh/client-reconfig.sh

#!/bin/sh 
#
monitor_name="$1"
master_old_ip="$4"
master_old_port="$5"
master_new_ip="$6"
master_new_port="$7"
twemproxy_name=$(echo $monitor_name |awk -F'_' '{print $1"_"$2}')

twemproxy_bin="/usr/local/twemproxy/sbin/nutcracker"
twemproxy_conf="/usr/local/twemproxy/conf/${twemproxy_name}.conf"
twemproxy_pid="/usr/local/twemproxy/pid/${twemproxy_name}.pid"
twemproxy_log="/usr/local/twemproxy/logs/${twemproxy_name}.log"
twemproxy_cmd="${twemproxy_bin} -c ${twemproxy_conf} -p ${twemproxy_pid} -o ${twemproxy_log} -d"

sed -i "s/${master_old_ip}:${master_old_port}/${master_new_ip}:${master_new_port}/" ${twemproxy_conf}

ps -ef |grep "${twemproxy_cmd}" |grep -v grep |awk '{print $2}'|xargs kill
${twemproxy_cmd}

sleep 1
ps -ef |grep "${twemproxy_cmd}" |grep -v grep

6.tw-proxy-server-a为脚本授予全部执行权限:chmod 777 /usr/local/twemproxy/sh/client-reconfig.sh

7.tw-proxy-server-a现在要连接本机的哨兵进程,这样才可以与此脚本文件有关联:

/usr/local/redis/bin/redis-cli -h 192.168.125.162 -p 26379 sentinel set redis_master_group1 client-reconfig-script /usr/local/twemproxy/sh/client-reconfig.sh

/usr/local/redis/bin/redis-cli -h 192.168.125.162 -p 26379 sentinel set redis_master_group2 client-reconfig-script /usr/local/twemproxy/sh/client-reconfig.sh

/usr/local/redis/bin/redis-cli -h 192.168.125.162 -p 26379 sentinel set redis_master_group3 client-reconfig-script /usr/local/twemproxy/sh/client-reconfig.sh

那么此时哨兵机制一旦发生了重新的选举之后,那么会立刻重新启动twemproxy的相关进程。

8.tw-redis-server-a杀死掉redis-6379服务进程,这样master就消失了,消失之后应该会触发脚本,会重新配置redis_master.conf文件,这个时候对应在twemproxy主机上的哨兵会自动进行twemproxy的进程重新启动,以此保证twemproxy中Redis主机的高可用状态。

HAProxy负载均衡
Twemproxy主要功能在于数据的分片处理,而且会发现在整个的Redis集群里面,如果用户要想访问Redis集群必须通过Twemproxy,于是这个时候就有可能造成一种问题:

你后面的Redis集群一定速度暴快,因为一堆的数据库服务;
所有的性能都卡在了代理上。
HAProxy:haproxy是一个开源的,高性能的,基于TCP第四层和http第七层应用的千万级高并发负载均衡软件;

HAProxy优点:

最高可以同时维护40000-50000个并发连接。单位时间内处理最大的请求数为20000.最大数据处理能力可达10GBPS
支持多余8种负载均衡算法,同时也支持session保持
支持虚拟主机功能
拥有服务器性能监控工具。
配置HAProxy
现在为了观察问题准备出来了三台twemproxy主机,而后现在再需要准备出一台haproxy主机tw-haproxy-server-a。而后通过这个haproxy主机先进行haproxy运行的处理。

1.tw-haproxy-server-a将haproxy的开发包上传到系统之中,随后为其进行解压缩控制:tar xzvf /srv/ftp/haproxy-1.5.18.tar.gz -C /usr/local/src/

2.tw-haproxy-server-a进入到haproxy源代码目录:cd /usr/local/src/haproxy-1.5.18/

建立一个Haproxy编译后的保存目录:mkdir -p /usr/local/haproxy
如果要想进行HAProxy的处理一定要选择好你当前的系统架构的信息:
make TARGET=linux26 PREFIX=/usr/local/haproxy ARCH=x86_64
进行HAProxy的安装处理:make install PREFIX=/usr/local/haproxy;
安装完成之后会在 /usr/local/haproxy 目录下存在有:doc、sbin、share三个目录;

3.tw-haproxy-server-a建立一个haproxy的数据保存工作目录:mkdir -p /usr/data/haprox

4.tw-haproxy-server-a通过源代码拷贝出一个配置文件到指定的目录之中;

cp /usr/local/src/haproxy-1.5.18/examples/haproxy.cfg /usr/local/haproxy/
1
5.tw-haproxy-server-a编辑 haproxy.cfg 配置文件:vim /usr/local/haproxy/haproxy.cfg

global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        chroot /usr/local/haproxy
        pidfile /usr/data/haproxy/haproxy.pid
        uid 99
        gid 99
        daemon
        #debug
        #quiet

defaults
        log     global
        mode    tcp
        option  httplog
        option  dontlognull
        retries 3
        redispatch
        maxconn 2000
        contimeout      5000
        clitimeout      50000
        srvtimeout      50000

listen  appli1-rewrite 0.0.0.0:10001
        cookie  SERVERID rewrite
        balance roundrobin
        option  abortonclose
        option  redispatch
        retries 3
        maxconn 2000
        timeout connect 5000
        timeout client  50000
        timeout server  50000

listen  proxy_status 
        bind :16001
        mode tcp
        balance roundrobin
        server tw_proxy_1 192.168.125.165:22121 check inter 10s
        server tw_proxy_2 192.168.125.166:22121 check inter 10s
        server tw_proxy_3 192.168.125.167:22121 check inter 10s

frontend admin_stats
        bind :7777
        mode http
        stats enable
        option httplog
        maxconn 10
        stats refresh 30s
        stats uri /admin
        stats auth mldn:java
        stats hide-version
        stats admin if TRUE

6.tw-haproxy-server-a需要进行haproxy的进程启动:

/usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.cfg
1
7.如果现在要想确认当前已经正常启动,则可以通过管理控制台查看:http://192.168.125.165:7777/admin

访问的时候输入之前配置好的认证名称:mldn、java 

HAProxy测试
1.如果要想正常完成测试,一定要先保证Redis、sentinel、twemproxy的服务进程全部正常启动;

tw-redis-server-* Redis进程应该是可以正常启动的:ps -ef | grep redis
tw-redis-server-、tw-proxy-server-重新配置哨兵文件:cp /usr/data/redis/back/sentinel.conf /usr/local/redis/conf/ 
启动哨兵:/usr/local/redis/bin/redis-server /usr/local/redis/conf/sentinel.conf
2.tw-proxy-server-*启动twemproxy的进程:

/usr/local/twemproxy/sbin/nutcracker -c /usr/local/twemproxy/conf/redis_master.conf -p /usr/local/twemproxy/pid/redis_master.pid -o /usr/local/twemproxy/logs/redis_master.log -d

3.先保证各个 twemproxy 的节点可以正常使用。

4.使用haproxy进行代理操,使用redis客户端登录:/usr/local/redis/bin/redis-cli -h 192.168.125.167 -p 16001 -a mldnjava

5.通过jedis的java客户端进行数据访问:

package cn.mldn.jedis; 
import redis.clients.jedis.Jedis; 
import redis.clients.jedis.JedisPool; 
import redis.clients.jedis.JedisPoolConfig; 
public class TwemproxyDemo { 
    public static final String REDIS_HOST = "192.168.125.167" ; // 主机地址 
    public static final int REDIS_PORT = 16001 ; // 端口号 
    public static final int TIMEOUT = 2000 ; // 连接超时时间 
    public static final String REDIS_AUTH = "mldnjava" ; // 认证密码 
    public static final int MAX_TOTAL = 1000 ; // 设置最大连接数 
    public static final int MAX_IDLE = 200 ; // 设置最小维持连接数
    public static final int MAX_WAIT_MILLIS = 1000 ; // 设置最大等待时间 
    public static void main(String[] args) { 
        // 首先如果要想使用Jedis连接池,则必须有一个类可以保存所有连接池相关属性的配置项 
        JedisPoolConfig poolConfig = new JedisPoolConfig() ; 
        poolConfig.setMaxTotal(MAX_TOTAL); // 设置最大连接数 
        poolConfig.setMaxIdle(MAX_IDLE); // 设置空闲的连接数 
        poolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);// 最大等待时间 
        JedisPool pool = new JedisPool(poolConfig,REDIS_HOST,REDIS_PORT,TIMEOUT,REDIS_AUTH) ; 
        Jedis jedis = pool.getResource() ; // 通过连接池获取连接对象 
        for (int x = 0 ; x < 10000 ; x ++) { 
            jedis.set("mldn-" + x, "www.mldn.cn") ; 
        } 
        jedis.close(); 
        pool.close(); // 关闭连接池 
    } 
}

如果此时的程序出现有如下的错误信息:

Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: READONLY You can't write against a read only slave.


Keepalived高可用机制
如果面对Redis集群,只是依靠twemproxy的分片是不够的,还需要有一系列的代理设计,例如:HAProxy可以实现负载均衡设计。

Keepalived是一个基于VRRP协议来实现的服务高可用方案,可以利用其来避免IP单点故障,类似的工具还有heartbeat、corosync、pacemaker。但是它一般不会单独出现,而是与其他负载均衡技术如lvs、haproxy、nginx一起工作来达到集群的高可用。 
下载地址:http://wwww.keepalived.org/

安装与配置Keepalived组件
keepalived组件给出的是一个源代码的开发包,所以你依然需要在你的系统之中进行编译与配置处理。

1.tw-haproxy-server-a主机将keepalived开发包上传到Linux系统之中,随后将其进行解压缩。

tar xzvf /srv/ftp/keepalived-1.2.24.tar.gz -C /usr/local/

2.tw-haproxy-server-a主机为了配置方便进行一下更名处理:

mv /usr/local/keepalived-1.2.24/ /usr/local/keepalived`

3.tw-haproxy-server-a主机进入到keepalived组件包的目录:cd /usr/local/keepalived/

4.tw-haproxy-server-a主机进行编译处理:

进行编译配置:./configure --prefix=/usr
编译与安装:make && make install
5.tw-haproxy-server-a主机将keepalived的配置文件拷贝到 /etc/keepalived 目录之中;

建立一个新的目录:mkdir -p /etc/keepalived
拷贝配置文件:cp /usr/etc/keepalived/keepalived.conf /etc/keepalived/
6.tw-haproxy-server-a主机编辑keepalived.conf配置文件:vim /etc/keepalived/keepalived.conf

keepalived由于需要进行虚拟IP的设计,所以首先要取得网卡名称:cat /proc/net/dev、ifconfig,现在的名称为 ens33 ;
! Configuration File for keepalived

global_defs {
   notification_email {
     [email protected]
     [email protected]
     [email protected]
   }
   notification_email_from [email protected]
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.68.250
    }
}

virtual_server 192.168.125.250 16001 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 192.168.125.168 16001 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            delay_before_retry 3
        }
    }
    real_server 192.168.125.169 16001 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            delay_before_retry 3
        }
    }
}

7.tw-haproxy-server-b主机关闭 tw-haproxy-server-a 这台虚拟机,而后将其克隆为 tw-haproxy-server-b 主机。 
8.tw-haproxy-server-b主机修改 keepalived.conf 配置文件,一定要设置为BACKUP,同时优先级别一定要降低。 
- 修改配置文件:vim /etc/keepalived/keepalived.conf;


vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.68.250
    }
}

virtual_server 192.168.125.250 16001 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 192.168.125.168 16001 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            delay_before_retry 3
        }
    }
    real_server 192.168.125.169 16001 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            delay_before_retry 3
        }
    }
}

9.tw-haproxy-server-*启动HAProxy:

/usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.cfg
1
10.tw-haproxy-server-*启动keepalived服务:service keepalived start

查看一下keepalived相关进程:ps -ef | grep keepalived
直接查看keepalived状态:service keepalived status
停止keepalived服务:service keepalived stop
11.此时如果在进行访问,一定要使用VIP进行访问。

猜你喜欢

转载自blog.csdn.net/weixin_39793123/article/details/83744769