原來項目中使用枚舉類定義了一些模塊信息,在使用springboot後,想讓這些枚舉信息變成可編輯的配置信息,能隨時通過頁面進行編輯,然後在不停止服務的前提下更新數據,嘗試了幾種方案,最後覺得使用redis的發佈訂閱方式比較不錯,下面就是記錄的我的做法:
- 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- 添加監聽器:用於監聽處理redis發佈的最新數據通知
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.List;
public class ConsumePubTableConfigListener implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(ConsumePubTableConfigListener.class);
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private UpdateConfig config;
@Autowired
private IAppInfoDao iAppInfoDao;
@Autowired
private IModuleInfoDao iModuleInfoDao;
@Override
public void onMessage(Message message, byte[] pattern) {
log.info("receive message ......");
handleMessage(message);
}
public void handleMessage(Message message) {
Object channel = stringRedisTemplate.getValueSerializer().deserialize(message.getChannel());
Object value = stringRedisTemplate.getValueSerializer().deserialize(message.getBody());
String channelStr = String.valueOf(channel);
String messageStr = String.valueOf(value);
log.info("message channel from :"+ channelStr + ":::::::: consumer message: "+ messageStr);
//一種配置 處理
if(SysConstants.TABLE_CONFIG_APP_UPDATE.equals(channelStr)){
log.info("reload appConfig from table....");
List<SmsAppInfo> infos = iAppInfoDao.queryAll();
for(SmsAppInfo info:infos){
config.getSmsApp().setSmsAppInfo(info);
}
}
//另一種配置處理
if(SysConstants.TABLE_CONFIG_MODULE_UPDATE.equals(channelStr)){
log.info("reload moduleConfig from table....");
List<SmsModuleInfo> infos = iModuleInfoDao.queryAll();
for(SmsModuleInfo info:infos){
config.getSmsModule().setSmsModuleInfo(info);
}
}
}
}
- 把監聽器添加到系統的配置中
import com.borui.lejane.listener.ConsumePubTableConfigListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
* 表配置變更
*/
@Configuration
public class ChangeConfig {
@Autowired
private LettuceConnectionFactory lettuceConnectionFactory;
//剛纔的監聽器
@Bean
public ConsumePubTableConfigListener getConsumerRedis() {
return new ConsumePubTableConfigListener();
}
@Bean
public ChannelTopic appTopic() {
return new ChannelTopic("business-topic-app");
}
@Bean
public ChannelTopic moduleTopic() {
return new ChannelTopic("business-topic-module");
}
//讓監聽器監聽關心的話題
@Bean
public RedisMessageListenerContainer setRedisMessageListenerContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(lettuceConnectionFactory);
//話題1
container.addMessageListener(getConsumerRedis() , appTopic());
//話題2
container.addMessageListener(getConsumerRedis() , moduleTopic());
return container;
}
}
- 系統啓動時的數據加載
@Configuration
public class UpdateConfig {
@Bean(initMethod = "loadDataFromDb")
public BusinessApp getApp(){
return new BusinessApp();
}
@Bean(initMethod = "loadDataFromDb")
public BusinessModule getModule(){
return new BusinessModule();
}
}
- 業務類的處理
public class SmsApp {
private static final Logger log = LoggerFactory.getLogger(SmsApp.class);
private ConcurrentHashMap<String,SmsAppInfo> projectsMap = new ConcurrentHashMap<>();
@Autowired
private IAppInfoDao projectInfoDao;
public void loadDataFromDb() {
log.info("load app datas from db ...");
List<AppInfo> projects = projectInfoDao.queryAll();
for(AppInfo project:projects){
if(project.getStatus()){
projectsMap.put(project.getAppId(),project);
}
}
log.info("load app datas from db end");
}
public AppInfo getProjectByAppId(String appId){
return projectsMap.get(appId);
}
public boolean haveAppId(String appId){
return projectsMap.containsKey(appId);
}
public Map<String, AppInfo> getProjectsMap() {
return projectsMap;
}
public void setAppInfo(AppInfo info){
String key = info.getAppId();
if(!projectsMap.containsKey(key) && info.getStatus()){
log.info("add new app info to sys");
projectsMap.put(key,info);
}else if(projectsMap.containsKey(key) && !info.equals(projectsMap.get(key))){
if(info.getStatus()){
log.info("reset app info to sys");
projectsMap.put(key,info);
} else{
log.info("delete app info to sys");
projectsMap.remove(key);
}
}
}
}