• SpringBoot开发网页即时聊天室/IM通信/WebSocket/即时通讯


    现在的java开发基本都是基于springboot,所以我们就基于springboot2环境。

    效果展示(基于网页浏览器的,不是桌面程序)

    通讯协议:

    现在的主流浏览器基本都支持html5标准,所以我们就用websocket作为通信协议。

    后端框架:

    后端采用springboot作为主框架,配合websocket模块与前端通信,通过springboot整合redis,mybatis,mysql。

    消息中转:

    使用redis的订阅发布机制作为消息中转,实现消息的顺序消费,即保证数据的实时性,也保证数据的安全性。

    聊天方式:

    单人聊天,多人群聊,

    离线消息:

    当群聊时,或者单人聊天时,如果对方不在线,则记录离线消息,当对方上线时会统一接收。

    代码参考

    redis配置

    1. @Configuration
    2. public class RedisConfig {
    3. /**
    4. * redis消息监听器容器
    5. * 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
    6. * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
    7. * @param connectionFactory
    8. * @param listenerAdapter
    9. * @return
    10. */
    11. @Bean
    12. RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
    13. MessageListenerAdapter listenerAdapter) {
    14. RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    15. container.setConnectionFactory(connectionFactory);
    16. //订阅了一个叫chat 的通道
    17. container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
    18. //这个container 可以添加多个 messageListener
    19. return container;
    20. }
    21. /**
    22. * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
    23. * @param receiver
    24. * @return
    25. */
    26. @Bean
    27. MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
    28. //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
    29. //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
    30. return new MessageListenerAdapter(receiver, "receiveMessage");
    31. }
    32. /**redis 读取内容的template */
    33. @Bean(name="stringRedisTemplate")
    34. StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
    35. return new StringRedisTemplate(connectionFactory);
    36. }
    37. }

    消息中转器

    1. @EnableScheduling
    2. @Component
    3. public class RedisReceiver {
    4. @Autowired
    5. StringRedisTemplate stringRedisTemplate;
    6. @Autowired
    7. UserDao userDao;
    8. @Autowired
    9. RecordDao recordDao;
    10. public static class SessionInfo {
    11. public Session session;
    12. public long userid;
    13. public SessionInfo(Session session, long userid) {
    14. this.session = session;
    15. this.userid = userid;
    16. }
    17. }
    18. ObjectMapper objectMapper = new ObjectMapper();
    19. public List list = new LinkedList();
    20. public void onOpen(Session session, long userid){
    21. if(isUserOnline(userid)){
    22. try {
    23. System.out.println("已经在线");
    24. send(session,"已经在线");
    25. session.close();
    26. return;
    27. } catch (IOException e) {
    28. e.printStackTrace();
    29. }
    30. }
    31. synchronized (list) {
    32. list.add(new SessionInfo(session,userid));
    33. System.out.println("有人上线了:"+list.size());
    34. stringRedisTemplate.opsForValue().setBit("ONLINE", userid, true);
    35. sendOnlineOrOffline(userid,"online");
    36. }
    37. //推送离线消息
    38. List records=recordDao.findUnread(userid);
    39. records.forEach(record -> {
    40. try {
    41. sendToUserid(userid,record.getContent());
    42. record.setUsed(true);
    43. recordDao.save(record);
    44. } catch (Exception e) {
    45. e.printStackTrace();
    46. }
    47. });
    48. }
    49. }

    用户模型

    1. @Data
    2. @Entity
    3. @Table(name="im_user")
    4. public class User {
    5. @Id@GeneratedValue
    6. private Long id;
    7. private String username;
    8. private String sign;
    9. private String status;
    10. private String avatar;
    11. private Date created;
    12. private String password;
    13. }

    消息记录

    1. @Data
    2. @Entity
    3. @Table(name="im_record")
    4. public class Record {
    5. @Id
    6. @GeneratedValue
    7. private Long id;
    8. @Column(columnDefinition = "TEXT")
    9. private String content;
    10. private Date created;
    11. @Column(name = "from_user_id")
    12. private Long from;
    13. @Column(name = "to_user_id")
    14. private Long to;
    15. private Boolean used; //是否已经读取过,true代表读取过,false代表未读
    16. }

    群组聊天

    1. @Data
    2. @Entity
    3. @Table(name="im_group")
    4. public class Group {
    5. @Id
    6. @GeneratedValue
    7. private Long id;
    8. private String name;
    9. private Date created;
    10. @ManyToOne()
    11. @JoinColumn(name="user_id")
    12. private User user; //此分组的主人
    13. @ManyToMany()
    14. @JoinTable(name="im_group_user",
    15. joinColumns = {@JoinColumn(name="group_id")}, //连接本类
    16. inverseJoinColumns = {@JoinColumn(name="user_id")})//连接此属性的类
    17. private List list; //分组中的好友
    18. }

    使用redis的优势

    • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
    • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
    • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

    参考链接 完整项目参考链接点击这里

  • 相关阅读:
    新版IDEA没有办法选择Java8版本解决方法
    php ip区间判断
    在用element-plus时select下拉赋值没反应
    《从零开始的Java世界》10File类与IO流
    简单谈下Spring、Spring MVC和Spring Boot
    ShopXO商城系统文件上传0Day代审历程
    什么是线程?
    使用单个mybatis框架进行mysql数据库的连接和操作?
    AWS-PatchAsgInstance自动化定时ASG组打补丁
    设计链表复习
  • 原文地址:https://blog.csdn.net/xia15000506007/article/details/125917482