2. Tutorial 2 注解式分布式的配置文件高级篇: 配置更新的通知(最佳实践)

Tutorial 1 里,
我们实现了一个简单的Redis服务程序,它使用分布式配置进行管理,此Redis的配置文件存储在分布式服务器 disconf-web 上。

也许有一天,我们需要更新Redis的Host和Port配置数据。由于 redis是根据配置生成的实例,因此,这种情况下,你有三种选择:

  • 不使用Disconf(Tutorial 1 里第一部分的使用方法)。那么你需要 更改线上机器的配置文件 redis.properties,重启服务就可以了。
  • 使用Disconf, 采用 Tutorial 1 里第二部分的使用的方案。那么你需要 更改 分布式服务器 disconf-web 上的 redis.properties 文件。 然后重启服务就可以了。和第一种方法的区别在于,不需要更改线上机器的配置文件。
  • 使用Disconf,采用 Tutorial 1 里第二部分的使用的方案,并额外加上本Tutorial的方案,那么你 只需要 更改 分布式服务器 disconf-web 上的 redis .properties 文件。然后服务的配置自动生效。此过程无需要重新启动服务。

本教程就是阐述如何通过简单的配置和极简代码实现第三步的功能。

2.1. 第一步:准备工作

完成 Tutorial 1 上第二部分方案里的步骤。

2.2. 第二步:修改 SimpleRedisService,支持Redis重连接

在这里,我们这个类添加了一个方法,重新生成了一个Jedis对象,代码如下:

/**
 * 更改Jedis
 */
public void changeJedis() {

    LOGGER.info("start to change jedis hosts to: " + jedisConfig.getHost()
            + " : " + jedisConfig.getPort());

    jedis = JedisUtil.createJedis(jedisConfig.getHost(),
            jedisConfig.getPort());

    LOGGER.info("change ok.");
}

之所以添加这个函数的原因是:在配置更新时,此函数要被调用,从而更改Jedis实例。

整个类的完整代码如下:

package com.example.disconf.demo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import com.example.disconf.demo.config.JedisConfig;
import com.example.disconf.demo.utils.JedisUtil;

import redis.clients.jedis.Jedis;

/**
 * 一个简单的Redis服务
 *
 * @author liaoqiqi
 * @version 2014-6-17
 */
@Service
@Scope("singleton")
public class SimpleRedisService implements InitializingBean, DisposableBean {

    protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisService.class);

    // jedis 实例
    private Jedis jedis = null;

    /**
     * 分布式配置
     */
    @Autowired
    private JedisConfig jedisConfig;

    /**
     * 关闭
     */
    public void destroy() throws Exception {

        if (jedis != null) {
            jedis.disconnect();
        }
    }

    /**
     * 进行连接
     */
    public void afterPropertiesSet() throws Exception {

        jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort());
    }

    /**
     * 获取一个值
     *
     * @param key
     *
     * @return
     */
    public String getKey(String key) {
        if (jedis != null) {
            return jedis.get(key);
        }

        return null;
    }

    /**
     * 更改Jedis
     */
    public void changeJedis() {

        LOGGER.info("start to change jedis hosts to: " + jedisConfig.getHost() + " : " + jedisConfig.getPort());

        jedis = JedisUtil.createJedis(jedisConfig.getHost(), jedisConfig.getPort());

        LOGGER.info("change ok.");
    }
}

2.3. 第三步: 撰写配置更新回调类

当配置更新时,应用程序要得到通知。因此我们要写一个回调类来响应此“通知”。完整的类如下:

package com.example.disconf.demo.service.callbacks;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import com.baidu.disconf.client.common.annotations.DisconfUpdateService;
import com.baidu.disconf.client.common.update.IDisconfUpdate;
import com.example.disconf.demo.config.Coefficients;
import com.example.disconf.demo.config.JedisConfig;
import com.example.disconf.demo.service.SimpleRedisService;

/**
 * 更新Redis配置时的回调函数
 *
 * @author liaoqiqi
 * @version 2014-6-17
 */
@Service
@Scope("singleton")
@DisconfUpdateService(classes = {JedisConfig.class}, itemKeys = {Coefficients.key})
public class SimpleRedisServiceUpdateCallback implements IDisconfUpdate {

    protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisServiceUpdateCallback.class);

    @Autowired
    private SimpleRedisService simpleRedisService;

    /**
     *
     */
    public void reload() throws Exception {

        simpleRedisService.changeJedis();
    }

}

具体步骤是:

  • 撰写此类,实现 IDisconfUpdate 接口。此类必须是JavaBean,Spring托管的,且 “scope” 都必须是singleton的。
  • 添加 @DisconfUpdateService 注解,classes 值加上 JedisConfig.class ,表示当 JedisConfig.class 这个配置文件更新时,此回调类将会被调用。或者,使用 confFileKeys 也可以。
  • 在函数 reload() 里调用 SimpleRedisService 的 changeJedis() 方法

回调类与配置类放在一起

如果你觉得写两个类太累,在某些场景下,则可以将回调与配置类放在一起的。

/**
 * Redis配置文件
 *
 * @author liaoqiqi
 * @version 2014-6-17
 */
@Service
@Scope("singleton")
@DisconfFile(filename = "redis.properties")
@DisconfUpdateService(classes = {JedisConfig.class})
public class JedisConfig implements IDisconfUpdate {

    protected static final Logger LOGGER = LoggerFactory.getLogger(JedisConfig.class);

    // 代表连接地址
    private String host;

    // 代表连接port
    private int port;

    /**
     * 地址, 分布式文件配置
     *
     * @return
     */
    @DisconfFileItem(name = "redis.host", associateField = "host")
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    /**
     * 端口, 分布式文件配置
     *
     * @return
     */
    @DisconfFileItem(name = "redis.port", associateField = "port")
    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public void reload() throws Exception {
        LOGGER.info("host: " + host);
    }
}

2.4. 完结

至此,支持配置更新的 分布式配置文件 的撰写就已经写完了。

当用户在 disconf-web 上更新配置时,你的服务里的Redis就会指向新的地址。