常規配置
<!-- CometD Servlet --> <servlet> <servlet-name>cometd</servlet-name>
<!--<1>--> <servlet-class>org.cometd.annotation.server.CometDServlet</servlet-class> <!--liqiang todo 600000--> <init-param> <param-name>maxProcessing</param-name> <param-value>600000</param-value> </init-param> <init-param> <param-name>timeout</param-name> <param-value>20000</param-value> </init-param> <init-param> <param-name>interval</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>maxInterval</param-name> <param-value>10000</param-value> </init-param> <init-param> <param-name>handshakeReconnect</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>maxLazyTimeout</param-name> <param-value>5000</param-value> </init-param> <init-param> <param-name>long-polling.multiSessionInterval</param-name> <param-value>2000</param-value> </init-param> <init-param> <param-name>services</param-name> <param-value>org.cometd.examples.ChatService</param-value> </init-param> <init-param> <param-name>ws.cometdURLMapping</param-name> <param-value>/cometd/*</param-value> </init-param> <!--容器啓動時調用init方法初始化 而不是第一次調用時--> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet>
<1>
通過ServletInit爲切入點進行初始化
org.cometd.server.CometDServlet#init
@Override public void init() throws ServletException { try { boolean export = false; //可以通過ServletContext如果在別的地方自定義初始化 需要指定此值 比如自己根據spring容器初始化了 _bayeux = (BayeuxServerImpl)getServletContext().getAttribute(BayeuxServer.ATTRIBUTE); if (_bayeux == null) { export = true; //初始化 _bayeux = newBayeuxServer(); //這裏主要是設置Servlet參數到_bayeux 供後續初始化使用 // Transfer all servlet init parameters to the BayeuxServer implementation for (String initParamName : Collections.list(getInitParameterNames())) { _bayeux.setOption(initParamName, getInitParameter(initParamName)); } //添加ServletContext到_bayeux // Add the ServletContext to the options _bayeux.setOption(ServletContext.class.getName(), getServletContext()); } //調用start進行組件初始化<2> _bayeux.start(); if (export) { //設置到Attribute getServletContext().setAttribute(BayeuxServer.ATTRIBUTE, _bayeux); } } catch (Exception x) { throw new ServletException(x); } }
<2>
org.cometd.server.BayeuxServerImpl#doStart
@Override protected void doStart() throws Exception { //<3>初始化內置的渠道 initializeMetaChannels(); //<4>初始化消息序列化轉換器 initializeJSONContext(); //<5>初始化Transport initializeServerTransports(); //如果線程池爲空 創建線程池 if (_executor == null) { //<7> _executor = new MarkedReference<>(newExecutor(), true); } addBean(_executor.getReference()); //如果Schedule爲空 創建Scheduler if (_scheduler == null) { _scheduler = new MarkedReference<>(newScheduler(), true); } addBean(_scheduler.getReference()); //配置獲得validateMessageFields 默認爲true 是否進行消息格式校驗 _validation = getOption(VALIDATE_MESSAGE_FIELDS_OPTION, true); //配置獲得broadcastToPublisher 默認爲true 此消息是否廣播到發佈者,比如都訂閱了同一個渠道,是否廣播給自己 _broadcastToPublisher = getOption(BROADCAST_TO_PUBLISHER_OPTION, true); super.doStart(); long defaultSweepPeriod = 997; //獲得配置的sweepPeriodOption 會話掃描週期檢查會話是否需要移除 默認997 毫秒單位 long sweepPeriodOption = getOption(SWEEP_PERIOD_OPTION, defaultSweepPeriod); if (sweepPeriodOption < 0) { sweepPeriodOption = defaultSweepPeriod; } long sweepPeriod = sweepPeriodOption; //開啓session檢查,檢查是否需要剔除 schedule(new Runnable() { @Override public void run() { //並行執行asyncSweep的四個任務 執行完後指定週期後 開啓下一輪 實現了定時任務效果 asyncSweep().whenComplete((r, x) -> schedule(this, sweepPeriod)); } }, sweepPeriod); }
<3>
org.cometd.server.BayeuxServerImpl#initializeMetaChannels
protected void initializeMetaChannels() { //握手<8> createChannelIfAbsent(Channel.META_HANDSHAKE); //續約連接 createChannelIfAbsent(Channel.META_CONNECT); //訂閱渠道 createChannelIfAbsent(Channel.META_SUBSCRIBE); //取消訂閱 createChannelIfAbsent(Channel.META_UNSUBSCRIBE); //斷開連接 createChannelIfAbsent(Channel.META_DISCONNECT); }
<4>
org.cometd.server.BayeuxServerImpl#initializeJSONContext
protected void initializeJSONContext() throws Exception { //默認通過 option jsonContext去獲取 Object option = getOption(AbstractServerTransport.JSON_CONTEXT_OPTION); if (option == null) { //未配置則獲取默認的 _jsonContext = new JettyJSONContextServer(); } else { //如果我們有配置的是class全名稱 if (option instanceof String) { Class<?> jsonContextClass = Thread.currentThread().getContextClassLoader().loadClass((String)option); if (JSONContextServer.class.isAssignableFrom(jsonContextClass)) { _jsonContext = (JSONContextServer)jsonContextClass.getConstructor().newInstance(); } else { throw new IllegalArgumentException("Invalid " + JSONContextServer.class.getName() + " implementation class"); } } else if (option instanceof JSONContextServer) {//如果是context對象 _jsonContext = (JSONContextServer)option; } else { throw new IllegalArgumentException("Invalid " + JSONContextServer.class.getName() + " implementation class"); } } _options.put(AbstractServerTransport.JSON_CONTEXT_OPTION, _jsonContext); }
<5>
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() //<6> 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方法進行初始化 ((AbstractServerTransport)serverTransport).init(); //加入到已激活的transpor activeTransports.add(serverTransport.getName()); } } if (_logger.isDebugEnabled()) { _logger.debug("Active transports: {}", activeTransports); } }
<6>
org.cometd.server.BayeuxServerImpl#newWebSocketTransport
private ServerTransport newWebSocketTransport() { try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); //加載服務端websoket組件 websocket組件 loader.loadClass("javax.websocket.server.ServerContainer"); //初始化websoketTransport String transportClass = "org.cometd.server.websocket.javax.WebSocketTransport"; ServerTransport transport = newServerTransport(transportClass); if (transport == null) { _logger.info("JSR 356 WebSocket classes available, but " + transportClass + " unavailable: JSR 356 WebSocket transport disabled"); } return transport; } catch (Exception x) { return null; } }
<7>
org.cometd.server.BayeuxServerImpl#newExecutor
private Executor newExecutor() { String name = _name + "-Executor"; //默認線程大小爲128 int maxThreads = (int)getOption(EXECUTOR_MAX_THREADS, 128); QueuedThreadPool executor = new QueuedThreadPool(maxThreads, 0); executor.setName(name); executor.setReservedThreads(0); return executor; }
<8>
org.cometd.server.BayeuxServerImpl#createChannelIfAbsent
@Override public MarkedReference<ServerChannel> createChannelIfAbsent(String channelName, Initializer... initializers) { ChannelId channelId; boolean initialized = false; //嘗試根據channelName獲取 判斷是否存在 ServerChannelImpl channel = _channels.get(channelName); if (channel == null) { // Creating the ChannelId will also normalize the channelName. //嘗試通過處理過的channelId獲取 channelId = new ChannelId(channelName); String id = channelId.getId(); if (!id.equals(channelName)) { channelName = id; channel = _channels.get(channelName); } } else { channelId = channel.getChannelId(); } //表示沒有被初始化 if (channel == null) { //新建一個channel ServerChannelImpl candidate = new ServerChannelImpl(this, channelId); //放入_channels channel = _channels.putIfAbsent(channelName, candidate); if (channel == null) { // My candidate channel was added to the map, so I'd better initialize it channel = candidate; if (_logger.isDebugEnabled()) { _logger.debug("Added channel {}", channel); } try { //通知 Initializer實現 可以對ServerChannelImpl做自定義配置 for (Initializer initializer : initializers) { notifyConfigureChannel(initializer, channel); } //調用listeners中ChannelListener的configureChannel方法可以對channel進行自定義配置 for (BayeuxServer.BayeuxServerListener listener : _listeners) { if (listener instanceof ServerChannel.Initializer) { notifyConfigureChannel((Initializer)listener, channel); } } } finally { channel.initialized(); } //調用listeners中ChannelListener的channelAdded表示已經被初始化 for (BayeuxServer.BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.ChannelListener) { notifyChannelAdded((ChannelListener)listener, channel); } } initialized = true; } } else { channel.resetSweeperPasses(); // Double check if the sweeper removed this channel between the check at the top and here. // This is not 100% fool proof (e.g. this thread is preempted long enough for the sweeper // to remove the channel, but the alternative is to have a global lock) _channels.putIfAbsent(channelName, channel); } // Another thread may add this channel concurrently, so wait until it is initialized channel.waitForInitialized(); return new MarkedReference<>(channel, initialized); }