南翔企业网站开发建设,wordpress同类软件,淘宝上做网站怎么样,可以自己做网站赚钱吗一、背景
项目中需要建立客户端与服务端之间的长链接#xff0c;首先就考虑用WebSocket#xff0c;再来SpringBoot原来整合WebSocket方式并不高效#xff0c;因此找到了netty-websocket-spring-boot-starter 这款脚手架#xff0c;它能让我们在SpringBoot中使用Netty来开发…一、背景
项目中需要建立客户端与服务端之间的长链接首先就考虑用WebSocket再来SpringBoot原来整合WebSocket方式并不高效因此找到了netty-websocket-spring-boot-starter 这款脚手架它能让我们在SpringBoot中使用Netty来开发WebSocket服务器并像spring-websocket的注解开发一样简单
二、netty-websocket-spring-boot-starter 入门介绍
2.1 核心注解
2.1.1 ServerEndpoint
当ServerEndpointExporter类通过Spring配置进行声明并被使用它将会去扫描带有ServerEndpoint注解的类 被注解的类将被注册成为一个WebSocket端点 所有的配置项都在这个注解的属性中 ( 如:ServerEndpoint(“/ws”) )
2.1.2 OnOpen
当有新的WebSocket连接完成时对该方法进行回调 注入参数的类型:Session、HttpHeaders…
2.1.3 OnClose
当有WebSocket连接关闭时对该方法进行回调 注入参数的类型:Session
2.1.4 OnError
当有WebSocket抛出异常时对该方法进行回调 注入参数的类型:Session、Throwable
2.1.5 OnMessage
当接收到字符串消息时对该方法进行回调 注入参数的类型:Session、String
2.2 核心配置
属性属性说明pathWebSocket的path,也可以用value来设置hostWebSocket的host,0.0.0.0即是所有本地地址portWebSocket绑定端口号。如果为0则使用随机端口(端口获取可见 多端点服务)maxFramePayloadLength最大允许帧载荷长度allIdleTimeSeconds与IdleStateHandler中的allIdleTimeSeconds一致并且当它不为0时将在pipeline中添加IdleStateHandler
三、实践netty-websocket-spring-boot-starter
3.1引入POM文件
主要添加包括以下依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId
/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope
/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependencydependencygroupIdorg.yeauty/groupIdartifactIdnetty-websocket-spring-boot-starter/artifactIdversion0.9.5/version
/dependencydependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.4.6/version
/dependency3.2 在主程序类中排除数据库使用
/*** 主程序启动类*/
SpringBootApplication(exclude {DataSourceAutoConfiguration.class})
public class WebsocketApplication {public static void main(String[] args) {SpringApplication.run(WebsocketApplication.class, args);}}3.3 开启WebSocket支持
Configuration
public class WebSocketConfig {Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}3.4 定义WebSocketServer服务器核心代码
在端点类上加上ServerEndpoint注解并在相应的方法上加上OnOpen、OnMessage、OnError、OnClose注解 代码如下
ServerEndpoint(port ${ws.port}, path /demo/{version}, maxFramePayloadLength 6553600, allIdleTimeSeconds 300)
public class WebSocketServer {private static Log LOGGER LogFactory.get();// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象。private static CopyOnWriteArraySetWebSocketServer webSocketSet new CopyOnWriteArraySetWebSocketServer();// 与某个客户端的连接会话需要通过它来给客户端发送数据private Session session;// 接收用户IDprotected StringBuilder userInfo new StringBuilder();Autowiredprivate RedisTemplateString, String redisTemplate;/*** 连接建立成功调用的方法*/OnOpenpublic void onOpen(Session session,HttpHeaders headers,RequestParam String req,RequestParam MultiValueMapString, Object reqMap,PathVariable String arg,PathVariable MapString, Object pathMap) {this.session session;// 加入set中webSocketSet.add(this);// 在线数加1addOnlineCount();LOGGER.debug(UserId {}, 通道ID{}, 当前连接人数{}, userInfo.toString(), getSessionId(session), getOnlineCount());}/*** 收到客户端消息后调用的方法*/OnMessagepublic void onMessage(Session session, String message) {JSONObject jsonData JSONUtil.parseObj(message);if (!jsonData.containsKey(command)) {LOGGER.debug(UserId {}, 通道ID{}, 上行内容{}, 上行请求非法缺少command参数, 处理结束,userInfo.toString(), getSessionId(session), message);return;}String userId jsonData.getStr(userId);this.userInfo new StringBuilder(userId);String command jsonData.getStr(command);Class? service Command.getService(command);if (Objects.isNull(service)) {errorMessage(command);LOGGER.error(UserId {}, 通道ID{}, 解析指令执行出错, userInfo.toString(), getSessionId(session));return;}LOGGER.info(UserId {}, 通道ID{}, 处理类{}, 开始处理请求内容{},userInfo.toString(), getSessionId(session), service, jsonData.toString());BaseMessage baseMessage getBaseMessage(service, session, command);if (baseMessage null) {return;}try {jsonData.set(SessionId, getSessionId(session));JSON resp baseMessage.handlerMessage(userInfo, jsonData);resp.putByPath(command, command);resp.putByPath(userId, userId);String value resp.toString();//将结果写回客户端, 实现服务器主动推送ChannelFuture future sendMessage(value);LOGGER.info(UserId {}, 通道ID {}, 返回内容 {}, future {}, 处理结束,userInfo.toString(), getSessionId(session), value, future.toString());} catch (Exception e) {LOGGER.error(UserId {}, 通道ID{}, 解析执行出错信息{}, userInfo.toString(), getSessionId(session), e.getMessage());}}/*** 连接关闭调用的方法*/OnClosepublic void onClose(Session session) {// 从set中删除webSocketSet.remove(this);// 在线数减1subOnlineCount();String userId this.userInfo.toString();LOGGER.warn(UserId {}, 通道ID {}, 有一连接关闭当前在线人数{}, userId, getSessionId(session), getOnlineCount());userInfo.delete(0, userInfo.length());if (ObjectUtil.isNotNull(userId)) {String keyStr ConstDef.ONLINE_USER_TYPE userId;redisTemplate.delete(keyStr);}session.close();}/*** 出错方法*/OnErrorpublic void onError(Session session, Throwable cause) {if (Objects.nonNull(this.session) Objects.nonNull(cause) !(cause instanceof EOFException)) {LOGGER.error(UserId {}, 通道ID{}, 出错信息{}, userInfo.toString(), this.session.id(), cause.toString());}if (Objects.nonNull(session) session.isOpen()) {session.close();}}/*** 通过class获取Bean*/private BaseMessage getBaseMessage(Class? service, Session session, String command) {BaseMessage baseMessage;try {baseMessage (BaseMessage) SpringUtils.getBean(service);} catch (Exception e) {LOGGER.error(UserId {}, 通道ID {}, 未找到协议头 {} 的处理类, userInfo.toString(), getSessionId(session), service);errorMessage(command);return null;}return baseMessage;}/*** 获取通道ID*/private String getSessionId(Session session) {return session.id().asShortText();}/*** 协议错误*/public void errorMessage(String command) {JSONObject retObj new JSONObject();retObj.set(code, ConstDef.ERROR_CODE_10001);retObj.set(msg, ConstDef.ERROR_CODE_10001_DESP);retObj.set(command, command);try {sendMessage(retObj.toString());} catch (IOException e) {LOGGER.error(UserId {}, 通道ID{}, 解析执行出错信息{}, userInfo.toString(), getSessionId(session), e.getMessage());}}/*** 实现服务器主动推送*/public ChannelFuture sendMessage(String message) throws IOException {return this.session.sendText(message);}/*** 在线用户数*/public long getOnlineCount() {String onlineCountValue redisTemplate.opsForValue().get(ConstDef.ONLINE_COUNT_KEY);if (StrUtil.isBlank(onlineCountValue) || !NumberUtil.isNumber(onlineCountValue)) {return 0L;}return Long.parseLong(onlineCountValue);}/*** 在线数1*/private void addOnlineCount() {redisTemplate.opsForValue().increment(ConstDef.ONLINE_COUNT_KEY);}/*** 在线数-1*/private void subOnlineCount() {redisTemplate.opsForValue().decrement(ConstDef.ONLINE_COUNT_KEY);}
}3.5 定义接口
/*** 消息处理接口*/
public interface BaseMessage {Log LOGGER LogFactory.get();/*** 处理类、处理方法*/JSON handlerMessage(StringBuilder vin, JSONObject jsonData);
}3.6 定义接口实现类 业务处理逻辑
该类是各业务的处理逻辑类是接口类的具体实现。
Component
Configuration
public class QueryAllActivityListMessage implements BaseMessage {Overridepublic JSON handlerMessage(StringBuilder userId, JSONObject jsonData) {LOGGER.debug(开始处理QueryAllActivityListMessage请求, 参数{}, JSONUtil.toJsonStr(jsonData));String resp 我是服务器端返回的处理结果;LOGGER.info(UserId {}, param{}, QueryAllActivityListMessage回复 {}, userId.toString(), jsonData, resp);JSONObject respStr new JSONObject();return respStr.set(handleResult, resp);}
}3.7 定义枚举Command
每增加一个业务接口的实现就需要在这个枚举类注册一下。
/*** 指令-服务 枚举*/
public enum Command {/*** 业务1处理逻辑*/queryAllActivityList(queryAllActivityList, QueryAllActivityListMessage.class, 业务1处理逻辑);/*** 业务2处理逻辑*///略/*** 业务3处理逻辑*///略/*** 服务编码*/private String processCode;/*** 服务接口类*/private Class? service;/*** 接口描述*/private String desc;Command(String processCode, Class? service, String desc) {this.processCode processCode;this.service service;this.desc desc;}public Class? getService() {return service;}public static Class? getService(String processCode) {for (Command command : Command.values()) {if (command.processCode.equals(processCode)) {return command.getService();}}return null;}
}3.8 编写SpringUtils 工具类
用于搜索Bean 通过class获取Bean
/*** SpringUtils 工具类用于搜索*/
Component
public class SpringUtils implements ApplicationContextAware {private static ApplicationContext applicationContext;Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {if (SpringUtils.applicationContext null) {SpringUtils.applicationContext applicationContext;}}/*** 获取applicationContext*/public static ApplicationContext getApplicationContext() {return applicationContext;}/*** 通过class获取Bean*/public static T T getBean(ClassT clazz) {return getApplicationContext().getBean(clazz);}/*** 通过name获取 Bean.*/public static Object getBean(String name) {return getApplicationContext().getBean(name);}/*** 通过name,以及Clazz返回指定的Bean*/public static T T getBean(String name, ClassT clazz) {return getApplicationContext().getBean(name, clazz);}}3.9 定义常量定义类 返回码
/*** 常量定义类 返回码*/
public class ConstDef {/*** 返回码*/public static final int ERROR_CODE_10001 10001;public static final String ERROR_CODE_10001_DESP 请求参数不合法;/*** 按UserId决定用户在线类型车机或者手机*/public static final String ONLINE_USER_TYPE ONLINE_USER_TYPE_;/*** 在线用户数*/public static final String ONLINE_COUNT_KEY ONLINE_COUNT_KEY;
}四、功能验证
打开WebSocket客户端连接到ws://127.0.0.1:9095/demo/1
从截图来看WebSocket服务端能正常接受并处理来自客户端的请求验证成功
文章转载自: http://www.morning.sxfmg.cn.gov.cn.sxfmg.cn http://www.morning.zdmrf.cn.gov.cn.zdmrf.cn http://www.morning.zqzzn.cn.gov.cn.zqzzn.cn http://www.morning.mqghs.cn.gov.cn.mqghs.cn http://www.morning.tlfmr.cn.gov.cn.tlfmr.cn http://www.morning.tqpnf.cn.gov.cn.tqpnf.cn http://www.morning.sxwfx.cn.gov.cn.sxwfx.cn http://www.morning.nxnrt.cn.gov.cn.nxnrt.cn http://www.morning.trqzk.cn.gov.cn.trqzk.cn http://www.morning.tsqrc.cn.gov.cn.tsqrc.cn http://www.morning.gfkb.cn.gov.cn.gfkb.cn http://www.morning.benqc.com.gov.cn.benqc.com http://www.morning.bpyps.cn.gov.cn.bpyps.cn http://www.morning.yrmpr.cn.gov.cn.yrmpr.cn http://www.morning.bljcb.cn.gov.cn.bljcb.cn http://www.morning.kyctc.cn.gov.cn.kyctc.cn http://www.morning.frfpx.cn.gov.cn.frfpx.cn http://www.morning.rzcmn.cn.gov.cn.rzcmn.cn http://www.morning.yppln.cn.gov.cn.yppln.cn http://www.morning.mqwdh.cn.gov.cn.mqwdh.cn http://www.morning.qtbnm.cn.gov.cn.qtbnm.cn http://www.morning.qgwpx.cn.gov.cn.qgwpx.cn http://www.morning.njqpg.cn.gov.cn.njqpg.cn http://www.morning.rfhmb.cn.gov.cn.rfhmb.cn http://www.morning.gpsrk.cn.gov.cn.gpsrk.cn http://www.morning.plqsz.cn.gov.cn.plqsz.cn http://www.morning.xczyj.cn.gov.cn.xczyj.cn http://www.morning.zpqbh.cn.gov.cn.zpqbh.cn http://www.morning.snbrs.cn.gov.cn.snbrs.cn http://www.morning.sfmqm.cn.gov.cn.sfmqm.cn http://www.morning.fpxsd.cn.gov.cn.fpxsd.cn http://www.morning.dlrsjc.com.gov.cn.dlrsjc.com http://www.morning.twwzk.cn.gov.cn.twwzk.cn http://www.morning.tgts.cn.gov.cn.tgts.cn http://www.morning.lwzpp.cn.gov.cn.lwzpp.cn http://www.morning.txfxy.cn.gov.cn.txfxy.cn http://www.morning.mpmtz.cn.gov.cn.mpmtz.cn http://www.morning.sfzwm.cn.gov.cn.sfzwm.cn http://www.morning.mlffg.cn.gov.cn.mlffg.cn http://www.morning.fqlxg.cn.gov.cn.fqlxg.cn http://www.morning.tnbsh.cn.gov.cn.tnbsh.cn http://www.morning.hjsrl.cn.gov.cn.hjsrl.cn http://www.morning.mmplj.cn.gov.cn.mmplj.cn http://www.morning.qymqh.cn.gov.cn.qymqh.cn http://www.morning.krkwp.cn.gov.cn.krkwp.cn http://www.morning.smcfk.cn.gov.cn.smcfk.cn http://www.morning.zwznz.cn.gov.cn.zwznz.cn http://www.morning.tlrxp.cn.gov.cn.tlrxp.cn http://www.morning.bbtn.cn.gov.cn.bbtn.cn http://www.morning.nzsx.cn.gov.cn.nzsx.cn http://www.morning.yrck.cn.gov.cn.yrck.cn http://www.morning.plhyc.cn.gov.cn.plhyc.cn http://www.morning.jpjpb.cn.gov.cn.jpjpb.cn http://www.morning.clxpp.cn.gov.cn.clxpp.cn http://www.morning.xbrxk.cn.gov.cn.xbrxk.cn http://www.morning.xhgxd.cn.gov.cn.xhgxd.cn http://www.morning.mtmnk.cn.gov.cn.mtmnk.cn http://www.morning.ptzf.cn.gov.cn.ptzf.cn http://www.morning.pqsys.cn.gov.cn.pqsys.cn http://www.morning.rfpb.cn.gov.cn.rfpb.cn http://www.morning.mhxlb.cn.gov.cn.mhxlb.cn http://www.morning.ptqds.cn.gov.cn.ptqds.cn http://www.morning.jlnlr.cn.gov.cn.jlnlr.cn http://www.morning.rpgdd.cn.gov.cn.rpgdd.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.ngjpt.cn.gov.cn.ngjpt.cn http://www.morning.cylbs.cn.gov.cn.cylbs.cn http://www.morning.ypqwm.cn.gov.cn.ypqwm.cn http://www.morning.knqck.cn.gov.cn.knqck.cn http://www.morning.ydwnc.cn.gov.cn.ydwnc.cn http://www.morning.fmrwl.cn.gov.cn.fmrwl.cn http://www.morning.zxqxx.cn.gov.cn.zxqxx.cn http://www.morning.ltrms.cn.gov.cn.ltrms.cn http://www.morning.zcnfm.cn.gov.cn.zcnfm.cn http://www.morning.gsqw.cn.gov.cn.gsqw.cn http://www.morning.cyfsl.cn.gov.cn.cyfsl.cn http://www.morning.ztcxx.com.gov.cn.ztcxx.com http://www.morning.mhnr.cn.gov.cn.mhnr.cn http://www.morning.brxzt.cn.gov.cn.brxzt.cn http://www.morning.fnpyk.cn.gov.cn.fnpyk.cn