spring boot 集成socketIo 做消息推送

spring boot 集成socketIo 做消息推送

项目需求

后台管理系统用户小铃铛,消息推送功能并展示有多少条消息或者小红点

代码展示

客户端代码


1.引入 socketIo.js
 <script src="/js/socket.io/socket.io.js"></script>
2. 自定义 socket.io 的请求处理
 <script src="/js/socket.io/socket-tools.js?t=<%=date.getTime()%>"></script>
 
 主要代码
// 创建socket 在后端项目中配置的socket 的端口
var socket = io("http://localhost:9098");// 用于连接后端的服务
/*
 * 创建自定义事件 'news' 作用:接受服务端 socket.emit('news', 数据); 发出的数据
 */

socket.on('connect', function() {
	socket.emit('accept_send', JSON.stringify({
		//登录用户id和客户机的ip 作为请求socekt.io 的一个表示
		sysUserId : sysUserId
	}));
});

socket.on('accept_response', function(data) {
	var obj = JSON.parse(data);
	// 输出服务端响应了数据
	if (obj.length > 99) {
		$("#sys_message_count").html("99+");
	} else {
		$("#sys_message_count").html(obj.length);
	}
	var html="";
	for (var i=0 ;i< obj.length && i<4;i++) {
	//小铃铛内容设置
		html +="<li class=\"msg-list\">" +
				"<a  οnclick=\"Detail(\'"+obj[i].id+"\',\'"+obj[i].type+"\')\" target=\"toolsAdmin\">"+
				"<p class=\"msg-title\">" +obj[i].title+"</p>"+
				"<p class=\"msg-content\">" +obj[i].content.substr(0,20)+"..."+"</p>"+
				"<p class=\"msg-time\">"+obj[i].createTimeTo+"</p></a></li>";
	}
	html+="";
	console.log(html);
	//删除ui 内容重新 然后重新添加
	$("#message_show").find("li").remove(); 
	$("#message_show").append(html);
	
});

服务端代码

后端项目结构

服务端pom 主要是 netty-socketio

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>SOC_IM</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!-- <scope>runtime</scope> -->
        </dependency>
        <!-- https://github.com/mrniko/netty-socketio -->
        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.7</version>
        </dependency>
    </dependencies>
</project>

application.properties 文件 配置也是从别的地方拿的 没有深究每一项的配置

server.port=8084

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://local:3306/xkb?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
# 搜索指定包别名
#mybatis.typeAliasesPackage=com.xkb.ctc
# 配置mapper的扫描,找到所有的mapper.xml映射文件
#mybatis.mapperLocations=classpath*:mybatis/mapper/**/*Mapper.xml
# 加载全局的配置文件
#mybatis.configLocation=classpath:mybatis/mapper/mybatis-config.xml

#============================================================================
# netty socket io setting
#============================================================================
# host在本地测试可以设置为localhost或者本机IP,在Linux服务器跑可换成服务器IP
socketio.host=localhost
socketio.port=9098
# 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
socketio.maxFramePayloadLength=1048576
# 设置http交互最大内容长度
socketio.maxHttpContentLength=1048576
# socket连接数大小(如只监听一个端口boss线程组为1即可)
socketio.bossCount=1
socketio.workCount=100
socketio.allowCustomRequests=true
# 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
socketio.upgradeTimeout=1000000
# Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
socketio.pingTimeout=6000000
# Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
socketio.pingInterval=25000
Application.java
@SpringBootApplication
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}


import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
 * 存放前端链接用户ip 与sysUserId
 *  原版本只保存了客户端连接的ip 会导致推送 错误,需要绑定到对应的用户
 * @author K
 *
 */
public class UserInfoMapUtil {
    public static ConcurrentMap<String, Object> webUserInfoMap = new ConcurrentHashMap<>();

    public static void put(String key,Object object) {
        webUserInfoMap.put(key, object);
    }

    public static Object get(String key) {
        return webUserInfoMap.get(key);
    }

    public static void remove(String key) {
        webUserInfoMap.remove(key);
    }

    public static Collection<Object> getValues(){
        return webUserInfoMap.values();
    }

    public static ConcurrentMap<String, Object> getWebUserInfoMap(){
        return webUserInfoMap;
    }
}



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import com.corundumstudio.socketio.SocketIOServer;
/**
 * 服务启动执行,SpringBoot启动之后执行
 * @author K
 *
 */
@Primary
@Component
public class ServerRunner implements CommandLineRunner{
	
	private final Logger log = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private  SocketIOServer socketIOServer;
	
	@Override
	public void run(String... args) throws Exception
	{
		socketIOServer.start();
		log.info("socket.io启动成功!");
	}

}



import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;

@Configuration
public class SocketIOConfig {

	@Value("${socketio.host}")
	private String host;

	@Value("${socketio.port}")
	private Integer port;

	@Value("${socketio.bossCount}")
	private int bossCount;

	@Value("${socketio.workCount}")
	private int workCount;

	@Value("${socketio.allowCustomRequests}")
	private boolean allowCustomRequests;

	@Value("${socketio.upgradeTimeout}")
	private int upgradeTimeout;

	@Value("${socketio.pingTimeout}")
	private int pingTimeout;

	@Value("${socketio.pingInterval}")
	private int pingInterval;
	
	/**
	 * 以下配置在上面的application.properties中已经注明
	 * @return
	 */
	@Bean
	public SocketIOServer socketIOServer()
	{
		SocketConfig socketConfig = new SocketConfig();
		socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setSocketConfig(socketConfig);
        config.setHostname(host);
        config.setPort(port);
        socketConfig.setReuseAddress(true);//添加配置
        config.setBossThreads(bossCount);
        config.setWorkerThreads(workCount);
        config.setAllowCustomRequests(allowCustomRequests);
        config.setUpgradeTimeout(upgradeTimeout);
        config.setPingTimeout(pingTimeout);
        config.setPingInterval(pingInterval);
        return new SocketIOServer(config);
	}
	@Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
        return new SpringAnnotationScanner(socketServer);
    }
}

package com.soc.im.service;

import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.soc.im.utils.UserInfoMapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@EnableScheduling // 开启定时任务
public class SocketIoServer {

    private static final Logger log= LoggerFactory.getLogger(SocketIoServer.class);

    private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String address = client.getSessionId().toString();
        log.info(address + "-------------------------" + "客户端已连接");
        clientMap.put(address, client);
        UserInfoMapUtil.put(address,client);

    }

    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String address = client.getSessionId().toString();
        log.info(address + "-------------------------" + "客户端已断开连接");
        clientMap.remove(address);
        UserInfoMapUtil.remove(address);
    }


    @OnEvent(value = "accept_send")
    public void onEvent(SocketIOClient client, AckRequest ackRequest, String data) {
        /**
         1.客户端推送advert_info事件时,onData接受数据,
         2.这里是string类型的json数据,还可以为Byte[],object其他类型
         3.获取客户端连接的ip
         4. sysUserId与 前端页面socket.io 请求 传参属性名称一致
         */
        String address = client.getSessionId().toString();
        log.info("address:{}",address);
        log.info(address + ":客户端:************" + data);
        JSONObject gpsData = (JSONObject) JSONObject.parse(data);
        String sysUserId = gpsData.get("sysUserId") + "";
        UserInfoMapUtil.put(address, sysUserId);
        clientMap.put(address, client);
//      这里处理相关业务然后返回 String message 给前端
        log.info("返回客户端IP:" + address+"ID: "+sysUserId+"message: ");
        client.sendEvent("accept_response", "message");
    }

/**
     1.获取全部客户端
     2.获取客户端连接的ip
      定时更新小铃铛信息
     */
    @Scheduled(cron = "0 0/30 * * * ?")
    public void pushMessageAllClients() {
        logger.info("开始-------->pushMessageAllClients...{}",System.currentTimeMillis());
        clientMap.forEach((address, client)->{
            Object object = UserInfoMapUtil.get(address);
            logger.info("用户信息...{}",String.valueOf(object));
            String message = sysMessage(String.valueOf(object));
            client.sendEvent("accept_response", message);
            logger.info("sendEvent:{}",client);
        });
    }


    public static Map<String, SocketIOClient> getClientMap() {
        return clientMap;
    }

    public static void setClientMap(Map<String, SocketIOClient> clientMap) {
        SocketIoServer.clientMap = clientMap;
    }

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值