redis5新加了stream流類型,可以代替之前基於list的發佈訂閱,採用類似kafka的設計方式,而且支持持久化,常規場景mq代替品,是廣大碼農的福音,如果不是具有巨大流量,則可以完全棄用其他單獨
(因爲是key設計模式,所以基本是單節點承受壓力,所以單個topic流量不能過於巨大,不過萬級不是問題)
以下是基本命令:
增加一條數據: xadd mzvd:vedio * data aaa
創建一個消費組: XGROUP create mzvd:vedio g1 $
通過組讀取數據: XREADGROUP group g1 cons1 count 1 block 0
處理完消息進行回執:XACK mzvd:vedio group 1000001
如果採用spring-data-redis,springboot版本到2.2.7,redisTemaplet已經有了封裝,我這提供了3個類,分別爲消費者、提供者、屬性配置類
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import com.alibaba.fastjson.JSONObject;
import cn.stylefeng.guns.base.utils.SpringUtil;
import io.lettuce.core.XReadArgs.StreamOffset;
import lombok.extern.slf4j.Slf4j;
/**
* @author yy
* @date 2020年5月12日
* @description redis隊列
*
*/
@Slf4j
public abstract class RedisMQConsumer implements InitializingBean{
private RedisTemplate<String, String> redisTemplate;
private RedisMQProperty mqProperty;
private static String key="";
public void setMqProperty(RedisMQProperty mqProperty) {
this.mqProperty = mqProperty;
}
/**
* @author yy
* @date 2020年5月12日
* @description 初始化信息
*/
public void initConsumer(RedisTemplate<String, String> redisTemplate,RedisMQProperty mqProperty) {
this.redisTemplate=redisTemplate;
this.mqProperty=mqProperty;
synchronized(key) {
log.info("MQ初始化信息開始");
//初始化隊列
key = RedisMQProperty.prex+mqProperty.topic;
RecordId recordId = redisTemplate.boundStreamOps(key).add(new HashedMap() {{put("-1", "-1");}});
this.redisTemplate.boundStreamOps(key).delete(recordId.getValue());
//初始化組
this.redisTemplate.boundStreamOps(key).destroyGroup(mqProperty.group);
redisTemplate.boundStreamOps(key).createGroup(ReadOffset.latest(), mqProperty.group);
log.info("MQ初始化信息完畢");
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//觸發
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
event();
}
}
}).start();
}
}
private void event() {
List<MapRecord<String, Object, Object>> list=
this.redisTemplate.boundStreamOps(RedisMQConsumer.key).read(Consumer.from(mqProperty.group, mqProperty.consname),ReadOffset.lastConsumed());
if(list==null || list.isEmpty())
return;
try {
message(list.get(0).getValue().get("data").toString());
} catch (Exception e) {
handlerException(list.get(0).getValue(), e);
}
this.redisTemplate.boundStreamOps(key).acknowledge(mqProperty.group, list.get(0).getId().getValue());
}
/**
* @author yy
* @date 2020年5月12日
* @description 處理消息
* @param o
*/
public abstract void message(String json) ;
/**
* @author yy
* @date 2020年5月12日
* @description 處理異常
* @param map
* @param exception
*/
protected void handlerException(Map<Object, Object> map,Exception exception) {
log.info("mq消費異常:"+JSONObject.toJSONString(map),exception);
}
}
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* @author yy
* @date 2020年5月12日
* @description redis隊列
*
*/
@Component
public class RedisMQProvier {
@Resource
private RedisTemplate<String, String> redisTemplate;
private String prex=RedisMQProperty.prex;
/**
* @author yy
* @date 2020年5月12日
* @description 發佈隊列
* @param <T>
* @param topic
* @param obj
*/
public <T> void pub(String topic,T obj) {
Map<Object,Object> data = new HashedMap();
data.put("data", JSONObject.toJSONString(obj));
redisTemplate.boundStreamOps(prex+topic).add(data);
redisTemplate.boundStreamOps(prex+topic).trim(20000);
}
int i=0;
// @Scheduled(fixedDelay = 10)
private void pubSomething() {
pub("vedio", i);
i++;
System.out.println(i);
// pub("vedio", System.currentTimeMillis()+"====>");
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* @author yy
* @date 2020年5月12日
* @description redismq配置
*
*/
@Component
@NoArgsConstructor
@AllArgsConstructor
public class RedisMQProperty {
public static final String prex="mq:";
public String topic;
public String group;
public String consname;
public void setRedisMQProperty(String topic, String group, String consname) {
this.topic = topic;
this.group = group;
this.consname = consname;
}
}
配置文件
#redismq
redis.mq.vedio.topic=vedio
redis.mq.vedio.group=g1
redis.mq.vedio.consname=c1