转至:https://www.cnblogs.com/zwcry/p/9723447.html
1.application.properties
server.port=8081
#thymeleaf配置
#是否启用模板缓存。
spring.thymeleaf.cache=false
#是否为Web框架启用Thymeleaf视图解析。
spring.thymeleaf.enabled=true
#在SpringEL表达式中启用SpringEL编译器。
spring.thymeleaf.enable-spring-el-compiler=true
#模板文件编码。
spring.thymeleaf.encoding=UTF-8
#要应用于模板的模板模式。另请参见Thymeleaf的TemplateMode枚举。
spring.thymeleaf.mode=HTML5
#在构建URL时添加前缀以查看名称的前缀。
spring.thymeleaf.prefix=classpath:/templates/
#Content-Type写入HTTP响应的值。
spring.thymeleaf.servlet.content-type=text/html
#在构建URL时附加到视图名称的后缀。
spring.thymeleaf.suffix=.html
##单服务器
spring.redis.host=192.168.159.129
##单端口
spring.redis.port=6379
## 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=300
## Redis数据库索引(默认为0)
spring.redis.database=0
## 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
## 连接池中的最大空闲连接
spring.redis.pool.max-idle=100
## 连接池中的最小空闲连接
spring.redis.pool.min-idle=20
## 连接超时时间(毫秒)
spring.redis.timeout=60000
2.pom.xml
4.0.0
com.szw.learn
websocket_redis_mq_01
0.0.1-SNAPSHOT
websocket_redis_mq_01
org.springframework.boot
spring-boot-starter-parent
1.5.16.RELEASE
UTF-8
UTF-8
1.8
true
true
3.0.7.RELEASE
2.1.2
com.szw.learn.WsMqApplication
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-websocket
nexus-aliyun
Nexus aliyun
http://maven.aliyun.com/nexus/content/groups/public
true
false
nexus-aliyun
Nexus aliyun
http://maven.aliyun.com/nexus/content/groups/public
true
false
org.apache.maven.plugins
maven-source-plugin
true
compile
jar
org.springframework.boot
spring-boot-maven-plugin
true
3.SpringUtils.java
package com.szw.learn.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.Repository;
@Repository
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;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static T getBean(String name) throws BeansException {
return (T) getBeanFactory().getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static T getBean(Class clz) throws BeansException {
T result = (T) getBeanFactory().getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getAliases(name);
}
}
4.redis
发布service:
package com.szw.learn.redismq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
/**
* @author 七脉 描述:发布service
*/
@Component
public class PublishService {
@Autowired
StringRedisTemplate redisTemplate;
/**
* @author 七脉 描述:发布方法
* @param channel 消息发布订阅 主题
* @param message 消息信息
*/
public void publish(String channel, Object message) {
// 该方法封装的 connection.publish(rawChannel, rawMessage);
redisTemplate.convertAndSend(channel, message);
}
}
订阅监听类:
package com.szw.learn.redismq;
import java.io.IOException;
import javax.websocket.Session;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* @author 七脉 描述:订阅监听类
*/
public class SubscribeListener implements MessageListener {
private StringRedisTemplate stringRedisTemplate;
private 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){
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public StringRedisTemplate getStringRedisTemplate() {
return stringRedisTemplate;
}
public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
注册redis监听容器:
package com.szw.learn.redismq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisConfig {
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
/**
* @author 七脉 描述:需要手动注册RedisMessageListenerContainer加入IOC容器
* @return
*/
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(jedisConnectionFactory);
return container;
}
}
5.websocket
websocket注册:
package com.szw.learn.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebsocketConfig {
/**
*
描 述: @Endpoint注解的websocket交给ServerEndpointExporter自动注册管理
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
websocket端点:
package com.szw.learn.websocket;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import com.szw.learn.redismq.PublishService;
import com.szw.learn.redismq.SubscribeListener;
import com.szw.learn.util.SpringUtils;
/**
*@ServerEndpoint(value="/websocket")value值必须以/开路
*备注:@ServerEndpoint注解类不支持使用@Autowire
*{topic}指:向哪个频道主题里发消息
*{myname}指:这个消息是谁的。真实环境里可以使用当前登录用户信息
*/
@Component
@ServerEndpoint(value="/websocket/{topic}/{myname}")
public class WebsocketEndpoint {
/**
* 因为@ServerEndpoint不支持注入,所以使用SpringUtils获取IOC实例
*/
private StringRedisTemplate redisTampate = SpringUtils.getBean(StringRedisTemplate.class);
private RedisMessageListenerContainer redisMessageListenerContainer = SpringUtils.getBean(RedisMessageListenerContainer.class);
//存放该服务器该ws的所有连接。用处:比如向所有连接该ws的用户发送通知消息。
private static CopyOnWriteArraySet sessions = new CopyOnWriteArraySet<>();
private Session session;
@OnOpen
public void onOpen(Session session,@PathParam("topic")String topic){
System.out.println("java websocket:打开连接");
this.session = session;
sessions.add(this);
SubscribeListener subscribeListener = new SubscribeListener();
subscribeListener.setSession(session);
subscribeListener.setStringRedisTemplate(redisTampate);
//设置订阅topic
redisMessageListenerContainer.addMessageListener(subscribeListener, new ChannelTopic(topic));
}
@OnClose
public void onClose(Session session){
System.out.println("java websocket:关闭连接");
sessions.remove(this);
}
@OnMessage
public void onMessage(Session session,String message,@PathParam("topic")String topic,@PathParam("myname")String myname) throws IOException{
message = myname+":"+message;
System.out.println("java websocket 收到消息=="+message);
PublishService publishService = SpringUtils.getBean(PublishService.class);
publishService.publish(topic, message);
}
@OnError
public void onError(Session session,Throwable error){
System.out.println("java websocket 出现错误");
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
测试controller
package com.szw.learn.websocket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("websocket")
public class WebsocketController {
@Value("${server.port}")
private String port;
public static final String INDEX = "websocket/index";
/**
* @author 七脉
* 描述:聊天页
* @param topic 发布订阅的频道主题
* @param myname 发布者的显示名称
* @return
*/
@RequestMapping("index/{topic}/{myname}")
public ModelAndView index(@PathVariable("topic")String topic,@PathVariable("myname")String myname){
ModelAndView mav = new ModelAndView(INDEX);
mav.addObject("port", port);
mav.addObject("topic",topic);
mav.addObject("myname",myname);
return mav;
}
}
6.启动类
package com.szw.learn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WsMqApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(WsMqApplication.class, args);
}
}
7.测试页面
websocket集群
本服务端口号:[[${port}]],使用redismq实现websocket集群
[[${topic}]] 频道 聊天中。。。