cometd源码阅读-WebSocketTransport初始化(六)
说明
comted的websocket实现是使用jetty的文档地址:jetty文档
Transprot初始化时机参考cometd源码阅读-初始化(二) <1>处 会调用transport 的init方法
org.cometd.server.BayeuxServerImpl#initializeServerTransports
protected void initializeServerTransports() { if (_transports.isEmpty()) { //初始化Transport 没重定义则创建默认 指定了则创建指定的 注:反射创建 会传入bayeux String option = (String)getOption(TRANSPORTS_OPTION); if (option == null) { //未定义则初始化处理websocket 和长轮询的Transport处理器 JSONP的处理器 // Order is important, see #findHttpTransport() // ServerTransport transport = newWebSocketTransport(); if (transport != null) { addTransport(transport); } addTransport(newJSONTransport()); addTransport(new JSONPTransport(this)); } else { //如果有进行类的全名称配置 根据累的全名称创建 for (String className : option.split(",")) { ServerTransport transport = newServerTransport(className.trim()); if (transport != null) { addTransport(transport); } } if (_transports.isEmpty()) { throw new IllegalArgumentException("Option '" + TRANSPORTS_OPTION + "' does not contain a valid list of server transport class names"); } } } //如果没有配置_allowedTransports 将transport加入到 _allowedTransports//liqiangtodo 暂时不晓得干嘛的 if (_allowedTransports.isEmpty()) { String option = (String)getOption(ALLOWED_TRANSPORTS_OPTION); if (option == null) { _allowedTransports.addAll(_transports.keySet()); } else { for (String transportName : option.split(",")) { if (_transports.containsKey(transportName)) { _allowedTransports.add(transportName); } } if (_allowedTransports.isEmpty()) { throw new IllegalArgumentException("Option '" + ALLOWED_TRANSPORTS_OPTION + "' does not contain at least one configured server transport name"); } } } //逐个调用transport init方法完成Transport的初始化 Transport 内部的相关参数自定义配置可以通过Option拿到 List<String> activeTransports = new ArrayList<>(); for (String transportName : _allowedTransports) { ServerTransport serverTransport = getTransport(transportName); if (serverTransport instanceof AbstractServerTransport) { //调用init方法进行初始化<1> ((AbstractServerTransport)serverTransport).init(); //加入到已激活的transpor activeTransports.add(serverTransport.getName()); } } if (_logger.isDebugEnabled()) { _logger.debug("Active transports: {}", activeTransports); } }
源码
<1>
org.cometd.server.websocket.javax.WebSocketTransport#init
//配置的websoket连接地址可参考点击跳转>
public static final String COMETD_URL_MAPPING_OPTION = "cometdURLMapping";
public static final String IDLE_TIMEOUT_OPTION = "idleTimeout";
@Override public void init() {
//<2>先调用父类的init方法 super.init(); ServletContext context = (ServletContext)getOption(ServletContext.class.getName()); if (context == null) { throw new IllegalArgumentException("Missing ServletContext"); } String cometdURLMapping = (String)getOption(COMETD_URL_MAPPING_OPTION); if (cometdURLMapping == null) { throw new IllegalArgumentException("Missing '" + COMETD_URL_MAPPING_OPTION + "' parameter"); } /** * org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer.initialize * jetty的webSocket模块 */ ServerContainer container = (ServerContainer)context.getAttribute(ServerContainer.class.getName()); if (container == null) { throw new IllegalArgumentException("Missing WebSocket ServerContainer"); } // JSR 356 does not support a input buffer size option //从option配置获取最大接收文本的缓冲区大小 从option获取 int maxMessageSize = getMaxMessageSize(); if (maxMessageSize < 0) { maxMessageSize = container.getDefaultMaxTextMessageBufferSize(); } container.setDefaultMaxTextMessageBufferSize(maxMessageSize); //从option获取在多久没收收到输入后链接关闭 long idleTimeout = getOption(IDLE_TIMEOUT_OPTION, container.getDefaultMaxSessionIdleTimeout()); container.setDefaultMaxSessionIdleTimeout(idleTimeout); String protocol = getProtocol(); List<String> protocols = protocol == null ? null : Collections.singletonList(protocol); //<4> Configurator configurator = new Configurator(context); //根据configurator 自定义配置处理器用于设置websocket mapping 以及接收连接 关闭 消息 等事件 org.cometd.server.websocket.javax.WebSocketEndPoint //遍历注册端点 ,号隔开可配置多个
for (String mapping : normalizeURLMapping(cometdURLMapping)) {
//<5> ServerEndpointConfig config = ServerEndpointConfig.Builder.create(WebSocketEndPoint.class, mapping) .subprotocols(protocols) .configurator(configurator) .build(); try { container.addEndpoint(config); } catch (DeploymentException x) { throw new RuntimeException(x); } } }
<2>
org.cometd.server.websocket.common.AbstractWebSocketTransport#init
public static final String PROTOCOL_OPTION = "protocol"; public static final String MESSAGES_PER_FRAME_OPTION = "messagesPerFrame"; public static final String REQUIRE_HANDSHAKE_PER_CONNECTION_OPTION = "requireHandshakePerConnection"; @Override public void init() { //<3>调用父类的init方法 super.init(); _protocol = getOption(PROTOCOL_OPTION, null); _messagesPerFrame = getOption(MESSAGES_PER_FRAME_OPTION, 1); _requireHandshakePerConnection = getOption(REQUIRE_HANDSHAKE_PER_CONNECTION_OPTION, false); }
<3>
org.cometd.server.AbstractServerTransport#init
public static final String TIMEOUT_OPTION = "timeout";
public static final String INTERVAL_OPTION = "interval";
public static final String MAX_INTERVAL_OPTION = "maxInterval";
public static final String MAX_PROCESSING_OPTION = "maxProcessing";
public static final String MAX_LAZY_TIMEOUT_OPTION = "maxLazyTimeout";
public static final String META_CONNECT_DELIVERY_OPTION = "metaConnectDeliverOnly";
public static final String MAX_QUEUE_OPTION = "maxQueue";
public static final String JSON_CONTEXT_OPTION = "jsonContext";
public static final String HANDSHAKE_RECONNECT_OPTION = "handshakeReconnect";
public static final String ALLOW_MESSAGE_DELIVERY_DURING_HANDSHAKE = "allowMessageDeliveryDuringHandshake";
/** * Initializes the transport, resolving default and direct options. *初始化传输,解析默认和直接选项。 */ public void init() { _interval = getOption(INTERVAL_OPTION, _interval); _maxInterval = getOption(MAX_INTERVAL_OPTION, _maxInterval); _timeout = getOption(TIMEOUT_OPTION, _timeout); _maxLazyTimeout = getOption(MAX_LAZY_TIMEOUT_OPTION, _maxLazyTimeout); _metaConnectDeliveryOnly = getOption(META_CONNECT_DELIVERY_OPTION, _metaConnectDeliveryOnly); _jsonContext = (JSONContextServer)getOption(JSON_CONTEXT_OPTION); _handshakeReconnect = getOption(HANDSHAKE_RECONNECT_OPTION, false); _allowHandshakeDelivery = getOption(ALLOW_MESSAGE_DELIVERY_DURING_HANDSHAKE, false); _maxMessageSize = getOption(MAX_MESSAGE_SIZE_OPTION, -1); }
<4>
org.cometd.server.websocket.javax.WebSocketTransport.Configurator
private class Configurator extends ServerEndpointConfig.Configurator { private final ServletContext servletContext; private Configurator(ServletContext servletContext) { this.servletContext = servletContext; } /** * websocket握手 我们可以自定义请求头 * sec.put("trace_id",uuid) * response.put("trace_id,uuid) * @param sec * @param request * @param response */ @Override public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { BayeuxContextHolder context = provideContext(); context.bayeuxContext = new WebSocketContext(servletContext, request, sec.getUserProperties()); //WebSocketTransport钩子方法 WebSocketTransport.this.modifyHandshake(request, response); } /** * 用于判断请求是否websocket接口 * @param originHeaderValue 为请求头的org Origin: http://localhost:8080 * @return */ @Override public boolean checkOrigin(String originHeaderValue) { //内部写死的传的true return WebSocketTransport.this.checkOrigin(originHeaderValue); } /** * 如果提供了子协议,它将用于 WebSocket 升级响应标头Sec-WebSocket-Protocol * supported为请求头Sec-WebSocket-Protocol * 应是用来判断是否支持此协议 * @param supported * @param requested * @return */ @Override public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) { BayeuxContextHolder context = provideContext(); context.protocolMatches = checkProtocol(supported, requested); if (context.protocolMatches) { return super.getNegotiatedSubprotocol(supported, requested); } LOGGER.warn("Could not negotiate WebSocket SubProtocols: server{} != client{}", supported, requested); return null; } /** * 返回的值用于 WebSocket 升级响应标头Sec-WebSocket-Extensions * @param installed * @param requested * @return */ @Override public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) { Set<Extension> negotiated = new LinkedHashSet<>(); for (Extension requestedExtension : requested) { String name = requestedExtension.getName(); boolean option = getOption(ENABLE_EXTENSION_PREFIX_OPTION + name, true); if (option) { for (Extension installedExtension : installed) { if (installedExtension.getName().equals(name)) { negotiated.add(requestedExtension); break; } } } } return new ArrayList<>(negotiated); } /** * 这也是cometd的切入点 当客户端 申请建立连接 创建的处理器 这个处理器处理器可以接收连接打开 关闭 关闭等事件 多例每次创建链接都会调用一次 * 如果在任何时候您不希望升级此请求,只需从getEndpointInstance类中抛出异常即可。我推荐一个 java.lang.InstantiationException。这将导致 Jetty 不执行升级并将请求发送到 servlet 处理链。 * @param endpointClass * @param <T> * @return * @throws InstantiationException */ @Override @SuppressWarnings("unchecked") public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException { //初始化BayeuxContextHolder 后续线程共享 BayeuxContextHolder holder = provideContext(); if (!getBayeux().getAllowedTransports().contains(getName())) { throw new InstantiationException("Transport not allowed"); } if (!holder.protocolMatches) { throw new InstantiationException("Could not negotiate WebSocket SubProtocols"); } T instance = (T)newWebSocketEndPoint(holder.bayeuxContext); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Created {}", instance); } holder.clear(); return instance; } private boolean checkProtocol(List<String> serverProtocols, List<String> clientProtocols) { if (serverProtocols.isEmpty()) { return true; } for (String clientProtocol : clientProtocols) { if (serverProtocols.contains(clientProtocol)) { return true; } } return false; } private BayeuxContextHolder provideContext() { BayeuxContextHolder holder = BayeuxContextHolder.holder.get(); if (holder == null) { holder = new BayeuxContextHolder(); holder.clear(); BayeuxContextHolder.holder.set(holder); } return holder; } } private static class BayeuxContextHolder { private static final ThreadLocal<BayeuxContextHolder> holder = new ThreadLocal<>(); private WebSocketContext bayeuxContext; private boolean protocolMatches; public void clear() { BayeuxContextHolder.holder.set(null); bayeuxContext = null; // Use a sensible default in case getNegotiatedSubprotocol() is not invoked. protocolMatches = true; } } private class EndPoint extends WebSocketEndPoint { private EndPoint(BayeuxContext bayeuxContext) { super(WebSocketTransport.this, bayeuxContext); } @Override protected void writeComplete(AbstractWebSocketEndPoint.Context context, List<ServerMessage> messages) { WebSocketTransport.this.writeComplete(context, messages); } }
<5>
基于jetty的websoket扩展 最终对应事件转发到comted内部做处理
org.cometd.server.websocket.javax.WebSocketEndPoint
public class WebSocketEndPoint extends Endpoint implements MessageHandler.Whole<String> { private final Logger _logger = LoggerFactory.getLogger(getClass()); private final AbstractWebSocketEndPoint _delegate; private volatile Session _wsSession; public WebSocketEndPoint(AbstractWebSocketTransport transport, BayeuxContext bayeuxContext) { //通过delegate封装transport和bayeuxContext 相关相关监听处理委托给deletegate _delegate = new Delegate(transport, bayeuxContext); } @Override public void onOpen(Session wsSession, EndpointConfig config) { _wsSession = wsSession; //设置当前session的处理handle 监听消息,关闭事件 wsSession.addMessageHandler(this); } @Override public void onMessage(String data) { if (_logger.isDebugEnabled()) { _logger.debug("WebSocket Text message on {}", this); } try { try { Promise.Completable<Void> completable = new Promise.Completable<>(); _delegate.onMessage(data, completable); // Wait, to apply backpressure to the client. completable.get(); } catch (ExecutionException x) { throw x.getCause(); } } catch (Throwable failure) { if (_logger.isDebugEnabled()) { _logger.debug("", failure); } _delegate.close(1011, failure.toString()); } } @Override public void onClose(Session wsSession, CloseReason closeReason) { _delegate.onClose(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase()); } @Override public void onError(Session wsSession, Throwable failure) { _delegate.onError(failure); } protected void writeComplete(AbstractWebSocketEndPoint.Context context, List<ServerMessage> messages) { } @Override public String toString() { return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), _delegate); } private class Delegate extends AbstractWebSocketEndPoint { public Delegate(AbstractWebSocketTransport transport, BayeuxContext bayeuxContext) { super(transport, bayeuxContext); } @Override protected void send(ServerSession session, String data, Callback callback) { if (_logger.isDebugEnabled()) { _logger.debug("Sending {} on {}", data, this); } // Async write. _wsSession.getAsyncRemote().sendText(data, result -> { Throwable failure = result.getException(); if (failure == null) { callback.succeeded(); } else { callback.failed(failure); } }); } @Override public void close(int code, String reason) { try { // Limits of the WebSocket APIs, otherwise an exception is thrown. reason = reason.substring(0, Math.min(reason.length(), 30)); if (_logger.isDebugEnabled()) { _logger.debug("Closing {}/{} on {}", code, reason, this); } _wsSession.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(code), reason)); } catch (Throwable x) { _logger.trace("Could not close WebSocket session on {}", this, x); } } @Override protected void writeComplete(Context context, List<ServerMessage> messages) { WebSocketEndPoint.this.writeComplete(context, messages); } @Override public String toString() { return String.format("%s[%s]", super.toString(), _wsSession); } } }