redis master-slave structure and sentinel mechanism

Master-slave architecture

Create one master and two slave folders.

mkdir master slave1 slave2

Copy etc / redis / redis The conf files are stored in three folders.

cp /etc/redis/redis.conf /master
cp /etc/redis/redis.conf /slave1
cp /etc/redis/redis.conf /slave2

Modify the configuration in turn
Set the master port number to 7000, slave1 port number to 7001, and slave2 port number to 7002
Modify the bind in master, slave1 and slave2 to 0.0 zero
Find slaveof from slave1 and slave2 configuration files

After uncommenting, the bind of the master is filled in the first data, and the port number of the master is filled in the second data

salveof 0.0.0.0 7000

After configuration, use the following code to open ports 7000, 7001 and 7002 in turn

reids-server master/redis.conf
reids-server slave1/redis.conf
reids-server slave2/redis.conf

Open the three pages to enter master, slave1 and slave2 respectively

redis-cli =p 7000 --raw
redis-cli =p 7001 --raw
redis-cli =p 7002 --raw

At this time:
After the master node writes successfully, the data will be synchronized to the two slave nodes.
The slave on ports 7001 and 7002 becomes read-only and cannot be written.
Even if the Master goes down, the slave node cannot temporarily replace the master node for write operations.

In order to realize that after the Master goes down, a new node can replace the master for write operations, there is a sentinel mechanism

Sentinel mechanism

When the master node goes down, a slave node will be automatically selected to replace the master node for read and write operations.

Create sentinel Conf file, add a line

sentinel monitor Name the sentinel (self named) master node ip How many votes are required for the port number of the primary node (odd number)
example: sentinel monitor mymaster 0.0.0.0 7000 1

For the last item, sentinel has its own algorithm. When the master node goes down, sentinel will elect a new master node from the slave node. The final number represents how many votes are needed to pass the election. You must write an odd number.

Install redis Sentinel

apt install redis-sentinel

Use after installation:

redis-sentinel /master/sentinel.conf

Enter sentinel


Open the three nodes master, slave1 and slave2 according to the master-slave settings in the previous section. Run at this time

redis-cli -p 7000 shutdown

Turn off the current master node to simulate downtime.


At this time, the sentinel will wait for the response of the master node, which defaults to 15 seconds. If you need to change the time, add it in the configuration file

sentinel down-after-milliseconds mymaster 5000

It means that the election will be triggered if the master node fails to respond for 5 seconds.



Test in redis

Open three nodes, open sentry


You can see at the bottom that the current primary node is the node corresponding to port number 7001. Now I close port 7001.


At this point, you can see that port 7002 is selected as the current primary node through the sentinel mechanism of redis.

Let's test it in springboot

Add a sentence under port in the sentinel configuration file

bind 0.0.0.0

Indicates that a remote connection is open

In the springboot configuration file, change the port number in the configuration with the sentinel election

#Single node
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0

redis dependency:

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.properties

server.port=8080

#Single node
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#Enable L2 cache
mybatis-plus.configuration.cache-enabled=true

#Open log
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

Emp entity class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
    private int id;
    private String occ;
}

EmpMapper

@Mapper
//Enable cache
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
@CacheNamespaceRef(EmpMapper.class)
Turn on transaction support
//@Transactional(propagation = Propagation.SUPPORTS)
@EnableTransactionManagement
public interface EmpMapper extends BaseMapper<Emp> {
}

ApplicationUtil get spring factory

@Component
public class ApplicationUtil implements ApplicationContextAware {

    //Get spring factory data
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    //Gets the method in the factory and returns
    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }
}

RedisCache implements the Cache interface

public class RedisCache implements Cache {

    //Put in cached namespace com tan. UserMapper. UserMapper"
    private final String id;
    private RedisTemplate redisTemplate;
    public RedisCache(String id) {
        this.id = id;
    }

    @Override
    public String getId() {
        return this.id;
    }

    //Cache put value
    @Override
    public void putObject(Object o, Object o1) {
        //Obtain the redisTemplate through the applicationContext factory tool class
        getRedisTemplate();
        redisTemplate.opsForHash().put(id.toString(), getKeyToMD5(o.toString()), o1);
    }

    //Get data from redis
    @Override
    public Object getObject(Object o) {
        getRedisTemplate();
        return redisTemplate.opsForHash().get(id.toString(), getKeyToMD5(o.toString()));
    }

    //Deletes the cache according to the o specified
    @Override
    public Object removeObject(Object o) {

        return null;
    }

    //Delete all caches
    @Override
    public void clear() {
        getRedisTemplate();
        redisTemplate.delete(id);
    }

    @Override
    public int getSize() {
        getRedisTemplate();
        return  redisTemplate.opsForHash().size(id).intValue();
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }

    //Prevent code redundancy
    private void getRedisTemplate(){
        if (null == redisTemplate){
            redisTemplate = (RedisTemplate) ApplicationUtil.getBean("redisTemplate");
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        }
    }

    //Unified key format
    private String getKeyToMD5(String key){
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }
}

Test class

@SpringBootTest
class JedisApplicationTests {

    @Autowired
    private EmpMapper empMapper;
    
    @Test
    public void test1(){
        empMapper.insert(new Emp(1,"Finance Department"));
    }
}

Let's test with redis single node configuration first:

#Single node
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0


You can see the prompt that data cannot be written from the slave node, which proves that the current configuration cannot use redis to write after the primary node goes down.
Now you need to dynamically change the nodes that can write according to the sentinel election:

#Get nodes according to sentinel election
#master is the name of the sentry
#nodes is the current master node address and sentinel port
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=127.0.0.1:26379

After modification, run the program again and report an error: the data already exists.
This is because I just ran an insert operation. Errors in the cache will not affect the data stored in mysql. Delete the corresponding data in mysql and run again:

Run successfully.

Now I shut down the current master node 7002, and then test it after the sentry selects a new master node.


You can see that the current master node is 7001, and the springboot still obtains the master node that can perform write operations from the current sentinel.

Keywords: Java Database Redis

Added by simple_man_11 on Fri, 31 Dec 2021 23:03:00 +0200