I am participating in the nuggets Community Game Creativity Submission Contest. For details, please see: Game Creativity Submission Contest.

Today is a review of redis on our game server.

1. Introduction to Redis

Redis is fully open source and is a high-performance key-value database.

The advantage of redis

  • High performance – Redis can read 110000 times /s and write 81000 times /s.
  • Rich data types – Redis supports binary case Strings, Lists, Hashes, Sets and Ordered Sets data type operations.
  • Atomic – All operations in Redis are atomic, meaning that they either succeed or fail at all. Individual operations are atomic. Multiple operations also support transactions, namely atomicity, wrapped in MULTI and EXEC instructions.
  • Rich features – Redis also supports publish/subscribe, notification, key expiration, and more.

2. Application in the project

The application in the game server mainly has the following scenarios:

Because of the high performance of Redis, we use it as a cross-server data store

Redis based on the publish and subscribe function, we use it as a message publishing between servers, governing all servers

Based on the redis ranking function, we use it as a leaderboard across servers.

Take a look at our server architecture:

Note:

MS: cross-server server

G: Game logic server

Only the link to Redis is drawn here, not the link to the game

3. Code presentation

Add the following dependencies to your project

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
Copy the code

If you use Springboot to build projects, you can add the following to poM

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code

1. Data storage

Data storage is relatively simple, routine use on the line

@Component
public class RedisUtil {
 
    public static RedisUtil util;
 
    public RedisUtil(@Autowired  JedisPool jedisPool) {
        this.jedisPool = jedisPool;
        RedisUtil.util = this;
    }
 
    public static RedisUtil getInstance(a){
        return util;
    }
 
 
    @Autowired
    private JedisPool jedisPool;
 
    /** * Save value to Redis permanently */
    public String set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.set(key, value);
        } catch (Exception e) {
            return "0";
        } finally{ jedis.close(); }}/** * gets the specified Value */ based on the passed Key
    public String get(String key) {
        Jedis jedis = null;
        String value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.get(key);
        } catch (Exception e) {
            return "0";
        } finally {
            jedis.close();
        }
        return value;
    }
 
    /** * Check whether the Key exists */
    public Boolean exists(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.exists(key);
        } catch (Exception e) {
            return false;
        } finally{ jedis.close(); }}/** * delete the specified key-value */
    public Long del(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.del(key);
        } catch (Exception e) {
            return 0L;
        } finally{ jedis.close(); }}/** * distributed lock *@param key
     * @param value
     * @paramTime Lock timeout period (unit: second * *)@returnReturn "OK" on success, null */ on failure
    public String getDistributedLock(String key,String value,int time){
        Jedis jedis = null;
        String ret = "";
        try {
            jedis = jedisPool.getResource();
 
            ret = jedis.set(key, value, new SetParams().nx().ex(time));
            return ret;
        } catch (Exception e) {
            return null;
        } finally{ jedis.close(); }}public void pub(a){
        jedisPool.getResource().publish("test"."msg"); }}Copy the code

2. Ranking function

The function of the leaderboard is mainly to use the data structure of sortset, but there is a case to pay attention to is the problem of time.

For example, in the same battle, the first player on the list will rank higher, no doubt about it, but some students will forget this.

At the same time, when updating, it is necessary to remove the decimal of the original value and add the value of the current time operation

Add player ID as key, player combat as score, and time as decimal

/** * Calculate score by subtracting lastOrderTime and dividing it by the base time to get a decimal number less than 1@param orderNum
     * @param lastOrderTime
     * @return* /
    private double getOrderNum(int orderNum, long lastOrderTime) {
        return orderNum + (BASE_TIME - lastOrderTime) * 1.0 / BASE_TIME;
    }
 
        /** * Store data to Redis * Store processed data *@param key
     * @param value
     */
    public long put(String key, int value) {
        long time = System.currentTimeMillis();
        double dValue = value + 1 - time / Math.pow(10, (int) Math.log10(time) + 1d);
        dbData.put(key, dValue);
        return time;
    }
Copy the code

3. Publish and subscribe

@Component
public class JedisPubListener extends JedisPubSub implements ApplicationRunner {
 
    @Override
    public void onMessage(String channel, String message) {
        System.out.println(String.format("receive redis published message, channel %s, message %s", channel, message));
    }
 
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        System.out.println(String.format("subscribe redis channel success, channel %s, subscribedChannels %d",
                channel, subscribedChannels));
    }
 
    @Override
    public void onUnsubscribe(String channel, int subscribedChannels) {
        System.out.println(String.format("unsubscribe redis channel, channel %s, subscribedChannels %d",
                channel, subscribedChannels));
 
    }
 
    @Override
    public void run(ApplicationArguments args) throws Exception {}}Copy the code

Since the Jedis subscription is blocked, a separate thread is opened

@Component
public class ListenerThread extends Thread{
    @Autowired
    public JedisPubListener listener;
    @Autowired
    private JedisPool jedisPool;
    @PostConstruct
    public void init(a){
        this.start();
        System.err.println("thread start");
    }
    @Override
    public void run(a) {
        Jedis resource = jedisPool.getResource();
        resource.subscribe(listener,"test"); }}Copy the code

4, summarize

The high performance of Redis meets the requirements of the game industry, so now many game servers start to use Redis.

Redis can be used as data storage, it can be used as a fast leaderboard, it can be used as a cache, it is really good.