12 changed files with 600 additions and 74 deletions
@ -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: |
server: |
||||
port: 8999 |
port: 8999 |
||||
|
spring: |
||||
|
redis: |
||||
|
database: 5 |
||||
|
host: 192.168.0.120 |
||||
|
port: 6379 |
Loading…
Reference in new issue