websocket+SpringBoot定时任务完成实时数据推送至前端

文章目录

  • 一、业务场景
  • 二、实现步骤
    • 1.websocket
    • 2.SpringBoot定时任务(基于注解(@Scheduled))
    • 3.传输加密
    • 4.代码分析
    • 5.测试


一、业务场景

登录数,与游客数,实时推送至前端,完成看板统计功能的页面动态展示

具体业务的实现请移步:
https://blog.csdn.net/qq_43578385/article/details/109387532

二、实现步骤

1.websocket

完成实时推送我使用的是websocket.
每当使用SpringBoot进行Weboscket开发时,最容易想到的就是spring-boot-starter-websocket(或spring-websocket)。它可以让我们使用注解,很简单的进行Websocket开发,让我们更多的关注业务逻辑。它底层使用的是Tomcat,且不说把整个Tomcat放进一个WebSocket服务中是否会太重,但在大数据量高并发的场景下,它的表现并不是非常理想。

Netty一款高性能的NIO网络编程框架,在推送量激增时,表现依然出色。(关于性能与表现的讨论,网上很多,这里不过多说明。)很多流行开源项目都在使用Netty,如:Dubbo、Storm、Spark、Elasticsearch、Apache Cassandra等,这得益于Netty的并发高、传输快、封装好等特点。

但是,要在SpringBoot项目中整合Netty来开发WebSocket不是一件舒服的事,这会让你过多的关注非业务逻辑的实现。那么,是否有一款框架,能使得在SpringBoot项目中使用Netty开发WebSocket变得简单,甚至优雅,并且可以从使用spring-boot-starter-websocket开发的项目无缝的迁移过来呢?

netty-websocket-spring-boot-starter
这是个开源的框架。通过它,我们可以像spring-boot-starter-websocket一样使用注解进行开发,只需关注需要的事件(如OnMessage)。并且底层是使用Netty,当需要调参的时候只需要修改配置参数即可,无需过多的关心handler的设置。

添加依赖

	<dependency>
		<groupId>org.yeauty</groupId>
		<artifactId>netty-websocket-spring-boot-starter</artifactId>
		<version>0.9.5</version>
	</dependency>

详细使用请移步 https://gitee.com/Yeauty/netty-websocket-spring-boot-starter

2.SpringBoot定时任务(基于注解(@Scheduled))

基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。

1.创建定时器

@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class SaticScheduleTask {
    //3.添加定时任务
    @Scheduled(cron = "0/5 * * * * ?")
    //或直接指定时间间隔,例如:5秒
    //@Scheduled(fixedRate=5000)
    private void configureTasks() {
        System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
    }
}

3.传输加密

websocket一般在传输过程中加密,我采用的是Aes加密方法,具体加密代码请移步:

https://blog.csdn.net/qq_43578385/article/details/109470586

包含完整的java加密工具类跟前端解密对应的代码

4.代码分析

/**
 * @author Boss
 * @ServerEndpoint 当ServerEndpointExporter类通过Spring配置进行声明并被使用,
 * 它将会去扫描带有@ServerEndpoint注解的类 被注解的类将被注册成为一个WebSocket端点
 * 所有的配置项都在这个注解的属性中
 */
@Slf4j
@ServerEndpoint(path = "/ws", port = "8189")
@Component
public class WebSocketServer {
    
    private static ConcurrentHashMap<String, Session> Allsession = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {
        SocketAddress socketAddress = session.remoteAddress();
        String clientIP = socketAddress != null ? socketAddress.toString().replace("/", "").split(":")[0] : "";
        log.info(clientIP + "连接成功");
        Allsession.put(clientIP, session);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        SocketAddress socketAddress = session.remoteAddress();
        String clientIP = socketAddress != null ? socketAddress.toString().replace("/", "").split(":")[0] : "";
        if (StringUtils.isNoneBlank(clientIP)) {
            Allsession.remove(clientIP);
        }
        log.info("one connection closed:{}",clientIP);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @return
     */
    @OnMessage
    public String onMessage(String message, Session session) {
        return "servet 发送:" + message;
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("Websocket_error_{}", error.getMessage());
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(Object object) throws IOException {
        for (String key : Allsession.keySet()) {
            Allsession.get(key).sendText(JSON.toJSONString(object));
        }
    }
@Configuration
@EnableScheduling
public class WebsocketTask {
    private static final String KEY = "abcdefgabcdefg12";

    @Autowired
    private WebSocketServer webSocketServer;

    @Autowired
    private RedisUtil redisUtil;

    @Scheduled(cron = "0/5 * * * * ?")
    private void configureTasks() throws Exception {
        String loginAmount = null;
        if (redisUtil.hasKey("loginAmount")) {
            Map<Object, Object> map = redisUtil.hmget("loginAmount");
            loginAmount = "当日登录客户数" + (map.size());
        } else {
            loginAmount = "当日登录客户数" + (0);
        }
        String touristIP = null;
        if (redisUtil.hasKey("touristIP")) {
            Map<Object, Object> map = redisUtil.hmget("touristIP");
            touristIP = "当日系统游客数" + (map.size());
        } else {
            touristIP = "当日系统游客数" + (0);
        }
        String mes = loginAmount + "," + touristIP;
        String encrypt = AesUtils.aesEncrypt(mes, KEY);
        //解密方法  String decrypt = aesDecrypt(encrypt, key);
        webSocketServer.sendMessage(encrypt);
    }
}

5.测试

测试网址 : http://coolaf.com/tool/chattest
websocket+SpringBoot定时任务完成实时数据推送至前端_第1张图片
成功

你可能感兴趣的:(springboot项目学习)