栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

redis整合websocket实现消息推送功能

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

redis整合websocket实现消息推送功能

假设有这样一个需求,前端需要实时提醒数据库进入的数据,或者监听数据库里面的数据里面有什么变动,需要后端及时的提醒前端消息,这里可以使用websocket实现后端主动推送数据给前端,可以用redis保存每个用户的session,然后分别给不同用户发送不同的消息,这个例子我用的是redis中的发布订阅的功能(subscribe,publish命令)redis中可以用subscribe …(例如 subscribe channer) 订阅了channer这个频道,换句话说就是创建了这个频道,哪个用户订阅了channer这个频道,就可以接收到里面的数据,可以用publish…(例如:sublish channer “往channer频道存入数据”)往频道中发送数据这里是看别人的代码拉取下来学习看的,这里做一个笔记下面先看代码先把项目结构贴出来
pom.xml文件的依赖

    

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

        
            org.springframework.boot
            spring-boot-starter-websocket
            1.3.5.RELEASE
        

项目启动的时候就加载,将订阅的频道给创建出来,就是相当于我已经订阅 了这三个频道了(订阅哪几个频道可以自己进行设置)

package com.demo.redisandwebsocket.config;

import com.demo.redisandwebsocket.listener.SubscribeListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;

import java.util.ArrayList;
import java.util.List;


@Configuration //相当于xml中的beans
public class RedisConfig {
    
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //订阅频道 的通道
        // 原来的写法   订阅了ZHOU这个频道
//        container.addMessageListener(new MessageListener(){
//            @Override
//            public void onMessage(Message message, byte[] pattern) {
//                String msg = new String(message.getBody());
//                System.out.println(new String(pattern) + "主题发布:" + msg);
//            }
//        }, new PatternTopic("ZHOU"));


        // 改成lambda表达式的写法,更加的简洁   订阅了ZHOU这个频道
//        container.addMessageListener((a,b) -> {
//            String msg = new String(a.getBody());
//            System.out.println(new String(b) + "主题发布:" + msg);
//        },new PatternTopic("ZHOU"));


        
        List list = new ArrayList<>();
        list.add(new PatternTopic("ZHOU"));
        list.add(new PatternTopic("DA"));
        list.add(new PatternTopic("TOU"));
        container.addMessageListener(new SubscribeListener(),list);
        return container;
    }

}

然后自定义SubscribeListener类,实现MessageListener,这个类是监听redis中的频道是否有数据进来了,如果有数据进来这个监听器就会监听到,并且触发里面onMessage方法

package com.demo.redisandwebsocket.listener;

import com.demo.redisandwebsocket.web.WebSocketServer;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

import javax.annotation.Resource;
import javax.websocket.Session;
import java.io.IOException;


public class SubscribeListener implements MessageListener {

    @Resource
    private WebSocketServer webSocketServer;

    private Session session;
    public Session getSession() {   return session;  }
    public void setSession(Session session) {
        this.session = session;
    }

    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String msg = new String(message.getBody());
        System.out.println(new String(pattern) + "主题发布:" + msg);
        if (null != session && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

然后是WebSocketConfig类,这个类是初始化websocket的配置,也是初始化的时候将他对象注入到sping容器当中,因为websocket连接是基于http协议连接的,连接上以后就跟http协议没有关系了,然后就基于websocket进行连接了

package com.demo.redisandwebsocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


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

springUtils工具类,目前没有能力解释,希望有人帮我解释一下

package com.demo.redisandwebsocket.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public final class SpringUtils implements BeanFactoryPostProcessor {

    private static ConfigurableListableBeanFactory beanFactory; // Spring应用上下文环境

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    public static ConfigurableListableBeanFactory getBeanFactory() {
        return beanFactory;
    }

    
    @SuppressWarnings("unchecked")
    public static  T getBean(String name) throws BeansException {
        return (T) getBeanFactory().getBean(name);
    }

    
    public static  T getBean(Class clz) throws BeansException {
        T result = (T) getBeanFactory().getBean(clz);
        return result;
    }

    
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }

    
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }

    
    public static Class getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }

    
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }

}

这里就是将消息发送到前端的websocket的类了,里面有四个方法(四个注解)

package com.demo.redisandwebsocket.web;

import com.demo.redisandwebsocket.listener.SubscribeListener;
import com.demo.redisandwebsocket.util.SpringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;


@Component
@ServerEndpoint("/websocket/server")
public class WebSocketServer {
    
    private RedisMessageListenerContainer redisMessageListenerContainer = SpringUtils.getBean(RedisMessageListenerContainer.class);

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
     private static  AtomicInteger onlineCount=new AtomicInteger(0);
     //concurrent包的线程安全Set,用来存放每个客户端对应的webSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
     private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();

     //与某个客户端的连接会话,需要通过它来给客户端发送数据
     private Session session;

     private SubscribeListener subscribeListener;

    
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        subscribeListener = new SubscribeListener();
        subscribeListener.setSession(session);
        List list = new ArrayList<>();
        list.add(new PatternTopic("ZHOU"));
        list.add(new PatternTopic("DA"));
        list.add(new PatternTopic("TOU"));
        //设置订阅topic
        redisMessageListenerContainer.addMessageListener(subscribeListener, list);
    }

    
    @OnClose
    public void onClose() throws IOException {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        redisMessageListenerContainer.removeMessageListener(subscribeListener);
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
        //群发消息
        for(WebSocketServer item: webSocketSet){
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public   int getOnlineCount() {
        return onlineCount.get();
    }

    public   void addOnlineCount() {
        WebSocketServer.onlineCount.getAndIncrement();
    }

    public   void subOnlineCount() {
        WebSocketServer.onlineCount.getAndDecrement();
    }

}

把前端也贴出来

在这里插入代码片


    
    websocket

使用redis订阅消息和websocket实现消息推送


收到的订阅消息:

如果我推送消息到ZHOU或者DA或者TOU,前端就会立马的收到消息
有兴趣的人可以用这个做一个聊天室,因为都是实时提醒的

后面贴出效果图


这里需要注意一下,如果在项目初始化的时候并没有创建的频道,消息是推送不进去的
这里顺便把配置文件贴出来application.properties

server.port=8080
server.servlet.context-path=/demo
server.tomcat.uri-encoding=utf-8

#reids config
spring.redis.host=127.0.0.1
spring.redis.port=6379

spring.redis.jedis.pool.max-active=30

启动项目访问localhost:8080/demo/websocket.html就可以测试了
到这里就已经结束了
这个是一个redis中的(消息的发布/订阅)和websocket的消息推送整合

源码:https://gitee.com/zhou-datou/redis-websocket

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/755675.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号