SpringBoot使用线程池异步化解耦通知

SpringBoot使用线程池异步化解耦通知

1、前言

为什么我们需要使用WebSocket通信连接?在一般的HTTP协议中,当前端页面向后端发送一个请求后,会建立一条由客户端发起的通信连接,只能由客户端发起连接请求而服务端无法发起连接请求。
当我们在项目中集成了WebSocket通信连接之后,我们就可以建立起服务端与客户端之间的双向通信。
使用前提是需要在Application启动类中添加开启异步化通知

@EnableAsync            //   开启异步化:另起一个线程执行后面内容

2、导入依赖

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

3、添加WebSocket配置类

主要是将WebSocket通信连接通过Bean方式注入到SpringBoot中

/**
 * ClassName:   WebSocket配置类
 * Author:      挽风
 * Date:        2020
 * Copyright:   2020 by 挽风1.0版本
 * Description:
 **/

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

3、创建WebSocket服务

/**
 * ClassName:   WebSocket服务端
 * Author:      挽风
 * Date:        2020
 * Copyright:   2020 by 挽风1.0版本
 * Description:
 *
 *      开启一个WebSocket连接
 *
 **/

@Component
@ServerEndpoint("/ws/{token}")
public class WebSocketServer {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 每个客户端一个token
     */
    private String token = "";

    private static HashMap<String, Session> map = new HashMap<String, Session>();

    /**
     * 连接成功向map中加入key-value对
     *
     * @param token 键
     * @param session 值
     */
    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session){
        map.put(token, session);
        this.token = token;
        logger.info("创建新连接:token:{},session id:{}, 当前连接数:{}",
                token, session.getId(), map.size());
    }

    /**
     * 连接关闭,删除session
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session){
        map.remove(this.token);
        logger.info("连接已关闭:token:{},session id:{},剩余连接数:{}",
                this.token, session.getId(), map.size());
    }

    /**
     * 连接错误
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        logger.info("连接错误:", error);
    }

    /**
     * 收到消息
     *
     * @param session 会话
     * @param message 消息内容
     */
    @OnMessage
    public void onMessage(Session session, String message){
        logger.info("收到消息:{},内容:{}", token, message);
    }

    /**
     * 群发消息
     *
     * @param message
     */
    public void sendInfo(String message){
        for (String token : map.keySet()){
            Session session = map.get(token);
            try{
                session.getBasicRemote().sendText(message);
            }catch (IOException e){
                logger.info("推送消息失败:{},内容:{}", token, message);
            }
            logger.info("推送消息:{},内容:{}", token, message);
        }
    }
}

4、配置线程池

/**
 * ClassName:   异步化线程池配置
 * Author:      挽风
 * Date:        2020
 * Copyright:   2020 by 挽风1.0版本
 * Description:
 **/

@Configuration
public class ThreadPoolConfig {

    /**
     * 线程池
     *
     * @return
     */
    @Bean(name = "threadPoolTaskExecutor")
    public Executor taskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 配置线程池核心数量为5
        executor.setCorePoolSize(5);
        // 配置线程池最大数量为10
        executor.setMaxPoolSize(10);
        // 设置队列容量为200
        executor.setQueueCapacity(200);
        // 设置活动时间秒数为1分钟
        executor.setKeepAliveSeconds(60);
        // 设置线程名前缀
        executor.setThreadNamePrefix("voteThreadPool-");

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

5、创建异步化解耦通知服务

在其他Service层中使用线程池进行异步化解耦通知时,必须将异步化方法单独定义到一个Service服务中,不能与调用异步化方法的服务在同一个类中,否则会无法使Async异步化生效。
通过@Async(value = “线程池名称”)来标注方法为异步化执行。

/**
 * ClassName:   WebSocket异步化解耦点赞通知服务
 * Author:      挽风
 * Date:        2020
 * Copyright:   2020 by 挽风1.0版本
 * Description:
 *
 *      这里通过集成线程池来控制线程数量,但当业务量太大时还是会变为同步
 *      最好的解决方案是使用rocketMQ消息队列来进行解决
 *
 *
 **/
@Service
public class WsAsyncService {

    private static final Logger logger = LoggerFactory.getLogger(WsAsyncService.class);

    @Resource
    private WebSocketServer webSocketServer;

    /**
     * 异步化解耦执行点赞通知发送消息
     *
     *      @Async 标注该方法需要异步化执行(值为线程池)
     *             单独另起一个线程执行,不干扰其他线程运行
     *      使用该注解必须要单独写到另一个类中,否则无法实现异步化调用
     *
     * @param message 需要发送的消息字符串
     */
    @Async(value = "threadPoolTaskExecutor")
    public void sendInfo(String message){
        logger.info("线程池执行:" + Thread.currentThread());

        // 调用WebSocket服务端发送消息
        webSocketServer.sendInfo(message);
    }
}

6、调用异步化服务方法

将WebSocket服务注入到其他服务中,然后调用即可。

        // 调用异步化推送点赞消息
        wsAsyncService.sendInfo("【" + doc.getName() + "】被贵人赏识啦!");

7、前端Vue接收消息通知

<template>
    <a-layout-footer style="text-align: center">
        System of record ©2021 Created by {{userInfo.name}}
    </a-layout-footer>
</template>

<!-- 添加组件命名规则-->
<script lang="ts">
    import { defineComponent, onMounted, computed } from 'vue';
    import store from "@/store";
    import {Tool} from "@/util/tool";
    import {notification} from "ant-design-vue";        // 引入通知组件


    export default defineComponent({
        name: 'the-footer',
        setup() {
            // 一个响应式变量要根据某个变量的变化来进行计算,用computed
            const userInfo = computed(() => store.state.userInfo);

            // 定义加载WebSocket连接
            let websocket: any;
            let token: any;

            /**
             * 定义WebSocket内部根据当前事件触发自动调用的方法
             */
            const onOpen = () => {
                console.log('WebSocket连接成功,状态码:', websocket.readyState);
            };

            const onError = () => {
                console.log('WebSocket连接错误,状态码:', websocket.readyState);
            };

            const onClose = () => {
                console.log('WebSocket连接关闭,状态码:', websocket.readyState);
            };

            const onMessage = (event: any) => {
                console.log('WebSocket收到消息,状态码:', event.data);
                // 接收到消息后弹出点赞内容
                notification['success']({
                    message: '收到消息',
                    description: event.data
                });
            };

            /**
             * 初始化回调
             */
            const initWebSocket = () => {
                // 连接成功
                websocket.onopen = onOpen;
                // 手动消息
                websocket.onmessage = onMessage;
                // 连接错误
                websocket.onerror = onError;
                // 连接关闭
                websocket.onclose = onClose;
            };

            onMounted(() => {
                // websocket
                if ('WebSocket' in window){
                    token = Tool.uuid(10);
                    // 连接地址
                    websocket = new WebSocket(process.env.VUE_APP_WS_SERVER + '/ws/' + token);
                    initWebSocket();

                    // 关闭
                    // websocket.close();
                } else {
                    alert('当前浏览器不支持连接');
                }
            });

            return{
                userInfo
            }
        }
    });
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值