springboot集成websocket实战:站内消息实时推送

简介: 现有一个类似boss直聘的招聘小程序,求职端和招聘端可以根据身份进行切换.要求实现两个问题: 1.求职端或是招聘端上线时,如果有未读消息需要显示未读消息数; 2.求职端和招聘端同时在线时,求职端投递简历之后,要求招聘端能够实时显示有新投递简历的消息信息;招聘端发送面试邀请时,求职端消息列表中实时显示出面试要求的消息信息.

背景


   现有一个类似boss直聘的招聘小程序,求职端和招聘端可以根据身份进行切换.要求实现两个问题:

   1.求职端或是招聘端上线时,如果有未读消息需要显示未读消息数;

   2.求职端和招聘端同时在线时,求职端投递简历之后,要求招聘端能够实时显示有新投递简历的消息信息;招聘端发送面试邀请时,求职端消息列表中实时显示出面试要求的消息信息.


处理方案梳理


   对于第一个问题,可以在进入到小程序页面之后,服务端提供一个获取用户未读消息数据查询的接口.

   对于第二个问题想到的处理方案是可以在小程序里面做一个轮询处理,间隔一定的时间去不断去调用获取用户消息列表接口.存在的问题就是请求中有大半是无用,浪费带宽和服务器资源。所以考虑的处理方案是使用websocket处理,websocket的优点是服务端可以主动向客户端发送消息.能处理客户端轮询查询带来的问题.

   自己实现了springboot+websocket进行消息推送的简单demo,使用在线的websocket地址用作客户端进行测试.有同样需求的同学可以参考并加入到自己的业务逻辑中.下面说下实现过程;


实现过程


   主要展示服务端实现方案,前端只需要创建websocket连接,每种客户端创建连接的方式不同,这里不提供实现,测试的时候使用在线websocket测试网站作为客户端.


服务端配置

   需要引入的依赖:


 

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>


websocket配置类:

@Configuration
public class WebSocketConfig {
    // 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}


websocket服务端主要逻辑:

@Component
@Slf4j
@Service
@ServerEndpoint("/webSocket/{login}/{type}")  // login表示用户唯一标识,type表示用户类型:1.求职身份;2.面试身份
public class WebSocketServer {
    //当前在线连接数
    private static int onlineCount = 0;
    // 每个在线用户会创建一个WebSocketServer对象
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
    // 存放所有在线的客户端 key为用户的唯一标识:login,value为每个会话连接
    private static Map<String, Session> clients = new ConcurrentHashMap<>();
//    private Session session;
    // 用户login
    private String login = "";
    // 处理使用@Autowire注入为空的问题,使用静态变量处理
    private static NewsMapper newsMapper= SpringUtils.getBean(NewsMapper.class);
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("login") String login, @PathParam("type") Integer type) {
        clients.put(login, session);
//        this.session = session;
        webSocketSet.add(this);     //加入set中
        this.login = login;
        addOnlineCount();           //在线数加1
        try {
            // 查询用户未读消息数
            Integer unReadMsg=0;
            List<Long> noReadingNewsIds = newsMapper.findNoReadingNewsId(type, login);
            if(CollectionUtil.isNotEmpty(noReadingNewsIds)){
                unReadMsg=noReadingNewsIds.size();
            }
           // 用户上线提醒
            sendMessage("用户"+login+"已上线("+("1".equals(login) ? "求职者)":"招聘者)")+",未读消息数:"+unReadMsg,session);
            log.info("有新窗口开始监听用户详情id:" + login +",当前在线人数为:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        clients.remove(login);
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("释放的login为:"+login);
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }
    /**
     * 收到客户端消息后调用的方法
     * @ Param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口" + login + "的信息:" + message);
        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message,session);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * @ Param session
     * @ Param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message,Session session) throws IOException {
        if(session != null){
            session.getBasicRemote().sendText(message);
        }
    }
    /**
     * 校验是否在线,在线需要返回用户session信息
     */
    public Session checkIsOnline(String login) throws IOException {
        for (String onLineLogin : clients.keySet()) {
            if(login.equals(onLineLogin)){
                return clients.get(login);
            }
        }
        return null;
    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }
}


模拟业务场景:求职端发送简历给求职者

@RestController
@RequestMapping("/webSocket")
public class WebSocketServerController {
    @Autowired
    private WebSocketServer webSocketServer;
    @GetMapping("/serverToClient")
    public ResultVo sendServerToClient(String login) throws IOException {
        System.out.println("求职者给招聘者发送简历操作..............");
        // 判断用户是否在线
        Session session = webSocketServer.checkIsOnline(login);
        if(ObjectUtil.isNotNull(session)){
            webSocketServer.sendMessage("求职者给招聘者发送简历,招聘者已接收",session);
        }
        return ResultVoUtil.success();
    }
}


模拟客户端

   这里客户端使用在线websocket测试网站,地址:http://www.websocket-test.com/,测试数据:

login:1,userType:1 模拟求职端;
login:2,userType:2 模拟招聘端;

请求地址为websocket服务器所在项目ip以及端口和请求参数,本地项目参数如下:

ws://172.16.0.131:8080/webSocket/1/1


模拟用户1登录上线:

7918d07b420de3c312bbe080349ba3e7_f2fa14044aa04467a5c17033abcc16b5.png


模拟用户2登录上线:

4358f66884a230b2833fdd3d98323434_041748898bad4be9ad68eb6b541e925c.png

用户上线之后可以获取到未读消息数!

模拟用户1求职者发送简历给用户2应聘者

这里提供模拟接口:

f86a4ac07c91780d6642a92aa3e7ef96_642c9bc43e7b4f2093354aec9099600e.png

以上是模拟客户端,不刷新情况下可以接收到服务端消息.

c6c2e2ff59e66ae9b97133cba33f314b_dff2fbf1c20a44759789f973959c62a5.png

以上是站内消息推送的实现方案,希望对有同样需求的同学有所帮助!


目录
打赏
0
0
0
0
5
分享
相关文章
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
16 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
2天前
|
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
本文详细介绍了Swagger2的使用方法,包括在Spring Boot项目中的配置与应用。重点讲解了Swagger2中常用的注解,如实体类上的`@ApiModel`和`@ApiModelProperty`,Controller类上的`@Api`、`@ApiOperation`以及参数上的`@ApiParam`等。通过示例代码展示了如何为实体类和接口添加注解,并在页面上生成在线接口文档,实现接口测试。最后总结了Swagger的优势及其在项目开发中的重要性,提供了课程源代码下载链接供学习参考。
16 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
|
2天前
|
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
19 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
2天前
|
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
21 0
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
18 0
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
15 0
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
12 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
13 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档—— Swagger 简介
第6课介绍了在Spring Boot中集成Swagger2以展示在线接口文档的方法。随着前后端分离架构的发展,API文档成为连接前端与后端开发的重要纽带。然而,代码更新频繁导致文档难以同步维护,Swagger2解决了这一问题。通过Swagger,在线API文档不仅方便了接口调用方查看和测试,还支持开发者实时测试接口数据。本文使用Swagger 2.2.2版本,讲解如何在Spring Boot项目中导入并配置Swagger2工具,从而高效管理接口文档。
18 0
|
8天前
|
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
111 79

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等