共有 12 個檔案被更改,包括 600 行新增 和 74 行删除
@ -1,23 +0,0 @@ |
|||
package com.aiprose.im; |
|||
|
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc TODO |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/2/25 15:12 |
|||
*/ |
|||
@Data |
|||
public class MsgVo implements Serializable { |
|||
private String sendUserId; |
|||
private String receiveUserId; |
|||
private String message; |
|||
// 1在线用户列表 2点对点消息
|
|||
private Integer msgType; |
|||
} |
@ -0,0 +1,73 @@ |
|||
package com.aiprose.im.config; |
|||
|
|||
import com.aiprose.im.socket.WebSocketPool; |
|||
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.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
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.data.redis.serializer.Jackson2JsonRedisSerializer; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc redis序列化配置,去掉key前面的前缀 |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/3/4 9:52 |
|||
*/ |
|||
@Slf4j |
|||
@Configuration |
|||
public class RedisConfig { |
|||
/** |
|||
* redis消息监听器容器 |
|||
* 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 |
|||
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 |
|||
* @param connectionFactory |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { |
|||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
|||
container.setConnectionFactory(connectionFactory); |
|||
// 监听msgToAll
|
|||
container.addMessageListener(listenerAdapter, new PatternTopic(WebSocketPool.CHANNEL)); |
|||
log.info("Subscribed Redis channel: " + WebSocketPool.CHANNEL); |
|||
return container; |
|||
} |
|||
|
|||
@Bean |
|||
public MessageListenerAdapter messageListenerAdapter(RedisReceiver redisReceiver){ |
|||
return new MessageListenerAdapter(redisReceiver,"receiveMessage"); |
|||
} |
|||
|
|||
@Bean |
|||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); |
|||
redisTemplate.setConnectionFactory(redisConnectionFactory); |
|||
|
|||
// 使用Jackson2JsonRedisSerialize 替换默认序列化
|
|||
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); |
|||
|
|||
// 设置value的序列化规则和 key的序列化规则
|
|||
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); |
|||
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); |
|||
|
|||
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); |
|||
redisTemplate.setKeySerializer(new StringRedisSerializer()); |
|||
redisTemplate.afterPropertiesSet(); |
|||
return redisTemplate; |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.aiprose.im.config; |
|||
|
|||
import com.aiprose.im.socket.MsgVo; |
|||
import com.aiprose.im.socket.WebSocketPool; |
|||
import com.aiprose.im.utils.RedisUtil; |
|||
import com.alibaba.fastjson.JSON; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc TODO |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/3/3 13:49 |
|||
*/ |
|||
@Component |
|||
public class RedisReceiver { |
|||
|
|||
@Autowired |
|||
private WebSocketPool socketPool; |
|||
|
|||
@Autowired |
|||
private RedisUtil redisUtil; |
|||
|
|||
public void receiveMessage(String message){ |
|||
MsgVo msgVo = JSON.parseObject(message, MsgVo.class); |
|||
try { |
|||
// 点对点发送消息
|
|||
if (StringUtils.isNotBlank(msgVo.getReceiveUserId())) { // p2p
|
|||
if(redisUtil.hasKeyHash(WebSocketPool.HASHKEY,msgVo.getReceiveUserId())){ |
|||
String sessionid = redisUtil.getHashIndex(WebSocketPool.HASHKEY, msgVo.getReceiveUserId()).toString(); |
|||
if(socketPool.has(sessionid)){ |
|||
socketPool.get(sessionid).getAsyncRemote().sendText(message); |
|||
} |
|||
} |
|||
} else { // 群发
|
|||
List<Object> sessions = redisUtil.getHash(WebSocketPool.HASHKEY); |
|||
for (Object item : sessions) { |
|||
//如果某个session不存在,分布式环境下可能session在其他的节点,继续给其他人广播
|
|||
try { |
|||
if(socketPool.has(item.toString())){ |
|||
socketPool.get(item.toString()).getAsyncRemote().sendText(message); |
|||
} |
|||
}catch (Exception e1){ |
|||
e1.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.aiprose.im.socket; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc TODO |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/2/25 15:12 |
|||
*/ |
|||
@Data |
|||
public class MsgVo implements Serializable { |
|||
/**发送用户ID*/ |
|||
private String sendUserId; |
|||
/**目标用户ID*/ |
|||
private String receiveUserId; |
|||
/**消息内容*/ |
|||
private String message; |
|||
/** |
|||
* 1在线用户列表 2点对点聊天消息 3返回消息发送状态 |
|||
*/ |
|||
private Integer msgType; |
|||
|
|||
/** 0 成功 1失败 */ |
|||
private Integer msgStatus = 0; |
|||
|
|||
/** 单条消息标识,返回消息发送状态 */ |
|||
private String uuid; |
|||
|
|||
/**发送时间*/ |
|||
private Long sendDate; |
|||
|
|||
private String appId; |
|||
} |
@ -0,0 +1,49 @@ |
|||
package com.aiprose.im.socket; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.websocket.Session; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc TODO |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/3/3 14:04 |
|||
*/ |
|||
@Component |
|||
public class WebSocketPool { |
|||
public static final String HASHKEY = "imonline"; |
|||
public static final String CHANNEL = "channel:ceisim"; |
|||
|
|||
private static Map<String, Session> sessions = null; |
|||
|
|||
@PostConstruct |
|||
public void initMap(){ |
|||
if(sessions == null){ |
|||
sessions = new ConcurrentHashMap<>(); |
|||
} |
|||
} |
|||
|
|||
public void open(Session session){ |
|||
if(sessions == null){ |
|||
sessions = new ConcurrentHashMap<>(); |
|||
} |
|||
sessions.put(session.getId(),session); |
|||
} |
|||
|
|||
public void close(String key){ |
|||
sessions.remove(key); |
|||
} |
|||
|
|||
public Session get(String key){ |
|||
return sessions.get(key); |
|||
} |
|||
|
|||
public Boolean has(String key){ |
|||
return sessions.containsKey(key); |
|||
} |
|||
} |
@ -0,0 +1,273 @@ |
|||
package com.aiprose.im.utils; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.redis.core.HashOperations; |
|||
import org.springframework.data.redis.core.ListOperations; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.core.ValueOperations; |
|||
import org.springframework.data.redis.support.atomic.RedisAtomicLong; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author yanpeng |
|||
* @version 1.0 |
|||
* @desc TODO |
|||
* @company 北京中经网软件有限公司 |
|||
* @date 2022/3/3 10:35 |
|||
*/ |
|||
@Component |
|||
public class RedisUtil { |
|||
|
|||
@Autowired |
|||
private RedisTemplate redisTemplate; |
|||
|
|||
@Resource(name="redisTemplate") |
|||
private ValueOperations<String, Object> valueOperations; |
|||
@Resource(name="redisTemplate") |
|||
private HashOperations<String, String, Object> hashOperations; |
|||
@Resource(name="redisTemplate") |
|||
private ListOperations<Object, Object> listOperations; |
|||
|
|||
/** |
|||
* @param key |
|||
* @param value |
|||
* @param expireTime |
|||
* @Title: set |
|||
* @Description: set cache. |
|||
*/ |
|||
public void set(String key, int value, Date expireTime) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
counter.set(value); |
|||
counter.expireAt(expireTime); |
|||
} |
|||
|
|||
/** |
|||
* @param key |
|||
* @param value |
|||
* @param timeout |
|||
* @param unit |
|||
* @Title: set |
|||
* @Description: set cache. |
|||
*/ |
|||
public void set(String key, int value, long timeout, TimeUnit unit) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
counter.set(value); |
|||
counter.expire(timeout, unit); |
|||
} |
|||
|
|||
/** |
|||
* @param key |
|||
* @return |
|||
* @Title: generate |
|||
* @Description: Atomically increments by one the current value. |
|||
*/ |
|||
public long generate(String key) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
return counter.incrementAndGet(); |
|||
} |
|||
|
|||
/** |
|||
* @param key |
|||
* @return |
|||
* @Title: generate |
|||
* @Description: Atomically increments by one the current value. |
|||
*/ |
|||
public long generate(String key, Date expireTime) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
counter.expireAt(expireTime); |
|||
return counter.incrementAndGet(); |
|||
} |
|||
|
|||
/** |
|||
* @param key |
|||
* @param increment |
|||
* @return |
|||
* @Title: generate |
|||
* @Description: Atomically adds the given value to the current value. |
|||
*/ |
|||
public long generate(String key, int increment) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
return counter.addAndGet(increment); |
|||
} |
|||
|
|||
/** |
|||
* @param key |
|||
* @param increment |
|||
* @param expireTime |
|||
* @return |
|||
* @Title: generate |
|||
* @Description: Atomically adds the given value to the current value. |
|||
*/ |
|||
public long generate(String key, int increment, Date expireTime) { |
|||
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); |
|||
counter.expireAt(expireTime); |
|||
return counter.addAndGet(increment); |
|||
} |
|||
|
|||
/** |
|||
* 保存对象 |
|||
* @param key |
|||
* @param value |
|||
*/ |
|||
public void setObject(String key,Object value) { |
|||
valueOperations.set(key, value); |
|||
} |
|||
|
|||
/** |
|||
* 获取对象 |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Object getObect(String key) { |
|||
return valueOperations.get(key); |
|||
} |
|||
|
|||
/** |
|||
* 删除对象 |
|||
* @param key |
|||
*/ |
|||
public void deleteObject(String key) { |
|||
redisTemplate.delete(key); |
|||
} |
|||
|
|||
/** |
|||
* 插入HaspMap |
|||
* @param key |
|||
* @param hashKey |
|||
* @param value |
|||
*/ |
|||
public void setHash(String key, String hashKey, Object value) { |
|||
hashOperations.put(key, hashKey, value); |
|||
} |
|||
|
|||
/** |
|||
* 获取知道下标HaspMap |
|||
* @param key |
|||
* @param hashKey |
|||
* @return |
|||
*/ |
|||
public Object getHashIndex(String key, String hashKey) { |
|||
return hashOperations.get(key, hashKey); |
|||
} |
|||
|
|||
/** |
|||
* 获取HaspMap |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public List<Object> getHash(String key) { |
|||
return hashOperations.values(key); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否存在某个key |
|||
* @param key |
|||
* @param hashKey |
|||
* @return |
|||
*/ |
|||
public Boolean hasKeyHash(String key, String hashKey) { |
|||
return hashOperations.hasKey(key, hashKey); |
|||
} |
|||
|
|||
/** |
|||
* 获取Map |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Map<String, Object> entriesHasp(String key){ |
|||
return hashOperations.entries(key); |
|||
} |
|||
|
|||
/** |
|||
* 获取Map |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Set<String> hashKeys(String key){ |
|||
return hashOperations.keys(key); |
|||
} |
|||
|
|||
/** |
|||
* 大小 |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public long sizeHash(String key) { |
|||
return hashOperations.size(key); |
|||
} |
|||
|
|||
/** |
|||
* 删除HaspMap |
|||
* @param key |
|||
* @param value |
|||
*/ |
|||
public void deleteHash(String key, Object value) { |
|||
hashOperations.delete(key, value); |
|||
} |
|||
|
|||
/** |
|||
* 数组里面添加元素 |
|||
* @param key |
|||
* @param value |
|||
*/ |
|||
public void setLeftList(String key,Object value) |
|||
{ |
|||
listOperations.leftPush(key, value); |
|||
} |
|||
|
|||
/** |
|||
* 全部添加 |
|||
* @param key |
|||
* @param list |
|||
*/ |
|||
public void setleftAllList(String key, List list) { |
|||
listOperations.leftPushAll(key, list); |
|||
} |
|||
|
|||
/** |
|||
* 对指定下标的数组元素进行替换 |
|||
* @param key |
|||
* @param index |
|||
* @param value |
|||
*/ |
|||
public void setList(String key,long index,Object value) { |
|||
listOperations.set(key, index, value); |
|||
} |
|||
|
|||
/** |
|||
* 数组大小 |
|||
* @param key |
|||
*/ |
|||
public long sizeList(String key) { |
|||
return listOperations.size(key); |
|||
} |
|||
|
|||
/** |
|||
* 获取指定下标元素 |
|||
* @param key |
|||
* @param index |
|||
* @return |
|||
*/ |
|||
public Object getListIndex(String key,long index) { |
|||
return listOperations.index(key, index); |
|||
} |
|||
|
|||
/** |
|||
* 获取list 指定开始-结束 |
|||
* @param key |
|||
* @param start 开始 |
|||
* @param end 结束 |
|||
* @return |
|||
*/ |
|||
public Object getList(String key,long start, long end) { |
|||
return listOperations.range(key, start, end); |
|||
} |
|||
|
|||
} |
@ -1,2 +1,7 @@ |
|||
server: |
|||
port: 8999 |
|||
spring: |
|||
redis: |
|||
database: 5 |
|||
host: 192.168.0.120 |
|||
port: 6379 |
載入中…
新增問題並參考