随记:在springboot中websocket的使用
我现在有两种方法
第一种:使用java封装的这个包下的javax.websocket.*
先配置这个配置类
import com.alibaba.nacos.common.utils.CollectionUtils;
import org.springframework.stereotype.Component;import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;@Component
public class WebSocketServerConfig extends ServerEndpointConfig.Configurator {@Overridepublic boolean checkOrigin(String originHeaderValue) {//todo webSocket的 过滤 权限校验return true;}@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {Map<String, List<String>> parameterMap = request.getParameterMap();List<String> erpList = parameterMap.get("erp");if(!CollectionUtils.isEmpty(erpList)){sec.getUserProperties().put("erp", erpList.get(0));}}}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}
创建一个socket
前端应该怎么连接呢
第二种:使用spring自定义的websocket + Disruptor。
先定义这个配置
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Resourceprivate PictureEditHandler pictureEditHandler;@Resourceprivate WsHandshakeInterceptor wsHandshakeInterceptor;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// websocketregistry.addHandler(pictureEditHandler, "/ws/picture/edit").addInterceptors(wsHandshakeInterceptor).setAllowedOrigins("*");}
}
再配置拦截器
/*** WebSocket 拦截器,建立连接前校验*/
@Slf4j
@Component
public class WsHandshakeInterceptor implements HandshakeInterceptor {@Resourceprivate UserService userService;@Resourceprivate PictureService pictureService;@Resourceprivate SpaceService spaceService;@Resourceprivate SpaceUserAuthManager spaceUserAuthManager;/*** 建立连接前要校验* @param request* @param response* @param wsHandler* @param attributes 给Session会话设置属性* @return* @throws Exception*/@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {if (request instanceof ServletServerHttpRequest) {HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) request).getServletRequest();// 从请求中获取用户信息String pictureId = httpServletRequest.getParameter("pictureId");if (StrUtil.isBlank(pictureId)){log.info("缺少图片参数,拒绝握手");return false;}// 获取当前登入用户User loginUser = userService.getLoginUser(httpServletRequest);if (ObjUtil.isEmpty(loginUser)){log.info("用户未登录,拒绝握手");return false;}// 校验用户是否有编辑当前图片的权限Picture picture = pictureService.getById(pictureId);if (ObjUtil.isEmpty(picture)){log.info("图片不存在,拒绝握手");return false;}Long spaceId = picture.getSpaceId();Space space =null;if (spaceId != null){space= spaceService.getById(spaceId);if (ObjUtil.isEmpty(space)){log.info("图片所在空间不存在,拒绝握手");return false;}if (space.getSpaceType() != SpaceTypeEnum.TEAM.getValue()){log.info("图片所在空间不是团队空间,拒绝握手");return false;}}List<String> permissionList = spaceUserAuthManager.getPermissionList(space, loginUser);if (!permissionList.contains(SpaceUserPermissionConstant.PICTURE_EDIT)){log.info("用户没有编辑图片的权限,拒绝握手");return false;}// 设置用户登入信息等属性到 WebSocket 会话中attributes.put("user", loginUser);attributes.put("userId", loginUser.getId());attributes.put("pictureId", Long.valueOf(pictureId)); // 记得转换为 Long 类型}return true;}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {}
}
再写这个disrupter的配置 这个东西就像一个工厂一样,但是不是啊。它可以根据消费者的消息类型来调用对应的方法。
/**
*disruptor 配置*/
@Configuration
public class PictureEditEventDisruptorConfig {@Resourceprivate PictureEditEventWorkHandler pictureEditEventWorkHandler;@Bean("pictureEditEventDisruptor")public Disruptor<PictureEditEvent> messageModelRingBuffer() {// ringBuffer 的大小int bufferSize = 1024 * 256;Disruptor<PictureEditEvent> disruptor = new Disruptor<>(PictureEditEvent::new,bufferSize,ThreadFactoryBuilder.create().setNamePrefix("pictureEditEventDisruptor").build());// 设置消费者disruptor.handleEventsWithWorkerPool(pictureEditEventWorkHandler);// 开启 disruptordisruptor.start();return disruptor;}
}
消费者 举的一个例子
/*** (消费者) 这只是一个例子*/
@Slf4j
@Component
public class PictureEditEventWorkHandler implements WorkHandler<PictureEditEvent> {@Resource@Lazyprivate PictureEditHandler pictureEditHandler;@Resourceprivate UserService userService;@Overridepublic void onEvent(PictureEditEvent event) throws Exception {PictureEditRequestMessage pictureEditRequestMessage = event.getPictureEditRequestMessage();WebSocketSession session = event.getSession();User user = event.getUser();Long pictureId = event.getPictureId();// 获取到消息类别String type = pictureEditRequestMessage.getType();PictureEditMessageTypeEnum pictureEditMessageTypeEnum = PictureEditMessageTypeEnum.valueOf(type);// 调用对应的消息处理方法switch (pictureEditMessageTypeEnum) {case ENTER_EDIT:pictureEditHandler.handleEnterEditMessage(pictureEditRequestMessage, session, user, pictureId);break;case EDIT_ACTION:pictureEditHandler.handleEditActionMessage(pictureEditRequestMessage, session, user, pictureId);break;case EXIT_EDIT:pictureEditHandler.handleExitEditMessage(pictureEditRequestMessage, session, user, pictureId);break;default:PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue());pictureEditResponseMessage.setMessage("消息类型错误");pictureEditResponseMessage.setUser(userService.getUserVO(user));session.sendMessage(new TextMessage(JSONUtil.toJsonStr(pictureEditResponseMessage)));}}
}
生产者的例子使用方法
/***生产者*/
@Component
@Slf4j
public class PictureEditEventProducer {@ResourceDisruptor<PictureEditEvent> pictureEditEventDisruptor;public void publishEvent(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) {RingBuffer<PictureEditEvent> ringBuffer = pictureEditEventDisruptor.getRingBuffer();// 获取可以生成的位置long next = ringBuffer.next();PictureEditEvent pictureEditEvent = ringBuffer.get(next);pictureEditEvent.setSession(session);pictureEditEvent.setPictureEditRequestMessage(pictureEditRequestMessage);pictureEditEvent.setUser(user);pictureEditEvent.setPictureId(pictureId);// 发布事件ringBuffer.publish(next);}/*** 优雅停机*/@PreDestroypublic void close() {pictureEditEventDisruptor.shutdown(); // 默认处理完全部事件,再关闭}
}
最后就是处理websocket的方法里面是发送消息的所有方法和实现,
/*** WebSocket处理器*/
@Component
public class PictureEditHandler extends TextWebSocketHandler {@Resourceprivate PictureEditEventProducer pictureEditEventProducer;
}