redis的订阅与发布功能

1:yml配置

server:port: 8082
spring:application:name: order-nacosredis:host: 127.0.0.1password: 123456database: 0
logging:level:root: info

2:pom.xm依赖

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.11</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83_noneautotype</version></dependency>

3-1:RedisConfig

package com.test.order.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import javax.annotation.Resource;
import java.time.Duration;import static java.util.Collections.singletonMap;/*** 开启缓存支持** @author xqf* @Return:*/
@Slf4j
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {@Resourceprivate LettuceConnectionFactory lettuceConnectionFactory;/*** RedisTemplate配置** @param lettuceConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {log.info(" --- redis config init --- ");Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);RedisSerializer<String> stringSerializer = new StringRedisSerializer();// key序列化redisTemplate.setKeySerializer(stringSerializer);// value序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// Hash key序列化redisTemplate.setHashKeySerializer(stringSerializer);// Hash value序列化redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}/*** 缓存配置管理器** @param factory* @return*/@Beanpublic CacheManager cacheManager(LettuceConnectionFactory factory) {Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();// 配置序列化(解决乱码的问题),并且配置缓存默认有效期 6小时RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));//.disableCachingNullValues();// 以锁写入的方式创建RedisCacheWriter对象//update-begin-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*RedisCacheWriter writer = new JeecgRedisCacheWriter(factory, Duration.ofMillis(50L));//RedisCacheWriter.lockingRedisCacheWriter(factory);// 创建默认缓存配置对象/* 默认配置,设置缓存有效期 1小时*///RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));// 自定义配置test:demo 的超时时间为 5分钟RedisCacheManager cacheManager = RedisCacheManager.builder(writer).cacheDefaults(redisCacheConfiguration).withInitialCacheConfigurations(singletonMap(CacheConstant.SYS_DICT_TABLE_CACHE,RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)))).withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues())).withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_RANKING, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues())).withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_PAGE_LIST, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues())).transactionAware().build();//update-end-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*return cacheManager;}/*** redis 监听配置** @param redisConnectionFactory redis 配置* @return*/@Beanpublic RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory, RedisReceiver redisReceiver, MessageListenerAdapter commonListenerAdapter) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(redisConnectionFactory);container.addMessageListener(commonListenerAdapter, new ChannelTopic(GlobalConstants.REDIS_TOPIC_NAME));return container;}@BeanMessageListenerAdapter commonListenerAdapter(RedisReceiver redisReceiver) {MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(redisReceiver, "onMessage");messageListenerAdapter.setSerializer(jacksonSerializer());return messageListenerAdapter;}private Jackson2JsonRedisSerializer jacksonSerializer() {Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);return jackson2JsonRedisSerializer;}}

3-2:RedisReceiver

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.test.order.config;import cn.hutool.core.util.ObjectUtil;
import org.springframework.stereotype.Component;@Component
public class RedisReceiver {public void onMessage(BaseMap params) {Object handlerName = params.get("handlerName");JeecgRedisListener messageListener = (JeecgRedisListener)SpringContextHolder.getHandler(handlerName.toString(), JeecgRedisListener.class);if (ObjectUtil.isNotEmpty(messageListener)) {messageListener.onMessage(params);}}public RedisReceiver() {}public boolean equals(final Object o) {if (o == this) {return true;} else if (!(o instanceof RedisReceiver)) {return false;} else {RedisReceiver other = (RedisReceiver)o;return other.canEqual(this);}}protected boolean canEqual(final Object other) {return other instanceof RedisReceiver;}public int hashCode() {//int result = true;return 1;}public String toString() {return "RedisReceiver()";}
}

3-3:JeecgRedisClient

package com.test.order.config;import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;import javax.annotation.Resource;@Configuration
public class JeecgRedisClient {@Resourceprivate RedisTemplate<String, Object> redisTemplate;public JeecgRedisClient() {}public void sendMessage(String handlerName, BaseMap params) {params.put("handlerName", handlerName);this.redisTemplate.convertAndSend("jeecg_redis_topic", params);}
}

3-4:JeecgRedisListener

package com.test.order.config;public interface JeecgRedisListener {void onMessage(BaseMap message);
}

3-5:BaseMap

package com.test.order.config;import cn.hutool.core.util.ObjectUtil;import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.beanutils.ConvertUtils;public class BaseMap extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public BaseMap() {}public BaseMap(Map<String, Object> map) {this.putAll(map);}public BaseMap put(String key, Object value) {super.put(key, Optional.ofNullable(value).orElse(""));return this;}public BaseMap add(String key, Object value) {super.put(key, Optional.ofNullable(value).orElse(""));return this;}public <T> T get(String key) {Object obj = super.get(key);return ObjectUtil.isNotEmpty(obj) ? (T) obj : null;}public Boolean getBoolean(String key) {Object obj = super.get(key);return ObjectUtil.isNotEmpty(obj) ? Boolean.valueOf(obj.toString()) : false;}public Long getLong(String key) {Object v = this.get(key);return ObjectUtil.isNotEmpty(v) ? new Long(v.toString()) : null;}public Long[] getLongs(String key) {Object v = this.get(key);return ObjectUtil.isNotEmpty(v) ? (Long[])((Long[])v) : null;}public List<Long> getListLong(String key) {List<String> list = (List)this.get(key);return ObjectUtil.isNotEmpty(list) ? (List)list.stream().map((e) -> {return new Long(e);}).collect(Collectors.toList()) : null;}public Long[] getLongIds(String key) {Object ids = this.get(key);return ObjectUtil.isNotEmpty(ids) ? (Long[])((Long[])ConvertUtils.convert(ids.toString().split(","), Long.class)) : null;}public Integer getInt(String key, Integer def) {Object v = this.get(key);return ObjectUtil.isNotEmpty(v) ? Integer.parseInt(v.toString()) : def;}public Integer getInt(String key) {Object v = this.get(key);return ObjectUtil.isNotEmpty(v) ? Integer.parseInt(v.toString()) : 0;}public BigDecimal getBigDecimal(String key) {Object v = this.get(key);return ObjectUtil.isNotEmpty(v) ? new BigDecimal(v.toString()) : new BigDecimal("0");}public <T> T get(String key, T def) {Object obj = super.get(key);return ObjectUtil.isEmpty(obj) ? def : (T) obj;}public static BaseMap toBaseMap(Map<String, Object> obj) {BaseMap map = new BaseMap();map.putAll(obj);return map;}
}

3-6:CacheConstant

package com.test.order.config;/*** @author: huangxutao* @date: 2019-06-14* @description: 缓存常量*/
public interface CacheConstant {/*** 字典信息缓存(含禁用的字典项)*/public static final String SYS_DICT_CACHE = "sys:cache:dict";/*** 字典信息缓存 status为有效的*/public static final String SYS_ENABLE_DICT_CACHE = "sys:cache:dictEnable";/*** 表字典信息缓存*/public static final String SYS_DICT_TABLE_CACHE = "sys:cache:dictTable";public static final String SYS_DICT_TABLE_BY_KEYS_CACHE = SYS_DICT_TABLE_CACHE + "ByKeys";/*** 数据权限配置缓存*/public static final String SYS_DATA_PERMISSIONS_CACHE = "sys:cache:permission:datarules";/*** 缓存用户信息【加密】*/public static final String SYS_USERS_CACHE = "sys:cache:encrypt:user";/*** 全部部门信息缓存*/public static final String SYS_DEPARTS_CACHE = "sys:cache:depart:alldata";/*** 全部部门ids缓存*/public static final String SYS_DEPART_IDS_CACHE = "sys:cache:depart:allids";/*** 测试缓存key*/public static final String TEST_DEMO_CACHE = "test:demo";/*** 字典信息缓存*/public static final String SYS_DYNAMICDB_CACHE = "sys:cache:dbconnect:dynamic:";/*** gateway路由缓存*/public static final String GATEWAY_ROUTES = "sys:cache:cloud:gateway_routes";/*** gateway路由 reload key*/public static final String ROUTE_JVM_RELOAD_TOPIC = "gateway_jvm_route_reload_topic";/*** TODO 冗余代码 待删除*插件商城排行榜*/public static final String PLUGIN_MALL_RANKING = "pluginMall::rankingList";/*** TODO 冗余代码 待删除*插件商城排行榜*/public static final String PLUGIN_MALL_PAGE_LIST = "pluginMall::queryPageList";/*** online列表页配置信息缓存key*/public static final String ONLINE_LIST = "sys:cache:online:list";/*** online表单页配置信息缓存key*/public static final String ONLINE_FORM = "sys:cache:online:form";/*** online报表*/public static final String ONLINE_RP = "sys:cache:online:rp";/*** online图表*/public static final String ONLINE_GRAPH = "sys:cache:online:graph";/*** 拖拽页面信息缓存*/public static final String DRAG_PAGE_CACHE = "drag:cache:page";
}

3-7:CommonAPI

package com.test.order.config;import java.util.List;
import java.util.Map;
import java.util.Set;/*** 通用api* @author: jeecg-boot*/
public interface CommonAPI {/*** 1查询用户角色信息* @param username* @return*/Set<String> queryUserRoles(String username);/*** 2查询用户权限信息* @param userId* @return*/Set<String> queryUserAuths(String userId);/*** 6字典表的 翻译* @param table* @param text* @param code* @param key* @return*/String translateDictFromTable(String table, String text, String code, String key);/*** 7普通字典的翻译* @param code* @param key* @return*/String translateDict(String code, String key);}

3-8:CommonConfig

package com.test.order.config;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class CommonConfig {/*** Spring上下文工具配置** @return*/@Bean//当bean为SpringContextHolder不存在时,就执行下面的方法创建bean@ConditionalOnMissingBean(SpringContextHolder.class)public SpringContextHolder springContextHolder() {SpringContextHolder holder = new SpringContextHolder();return holder;}
}

3-9:CommonSendStatus

package com.test.order.config;/*** 	系统通告 - 发布状态* @Author LeeShaoQing**/
public interface CommonSendStatus {/*** 未发布*/public static final String UNPUBLISHED_STATUS_0 = "0";/*** 已发布*/public static final String PUBLISHED_STATUS_1 = "1";/*** 撤销*/public static final String REVOKE_STATUS_2 = "2";/*** app端推送会话标识后缀*/public static final String  APP_SESSION_SUFFIX = "_app";/**-----【流程相关通知模板code】------------------------------------------------------------*//**流程催办——系统通知消息模板*/public static final String TZMB_BPM_CUIBAN = "bpm_cuiban";/**流程抄送——系统通知消息模板*/public static final String TZMB_BPM_CC = "bpm_cc";/**流程催办——邮件通知消息模板*/public static final String TZMB_BPM_CUIBAN_EMAIL = "bpm_cuiban_email";/**标准模板—系统消息通知*/public static final String TZMB_SYS_TS_NOTE = "sys_ts_note";/**流程超时提醒——系统通知消息模板*/public static final String TZMB_BPM_CHAOSHI_TIP = "bpm_chaoshi_tip";/**-----【流程相关通知模板code】-----------------------------------------------------------*//*** 系统通知拓展参数(比如:用于流程抄送和催办通知,这里额外传递流程跳转页面所需要的路由参数)*/public static final String MSG_ABSTRACT_JSON = "msg_abstract";
}

3-10:GlobalConstants

package com.test.order.config;/**
* @Description: GlobalConstants
* @author: scott
* @date: 2020/01/01 16:01
*/
public class GlobalConstants {/*** 业务处理器beanName传递参数*/public static final String HANDLER_NAME = "handlerName";/*** 路由刷新触发器*/public static final String LODER_ROUDER_HANDLER = "loderRouderHandler";/*** redis消息通道名称*/public static final String REDIS_TOPIC_NAME="jeecg_redis_topic";
}

3-11:JeecgRedisCacheWriter

package com.test.order.config;import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.data.redis.cache.CacheStatistics;
import org.springframework.data.redis.cache.CacheStatisticsCollector;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/**
* 该类参照 DefaultRedisCacheWriter 重写了 remove 方法实现通配符*删除
*
* @author: scott
* @date: 2020/01/01 16:18
*/
@Slf4j
public class JeecgRedisCacheWriter implements RedisCacheWriter {private final RedisConnectionFactory connectionFactory;private final Duration sleepTime;public JeecgRedisCacheWriter(RedisConnectionFactory connectionFactory) {this(connectionFactory, Duration.ZERO);}public JeecgRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) {Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");Assert.notNull(sleepTime, "SleepTime must not be null!");this.connectionFactory = connectionFactory;this.sleepTime = sleepTime;}@Overridepublic void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");Assert.notNull(value, "Value must not be null!");this.execute(name, (connection) -> {if (shouldExpireWithin(ttl)) {connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());} else {connection.set(key, value);}return "OK";});}@Overridepublic byte[] get(String name, byte[] key) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");return (byte[])this.execute(name, (connection) -> {return connection.get(key);});}@Overridepublic byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");Assert.notNull(value, "Value must not be null!");return (byte[])this.execute(name, (connection) -> {if (this.isLockingCacheWriter()) {this.doLock(name, connection);}Object var7;try {boolean put;if (shouldExpireWithin(ttl)) {put = connection.set(key, value, Expiration.from(ttl), SetOption.ifAbsent());} else {put = connection.setNX(key, value);}if (!put) {byte[] var11 = connection.get(key);return var11;}var7 = null;} finally {if (this.isLockingCacheWriter()) {this.doUnlock(name, connection);}}return (byte[])var7;});}@Overridepublic void remove(String name, byte[] key) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");String keyString = new String(key);log.info("redis remove key:" + keyString);String keyIsAll = "*";if(keyString!=null && keyString.endsWith(keyIsAll)){execute(name, connection -> {// 获取某个前缀所拥有的所有的键,某个前缀开头,后面肯定是*Set<byte[]> keys = connection.keys(key);int delNum = 0;for (byte[] keyByte : keys) {delNum += connection.del(keyByte);}return delNum;});}else{this.execute(name, (connection) -> {return connection.del(new byte[][]{key});});}}@Overridepublic void clean(String name, byte[] pattern) {Assert.notNull(name, "Name must not be null!");Assert.notNull(pattern, "Pattern must not be null!");this.execute(name, (connection) -> {boolean wasLocked = false;try {if (this.isLockingCacheWriter()) {this.doLock(name, connection);wasLocked = true;}byte[][] keys = (byte[][])((Set)Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())).toArray(new byte[0][]);if (keys.length > 0) {connection.del(keys);}} finally {if (wasLocked && this.isLockingCacheWriter()) {this.doUnlock(name, connection);}}return "OK";});}void lock(String name) {this.execute(name, (connection) -> {return this.doLock(name, connection);});}void unlock(String name) {this.executeLockFree((connection) -> {this.doUnlock(name, connection);});}private Boolean doLock(String name, RedisConnection connection) {return connection.setNX(createCacheLockKey(name), new byte[0]);}private Long doUnlock(String name, RedisConnection connection) {return connection.del(new byte[][]{createCacheLockKey(name)});}boolean doCheckLock(String name, RedisConnection connection) {return connection.exists(createCacheLockKey(name));}private boolean isLockingCacheWriter() {return !this.sleepTime.isZero() && !this.sleepTime.isNegative();}private <T> T execute(String name, Function<RedisConnection, T> callback) {RedisConnection connection = this.connectionFactory.getConnection();try {this.checkAndPotentiallyWaitUntilUnlocked(name, connection);return callback.apply(connection);} finally {connection.close();}}private void executeLockFree(Consumer<RedisConnection> callback) {RedisConnection connection = this.connectionFactory.getConnection();try {callback.accept(connection);} finally {connection.close();}}private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {if (this.isLockingCacheWriter()) {try {while(this.doCheckLock(name, connection)) {Thread.sleep(this.sleepTime.toMillis());}} catch (InterruptedException var4) {Thread.currentThread().interrupt();throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name), var4);}}}private static boolean shouldExpireWithin(@Nullable Duration ttl) {return ttl != null && !ttl.isZero() && !ttl.isNegative();}private static byte[] createCacheLockKey(String name) {return (name + "~lock").getBytes(StandardCharsets.UTF_8);}//update-begin-author:zyf date:20220216 for:升级springboot版本到2.4.0+以后需要实现的方法*private final CacheStatisticsCollector statistics = CacheStatisticsCollector.create();@Overridepublic CacheStatistics getCacheStatistics(String cacheName) {return statistics.getCacheStatistics(cacheName);}@Overridepublic void clearStatistics(String name) {}@Overridepublic RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheStatisticsCollector) {return null;}//update-begin-author:zyf date:20220216 for:升级springboot版本到2.4.0+以后需要实现的方法*
}

3-12:RedisUtil

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.test.order.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Component
public class RedisUtil {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public RedisUtil() {}public boolean expire(String key, long time) {try {if (time > 0L) {this.redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception var5) {var5.printStackTrace();return false;}}public long getExpire(String key) {return this.redisTemplate.getExpire(key, TimeUnit.SECONDS);}public boolean hasKey(String key) {try {return this.redisTemplate.hasKey(key);} catch (Exception var3) {var3.printStackTrace();return false;}}public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {this.redisTemplate.delete(key[0]);} else {this.redisTemplate.delete(Arrays.asList(key));}}}public Object get(String key) {return key == null ? null : this.redisTemplate.opsForValue().get(key);}public boolean set(String key, Object value) {try {this.redisTemplate.opsForValue().set(key, value);return true;} catch (Exception var4) {var4.printStackTrace();return false;}}public boolean set(String key, Object value, long time) {try {if (time > 0L) {this.redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {this.set(key, value);}return true;} catch (Exception var6) {var6.printStackTrace();return false;}}public long incr(String key, long delta) {if (delta < 0L) {throw new RuntimeException("递增因子必须大于0");} else {return this.redisTemplate.opsForValue().increment(key, delta);}}public long decr(String key, long delta) {if (delta < 0L) {throw new RuntimeException("递减因子必须大于0");} else {return this.redisTemplate.opsForValue().increment(key, -delta);}}public Object hget(String key, String item) {return this.redisTemplate.opsForHash().get(key, item);}public Map<Object, Object> hmget(String key) {return this.redisTemplate.opsForHash().entries(key);}public boolean hmset(String key, Map<String, Object> map) {try {this.redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception var4) {var4.printStackTrace();return false;}}public boolean hmset(String key, Map<String, Object> map, long time) {try {this.redisTemplate.opsForHash().putAll(key, map);if (time > 0L) {this.expire(key, time);}return true;} catch (Exception var6) {var6.printStackTrace();return false;}}public boolean hset(String key, String item, Object value) {try {this.redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception var5) {var5.printStackTrace();return false;}}public boolean hset(String key, String item, Object value, long time) {try {this.redisTemplate.opsForHash().put(key, item, value);if (time > 0L) {this.expire(key, time);}return true;} catch (Exception var7) {var7.printStackTrace();return false;}}public void hdel(String key, Object... item) {this.redisTemplate.opsForHash().delete(key, item);}public boolean hHasKey(String key, String item) {return this.redisTemplate.opsForHash().hasKey(key, item);}public double hincr(String key, String item, double by) {return this.redisTemplate.opsForHash().increment(key, item, by);}public double hdecr(String key, String item, double by) {return this.redisTemplate.opsForHash().increment(key, item, -by);}public Set<Object> sGet(String key) {try {return this.redisTemplate.opsForSet().members(key);} catch (Exception var3) {var3.printStackTrace();return null;}}public boolean sHasKey(String key, Object value) {try {return this.redisTemplate.opsForSet().isMember(key, value);} catch (Exception var4) {var4.printStackTrace();return false;}}public long sSet(String key, Object... values) {try {return this.redisTemplate.opsForSet().add(key, values);} catch (Exception var4) {var4.printStackTrace();return 0L;}}public long sSetAndTime(String key, long time, Object... values) {try {Long count = this.redisTemplate.opsForSet().add(key, values);if (time > 0L) {this.expire(key, time);}return count;} catch (Exception var6) {var6.printStackTrace();return 0L;}}public long sGetSetSize(String key) {try {return this.redisTemplate.opsForSet().size(key);} catch (Exception var3) {var3.printStackTrace();return 0L;}}public long setRemove(String key, Object... values) {try {Long count = this.redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception var4) {var4.printStackTrace();return 0L;}}public List<Object> lGet(String key, long start, long end) {try {return this.redisTemplate.opsForList().range(key, start, end);} catch (Exception var7) {var7.printStackTrace();return null;}}public long lGetListSize(String key) {try {return this.redisTemplate.opsForList().size(key);} catch (Exception var3) {var3.printStackTrace();return 0L;}}public Object lGetIndex(String key, long index) {try {return this.redisTemplate.opsForList().index(key, index);} catch (Exception var5) {var5.printStackTrace();return null;}}public boolean lSet(String key, Object value) {try {this.redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception var4) {var4.printStackTrace();return false;}}public boolean lSet(String key, Object value, long time) {try {this.redisTemplate.opsForList().rightPush(key, value);if (time > 0L) {this.expire(key, time);}return true;} catch (Exception var6) {var6.printStackTrace();return false;}}public boolean lSet(String key, List<Object> value) {try {this.redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception var4) {var4.printStackTrace();return false;}}public boolean lSet(String key, List<Object> value, long time) {try {this.redisTemplate.opsForList().rightPushAll(key, value);if (time > 0L) {this.expire(key, time);}return true;} catch (Exception var6) {var6.printStackTrace();return false;}}public boolean lUpdateIndex(String key, long index, Object value) {try {this.redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception var6) {var6.printStackTrace();return false;}}public long lRemove(String key, long count, Object value) {try {Long remove = this.redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception var6) {var6.printStackTrace();return 0L;}}}

3-13:SocketHandler

package com.test.order.config;import cn.hutool.core.util.ObjectUtil;
import com.test.order.controller.OrderController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Slf4j
@Component(WebSocket.REDIS_TOPIC_NAME)
public class SocketHandler implements JeecgRedisListener {@Autowiredprivate WebSocket webSocket;@Overridepublic void onMessage(BaseMap map) {log.info("【Redis发布订阅模式】redis Listener: {},参数:{}",WebSocket.REDIS_TOPIC_NAME, map.toString());String userId = map.get("userId");String message = map.get("message");if (ObjectUtil.isNotEmpty(userId)) {//pc端消息推送具体人webSocket.pushMessage(userId, message);//app端消息推送具体人webSocket.pushMessage(userId+CommonSendStatus.APP_SESSION_SUFFIX, message);} else {//推送全部webSocket.pushMessage(message);}}
}

3-14:SpringContextHolder

package com.test.order.config;import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;/*** 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.** @author zyf*/
@Slf4j
public class SpringContextHolder implements ApplicationContextAware {private static ApplicationContext applicationContext;/*** 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {// NOSONARSpringContextHolder.applicationContext = applicationContext;}/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {checkApplicationContext();return applicationContext;}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getBean(String name) {checkApplicationContext();return (T) applicationContext.getBean(name);}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getHandler(String name, Class<T> cls) {T t = null;if (ObjectUtil.isNotEmpty(name)) {checkApplicationContext();try {t = applicationContext.getBean(name, cls);} catch (Exception e) {log.warn("Customize redis listener handle [ " + name + " ], does not exist!");}}return t;}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getBean(Class<T> clazz) {checkApplicationContext();return applicationContext.getBean(clazz);}/*** 清除applicationContext静态变量.*/public static void cleanApplicationContext() {applicationContext = null;}private static void checkApplicationContext() {if (applicationContext == null) {throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");}}}

3-15:WebsocketConst

package com.test.order.config;/*** @Description: Websocket常量类* @author: taoyan* @date: 2020年03月23日*/
public class WebsocketConst {/*** 消息json key:cmd*/public static final String MSG_CMD = "cmd";/*** 消息json key:msgId*/public static final String MSG_ID = "msgId";/*** 消息json key:msgTxt*/public static final String MSG_TXT = "msgTxt";/*** 消息json key:userId*/public static final String MSG_USER_ID = "userId";/*** 消息json key:chat*/public static final String MSG_CHAT = "chat";/*** 消息类型 heartcheck*/public static final String CMD_CHECK = "heartcheck";/*** 消息类型 user 用户消息*/public static final String CMD_USER = "user";/*** 消息类型 topic 系统通知*/public static final String CMD_TOPIC = "topic";/*** 消息类型 email*/public static final String CMD_EMAIL = "email";/*** 消息类型 meetingsign 会议签到*/public static final String CMD_SIGN = "sign";/*** 消息类型 新闻发布/取消*/public static final String NEWS_PUBLISH = "publish";}

3-16:WebSocket

package com.test.order.config;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;/*** @Author scott* @Date 2019/11/29 9:41* @Description: 此注解相当于设置访问URL*/
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {/**线程安全Map*/private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();/*** Redis触发监听名字*/public static final String REDIS_TOPIC_NAME = "socketHandler";@Autowiredprivate JeecgRedisClient jeecgRedisClient;//==========【websocket接受、推送消息等方法 —— 具体服务节点推送ws消息】========================================================================================@OnOpenpublic void onOpen(Session session, @PathParam(value = "userId") String userId) {try {sessionPool.put(userId, session);log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPool.size());} catch (Exception e) {}}@OnClosepublic void onClose(@PathParam("userId") String userId) {try {sessionPool.remove(userId);log.info("【系统 WebSocket】连接断开,总数为:" + sessionPool.size());} catch (Exception e) {e.printStackTrace();}}/*** ws推送消息** @param userId* @param message*/public void pushMessage(String userId, String message) {for (Map.Entry<String, Session> item : sessionPool.entrySet()) {//userId key值= {用户id + "_"+ 登录token的md5串}//TODO vue2未改key新规则,暂时不影响逻辑if (item.getKey().contains(userId)) {Session session = item.getValue();try {//update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MUsynchronized (session){log.debug("【系统 WebSocket】推送单人消息:" + message);session.getBasicRemote().sendText(message);}//update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU} catch (Exception e) {log.error(e.getMessage(),e);}}}}/*** ws遍历群发消息*/public void pushMessage(String message) {try {for (Map.Entry<String, Session> item : sessionPool.entrySet()) {try {item.getValue().getAsyncRemote().sendText(message);} catch (Exception e) {log.error(e.getMessage(), e);}}log.debug("【系统 WebSocket】群发消息:" + message);} catch (Exception e) {log.error(e.getMessage(), e);}}/*** ws接受客户端消息*/@OnMessagepublic void onMessage(String message, @PathParam(value = "userId") String userId) {if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){log.debug("【系统 WebSocket】收到客户端消息:" + message);}else{log.debug("【系统 WebSocket】收到客户端消息:" + message);}//        //------------------------------------------------------------------------------
//        JSONObject obj = new JSONObject();
//        //业务类型
//        obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);
//        //消息内容
//        obj.put(WebsocketConst.MSG_TXT, "心跳响应");
//        this.pushMessage(userId, obj.toJSONString());
//        //------------------------------------------------------------------------------}/*** 配置错误信息处理** @param session* @param t*/@OnErrorpublic void onError(Session session, Throwable t) {log.warn("【系统 WebSocket】消息出现错误");t.printStackTrace();}//==========【系统 WebSocket接受、推送消息等方法 —— 具体服务节点推送ws消息】========================================================================================//==========【采用redis发布订阅模式——推送消息】========================================================================================/*** 后台发送消息到redis** @param message*/public void sendMessage(String message) {//log.debug("【系统 WebSocket】广播消息:" + message);BaseMap baseMap = new BaseMap();baseMap.put("userId", "");baseMap.put("message", message);jeecgRedisClient.sendMessage(WebSocket.REDIS_TOPIC_NAME, baseMap);}/*** 此为单点消息 redis** @param userId* @param message*/public void sendMessage(String userId, String message) {BaseMap baseMap = new BaseMap();baseMap.put("userId", userId);baseMap.put("message", message);jeecgRedisClient.sendMessage(WebSocket.REDIS_TOPIC_NAME, baseMap);}/*** 此为单点消息(多人) redis** @param userIds* @param message*/public void sendMessage(String[] userIds, String message) {for (String userId : userIds) {sendMessage(userId, message);}}//=======【采用redis发布订阅模式——推送消息】==========================================================================================}

WebSocket 使用@ServerEndpoint生效需要注入一下bean,如果需要WebSocket 认证功能点击连接去查看

    /*** 	注入ServerEndpointExporter,* 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}

3-17:OrderController

package com.test.order.controller;import com.alibaba.fastjson.JSONObject;
import com.test.order.config.WebSocket;
import com.test.order.config.WebsocketConst;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Description:* @Author: xu* @Data: 2024-2024/3/29-16* @Version: V1.0*/
@RestController
@RequestMapping("/order")
public class OrderController {@ResourceWebSocket webSocket;@RequestMapping("/test")public void add() {String message="message22";JSONObject obj = new JSONObject();obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_TOPIC);obj.put(WebsocketConst.MSG_ID, "M0001");obj.put(WebsocketConst.MSG_TXT, message);webSocket.sendMessage(obj.toJSONString());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

x-cmd ai | x openai - 用于发送 openai API 请求,以及与 ChatGPT 对话

介绍 Openai 模块是 Openai 大模型 Chatgpt 3 和 ChatGPT 4 命令行实现。x-cmd 提供了多个不同平台间多种 AI 大模型的调用能力。无论是本地模型还是 Web 服务上的模型&#xff0c;用户都可以在不同的 AI 大模型间直接无缝切换&#xff0c;并能把之前的聊天记录发送给新的大模…

CSS3 伪元素与伪类选择器区别、详解与应用实例

伪元素与伪类两者都是通过在选择器后附加一个特定的关键字来定义&#xff0c;遵循相似的语法规则&#xff0c;并在 CSS 规则块中设置相应的样式。伪元素 能够通过 content 属性添加或替换内容。例如&#xff0c;:before 和 :after 可以插入文本、图像或其他生成的内容。伪类 仅…

Tomcat核心组件深度解析

Server组件 Service组件 连接器Connector组件 容器Container组件

【研发管理】产品经理知识体系-产品创新管理

导读&#xff1a; 产品创新管理对企业的发展具有深远的影响&#xff0c;它不仅是企业保持竞争优势的关键&#xff0c;也是推动企业持续稳定发展的重要动力。因此&#xff0c;企业应高度重视产品创新管理&#xff0c;并采取有效的策略和方法来推动产品创新活动的开展。对于产品经…

第20天:信息打点-红蓝队自动化项目资产侦察企查产权武器库部署网络空间

第二十天 一、工具项目-红蓝队&自动化部署 自动化-武器库部署-F8x 项目地址&#xff1a;https://github.com/ffffffff0x/f8x 介绍&#xff1a;一款红/蓝队环境自动化部署工具,支持多种场景,渗透,开发,代理环境,服务可选项等.下载&#xff1a;wget -O f8x https://f8x.io…

MINIO安装的方法(WindowsLiunx)

2 minio安装教程 注&#xff1a;官方中文文档&#xff1a;MinIO对象存储 Windows — MinIO中文文档 | MinIO Windows中文文档 Liunx 安装方&#xff1a;MinIO对象存储 Linux — MinIO中文文档 | MinIO Linux中文文档 2.1 下载地址 https://dl.min.io/server/minio/…

js html生成图片

js html生成图片 下载依赖 npm install html2canvas引入依赖 import html2canvas from "html2canvas"代码 export const handleHtml2Img async (el, options {}) > {let canvas await new html2canvas(el, options);let imgUrl canvas.toDataURL();return…

论文笔记:UrbanGPT: Spatio-Temporal Large Language Models

1 intro 时空预测的目标是预测并洞察城市环境随时间和空间不断变化的动态。其目的是预见城市生活多个方面的未来模式、趋势和事件&#xff0c;包括交通、人口流动和犯罪率。虽然已有许多努力致力于开发神经网络技术&#xff0c;以准确预测时空数据&#xff0c;但重要的是要注意…

springboot是什么?

可以应用于Web相关的应用开发。 选择合适的框架&#xff0c;去开发相关的功能&#xff0c;会有更高的效率。 为什么Spring Boot才是你该学的!学java找工作必会技能!在职程序员带你梳理JavaEE框架_哔哩哔哩_bilibili java工程师的必备技能 Spring是Java EE领域的企业级开发宽…

html5与css3前端学习笔记

一、前端页面开发流程 创建页面项目目录使用Photoshop对效果图切图&#xff0c;切出网页制作中需要的小图片将装饰类图像合并&#xff0c;制作成雪碧图结合Photoshop和代码编辑器&#xff0c;参照效果图&#xff0c;进行html和css代码书写&#xff0c;制作页面 二、CSS权重 …

万兆网络的十字路口:电口还是光模块?

&#x1f335;在构建高速、高效的网络系统时&#xff0c;选择正确的连接技术至关重要。万兆电口&#xff08;10GBASE-T&#xff09;和万兆光模块&#xff08;SFP&#xff09;是目前市场上两种主流的高速网络解决方案。它们各有优势&#xff0c;但在不同的应用场景和需求下&…

稀碎从零算法笔记Day54-LeetCode:39. 组合总和

题型&#xff1a;数组、树、DFS、回溯 链接&#xff1a;39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数…

在PostgreSQL中如何进行全文搜索,以及如何优化全文搜索性能?

文章目录 如何进行全文搜索1. 创建全文搜索向量2. 执行全文搜索查询 如何优化全文搜索性能1. 使用GIN索引2. 限制搜索范围3. 优化文本处理4. 使用并发搜索5. 监控和调整配置 在PostgreSQL中&#xff0c;全文搜索通常通过使用tsvector和tsquery类型&#xff0c;以及to_tsvector和…

python笔记之面向对象

目录 一、面向对象&#xff08;OOP--Object Oriented Programming&#xff09; 二、面对对象的三大特征 1、封装 2、继承 3、多态 三、函数重载&#xff08;overload&#xff09; 一、面向对象&#xff08;OOP--Object Oriented Programming&#xff09; ——把解决问题…

安居水站:水站经营秘籍:年入30万不是梦。水站创业指南。

在这个快节奏的社会里&#xff0c;初创企业家们总是在寻找一条明路&#xff0c;以在竞争激烈的市场中立足。为了帮助他们更好地实现这一目标&#xff0c;我根据经验决定制定一份水站经营指导手册。这份手册将详细阐述如何从零起步&#xff0c;如何运营&#xff0c;如何进行市场…

制作一个RISC-V的操作系统十二-定时器中断

文章目录 CLINT定时器中断mtimemtimecmp机制总体框架流程时间节拍系统时钟代码 CLINT 产生软件中断和定时器中断 定时器中断 mtime 类似计数器&#xff0c;按照硬件对应的固定频率递增 上电后会自动复位为0&#xff0c;有硬件自动完成 mtimecmp 需要自己设置&#xff0…

Java 多线程加法求和

Java 多线程加法求和 代码 先上代码再上解析&#xff1a; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger;public class Sum implements …

文件IO总结

标准C库IO函数和Linux系统IO函数对比 标准c库IO函数 标准C库提供了一系列的输入输出&#xff08;IO&#xff09;函数&#xff0c;这些函数主要包括在 <stdio.h> 头文件中。这些函数可以大致分为几类&#xff1a; 文件操作函数&#xff1a; fopen&#xff1a;打开文件fc…

Linux内核之内核通知文件系统创建的路径:fsnotify_create用法实例(五十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【图解计算机网络】从浏览器地址输入到网页显示的整个过程

从浏览器地址输入到网页显示的整个过程 整体流程DHCPhttp协议报文组装DNSTCP协议封装与TCP三次握手IP协议封装与路由表MAC地址与ARP协议交换机路由器 整体流程 从往浏览器输入一个地址到网页的显示&#xff0c;要经过很长的一个流程&#xff0c;中间涉及到计算机网络的许多知识…