springboot快速集成jedis实现发布订阅
1.maven依赖以及yml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-common-test</artifactId>
<groupId>com.gwliuc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-pubsub</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
spring:
redis:
host: 127.0.0.1
database: 0
port: 6379
password: 123456
timeout: 5000
jedis:
pool:
max-active: 200
max-wait: -1
max-idle: 16
min-idle: 16
2.集成jedis的配置代码
package com.gwliuc.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Component
@Slf4j
public class CommonConfig {
@Bean
public JedisPoolConfig jedisPoolConfig(@Value("${spring.redis.jedis.pool.max-active}") int maxActive,
@Value("${spring.redis.jedis.pool.max-idle}") int maxIdle,
@Value("${spring.redis.jedis.pool.min-idle}") int minIdle,
@Value("${spring.redis.jedis.pool.max-wait}") long maxWaitMillis) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
return jedisPoolConfig;
}
@Bean
public JedisPool jedisPool(@Value("${spring.redis.host}") String host,
@Value("${spring.redis.password}") String password,
@Value("${spring.redis.port}") int port,
@Value("${spring.redis.timeout}") int timeout, JedisPoolConfig jedisPoolConfig) {
log.info("=====创建JedisPool连接池=====");
log.info("password{}", password);
return new JedisPool(jedisPoolConfig, host, port, timeout, password);
}
}
package com.gwliuc.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;
import javax.annotation.Resource;
@Component
@Slf4j
public class JedisUtil {
@Resource
private JedisPool jedisPool;
public void publish(String channel, String message, int indexDb) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.select(indexDb);
jedis.publish(channel,message);
} catch (Exception e) {
log.error("publish msg fail", e);
}
}
public void subscribe(String channel, JedisPubSub jedisPubSub, int indexDb) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.select(indexDb);
jedis.subscribe(jedisPubSub, channel);
} catch (Exception e) {
log.error("subscribe msg fail", e);
}
}
}
3.发布端
package com.gwliuc.service;
import com.gwliuc.util.JedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class RedisPublish {
@Resource
private JedisUtil jedisUtil;
public void publish(String channel, String message, int indexDb) {
log.info("发布消息{},频道为{}", message, channel);
jedisUtil.publish(channel, message, indexDb);
}
}
4.接收端
package com.gwliuc.service;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.gwliuc.util.JedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPubSub;
import javax.annotation.Resource;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class RedisSubscribe {
@Resource
private JedisUtil jedisUtil;
public void subscribe(String channel ,int indexDb) {
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("subscribe-thread-%d").build();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), factory, new ThreadPoolExecutor.AbortPolicy());
poolExecutor.execute(() -> jedisUtil.subscribe(channel, new RedisSubListener(), indexDb));
log.info("订阅者线程启动");
}
static class RedisSubListener extends JedisPubSub {
@Override
public void onMessage(String channel, String message) {
log.info("收到消息,频道为{},消息为{}", channel, message);
}
}
}
5.测试
5.1项目启动时需要加入订阅线程
package com.gwliuc.init;
import com.gwliuc.constant.RuntimeConstant;
import com.gwliuc.service.RedisSubscribe;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Order(value=0)
public class InitRunner implements CommandLineRunner {
@Resource
private RedisSubscribe redisSubscribe;
@Override
public void run(String... args) throws Exception {
// 项目启动时加入订阅线程
redisSubscribe.subscribe(RuntimeConstant.CHANNEL, RuntimeConstant.REDIS_DB);
}
}
5.2单元测试模拟发布消息
import com.gwliuc.RedisPubSubApplication;
import com.gwliuc.constant.RuntimeConstant;
import com.gwliuc.service.RedisPublish;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest(classes = RedisPubSubApplication.class)
public class RedisPubSubTest {
@Resource
private RedisPublish redisPublish;
@Test
public void testPublish() {
redisPublish.publish(RuntimeConstant.CHANNEL,"全体起立", RuntimeConstant.REDIS_DB);
}
}
5.3测试效果,消息发布之后,订阅者获取到消息
6.注意点
1.发布订阅开辟通道占用cpu,不宜过多。
2.订阅线程需要在发布之后启动,可以放在项目启动的时候加载。