import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
@Slf4j
@Configuration
@Component
@SuppressWarnings("unused")
public class RedisQueueScanEvent implements ApplicationListener<ContextRefreshedEvent> {
static final ConcurrentHashMap<String, AdapterConsumer> REDIS_QUEUE_LISTENER = new ConcurrentHashMap<>();
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Bean
RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
return container;
}
@Autowired
RedisMessageListenerContainer redisMessageListenerContainer;
public static class Message implements Serializable {
private static final long version = 1L;
private final byte[] body;
public Message(byte[] body) {
this.body = body;
}
public byte[] getBody() {
return body;
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisQueue {
String[] queues();
}
static class AdapterConsumer implements Serializable {
private static final long version = 1L;
private final CountDownLatch latch;
private final String queueName;
private final Object bean;
private final Method method;
public AdapterConsumer(CountDownLatch latch, String queueName, Object bean, Method method) {
this.latch = latch;
this.queueName = queueName;
this.bean = bean;
this.method = method;
}
/**
* 队列消息接收方法
*/
public void consumeMessage(String json) {
log.info(String.format("redis队列名:%s,消息内容:%s", queueName, json));
try {
method.invoke(bean, new Message(json.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
log.error(String.format("消费redis消息队列数据失败,失败信息:%s", e));
//推送失败的话可以保存到数据库,然后定时推送
} finally {
latch.countDown();
}
}
}
void addRedisMessageListenerContainer() {
REDIS_QUEUE_LISTENER.forEach((queueName, consumer) -> {
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(consumer, "consumeMessage");
messageListenerAdapter.afterPropertiesSet();
redisMessageListenerContainer.addMessageListener(messageListenerAdapter, new PatternTopic(queueName));
});
redisMessageListenerContainer.afterPropertiesSet();
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 根容器为Spring容器
if (Objects.nonNull(event.getApplicationContext().getParent())) {
return;
}
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(Component.class);
for (Object bean : beans.values()) {
Method[] methods = bean.getClass().getMethods();
for (Method declaredMethod : methods) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].isAssignableFrom(Message.class)) {
RedisQueue ma = AnnotationUtils.findAnnotation(declaredMethod, RedisQueue.class);
if (Objects.nonNull(ma)) {
for (String queueName : ma.queues()) {
REDIS_QUEUE_LISTENER.put(queueName, new AdapterConsumer(new CountDownLatch(1), queueName, bean, declaredMethod));
}
}
}
}
}
addRedisMessageListenerContainer();
}
}
redis-cli
publish fincmp.adapter.queue '{"name":"zs","age":18}'
写入队列
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.nio.charset.StandardCharsets;
public class RedisUtil {
/**
* 1.配置连接池参数
*/
public static JedisPoolConfig getPoolConfig() {
JedisPoolConfig jedisPoolConfig = new redis.clients.jedis.JedisPoolConfig();
jedisPoolConfig.setMaxTotal(1024);
jedisPoolConfig.setMaxIdle(100);
jedisPoolConfig.setMinEvictableIdleTimeMillis(50000);
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(20000);
jedisPoolConfig.setNumTestsPerEvictionRun(-1);
jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(10000);
jedisPoolConfig.setMaxWaitMillis(1000);
jedisPoolConfig.setTestOnBorrow(true);
jedisPoolConfig.setTestWhileIdle(true);
jedisPoolConfig.setTestOnReturn(false);
jedisPoolConfig.setJmxEnabled(true);
jedisPoolConfig.setJmxNamePrefix("pool");
jedisPoolConfig.setBlockWhenExhausted(false);
return jedisPoolConfig;
}
/**
* 2.获取连接工厂
*/
public static JedisConnectionFactory getConnectionFactory(JedisPoolConfig poolConfig) {
JedisConnectionFactory jedisConnectFactory = new JedisConnectionFactory();
//必须执行这个函数,初始化RedisTemplate
jedisConnectFactory.afterPropertiesSet();
return jedisConnectFactory;
}
/**
* 3.获取RedisTemplate实例
*/
public static RedisTemplate<String, String> getRedisTemplate(JedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
StringRedisSerializer serializer = new StringRedisSerializer();
redisTemplate.setDefaultSerializer(serializer);
redisTemplate.setKeySerializer(serializer);
redisTemplate.setValueSerializer(serializer);
//必须执行这个函数,初始化RedisTemplate
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
public static void main(String[] args) {
String queueName = "a";
String data = "{\"name\":\"张三\",\"age\":18}";
//1.配置连接池参数
JedisPoolConfig poolConfig = getPoolConfig();
//2.获取连接工厂
JedisConnectionFactory connectionFactory = getConnectionFactory(poolConfig);
connectionFactory.getConnection().publish(queueName.getBytes(StandardCharsets.UTF_8), data.getBytes(StandardCharsets.UTF_8));
}
}
import java.io.*;
import java.nio.charset.StandardCharsets;
@SuppressWarnings("unused")
public class Message {
public Message() {
throw new IllegalStateException("Utility class");
}
public static void main(String[] args) {
String s = runCmdByRuntime("D:\\apps\\Redis\\redis-cli", "publish a '{\"name\":\"张三\",\"age\":18}'");
System.out.println(s);
}
private static String getReaderLineString(InputStream in) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
if (!br.ready()) {
return "";
}
StringBuilder sb = new StringBuilder();
String curStr;
while ((curStr = br.readLine()) != null) {
sb.append(curStr).append("\n");
}
return sb.toString();
}
public static String runCmdByRuntime(String... cmds) {
StringBuilder stdOutput = new StringBuilder();
Process proc = null;
OutputStream out = null;
InputStream err = null;
InputStream in = null;
try {
Runtime runtime = Runtime.getRuntime();
proc = runtime.exec(cmds[0]);
out = proc.getOutputStream();
err = proc.getErrorStream();
in = proc.getInputStream();
for (int i = 1; i < cmds.length; i++) {
out.write(cmds[i].getBytes(StandardCharsets.UTF_8));
out.flush();
}
out.close();
stdOutput.append(getReaderLineString(err));
stdOutput.append(getReaderLineString(in));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (proc != null) {
proc.destroyForcibly();
}
if (in != null) {
in.close();
}
if (err != null) {
err.close();
}
if (out != null) {
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return stdOutput.toString();
}
}