├── .gitignore ├── README.md ├── notes ├── activeMQ.md ├── images │ ├── ActiveMQDispatcher.png │ ├── MessageDispatch.png │ ├── arrayternarytrie.png │ ├── arraytrie.png │ ├── cache_level.png │ ├── compact.png │ ├── component_structure.png │ ├── default_broker_chain.png │ ├── demo_flow_chart.png │ ├── eat_what_you_kill.png │ ├── full_broker_chain.png │ ├── lock.png │ ├── onCommand_chain.png │ ├── producer_init.jpg │ ├── task.png │ ├── task_runner.png │ ├── transport_chain.png │ └── uncompact.png └── note.md ├── pom.xml └── src └── main ├── java └── mq │ ├── Bootstrap.java │ ├── SessionListener.java │ ├── SimpleListener.java │ ├── SimpleProducer.java │ └── VirtualTopicListener.java └── resources ├── activemq.properties └── spring-activemq.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | /.idea/ 24 | /target/ 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # activemq-notes 2 | active-mq源码阅读 3 | -------------------------------------------------------------------------------- /notes/activeMQ.md: -------------------------------------------------------------------------------- 1 | # P2P模式 2 | 3 | ## Producer初始化 4 | 5 | ### ActiveMQConnectionFactory 6 | 7 | 此类实现了jmx定义的ConnectionFactory接口,此接口只有两个方法: 8 | 9 | ```java 10 | public interface ConnectionFactory { 11 | Connection createConnection() throws JMSException; 12 | 13 | Connection createConnection(String userName, String password) 14 | throws JMSException; 15 | } 16 | ``` 17 | 18 | #### 静态构造器 19 | 20 | ```java 21 | static{ 22 | String host = null; 23 | String port = null; 24 | host = AccessController.doPrivileged(new PrivilegedAction() { 25 | @Override 26 | public String run() { 27 | String result = System.getProperty("org.apache.activemq.AMQ_HOST"); 28 | result = (result==null||result.isEmpty()) ? System.getProperty("AMQ_HOST","localhost") : result; 29 | return result; 30 | } 31 | }); 32 | port = AccessController.doPrivileged(new PrivilegedAction() { 33 | @Override 34 | public String run() { 35 | String result = System.getProperty("org.apache.activemq.AMQ_PORT"); 36 | result = (result==null||result.isEmpty()) ? System.getProperty("AMQ_PORT","61616") : result; 37 | return result; 38 | } 39 | }); 40 | host = (host == null || host.isEmpty()) ? "localhost" : host; 41 | port = (port == null || port.isEmpty()) ? "61616" : port; 42 | DEFAULT_BROKER_HOST = host; 43 | DEFAULT_BROKER_PORT = Integer.parseInt(port); 44 | } 45 | 46 | static{ 47 | final String defaultURL = "tcp://" + DEFAULT_BROKER_HOST + ":" + DEFAULT_BROKER_PORT; 48 | String bindURL = null; 49 | 50 | try { 51 | bindURL = AccessController.doPrivileged(new PrivilegedAction() { 52 | @Override 53 | public String run() { 54 | String result = System.getProperty("org.apache.activemq.BROKER_BIND_URL"); 55 | result = (result==null||result.isEmpty()) ? System.getProperty("BROKER_BIND_URL",defaultURL) : result; 56 | return result; 57 | } 58 | }); 59 | }catch(Throwable e){ 60 | LOG.debug("Failed to look up System properties for host and port",e); 61 | } 62 | bindURL = (bindURL == null || bindURL.isEmpty()) ? defaultURL : bindURL; 63 | DEFAULT_BROKER_BIND_URL = bindURL; 64 | } 65 | ``` 66 | 67 | ##### AccessController 68 | 69 | 这就要说到java的安全模型了。java使用了域的概念,参考: 70 | 71 | [Java 安全模型介绍](http://www.ibm.com/developerworks/cn/java/j-lo-javasecurity/) 72 | 73 | doPrivileged方法正是用于非可信代码执行受限的方法,关于这一点的详细解释以及什么时候应该这么用可以参考: 74 | 75 | [AccessController.doPrivileged](http://stackoverflow.com/questions/852453/accesscontroller-doprivileged) 76 | 77 | [When should AccessController.doPrivileged() be used?](http://stackoverflow.com/questions/2233761/when-should-accesscontroller-doprivileged-be-used) 78 | 79 | [AccessController.doPrivileged](http://stackoverflow.com/questions/852453/accesscontroller-doprivileged) 80 | 81 | ##### System.getProperty 82 | 83 | 用于获取系统的一些变量,可以通过setProperties方法添加变量值。可见,activeMQ是支持通过此种方式配置服务器地址的。 84 | 85 | #### 构造器 86 | 87 | 构造器需要关注brokerURL的创建: 88 | 89 | ```java 90 | public void setBrokerURL(String brokerURL) { 91 | //用java.net.URI包装了url 92 | this.brokerURL = createURI(brokerURL); 93 | if (this.brokerURL.getQuery() != null) { 94 | // It might be a standard URI or... 95 | try { 96 | //将查询参数抽取到map中 97 | Map map = URISupport.parseQuery(this.brokerURL.getQuery()); 98 | //寻找jms.开头的项 99 | Map jmsOptionsMap = IntrospectionSupport.extractProperties(map, "jms."); 100 | //设置从map中解析的参数 101 | if (buildFromMap(jmsOptionsMap)) { 102 | //解析到正确的参数,便会将其从map中删除,所以留下的就是不正确的(未知的) 103 | if (!jmsOptionsMap.isEmpty()) { 104 | String msg = "There are " + jmsOptionsMap.size() 105 | + " jms options that couldn't be set on the ConnectionFactory." 106 | + " Check the options are spelled correctly." 107 | + " Unknown parameters=[" + jmsOptionsMap + "]." 108 | + " This connection factory cannot be started."; 109 | throw new IllegalArgumentException(msg); 110 | } 111 | //其它未知的非jms开头的依然保留在请求uri中 112 | this.brokerURL = URISupport.createRemainingURI(this.brokerURL, map); 113 | } 114 | 115 | } catch (URISyntaxException e) { 116 | } 117 | 118 | } else { 119 | 120 | // It might be a composite URI. 121 | //所谓的组合URI就是地址里参数也是一个URI的情况 122 | try { 123 | CompositeData data = URISupport.parseComposite(this.brokerURL); 124 | Map jmsOptionsMap = IntrospectionSupport.extractProperties(data.getParameters(), "jms."); 125 | if (buildFromMap(jmsOptionsMap)) { 126 | if (!jmsOptionsMap.isEmpty()) { 127 | String msg = "There are " + jmsOptionsMap.size() 128 | + " jms options that couldn't be set on the ConnectionFactory." 129 | + " Check the options are spelled correctly." 130 | + " Unknown parameters=[" + jmsOptionsMap + "]." 131 | + " This connection factory cannot be started."; 132 | throw new IllegalArgumentException(msg); 133 | } 134 | 135 | this.brokerURL = data.toURI(); 136 | } 137 | } catch (URISyntaxException e) { 138 | } 139 | } 140 | } 141 | ``` 142 | 143 | URI.getQuery: 144 | 145 | 返回的其实就是一个uri连接的查询部分(get方法),比如连接: http://cn.bing.com/dict/search?q=Transport&go=%E6%90%9C%E7%B4%A2&qs=n,那么返回的就是q=Transport&go=搜索&qs=n。**如果不是特殊设置的,默认的brokerurl是 没有这个的。** 146 | 147 | buildFromMap: 148 | 149 | ```java 150 | public boolean buildFromMap(Map properties) { 151 | boolean rc = false; 152 | 153 | ActiveMQPrefetchPolicy p = new ActiveMQPrefetchPolicy(); 154 | //设置以prefetchPolicy开头的参数,下面也是一样的 155 | if (IntrospectionSupport.setProperties(p, properties, "prefetchPolicy.")) { 156 | setPrefetchPolicy(p); 157 | rc = true; 158 | } 159 | 160 | RedeliveryPolicy rp = new RedeliveryPolicy(); 161 | if (IntrospectionSupport.setProperties(rp, properties, "redeliveryPolicy.")) { 162 | setRedeliveryPolicy(rp); 163 | rc = true; 164 | } 165 | 166 | BlobTransferPolicy blobTransferPolicy = new BlobTransferPolicy(); 167 | if (IntrospectionSupport.setProperties(blobTransferPolicy, properties, "blobTransferPolicy.")) { 168 | setBlobTransferPolicy(blobTransferPolicy); 169 | rc = true; 170 | } 171 | 172 | rc |= IntrospectionSupport.setProperties(this, properties); 173 | 174 | return rc; 175 | } 176 | ``` 177 | 178 | 可见,activeMQ支持用URL参数配置。 179 | 180 | ### Connection 181 | 182 | #### 创建 183 | 184 | connectionFactory.createConnection: 185 | 186 | ```java 187 | @Override 188 | public Connection createConnection() throws JMSException { 189 | return createActiveMQConnection(); 190 | } 191 | protected ActiveMQConnection createActiveMQConnection() throws JMSException { 192 | return createActiveMQConnection(userName, password); 193 | } 194 | protected ActiveMQConnection createActiveMQConnection(String userName, String password) { 195 | if (brokerURL == null) { 196 | throw new ConfigurationException("brokerURL not set."); 197 | } 198 | ActiveMQConnection connection = null; 199 | //省略try...catch 200 | Transport transport = createTransport(); 201 | connection = createActiveMQConnection(transport, factoryStats); 202 | 203 | connection.setUserName(userName); 204 | connection.setPassword(password); 205 | 206 | configureConnection(connection); 207 | 208 | transport.start(); 209 | 210 | if (clientID != null) { 211 | connection.setDefaultClientID(clientID); 212 | } 213 | 214 | return connection; 215 | } 216 | } 217 | ``` 218 | 219 | ##### Transport 220 | 221 | 根据此类的注释,Transport代表了一个传输的客户端一侧。具体的用途此处先挖个坑。 222 | 223 | ###### 创建 224 | 225 | createTransport: 226 | 227 | ```java 228 | protected Transport createTransport() { 229 | URI connectBrokerUL = brokerURL; 230 | String scheme = brokerURL.getScheme(); 231 | if (scheme == null) { 232 | throw new IOException("Transport not scheme specified: [" + brokerURL + "]"); 233 | } 234 | if (scheme.equals("auto")) { 235 | connectBrokerUL = new URI(brokerURL.toString().replace("auto", "tcp")); 236 | } else if (scheme.equals("auto+ssl")) { 237 | connectBrokerUL = new URI(brokerURL.toString().replace("auto+ssl", "ssl")); 238 | } else if (scheme.equals("auto+nio")) { 239 | connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio", "nio")); 240 | } else if (scheme.equals("auto+nio+ssl")) { 241 | connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio+ssl", "nio+ssl")); 242 | } 243 | 244 | return TransportFactory.connect(connectBrokerUL); 245 | } 246 | ``` 247 | 248 | scheme就是协议名,比如http协议的就返回http。默认是tcp的。 249 | 250 | TransportFactory.connect: 251 | 252 | ```java 253 | public static Transport connect(URI location) throws Exception { 254 | TransportFactory tf = findTransportFactory(location); 255 | return tf.doConnect(location); 256 | } 257 | ``` 258 | 259 | TransportFactory.findTransportFactory: 260 | 261 | ```java 262 | public static TransportFactory findTransportFactory(URI location) throws IOException { 263 | String scheme = location.getScheme(); 264 | if (scheme == null) { 265 | throw new IOException("Transport not scheme specified: [" + location + "]"); 266 | } 267 | //Map 268 | TransportFactory tf = TRANSPORT_FACTORYS.get(scheme); 269 | if (tf == null) { 270 | // Try to load if from a META-INF property. 271 | try { 272 | tf = (TransportFactory)TRANSPORT_FACTORY_FINDER.newInstance(scheme); 273 | TRANSPORT_FACTORYS.put(scheme, tf); 274 | } catch (Throwable e) { 275 | throw e; 276 | } 277 | } 278 | return tf; 279 | } 280 | ``` 281 | 282 | 可见,**对于每一种协议(Scheme都有)一种TransportFactory与其对应。**对应关系保存在jar包中:META-INF/services/org/apache/activemq/transport/,以tcp为例,文件内容就是: 283 | 284 | class=org.apache.activemq.transport.tcp.TcpTransportFactory。 285 | 286 | TransportFactory.doConnect: 287 | 288 | ```java 289 | public Transport doConnect(URI location) throws Exception { 290 | Map options = new HashMap(URISupport.parseParameters(location)); 291 | if( !options.containsKey("wireFormat.host") ) { 292 | options.put("wireFormat.host", location.getHost()); 293 | } 294 | WireFormat wf = createWireFormat(options); 295 | Transport transport = createTransport(location, wf); 296 | Transport rc = configure(transport, wf, options); 297 | //remove auto 298 | IntrospectionSupport.extractProperties(options, "auto."); 299 | 300 | if (!options.isEmpty()) { 301 | throw new IllegalArgumentException("Invalid connect parameters: " + options); 302 | } 303 | return rc; 304 | } 305 | ``` 306 | 307 | ###### WireFormat 308 | 309 | 首先创建的是WireFormat,代表了命令的序列化方式。 310 | 311 | ```java 312 | protected WireFormat createWireFormat(Map options) throws IOException { 313 | WireFormatFactory factory = createWireFormatFactory(options); 314 | WireFormat format = factory.createWireFormat(); 315 | return format; 316 | } 317 | 318 | protected WireFormatFactory createWireFormatFactory(Map options) throws IOException { 319 | String wireFormat = options.remove("wireFormat"); 320 | if (wireFormat == null) { 321 | //"default" 322 | wireFormat = getDefaultWireFormatType(); 323 | } 324 | 325 | WireFormatFactory wff = (WireFormatFactory)WIREFORMAT_FACTORY_FINDER.newInstance(wireFormat); 326 | //将WireFormat相关的参数(wireFormat.开头)设置进入WireFormatFactory 327 | IntrospectionSupport.setProperties(wff, options, "wireFormat."); 328 | return wff; 329 | } 330 | ``` 331 | 332 | 工厂的查找和Transport一个套路,这次保存在META-INF/services/org/apache/activemq/wireformat/,默认default的内容是: class=org.apache.activemq.openwire.OpenWireFormatFactory 333 | 334 | OpenWireFormatFactory.createWireFormat就是调用了构造器初始化了一个OpenWireFormat对象。 335 | 336 | TcpTransportFactory.createTransport方法初始化了 一个TcpTransport对象,构造器源码: 337 | 338 | ```java 339 | public TcpTransport(WireFormat wireFormat, SocketFactory socketFactory, URI remoteLocation, 340 | URI localLocation) throws UnknownHostException, IOException { 341 | this.wireFormat = wireFormat; 342 | this.socketFactory = socketFactory; 343 | try { 344 | this.socket = socketFactory.createSocket(); 345 | } catch (SocketException e) { 346 | this.socket = null; 347 | } 348 | this.remoteLocation = remoteLocation; 349 | this.localLocation = localLocation; 350 | this.initBuffer = null; 351 | setDaemon(false); 352 | } 353 | ``` 354 | 355 | ###### TransportFilter 356 | 357 | TransportFactory.configure方法就是为了设置这东西。这东西和Jetty的HandlerWrapper是一个套路,本身也是一个Transport,里面包装了下一个要调用的Transport,构造器源码: 358 | 359 | ```java 360 | public TransportFilter(Transport next) { 361 | this.next = next; 362 | } 363 | ``` 364 | 365 | 默认添加了MutexTransport和ResponseCorrelator,形成的调用链如下: 366 | 367 | ![Transport调用链](images/transport_chain.png) 368 | 369 | 除此之外注意ActiveMQConnection构造器的一句: 370 | 371 | ```java 372 | this.transport.setTransportListener(this); 373 | ``` 374 | 375 | setTransportListener在TransportFilter中定义: 376 | 377 | ```java 378 | @Override 379 | public void setTransportListener(TransportListener channelListener) { 380 | this.transportListener = channelListener; 381 | if (channelListener == null) { 382 | next.setTransportListener(null); 383 | } else { 384 | next.setTransportListener(this); 385 | } 386 | } 387 | ``` 388 | 389 | 这样就形成了onCommand方法的这样的调用链: 390 | 391 | ![onCommand调用链](images/onCommand_chain.png) 392 | 393 | 这种写法值得学习。 394 | 395 | ###### start 396 | 397 | ServiceSupport.start: 398 | 399 | ```java 400 | public void start() throws Exception { 401 | if (started.compareAndSet(false, true)) { 402 | boolean success = false; 403 | stopped.set(false); 404 | try { 405 | preStart(); 406 | doStart(); 407 | success = true; 408 | } finally { 409 | started.set(success); 410 | } 411 | for(ServiceListener l:this.serviceListeners) { 412 | l.started(this); 413 | } 414 | } 415 | } 416 | ``` 417 | 418 | 依然是非常主旋律的start方法。preStart没有一个子类实现,doStart实现链: 419 | 420 | TcpTransport.doStart: 421 | 422 | ```java 423 | @Override 424 | protected void doStart() throws Exception { 425 | //实际socket连接服务器 426 | connect(); 427 | stoppedLatch.set(new CountDownLatch(1)); 428 | super.doStart(); 429 | } 430 | ``` 431 | 432 | TransportThreadSupport.doStart: 433 | 434 | ```java 435 | protected void doStart() throws Exception { 436 | runner = new Thread(null, this, "ActiveMQ Transport: " + toString(), stackSize); 437 | runner.setDaemon(daemon); 438 | runner.start(); 439 | } 440 | ``` 441 | 442 | **此线程负责读取服务器发送来的命令**。run方法的实现在其子类TcpTransport。 443 | 444 | ```java 445 | @Override 446 | public void run() { 447 | while (!isStopped()) { 448 | doRun(); 449 | } 450 | } 451 | 452 | protected void doRun() throws IOException { 453 | Object command = readCommand(); 454 | doConsume(command); 455 | } 456 | 457 | protected Object readCommand() throws IOException { 458 | return wireFormat.unmarshal(dataIn); 459 | } 460 | ``` 461 | 462 | OpenWireFormat.unmarshal: 463 | 464 | ```java 465 | @Override 466 | public Object unmarshal(DataInput dis) throws IOException { 467 | DataInput dataIn = dis; 468 | //如果没有禁用,那么最开始是一个表示数据长度的int 469 | if (!sizePrefixDisabled) { 470 | int size = dis.readInt(); 471 | if (size > maxFrameSize) { 472 | throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize); 473 | } 474 | } 475 | return doUnmarshal(dataIn); 476 | } 477 | ``` 478 | 479 | doUnmarshal: 480 | 481 | ```java 482 | public Object doUnmarshal(DataInput dis) throws IOException { 483 | byte dataType = dis.readByte(); 484 | if (dataType != NULL_TYPE) { 485 | DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; 486 | if (dsm == null) { 487 | throw new IOException("Unknown data type: " + dataType); 488 | } 489 | Object data = dsm.createObject(); 490 | if (this.tightEncodingEnabled) { 491 | BooleanStream bs = new BooleanStream(); 492 | bs.unmarshal(dis); 493 | dsm.tightUnmarshal(this, data, dis, bs); 494 | } else { 495 | dsm.looseUnmarshal(this, data, dis); 496 | } 497 | return data; 498 | } else { 499 | return null; 500 | } 501 | } 502 | ``` 503 | 504 | 代码就是下面事务开启-marshal一节的反过程,不在赘述。 505 | 506 | 解析完成之后执行TransportSupport.doConsume: 507 | 508 | ```java 509 | public void doConsume(Object command) { 510 | if (command != null) { 511 | if (transportListener != null) { 512 | transportListener.onCommand(command); 513 | } else { 514 | LOG.error("No transportListener available to process inbound command: " + command); 515 | } 516 | } 517 | } 518 | ``` 519 | 520 | transportListener在ActiveMQConnection构造器中被设置,其实就是ActiveMQConnection.onCommand, 此处使用了Vistor模式,实现了CommandVisitor接口。 521 | 522 | #### 创建 523 | 524 | ActiveMQConnection构造器: 525 | 526 | ```java 527 | protected ActiveMQConnection(final Transport transport, IdGenerator clientIdGenerator, 528 | IdGenerator connectionIdGenerator, JMSStatsImpl factoryStats) throws Exception { 529 | 530 | this.transport = transport; 531 | this.clientIdGenerator = clientIdGenerator; 532 | this.factoryStats = factoryStats; 533 | executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, 534 | new LinkedBlockingQueue(), new ThreadFactory() { 535 | @Override 536 | public Thread newThread(Runnable r) { 537 | Thread thread = new Thread(r, "ActiveMQ Connection Executor: " + transport); 538 | //Don't make these daemon threads - see https://issues.apache.org/jira/browse/AMQ-796 539 | //thread.setDaemon(true); 540 | return thread; 541 | } 542 | }); 543 | String uniqueId = connectionIdGenerator.generateId(); 544 | 545 | this.transport.setTransportListener(this); 546 | } 547 | ``` 548 | 549 | #### start 550 | 551 | ActiveMQConnection.start: 552 | 553 | ```java 554 | public void start() throws JMSException { 555 | checkClosedOrFailed(); 556 | ensureConnectionInfoSent(); 557 | if (started.compareAndSet(false, true)) { 558 | for (Iterator i = sessions.iterator(); i.hasNext();) { 559 | ActiveMQSession session = i.next(); 560 | session.start(); 561 | } 562 | } 563 | } 564 | ``` 565 | 566 | 起初时sessions为空,所以没做什么。 567 | 568 | ### 总结 569 | 570 | 整个初始化的时序图: 571 | 572 | ![Producer初始化时序图](images/producer_init.jpg) 573 | 574 | ## Producer运行 575 | 576 | ### Session创建 577 | 578 | #### ActiveMQConnection.createSession 579 | 580 | ```java 581 | @Override 582 | public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException { 583 | checkClosedOrFailed(); 584 | ensureConnectionInfoSent(); 585 | if (!transacted) { 586 | if (acknowledgeMode == Session.SESSION_TRANSACTED) { 587 | throw new JMSException(); 588 | } else if (acknowledgeMode < Session.SESSION_TRANSACTED || 589 | acknowledgeMode > ActiveMQSession.MAX_ACK_CONSTANT) { 590 | throw new JMSException(); 591 | } 592 | } 593 | return new ActiveMQSession(this, getNextSessionId(), transacted ? Session.SESSION_TRANSACTED : 594 | acknowledgeMode, isDispatchAsync(), isAlwaysSessionAsync()); 595 | } 596 | ``` 597 | 598 | ActiveMQSession构造器: 599 | 600 | ```java 601 | protected ActiveMQSession(ActiveMQConnection connection, SessionId sessionId, 602 | int acknowledgeMode, boolean asyncDispatch, boolean sessionAsyncDispatch) { 603 | this.debug = LOG.isDebugEnabled(); 604 | this.connection = connection; 605 | this.acknowledgementMode = acknowledgeMode; 606 | this.asyncDispatch = asyncDispatch; 607 | this.sessionAsyncDispatch = sessionAsyncDispatch; 608 | this.info = new SessionInfo(connection.getConnectionInfo(), sessionId.getValue()); 609 | setTransactionContext(new TransactionContext(connection)); 610 | stats = new JMSSessionStatsImpl(producers, consumers); 611 | this.connection.asyncSendPacket(info); 612 | setTransformer(connection.getTransformer()); 613 | setBlobTransferPolicy(connection.getBlobTransferPolicy()); 614 | this.connectionExecutor=connection.getExecutor(); 615 | this.executor = new ActiveMQSessionExecutor(this); 616 | connection.addSession(this); 617 | if (connection.isStarted()) { 618 | start(); 619 | } 620 | } 621 | ``` 622 | 623 | #### ActiveMQSessionExecutor 624 | 625 | ActiveMQSessionExecutor构造器: 626 | 627 | 此执行器用于**向消费者分发消息**。 628 | 629 | ```java 630 | ActiveMQSessionExecutor(ActiveMQSession session) { 631 | this.session = session; 632 | if (this.session.connection != null && this.session.connection.isMessagePrioritySupported()) { 633 | this.messageQueue = new SimplePriorityMessageDispatchChannel(); 634 | }else { 635 | //默认这个 636 | this.messageQueue = new FifoMessageDispatchChannel(); 637 | } 638 | } 639 | ``` 640 | 641 | #### FifoMessageDispatchChannel 642 | 643 | FifoMessageDispatchChannel实现了MessageDispatchChannel接口,构造器: 644 | 645 | ```java 646 | public FifoMessageDispatchChannel() { 647 | this.list = new LinkedList(); 648 | } 649 | ``` 650 | 651 | #### start方法 652 | 653 | ```java 654 | protected void start() throws JMSException { 655 | started.set(true); 656 | //consumer现在还为空 657 | for (Iterator iter = consumers.iterator(); iter.hasNext();) { 658 | ActiveMQMessageConsumer c = iter.next(); 659 | c.start(); 660 | } 661 | executor.start(); 662 | } 663 | ``` 664 | 665 | ##### ActiveMQSessionExecutor.start 666 | 667 | ```java 668 | synchronized void start() { 669 | if (!messageQueue.isRunning()) { 670 | messageQueue.start(); 671 | //队列中是否有元素,开始时没有 672 | if (hasUncomsumedMessages()) { 673 | wakeup(); 674 | } 675 | } 676 | } 677 | ``` 678 | 679 | wakeUp: 680 | 681 | ```java 682 | public void wakeup() { 683 | //默认false 684 | if (!dispatchedBySessionPool) { 685 | //默认true,异步(另起一个线程)执行 686 | if (session.isSessionAsyncDispatch()) { 687 | try { 688 | TaskRunner taskRunner = this.taskRunner; 689 | if (taskRunner == null) { 690 | synchronized (this) { 691 | //默认为空 692 | if (this.taskRunner == null) { 693 | if (!isRunning()) { 694 | // stop has been called 695 | return; 696 | } 697 | this.taskRunner = session.connection.getSessionTaskRunner().createTaskRunner(this, 698 | "ActiveMQ Session: " + session.getSessionId()); 699 | } 700 | taskRunner = this.taskRunner; 701 | } 702 | } 703 | taskRunner.wakeup(); 704 | } catch (InterruptedException e) { 705 | Thread.currentThread().interrupt(); 706 | } 707 | } else { 708 | while (iterate()) { 709 | } 710 | } 711 | } 712 | } 713 | ``` 714 | 715 | ###### 异步分发 716 | 717 | ActiveMQConnection.getSessionTaskRunner: 718 | 719 | ```java 720 | public TaskRunnerFactory getSessionTaskRunner() { 721 | synchronized (this) { 722 | if (sessionTaskRunner == null) { 723 | sessionTaskRunner = new TaskRunnerFactory("ActiveMQ Session Task", 724 | ThreadPriorities.INBOUND_CLIENT_SESSION, false, 1000, isUseDedicatedTaskRunner(), 725 | maxThreadPoolSize); 726 | sessionTaskRunner.setRejectedTaskHandler(rejectedTaskHandler); 727 | } 728 | } 729 | return sessionTaskRunner; 730 | } 731 | ``` 732 | 733 | createTaskRunner实际上创建了一个线程,wakeup方法实际上是提交到线程 池运行,不再贴代码。此线程真正执行的方法是ActiveMQSessionExecutor.iterate: 734 | 735 | ```java 736 | public boolean iterate() { 737 | 738 | // Deliver any messages queued on the consumer to their listeners. 739 | for (ActiveMQMessageConsumer consumer : this.session.consumers) { 740 | if (consumer.iterate()) { 741 | return true; 742 | } 743 | } 744 | 745 | // No messages left queued on the listeners.. so now dispatch messages 746 | // queued on the session 747 | MessageDispatch message = messageQueue.dequeueNoWait(); 748 | if (message == null) { 749 | return false; 750 | } else { 751 | dispatch(message); 752 | return !messageQueue.isEmpty(); 753 | } 754 | } 755 | ``` 756 | 757 | 可以看出,**每个Consumer都有自己的队列缓冲区**。 758 | 759 | ###### 同步分发 760 | 761 | 就是直接调用iterate。 762 | 763 | ##### FifoMessageDispatchChannel.start 764 | 765 | ```java 766 | public void start() { 767 | synchronized (mutex) { 768 | running = true; 769 | mutex.notifyAll(); 770 | } 771 | } 772 | ``` 773 | 774 | ### 队列创建 775 | 776 | ActiveMQSession.createQueue: 777 | 778 | ```java 779 | @Override 780 | public Queue createQueue(String queueName) throws JMSException { 781 | checkClosed(); 782 | //ID: 783 | if (queueName.startsWith(ActiveMQDestination.TEMP_DESTINATION_NAME_PREFIX)) { 784 | return new ActiveMQTempQueue(queueName); 785 | } 786 | return new ActiveMQQueue(queueName); 787 | } 788 | ``` 789 | 790 | 创建了一个ActiveMQQueue对象。 791 | 792 | ### Producer创建 793 | 794 | 构建了一个ActiveMQMessageProducer对象。 795 | 796 | ### 消息发送 797 | 798 | #### ActiveMQMessageProducerSupport.send: 799 | 800 | ```java 801 | public void send(Message message) throws JMSException { 802 | this.send(this.getDestination(), 803 | message, 804 | this.defaultDeliveryMode, 805 | this.defaultPriority, 806 | this.defaultTimeToLive); 807 | } 808 | ``` 809 | 810 | ##### DeliveryMode 811 | 812 | 这东西是jms定义的两种消息类型: 813 | 814 | ```java 815 | public interface DeliveryMode { 816 | static final int NON_PERSISTENT = 1; 817 | static final int PERSISTENT = 2; 818 | } 819 | ``` 820 | 821 | 对于NON_PERSISTENT类型,jms不保证发送的成功,同时jms如果宕机也不会进行持久化操作,而PERSISTENT正相反。默认当然是后者。 822 | 823 | 可以调用producer/publisher的setDeliveryMode方法设置,这样所发送的所有消息 就都是此种mode。也可以使用Message的setJMSDeliveryMode方法,这样仅对此message有效。 824 | 825 | 参考: [理解JMS规范中消息的传输模式和消息持久化](http://blog.csdn.net/aitangyong/article/details/26132913) 826 | 827 | ##### defaultPriority 828 | 829 | 默认为4,不知道什么东西。 830 | 831 | ##### defaultTimeToLive 832 | 833 | 默认为0,不知道什么东西。 834 | 835 | #### ActiveMQMessageProducer.send 836 | 837 | ```java 838 | public void send(Destination destination, Message message, int deliveryMode, 839 | int priority, long timeToLive, AsyncCallback onComplete) { 840 | checkClosed(); 841 | if (destination == null) { 842 | //info里面的是构造的时候传入的 843 | if (info.getDestination() == null) { 844 | throw new UnsupportedOperationException("A destination must be specified."); 845 | } 846 | throw new InvalidDestinationException("Don't understand null destinations"); 847 | } 848 | 849 | ActiveMQDestination dest; 850 | //destination是由this.getDestination()而来,而此方法正是拿到的info的destination,所以默认就是第一种情况执行 851 | if (destination.equals(info.getDestination())) { 852 | dest = (ActiveMQDestination)destination; 853 | } else if (info.getDestination() == null) { 854 | dest = ActiveMQDestination.transform(destination); 855 | } else { 856 | throw new UnsupportedOperationException("This producer can only send messages to: " + 857 | this.info.getDestination().getPhysicalName()); 858 | } 859 | if (dest == null) { 860 | throw new JMSException("No destination specified"); 861 | } 862 | //transformer由session.getTransformer()而来 863 | if (transformer != null) { 864 | Message transformedMessage = transformer.producerTransform(session, this, message); 865 | if (transformedMessage != null) { 866 | message = transformedMessage; 867 | } 868 | } 869 | 870 | if (producerWindow != null) { 871 | try { 872 | producerWindow.waitForSpace(); 873 | } catch (InterruptedException e) { 874 | throw new JMSException("Send aborted due to thread interrupt."); 875 | } 876 | } 877 | 878 | this.session.send(this, dest, message, deliveryMode, priority, 879 | timeToLive, producerWindow, sendTimeout, onComplete); 880 | 881 | stats.onMessage(); 882 | } 883 | ``` 884 | 885 | ##### transformer 886 | 887 | transformer由session.getTransformer()而来,而session的又由connection.getTransformer()而来。此接口只有两个方法: 888 | 889 | ```java 890 | public interface MessageTransformer { 891 | 892 | /** 893 | * Transforms the given message inside the producer before it is sent to the JMS bus. 894 | */ 895 | Message producerTransform(Session session, MessageProducer producer, Message message); 896 | 897 | /** 898 | * Transforms the given message inside the consumer before being dispatched to the client code 899 | */ 900 | Message consumerTransform(Session session, MessageConsumer consumer, Message message); 901 | } 902 | ``` 903 | 904 | **目的是在消息被真正发送和消息被分发到消费者之前提供给使用者一次转化的机会**。默认为空。 905 | 906 | ##### producerWindow 907 | 908 | 来自构造器,源码: 909 | 910 | ```java 911 | //默认为0,如有需要,自己设置 912 | this.info.setWindowSize(session.connection.getProducerWindowSize()); 913 | // Enable producer window flow control if protocol >= 3 and the window size > 0 914 | //protocol version默认12 915 | if (session.connection.getProtocolVersion() >= 3 && this.info.getWindowSize() > 0) { 916 | producerWindow = new MemoryUsage("Producer Window: " + producerId); 917 | producerWindow.setExecutor(session.getConnectionExecutor()); 918 | producerWindow.setLimit(this.info.getWindowSize()); 919 | producerWindow.start(); 920 | } 921 | ``` 922 | 923 | producerWindow其实就是个MemoryUsage对象,查看其注释可以发现这个东西用以控制生产者使用的内存的大小。 924 | 925 | #### ActiveMQSession.send 926 | 927 | ```java 928 | protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination, 929 | Message message, int deliveryMode, int priority, long timeToLive, 930 | MemoryUsage producerWindow, int sendTimeout, AsyncCallback onComplete) { 931 | 932 | checkClosed(); 933 | //isTemporary直接返回false 934 | if (destination.isTemporary() && connection.isDeleted(destination)) { 935 | throw new InvalidDestinationException 936 | ("Cannot publish to a deleted Destination: " + destination); 937 | } 938 | synchronized (sendMutex) { 939 | // tell the Broker we are about to start a new transaction 940 | doStartTransaction(); 941 | TransactionId txid = transactionContext.getTransactionId(); 942 | long sequenceNumber = producer.getMessageSequence(); 943 | 944 | //Set the "JMS" header fields on the original message, see 1.1 spec section 3.4.11 945 | message.setJMSDeliveryMode(deliveryMode); 946 | long expiration = 0L; 947 | //默认false 948 | if (!producer.getDisableMessageTimestamp()) { 949 | long timeStamp = System.currentTimeMillis(); 950 | message.setJMSTimestamp(timeStamp); 951 | if (timeToLive > 0) { 952 | expiration = timeToLive + timeStamp; 953 | } 954 | } 955 | message.setJMSExpiration(expiration); 956 | message.setJMSPriority(priority); 957 | message.setJMSRedelivered(false); 958 | 959 | //将jms定义的Message转为ActiveMQMessage 960 | ActiveMQMessage msg = ActiveMQMessageTransformation.transformMessage(message, connection); 961 | msg.setDestination(destination); 962 | msg.setMessageId(new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber)); 963 | 964 | // Set the message id. 965 | if (msg != message) { 966 | message.setJMSMessageID(msg.getMessageId().toString()); 967 | // Make sure the JMS destination is set on the foreign messages too. 968 | message.setJMSDestination(destination); 969 | } 970 | //clear the brokerPath in case we are re-sending this message 971 | msg.setBrokerPath(null); 972 | 973 | msg.setTransactionId(txid); 974 | //默认为true,发送拷贝的副本,为了线程安全? 975 | if (connection.isCopyMessageOnSend()) { 976 | msg = (ActiveMQMessage)msg.copy(); 977 | } 978 | msg.setConnection(connection); 979 | msg.onSend(); 980 | msg.setProducerId(msg.getMessageId().getProducerId()); 981 | if (LOG.isTraceEnabled()) { 982 | LOG.trace(getSessionId() + " sending message: " + msg); 983 | } 984 | if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() 985 | && !connection.isAlwaysSyncSend() 986 | && (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) { 987 | this.connection.asyncSendPacket(msg); 988 | if (producerWindow != null) { 989 | // Since we defer lots of the marshaling till we hit the 990 | // wire, this might not 991 | // provide and accurate size. We may change over to doing 992 | // more aggressive marshaling, 993 | // to get more accurate sizes.. this is more important once 994 | // users start using producer window 995 | // flow control. 996 | int size = msg.getSize(); 997 | producerWindow.increaseUsage(size); 998 | } 999 | } else { 1000 | if (sendTimeout > 0 && onComplete==null) { 1001 | //真正执行发送 1002 | this.connection.syncSendPacket(msg,sendTimeout); 1003 | }else { 1004 | this.connection.syncSendPacket(msg, onComplete); 1005 | } 1006 | } 1007 | 1008 | } 1009 | } 1010 | ``` 1011 | 1012 | ##### 事务开启 1013 | 1014 | doStartTransaction: 1015 | 1016 | ```java 1017 | protected void doStartTransaction() throws JMSException { 1018 | //支持事务并且不再分布式事务中 1019 | if (getTransacted() && !transactionContext.isInXATransaction()) { 1020 | transactionContext.begin(); 1021 | } 1022 | } 1023 | ``` 1024 | 1025 | getTransacted用于检测当前session是否处于事务模式,也就是说应不应该开启事务。 1026 | 1027 | ```java 1028 | @Override 1029 | public boolean getTransacted() throws JMSException { 1030 | checkClosed(); 1031 | return isTransacted(); 1032 | } 1033 | public boolean isTransacted() { 1034 | return this.acknowledgementMode == Session.SESSION_TRANSACTED || 1035 | (transactionContext.isInXATransaction()); 1036 | } 1037 | ``` 1038 | 1039 | transactionContext在ActiveMQSession构造器中创建: 1040 | 1041 | ```java 1042 | setTransactionContext(new TransactionContext(connection)); 1043 | ``` 1044 | 1045 | **TransactionContext实现了jta XAResource接口,代表支持jta分布式事务的一种资源。ActiveMQ支持两种事务形式: 本地事务和分布式事务。默认使用的ActiveMQConnectionFactory代表是本地事务,如果需要分布式事务,那么需要ActiveMQXAConnectionFactory并配合atomikos,Spring使用**。 1046 | 1047 | 分布式事务概念: [JTA 深度历险 - 原理与实现](https://www.ibm.com/developerworks/cn/java/j-lo-jta/) 1048 | 1049 | ActiveMQ搭配Spring实现分布式事务: [JTA分布式事务之JMS篇](http://www.lai18.com/content/6587903.html) 1050 | 1051 | TransactionContext.begin: 1052 | 1053 | ```java 1054 | public void begin() throws JMSException { 1055 | 1056 | if (isInXATransaction()) { 1057 | throw new TransactionInProgressException 1058 | ("Cannot start local transaction. XA transaction is already in progress."); 1059 | } 1060 | 1061 | if (transactionId == null) { 1062 | synchronizations = null; 1063 | beforeEndIndex = 0; 1064 | setRollbackOnly(false); 1065 | //生成事务ID 1066 | this.transactionId = new LocalTransactionId(getConnectionId(), 1067 | localTransactionIdGenerator.getNextSequenceId()); 1068 | TransactionInfo info = new TransactionInfo(getConnectionId(), 1069 | transactionId, TransactionInfo.BEGIN); 1070 | //向服务器发送确认连接的消息 1071 | this.connection.ensureConnectionInfoSent(); 1072 | //向服务器发送事务信息 1073 | this.connection.asyncSendPacket(info); 1074 | 1075 | // Notify the listener that the tx was started. 1076 | if (localTransactionEventListener != null) { 1077 | localTransactionEventListener.beginEvent(); 1078 | } 1079 | 1080 | LOG.debug("Begin:{}", transactionId); 1081 | } 1082 | } 1083 | ``` 1084 | 1085 | ActiveMQConnection.ensureConnectionInfoSent: 1086 | 1087 | ```java 1088 | protected void ensureConnectionInfoSent() throws JMSException { 1089 | synchronized(this.ensureConnectionInfoSentMutex) { 1090 | // Can we skip sending the ConnectionInfo packet?? 1091 | if (isConnectionInfoSentToBroker || closed.get()) { 1092 | return; 1093 | } 1094 | //发送的是ConnectionInfo类的字段 1095 | syncSendPacket(info.copy(), getConnectResponseTimeout()); 1096 | //此消息一个连接只会发送一次 1097 | this.isConnectionInfoSentToBroker = true; 1098 | // Add a temp destination advisory consumer so that 1099 | // We know what the valid temporary destinations are on the 1100 | // broker without having to do an RPC to the broker. 1101 | 1102 | ConsumerId consumerId = new ConsumerId(new SessionId(info.getConnectionId(), -1), 1103 | consumerIdGenerator.getNextSequenceId()); 1104 | if (watchTopicAdvisories) { 1105 | advisoryConsumer = new AdvisoryConsumer(this, consumerId); 1106 | } 1107 | } 1108 | } 1109 | ``` 1110 | 1111 | ActiveMQConnection.syncSendPacket: 1112 | 1113 | ```java 1114 | public Response syncSendPacket(Command command, int timeout) { 1115 | Response response = (Response)(timeout > 0 1116 | ? this.transport.request(command, timeout) 1117 | : this.transport.request(command)); 1118 | } 1119 | ``` 1120 | 1121 | 此处执行的正是TransportFilter组成的调用链。 1122 | 1123 | ###### MutexTransport 1124 | 1125 | ```java 1126 | @Override 1127 | public Object request(Object command, int timeout) throws IOException { 1128 | writeLock.lock(); 1129 | try { 1130 | return next.request(command, timeout); 1131 | } finally { 1132 | writeLock.unlock(); 1133 | } 1134 | } 1135 | ``` 1136 | 1137 | 加了一把锁。 1138 | 1139 | ###### ResponseCorrelator 1140 | 1141 | ```java 1142 | public Object request(Object command, int timeout) throws IOException { 1143 | FutureResponse response = asyncRequest(command, null); 1144 | //阻塞等待response 1145 | return response.getResult(timeout); 1146 | } 1147 | public FutureResponse asyncRequest(Object o, ResponseCallback responseCallback) { 1148 | Command command = (Command) o; 1149 | //生成递增的,唯一的CommandID 1150 | command.setCommandId(sequenceGenerator.getNextSequenceId()); 1151 | command.setResponseRequired(true); 1152 | FutureResponse future = new FutureResponse(responseCallback, this); 1153 | IOException priorError = null; 1154 | synchronized (requestMap) { 1155 | priorError = this.error; 1156 | if (priorError == null) { 1157 | requestMap.put(new Integer(command.getCommandId()), future); 1158 | } 1159 | } 1160 | 1161 | if (priorError != null) { 1162 | future.set(new ExceptionResponse(priorError)); 1163 | throw priorError; 1164 | } 1165 | 1166 | next.oneway(command); 1167 | return future; 1168 | } 1169 | ``` 1170 | 1171 | 从这里便可以看出此Filter的作用: 1172 | 1173 | - 为每次请求(request/command)设置唯一的ID 1174 | - 通过阻塞的FutureResponse的使用形成请求-响应的语义 1175 | 1176 | 那是在什么地方设置future完成的呢?参见前面TransFilter一节,当读取到命令时,各Filter的onCommand会被倒叙调用,ResponseCorrelator.onCommand: 1177 | 1178 | ```java 1179 | public void onCommand(Object o) { 1180 | Command command = null; 1181 | if (o instanceof Command) { 1182 | command = (Command)o; 1183 | } else { 1184 | throw new ClassCastException 1185 | ("Object cannot be converted to a Command, Object: " + o); 1186 | } 1187 | //Response的子类会返回true 1188 | if (command.isResponse()) { 1189 | Response response = (Response)command; 1190 | FutureResponse future = null; 1191 | synchronized (requestMap) { 1192 | future = requestMap.remove(Integer.valueOf(response.getCorrelationId())); 1193 | } 1194 | if (future != null) { 1195 | //响应返回成功 1196 | future.set(response); 1197 | } else { 1198 | if (debug) { 1199 | LOG.debug("Received unexpected response: 1200 | {" + command + "}for command id: " + response.getCorrelationId()); 1201 | } 1202 | } 1203 | } else { 1204 | //不是Response(比如发来的消息),那么此时没有request,当然也就不需要设置future了 1205 | getTransportListener().onCommand(command); 1206 | } 1207 | } 1208 | ``` 1209 | 1210 | ###### TcpTransport 1211 | 1212 | 调用的是oneway方法: 1213 | 1214 | ```java 1215 | @Override 1216 | public void oneway(Object command) throws IOException { 1217 | checkStarted(); 1218 | wireFormat.marshal(command, dataOut); 1219 | dataOut.flush(); 1220 | } 1221 | ``` 1222 | 1223 | 这里才是真正发送的地方。dataOut是一个DataOutputStream类型。根据前文,此处的wireFormat是OpenWireFormat, 1224 | 1225 | ###### marshal 1226 | 1227 | ```java 1228 | @Override 1229 | public synchronized void marshal(Object o, DataOutput dataOut) { 1230 | //默认false 1231 | if (cacheEnabled) { 1232 | runMarshallCacheEvictionSweep(); 1233 | } 1234 | 1235 | int size = 1; 1236 | if (o != null) { 1237 | 1238 | DataStructure c = (DataStructure)o; 1239 | //每一种DataStructure都有唯一的type,定义在CommandTypes中 1240 | byte type = c.getDataStructureType(); 1241 | //以ConnectionInfo为例,就是ConnectionInfoMarshaller 1242 | DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 1243 | if (dsm == null) { 1244 | throw new IOException("Unknown data type: " + type); 1245 | } 1246 | //默认false,对数据进行压缩,可能消耗较多的CPU 1247 | if (tightEncodingEnabled) { 1248 | BooleanStream bs = new BooleanStream(); 1249 | size += dsm.tightMarshal1(this, c, bs); 1250 | size += bs.marshalledSize(); 1251 | //默认false 1252 | if (!sizePrefixDisabled) { 1253 | dataOut.writeInt(size); 1254 | } 1255 | 1256 | dataOut.writeByte(type); 1257 | bs.marshal(dataOut); 1258 | dsm.tightMarshal2(this, c, dataOut, bs); 1259 | 1260 | } else { 1261 | DataOutput looseOut = dataOut; 1262 | 1263 | if (!sizePrefixDisabled) { 1264 | bytesOut.restart(); 1265 | looseOut = bytesOut; 1266 | } 1267 | 1268 | looseOut.writeByte(type); 1269 | dsm.looseMarshal(this, c, looseOut); 1270 | 1271 | if (!sizePrefixDisabled) { 1272 | ByteSequence sequence = bytesOut.toByteSequence(); 1273 | dataOut.writeInt(sequence.getLength()); 1274 | dataOut.write(sequence.getData(), sequence.getOffset(), 1275 | sequence.getLength()); 1276 | } 1277 | 1278 | } 1279 | 1280 | } else { 1281 | if (!sizePrefixDisabled) { 1282 | dataOut.writeInt(size); 1283 | } 1284 | dataOut.writeByte(NULL_TYPE); 1285 | } 1286 | } 1287 | ``` 1288 | 1289 | dataMarshallers的来源: 1290 | 1291 | 构造器会调用setVersion方法,且默认11: 1292 | 1293 | ```java 1294 | //已去掉try catch 1295 | @Override 1296 | public void setVersion(int version) { 1297 | String mfName = "org.apache.activemq.openwire.v" + version + ".MarshallerFactory"; 1298 | Class mfClass; 1299 | mfClass = Class.forName(mfName, false, getClass().getClassLoader()); 1300 | Method method = mfClass.getMethod("createMarshallerMap", new Class[] {OpenWireFormat.class}); 1301 | dataMarshallers = (DataStreamMarshaller[])method.invoke(null, new Object[] {this}); 1302 | this.version = version; 1303 | } 1304 | ``` 1305 | 1306 | 实际上调用了对应版本号的MarshallerFactory的createMarshallerMap方法: 1307 | 1308 | ```java 1309 | static public DataStreamMarshaller[] createMarshallerMap(OpenWireFormat wireFormat) { 1310 | return marshaller; 1311 | } 1312 | static { 1313 | //片段 1314 | add(new ActiveMQTextMessageMarshaller()); 1315 | add(new ActiveMQTopicMarshaller()); 1316 | add(new BrokerIdMarshaller()); 1317 | add(new BrokerInfoMarshaller()); 1318 | add(new ConnectionInfoMarshaller()); 1319 | } 1320 | ``` 1321 | 1322 | 就是这么简单粗暴。 1323 | 1324 | 数据压缩主要体现在 两个方面: 1325 | 1326 | - boolean型变量用一位表示,而DataOutputStream用一个字节。 1327 | - 对于字符串类型判断其是否只包含ASCII码,服务器可能对其作出优化措施。 1328 | 1329 | 源码不贴了,参见BooleanStream和XXXMarshaller。 1330 | 1331 | 所以真正发送的时候**仍是以Socket发送的byte数组**。格式如下图所示: 1332 | 1333 | - 未压缩: 1334 | 1335 | ![未压缩格式](images/uncompact.png) 1336 | 1337 | - 已压缩: 1338 | 1339 | ![compact](images/compact.png) 1340 | 1341 | size的有没有以sizePrefixDisabled为准。 1342 | 1343 | 之后再发送事务信息,和上面是一样的。 1344 | 1345 | #### session提交 1346 | 1347 | ActiveMQSession.commit: 1348 | 1349 | ```java 1350 | @Override 1351 | public void commit() throws JMSException { 1352 | checkClosed(); 1353 | if (!getTransacted()) { 1354 | throw new javax.jms.IllegalStateException("Not a transacted session"); 1355 | } 1356 | if (LOG.isDebugEnabled()) { 1357 | LOG.debug(getSessionId() + " Transaction Commit :" + transactionContext.getTransactionId()); 1358 | } 1359 | transactionContext.commit(); 1360 | } 1361 | ``` 1362 | 1363 | TransactionContext.commit: 1364 | 1365 | ```java 1366 | public void commit() throws JMSException { 1367 | if (isInXATransaction()) { 1368 | throw new TransactionInProgressException(); 1369 | } 1370 | 1371 | try { 1372 | //默认什么也没做 1373 | beforeEnd(); 1374 | } catch (JMSException e) { 1375 | rollback(); 1376 | throw e; 1377 | } 1378 | 1379 | if (transactionId != null && rollbackOnly) { 1380 | final String message = "Commit of " + transactionId + " 1381 | failed due to rollback only request; typically due to failover with pending acks"; 1382 | try { 1383 | rollback(); 1384 | } finally { 1385 | LOG.warn(message); 1386 | throw new TransactionRolledBackException(message); 1387 | } 1388 | } 1389 | 1390 | // Only send commit if the transaction was started. 1391 | if (transactionId != null) { 1392 | LOG.debug("Commit: {} syncCount: {}", 1393 | transactionId, (synchronizations != null ? synchronizations.size() : 0)); 1394 | 1395 | TransactionInfo info = new TransactionInfo(getConnectionId(), 1396 | transactionId, TransactionInfo.COMMIT_ONE_PHASE); 1397 | this.transactionId = null; 1398 | // Notify the listener that the tx was committed back 1399 | try { 1400 | this.connection.syncSendPacket(info); 1401 | if (localTransactionEventListener != null) { 1402 | localTransactionEventListener.commitEvent(); 1403 | } 1404 | afterCommit(); 1405 | } catch (JMSException cause) { 1406 | LOG.info("commit failed for transaction {}", info.getTransactionId(), cause); 1407 | if (localTransactionEventListener != null) { 1408 | localTransactionEventListener.rollbackEvent(); 1409 | } 1410 | //默认什么也不做 1411 | afterRollback(); 1412 | throw cause; 1413 | } 1414 | } 1415 | } 1416 | ``` 1417 | 1418 | 可见,**Session的提交实际上是向服务器发送TransactionInfo(type为TransactionInfo.COMMIT_ONE_PHASE)来完成的**。 1419 | 1420 | #### 回滚 1421 | 1422 | ActiveMQSession.rollback只是调用了TransactionConext.rollback,用后脚跟也能想得出来,也是向服务器发送TransactionInfo实现的,不过type变成了TransactionInfo.ROLLBACK。 1423 | 1424 | ## Consumer初始化 1425 | 1426 | 过程和Producer大部分是相同的,下面只写一下不一样的地方。 1427 | 1428 | ### 应答模式 1429 | 1430 | createSession的第二个参数表示应答模式,但是只有对消费者和在transacted为false的情况下才有效。应答模式表示客户端何时向服务器确认消息已被消费成功。如果没有设置事务,那么有三种取值: 1431 | 1432 | - Session.AUTO_ACKNOWLEDGE: 1433 | 1434 | 当客户成功的从receive方法返回的时候,或者从MessageListener.onMessage方法成功返回的时候,会话自动确认客户收到的消息。 1435 | 1436 | - Session.CLIENT_ACKNOWLEDGE: 1437 | 1438 | 客户通过消息的acknowledge方法确认消息。需要注意的是,在这种模式中,确认是在会话层上进行:确认一个被消费的消息将自动确认所有已被会话消费的消息。例如,如果一个消息消费者消费了10个消息,然后确认第5个消息,那么所有10个消息都被确认。 1439 | 1440 | - Session.DUPS_ACKNOWLEDGE: 1441 | 1442 | 延时//批量通知: 这种确认方式允许JMS不必急于确认收到的消息,允许在收到多个消息之后一次完成确认,与Auto_AcKnowledge相比,这种确认方式在某些情况下可能更有效,因为没有确认,当系统崩溃或者网络出现故障的时候,消息可以被重新传递。 1443 | 1444 | 1445 | 这三种模式将在后续结合代码详细分析。 1446 | 1447 | ### 初始化 1448 | 1449 | ActiveMQSession.createConsumer: 1450 | 1451 | ```java 1452 | //最终调用 1453 | //messageSelector是一个表达式,用于过滤哪些消息可以被接收,默认null 1454 | //noLocal默认false: 如果为true并且destination是topic,那么此消费者不会接收到当前connection publish的消息 1455 | //messageListener = null 1456 | public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal, 1457 | MessageListener messageListener) { 1458 | checkClosed(); 1459 | 1460 | if (destination instanceof CustomDestination) { 1461 | CustomDestination customDestination = (CustomDestination)destination; 1462 | return customDestination.createConsumer(this, messageSelector, noLocal); 1463 | } 1464 | 1465 | ActiveMQPrefetchPolicy prefetchPolicy = connection.getPrefetchPolicy(); 1466 | int prefetch = 0; 1467 | if (destination instanceof Topic) { 1468 | prefetch = prefetchPolicy.getTopicPrefetch(); 1469 | } else { 1470 | prefetch = prefetchPolicy.getQueuePrefetch(); 1471 | } 1472 | ActiveMQDestination activemqDestination = 1473 | ActiveMQMessageTransformation.transformDestination(destination); 1474 | return new ActiveMQMessageConsumer(this, getNextConsumerId(), activemqDestination, 1475 | null, messageSelector, 1476 | prefetch, prefetchPolicy.getMaximumPendingMessageLimit(), 1477 | noLocal, false, isAsyncDispatch(), messageListener); 1478 | } 1479 | ``` 1480 | 1481 | 构造器会向服务器发送ConsumerInfo消息。 1482 | 1483 | ### start 1484 | 1485 | 此方法在构造器的最后被调用: 1486 | 1487 | ```java 1488 | public void start() throws JMSException { 1489 | if (unconsumedMessages.isClosed()) { 1490 | return; 1491 | } 1492 | started.set(true); 1493 | unconsumedMessages.start(); 1494 | session.executor.wakeup(); 1495 | } 1496 | ``` 1497 | 1498 | unconsumedMessages初始化代码: 1499 | 1500 | ```java 1501 | if (session.connection.isMessagePrioritySupported()) { 1502 | this.unconsumedMessages = new SimplePriorityMessageDispatchChannel(); 1503 | }else { 1504 | //默认是这个 1505 | this.unconsumedMessages = new FifoMessageDispatchChannel(); 1506 | } 1507 | ``` 1508 | 1509 | FifoMessageDispatchChannel.start: 1510 | 1511 | ```java 1512 | public void start() { 1513 | synchronized (mutex) { 1514 | running = true; 1515 | mutex.notifyAll(); 1516 | } 1517 | } 1518 | ``` 1519 | 1520 | 只是修改了标志位。关键在于ActiveMQSession的executor,是一个ActiveMQSessionExecutor实例,调用其wakeup方法,此方法前面Session创建时已经说过了,真正执行的是iterate方法: 1521 | 1522 | ```java 1523 | public boolean iterate() { 1524 | // Deliver any messages queued on the consumer to their listeners. 1525 | for (ActiveMQMessageConsumer consumer : this.session.consumers) { 1526 | if (consumer.iterate()) { 1527 | return true; 1528 | } 1529 | } 1530 | // No messages left queued on the listeners.. so now dispatch messages 1531 | // queued on the session 1532 | MessageDispatch message = messageQueue.dequeueNoWait(); 1533 | if (message == null) { 1534 | return false; 1535 | } else { 1536 | dispatch(message); 1537 | return !messageQueue.isEmpty(); 1538 | } 1539 | } 1540 | ``` 1541 | 1542 | 可以看出,每个消费者也有自己的队列。此处首先做的就是消费自己队列里的消息,之后再消费Session里的消息。 1543 | 1544 | #### Consumer队列 1545 | 1546 | ActiveMQMessageConsumer.iterate: 1547 | 1548 | ```java 1549 | public boolean iterate() { 1550 | MessageListener listener = this.messageListener.get(); 1551 | //默认为空 1552 | if (listener != null) { 1553 | MessageDispatch md = unconsumedMessages.dequeueNoWait(); 1554 | if (md != null) { 1555 | dispatch(md); 1556 | return true; 1557 | } 1558 | } 1559 | return false; 1560 | } 1561 | ``` 1562 | 1563 | #### Session队列 1564 | 1565 | 从上面可以看出,默认情况下消费者的iterate什么也没做,返回false。dispatch: 1566 | 1567 | ```java 1568 | void dispatch(MessageDispatch message) { 1569 | for (ActiveMQMessageConsumer consumer : this.session.consumers) { 1570 | ConsumerId consumerId = message.getConsumerId(); 1571 | if (consumerId.equals(consumer.getConsumerId())) { 1572 | consumer.dispatch(message); 1573 | break; 1574 | } 1575 | } 1576 | } 1577 | ``` 1578 | 1579 | 按照consumerID分发的。 1580 | 1581 | ActiveMQMessageConsumer.dispatch精简后的核心逻辑: 1582 | 1583 | ```java 1584 | @Override 1585 | public void dispatch(MessageDispatch md) { 1586 | MessageListener listener = this.messageListener.get(); 1587 | //Listener方式 1588 | if (listener != null && unconsumedMessages.isRunning()) { 1589 | ActiveMQMessage message = createActiveMQMessage(md); 1590 | beforeMessageIsConsumed(md); 1591 | boolean expired = isConsumerExpiryCheckEnabled() && message.isExpired(); 1592 | if (!expired) { 1593 | listener.onMessage(message); 1594 | } 1595 | afterMessageIsConsumed(md, expired); 1596 | } else { 1597 | if (!unconsumedMessages.isRunning()) { 1598 | // delayed redelivery, ensure it can be re delivered 1599 | session.connection.rollbackDuplicate(this, md.getMessage()); 1600 | } 1601 | 1602 | if (md.getMessage() == null) { 1603 | // End of browse or pull request timeout. 1604 | unconsumedMessages.enqueue(md); 1605 | } else { 1606 | if (!consumeExpiredMessage(md)) { 1607 | unconsumedMessages.enqueue(md); 1608 | if (availableListener != null) { 1609 | availableListener.onMessageAvailable(this); 1610 | } 1611 | } else { 1612 | //消息已过期 1613 | beforeMessageIsConsumed(md); 1614 | afterMessageIsConsumed(md, true); 1615 | 1616 | // Pull consumer needs to check if pull timed out and send 1617 | // a new pull command if not. 1618 | if (info.getCurrentPrefetchSize() == 0) { 1619 | unconsumedMessages.enqueue(null); 1620 | } 1621 | } 1622 | } 1623 | } 1624 | } 1625 | ``` 1626 | 1627 | ##### beforeMessageIsConsumed 1628 | 1629 | ```java 1630 | private void beforeMessageIsConsumed(MessageDispatch md) throws JMSException { 1631 | md.setDeliverySequenceId(session.getNextDeliveryId()); 1632 | lastDeliveredSequenceId = md.getMessage().getMessageId().getBrokerSequenceId(); 1633 | //当应答模式为Dups_OK_ACKnowledge并发布订阅模式时,先将消息存起来,批量进行ack应答 1634 | if (!isAutoAcknowledgeBatch()) { 1635 | synchronized(deliveredMessages) { 1636 | deliveredMessages.addFirst(md); 1637 | } 1638 | if (session.getTransacted()) { 1639 | if (transactedIndividualAck) { 1640 | immediateIndividualTransactedAck(md); 1641 | } else { 1642 | ackLater(md, MessageAck.DELIVERED_ACK_TYPE); 1643 | } 1644 | } 1645 | } 1646 | } 1647 | ``` 1648 | 1649 | ##### 消息消费 1650 | 1651 | 如果使用Listener方式,那么就会触发我们自己的监听器否则加入队列。 1652 | 1653 | ##### afterMessageIsConsumed 1654 | 1655 | 根据应答模式向服务器发送ack应答。 1656 | 1657 | ## Consumer运行 1658 | 1659 | Listener模式很简单,不说了。 1660 | 1661 | ActiveMQMessageConsumer.receive: 1662 | 1663 | ```java 1664 | @Override 1665 | public Message receive(long timeout) throws JMSException { 1666 | checkClosed(); 1667 | //检查Session和Consumer上是否注册有MessageListener,如果有直接抛出异常,因为两种模式不能共存 1668 | checkMessageListener(); 1669 | if (timeout == 0) { 1670 | return this.receive(); 1671 | } 1672 | //请求broker向此Consumer发送消息 1673 | sendPullCommand(timeout); 1674 | while (timeout > 0) { 1675 | MessageDispatch md; 1676 | //preFetchSize表示当前预存的尚未消费的消息的数量 1677 | if (info.getPrefetchSize() == 0) { 1678 | //-1表示线程将一直阻塞知道消息到来 1679 | md = dequeue(-1); 1680 | } else { 1681 | md = dequeue(timeout); 1682 | } 1683 | 1684 | if (md == null) { 1685 | return null; 1686 | } 1687 | 1688 | beforeMessageIsConsumed(md); 1689 | afterMessageIsConsumed(md, false); 1690 | return createActiveMQMessage(md); 1691 | } 1692 | return null; 1693 | } 1694 | ``` 1695 | 1696 | ### 消息来源 1697 | 1698 | 说了这么多,Session/Consumer队列的消息是怎么来的? 1699 | 1700 | 前面说了,Transport启动后有一个线程监听broker发来的数据,接收到数据到调用ActiveMQConnection.onCommand: 1701 | 1702 | ```java 1703 | @Override 1704 | public void onCommand(final Object o) { 1705 | final Command command = (Command)o; 1706 | if (!closed.get() && command != null) { 1707 | //Vistor模式 1708 | command.visit(new CommandVisitorAdapter() { 1709 | @Override 1710 | public Response processMessageDispatch(MessageDispatch md) throws Exception { 1711 | waitForTransportInterruptionProcessingToComplete(); 1712 | //其实就是ActiveMQConsumer对象 1713 | //dispatchers是一个ConcurrentMap 1714 | ActiveMQDispatcher dispatcher = dispatchers.get(md.getConsumerId()); 1715 | if (dispatcher != null) { 1716 | Message msg = md.getMessage(); 1717 | //Message拷贝,设为只读 1718 | if (msg != null) { 1719 | msg = msg.copy(); 1720 | msg.setReadOnlyBody(true); 1721 | msg.setReadOnlyProperties(true); 1722 | msg.setRedeliveryCounter(md.getRedeliveryCounter()); 1723 | msg.setConnection(ActiveMQConnection.this); 1724 | msg.setMemoryUsage(null); 1725 | md.setMessage(msg); 1726 | } 1727 | dispatcher.dispatch(md); 1728 | } else { 1729 | LOG.debug("{} no dispatcher for {} in {}", this, md, dispatchers); 1730 | } 1731 | return null; 1732 | } 1733 | } 1734 | } 1735 | } 1736 | ``` 1737 | 1738 | 所以接收到之后调用的还是ActiveMQConsumer.dispatch,见上面Session队列一节。 1739 | 1740 | # publish-subscribe 1741 | 1742 | ## publisher 1743 | 1744 | ActiveMQTopicPublisher类很有意思,它是ActiveMQMessageProducer的子类,有自己的几个方法: 1745 | 1746 | ```java 1747 | public void publish(Message message) throws JMSException { 1748 | super.send(message); 1749 | } 1750 | ``` 1751 | 1752 | 所以,**Publisher完全是语义上的概念,本质上和Producer完全一样**。 1753 | 1754 | 发送的消息也是一样的,queue和topic的区分完全取决于发送的消息里面destination字段。这就是服务器的事了。 1755 | 1756 | # 服务器(Broker) 1757 | 1758 | 下面开始记录服务器端。入口在org.apache.activemq.console.Main的main方法。Main又用反射的方式调用了ShellCommand的main方法。 1759 | 1760 | ## 启动 1761 | 1762 | ### ShellCommand 1763 | 1764 | main函数源码: 1765 | 1766 | ```java 1767 | public static void main(String[] args) { 1768 | main(args, System.in, System.out); 1769 | } 1770 | public static int main(String[] args, InputStream in, PrintStream out) { 1771 | //用于在执行时向控制台输出信息,忽略 1772 | CommandContext context = new CommandContext(); 1773 | context.setFormatter(new CommandShellOutputFormatter(out)); 1774 | // Convert arguments to list for easier management 1775 | List tokens = new ArrayList(Arrays.asList(args)); 1776 | ShellCommand main = new ShellCommand(); 1777 | try { 1778 | main.setCommandContext(context); 1779 | main.execute(tokens); 1780 | return 0; 1781 | } catch (Exception e) { 1782 | context.printException(e); 1783 | return 1; 1784 | } 1785 | } 1786 | ``` 1787 | 1788 | execute调用了runTask, 此方法根据解析得到的命令行参数执行相应的命令。所有的命令均是org.apache.activemq.console.command.Command的子类。这个查找、执行的过程很有意思。 1789 | 1790 | ServiceLoader.load方法可以根据指定class对象查找其子类,由于此类实现了Iterable接口,所以可以得到一个包含其子类对象的Iterator对象,源码: 1791 | 1792 | ```java 1793 | ServiceLoader loader = ServiceLoader.load(Command.class); 1794 | Iterator iterator = loader.iterator(); 1795 | ``` 1796 | 1797 | 那么此方法是如何查找子类的呢? 1798 | 1799 | 需要在jar包的/META-INF/services目录下建立名为完整类名的文件,比如 1800 | 1801 | org.apache.activemq.console.command.Command,内容为: 1802 | 1803 | ```ht 1804 | org.apache.activemq.console.command.CreateCommand 1805 | org.apache.activemq.console.command.StartCommand 1806 | org.apache.activemq.console.command.ShutdownCommand 1807 | org.apache.activemq.console.command.ListCommand 1808 | org.apache.activemq.console.command.AmqBrowseCommand 1809 | org.apache.activemq.console.command.QueryCommand 1810 | org.apache.activemq.console.command.BstatCommand 1811 | org.apache.activemq.console.command.DstatCommand 1812 | org.apache.activemq.console.command.EncryptCommand 1813 | org.apache.activemq.console.command.DecryptCommand 1814 | org.apache.activemq.console.command.StoreExportCommand 1815 | org.apache.activemq.console.command.PurgeCommand 1816 | org.apache.activemq.console.command.ProducerCommand 1817 | org.apache.activemq.console.command.ConsumerCommand 1818 | ``` 1819 | 1820 | 具体启动了哪几个不知道,但StartCommand应该是必须的。 1821 | 1822 | ### StartCommand 1823 | 1824 | ```java 1825 | protected void runTask(List brokerURIs) throws Exception { 1826 | URI configURI; 1827 | while( true ) { 1828 | final BrokerService broker; 1829 | try { 1830 | if (brokerURIs.isEmpty()) { 1831 | //使用默认的配置文件地址: xbean:activemq.xml 1832 | configURI = new URI(DEFAULT_CONFIG_URI); 1833 | } else { 1834 | configURI = new URI(brokerURIs.get(0)); 1835 | } 1836 | System.out.println("Loading message broker from: " + configURI); 1837 | //见Broker创建一节 1838 | broker = BrokerFactory.createBroker(configURI); 1839 | //见Broker启动一节 1840 | broker.start(); 1841 | } catch (Exception e) { 1842 | context.printException(new RuntimeException 1843 | ("Failed to execute start task. Reason: " + e, e)); 1844 | throw e; 1845 | } 1846 | if (!broker.waitUntilStarted()) { 1847 | throw new Exception(broker.getStartException()); 1848 | } 1849 | // The broker started up fine. Now lets wait for it to stop... 1850 | final CountDownLatch shutdownLatch = new CountDownLatch(1); 1851 | final Thread jvmShutdownHook = new Thread() { 1852 | public void run() { 1853 | try { 1854 | broker.stop(); 1855 | } catch (Exception e) { 1856 | } 1857 | } 1858 | }; 1859 | Runtime.getRuntime().addShutdownHook(jvmShutdownHook); 1860 | broker.addShutdownHook(new Runnable() { 1861 | public void run() { 1862 | shutdownLatch.countDown(); 1863 | } 1864 | }); 1865 | // The broker has stopped.. 1866 | shutdownLatch.await(); 1867 | try { 1868 | Runtime.getRuntime().removeShutdownHook(jvmShutdownHook); 1869 | } catch (Throwable e) { 1870 | // may already be shutdown in progress so ignore 1871 | } 1872 | if( !broker.isRestartRequested() ) { 1873 | break; 1874 | } 1875 | System.out.println("Restarting broker"); 1876 | } 1877 | } 1878 | ``` 1879 | 1880 | 注意其停止方式,注册了一个jvm钩子,在钩子里调用broker.stop。 1881 | 1882 | ### BrokerService创建 1883 | 1884 | BrokerFactory.createBroker: 1885 | 1886 | ```java 1887 | public static BrokerService createBroker(URI brokerURI, boolean startBroker) { 1888 | if (brokerURI.getScheme() == null) { 1889 | throw new IllegalArgumentException 1890 | ("Invalid broker URI, no scheme specified: " + brokerURI); 1891 | } 1892 | BrokerFactoryHandler handler = createBrokerFactoryHandler(brokerURI.getScheme()); 1893 | BrokerService broker = handler.createBroker(brokerURI); 1894 | //false 1895 | if (startBroker) { 1896 | broker.start(); 1897 | } 1898 | return broker; 1899 | } 1900 | ``` 1901 | 1902 | createBrokerFactoryHandler其实是在寻找BrokerFactory,套路还是写在/META-INF下,最终执行的是XBeanBrokerFactory.createBroker,这就很明确了,此方法去读取conf/activemq.xml,返回一个已经配置好的BrokerService对象。 1903 | 1904 | BrokerService构造器本身没有做任何事情,静态构造器进行了三个初始化工作: 1905 | 1906 | - 加载Bouncy Castle加密/解密组件 1907 | - 设置localHostName 1908 | - 从/org/apache/activemq/version.txt读取版本信息 1909 | 1910 | addConnector: 1911 | 1912 | ```java 1913 | public TransportConnector addConnector(URI bindAddress) throws Exception { 1914 | return addConnector(createTransportConnector(bindAddress)); 1915 | } 1916 | protected TransportConnector createTransportConnector(URI brokerURI) throws Exception { 1917 | TransportServer transport = TransportFactorySupport.bind(this, brokerURI); 1918 | return new TransportConnector(transport); 1919 | } 1920 | ``` 1921 | 1922 | ### BrokerService启动 1923 | 1924 | start简略版: 1925 | 1926 | ```java 1927 | @Override 1928 | public void start() throws Exception { 1929 | //jmx如果有可能在后面单独说 1930 | if (isUseJmx()) { 1931 | startManagementContext(); 1932 | for (NetworkConnector connector : getNetworkConnectors()) { 1933 | registerNetworkConnectorMBean(connector); 1934 | } 1935 | } 1936 | // in jvm master slave, lets not publish over existing broker till we get the lock 1937 | //ActiveMQ支持master-slave架构,slave启动必须在master之后 1938 | final BrokerRegistry brokerRegistry = BrokerRegistry.getInstance(); 1939 | if (brokerRegistry.lookup(getBrokerName()) == null) { 1940 | brokerRegistry.bind(getBrokerName(), BrokerService.this); 1941 | } 1942 | //startAsyn默认false,同步启动,否则启动一个新的线程用于启动,之后调用doStartPersistenceAdapter 1943 | startPersistenceAdapter(startAsync); 1944 | startBroker(startAsync); 1945 | brokerRegistry.bind(getBrokerName(), BrokerService.this); 1946 | } 1947 | ``` 1948 | 1949 | #### PersistenceAdapter 1950 | 1951 | 负责消息的持久化工作。 1952 | 1953 | doStartPersistenceAdapter: 1954 | 1955 | ```java 1956 | private void doStartPersistenceAdapter() throws Exception { 1957 | PersistenceAdapter persistenceAdapterToStart = getPersistenceAdapter(); 1958 | if (persistenceAdapterToStart == null) { 1959 | checkStartException(); 1960 | throw new ConfigurationException("Cannot start null persistence adapter"); 1961 | } 1962 | //限制资源使用? 1963 | persistenceAdapterToStart.setUsageManager(getProducerSystemUsage()); 1964 | persistenceAdapterToStart.setBrokerName(getBrokerName()); 1965 | LOG.info("Using Persistence Adapter: {}", persistenceAdapterToStart); 1966 | if (deleteAllMessagesOnStartup) { 1967 | deleteAllMessages(); 1968 | } 1969 | persistenceAdapterToStart.start(); 1970 | 1971 | getTempDataStore(); 1972 | if (tempDataStore != null) { 1973 | try { 1974 | // start after we have the store lock 1975 | tempDataStore.start(); 1976 | } catch (Exception e) { 1977 | RuntimeException exception = new RuntimeException( 1978 | "Failed to start temp data store: " + tempDataStore, e); 1979 | LOG.error(exception.getLocalizedMessage(), e); 1980 | throw exception; 1981 | } 1982 | } 1983 | 1984 | getJobSchedulerStore(); 1985 | if (jobSchedulerStore != null) { 1986 | try { 1987 | jobSchedulerStore.start(); 1988 | } catch (Exception e) { 1989 | RuntimeException exception = new RuntimeException( 1990 | "Failed to start job scheduler store: " + jobSchedulerStore, e); 1991 | LOG.error(exception.getLocalizedMessage(), e); 1992 | throw exception; 1993 | } 1994 | } 1995 | } 1996 | ``` 1997 | 1998 | ##### 创建 1999 | 2000 | getPersistenceAdapter: 2001 | 2002 | ```java 2003 | public synchronized PersistenceAdapter getPersistenceAdapter() throws IOException { 2004 | if (persistenceAdapter == null && !hasStartException()) { 2005 | persistenceAdapter = createPersistenceAdapter(); 2006 | configureService(persistenceAdapter); 2007 | //这个方法什么也没做,直接返回了 2008 | this.persistenceAdapter = registerPersistenceAdapterMBean(persistenceAdapter); 2009 | } 2010 | return persistenceAdapter; 2011 | } 2012 | ``` 2013 | 2014 | createPersistenceAdapter: 2015 | 2016 | ```java 2017 | protected PersistenceAdapter createPersistenceAdapter() throws IOException { 2018 | //默认true 2019 | if (isPersistent()) { 2020 | //默认为空 2021 | PersistenceAdapterFactory fac = getPersistenceFactory(); 2022 | if (fac != null) { 2023 | return fac.createPersistenceAdapter(); 2024 | } else { 2025 | try { 2026 | //默认使用KahaDB作为持久化策略 2027 | String clazz = 2028 | "org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter"; 2029 | PersistenceAdapter adaptor = (PersistenceAdapter)getClass() 2030 | .getClassLoader().loadClass(clazz).newInstance(); 2031 | File dir = new File(getBrokerDataDirectory(),"KahaDB"); 2032 | adaptor.setDirectory(dir); 2033 | return adaptor; 2034 | } catch (Throwable e) { 2035 | throw IOExceptionSupport.create(e); 2036 | } 2037 | } 2038 | } else { 2039 | //如果不需要持久化,那么存在内存里 2040 | return new MemoryPersistenceAdapter(); 2041 | } 2042 | } 2043 | ``` 2044 | 2045 | KahaDB是一个基于文件的消息存储机制,从ActiveMQ5.4之后就是默认的了。可以通过配置文件的方式进行配置: 2046 | 2047 | ```xm 2048 | broker brokerName="broker" ... > 2049 | 2050 | 2051 | 2052 | ... 2053 | 2054 | ``` 2055 | 2056 | ActiveMQ所有的持久化机制及其配置方式可以参考: [JMS&MQ系列之消息存储方式](http://blog.csdn.net/geloin/article/details/7967085) 2057 | 2058 | KahaDB较为详细的分析: [activeMQ 的kahadb存储引擎分析](http://www.iteye.com/topic/1121713) 2059 | 2060 | ##### 启动 2061 | 2062 | KahaDBPersistenceAdapter其实是ServiceSupport的子类,所以实际上执行的是ServiceSupport的start: 2063 | 2064 | ```java 2065 | public void start() throws Exception { 2066 | if (started.compareAndSet(false, true)) { 2067 | boolean success = false; 2068 | stopped.set(false); 2069 | try { 2070 | preStart(); 2071 | doStart(); 2072 | success = true; 2073 | } finally { 2074 | started.set(success); 2075 | } 2076 | for(ServiceListener l:this.serviceListeners) { 2077 | l.started(this); 2078 | } 2079 | } 2080 | } 2081 | ``` 2082 | 2083 | 前面介绍客户端的时候见过它了。 2084 | 2085 | ###### preStart 2086 | 2087 | KahaDBPersistenceAdapter的父类LockableServiceSupport实现了此方法: 2088 | 2089 | ```java 2090 | @Override 2091 | public void preStart() throws Exception { 2092 | //抽象方法,子类只是空实现 2093 | init(); 2094 | //默认true 2095 | if (useLock) { 2096 | if (getLocker() == null) { 2097 | LOG.warn("No locker configured"); 2098 | } else { 2099 | getLocker().start(); 2100 | //KahaDBPersistenceAdapter默认0 2101 | if (lockKeepAlivePeriod > 0) { 2102 | keepAliveTicket = getScheduledThreadPoolExecutor() 2103 | .scheduleAtFixedRate(new Runnable() { 2104 | public void run() { 2105 | keepLockAlive(); 2106 | } 2107 | }, lockKeepAlivePeriod, lockKeepAlivePeriod, 2108 | TimeUnit.MILLISECONDS); 2109 | } 2110 | } 2111 | } 2112 | } 2113 | ``` 2114 | 2115 | Locker的创建位于getLocker: 2116 | 2117 | ```java 2118 | public Locker getLocker() throws IOException { 2119 | if (this.locker == null) { 2120 | setLocker(createDefaultLocker()); 2121 | } 2122 | return this.locker; 2123 | } 2124 | ``` 2125 | 2126 | createDefaultLocker由子类KahaDBPersistenceAdapter实现: 2127 | 2128 | ```java 2129 | @Override 2130 | public Locker createDefaultLocker() throws IOException { 2131 | SharedFileLocker locker = new SharedFileLocker(); 2132 | //设置了数据存储目录 2133 | locker.configure(this); 2134 | return locker; 2135 | } 2136 | ``` 2137 | 2138 | SharedFileLocker同样是ServiceSupport的子类,所以只需关注其doStart方法: 2139 | 2140 | **在当前broker执行的数据目录下建立了一个lock文件,并尝试对此文件加锁,如果加锁成功,那么此broker就是master,否则就是slave,直到master宕机释放了锁。** 2141 | 2142 | 这种利用文件锁实现master-slave的方式值得学习。另外ActiveMQ原本还支持一种master-slave(不共享存储)的方式,不过在5.8版本之后就被移除了。 2143 | 2144 | 参考: [ActiveMQ与HA架构(master/slave)](http://shift-alt-ctrl.iteye.com/blog/2069250) 2145 | 2146 | ​ [Shared File System Master Slave](http://activemq.apache.org/shared-file-system-master-slave.html) 2147 | 2148 | doStart关键源码: 2149 | 2150 | ```java 2151 | @Override 2152 | public void doStart() throws Exception { 2153 | if (lockFile == null) { 2154 | //建立lock文件 2155 | File lockFileName = new File(directory, "lock"); 2156 | lockFile = new LockFile(lockFileName, false); 2157 | //默认false 2158 | if (failIfLocked) { 2159 | lockFile.lock(); 2160 | } else { 2161 | while ((!isStopped()) && (!isStopping())) { 2162 | try { 2163 | //尝试加锁 2164 | lockFile.lock(); 2165 | locked = keepAlive(); 2166 | break; 2167 | } catch (IOExecption e) { 2168 | //让循环继续,所以slave broker将会在此一直等待 2169 | } 2170 | } 2171 | } 2172 | } 2173 | } 2174 | ``` 2175 | 2176 | 对文件的加锁是通过nio FileChannel完成的。 2177 | 2178 | lock文件截图: 2179 | 2180 | ![lock文件](images/lock.png) 2181 | 2182 | ###### doStart 2183 | 2184 | ```java 2185 | private final KahaDBStore letter = new KahaDBStore(); 2186 | @Override 2187 | public void doStart() throws Exception { 2188 | this.letter.start(); 2189 | //jms相关,忽略 2190 | } 2191 | ``` 2192 | 2193 | KahaDBStore在构造器里初始化了一个KahaDBTransactionStore。 2194 | 2195 | KahaDBStore同样是ServiceSupport的子类,doStart: 2196 | 2197 | ```java 2198 | @Override 2199 | public void doStart() throws Exception { 2200 | //configure the metadata before start, right now 2201 | //this is just the open wire version 2202 | configureMetadata(); 2203 | 2204 | super.doStart(); 2205 | //统一消息序列化/反序列化的版本 2206 | if (brokerService != null) { 2207 | // In case the recovered store used a different OpenWire version log a warning 2208 | // to assist in determining why journal reads fail. 2209 | if (metadata.openwireVersion != brokerService.getStoreOpenWireVersion()) { 2210 | // Update the broker service instance to the actual version in use. 2211 | wireFormat.setVersion(metadata.openwireVersion); 2212 | brokerService.setStoreOpenWireVersion(metadata.openwireVersion); 2213 | } 2214 | } 2215 | 2216 | this.globalQueueSemaphore = new Semaphore(getMaxAsyncJobs()); 2217 | this.globalTopicSemaphore = new Semaphore(getMaxAsyncJobs()); 2218 | this.asyncQueueJobQueue = new LinkedBlockingQueue(getMaxAsyncJobs()); 2219 | this.asyncTopicJobQueue = new LinkedBlockingQueue(getMaxAsyncJobs()); 2220 | //线程数1,最大线程数如果没有设置也是1 2221 | this.queueExecutor = new StoreTaskExecutor(1, asyncExecutorMaxThreads, 0L, TimeUnit.MILLISECONDS, 2222 | asyncQueueJobQueue, new ThreadFactory() { 2223 | @Override 2224 | public Thread newThread(Runnable runnable) { 2225 | Thread thread = new Thread(runnable, "ConcurrentQueueStoreAndDispatch"); 2226 | thread.setDaemon(true); 2227 | return thread; 2228 | } 2229 | }); 2230 | this.topicExecutor = new StoreTaskExecutor(1, asyncExecutorMaxThreads, 0L, TimeUnit.MILLISECONDS, 2231 | asyncTopicJobQueue, new ThreadFactory() { 2232 | @Override 2233 | public Thread newThread(Runnable runnable) { 2234 | Thread thread = new Thread(runnable, "ConcurrentTopicStoreAndDispatch"); 2235 | thread.setDaemon(true); 2236 | return thread; 2237 | } 2238 | }); 2239 | } 2240 | ``` 2241 | 2242 | 看来持久化时用了队列(缓冲区)。 2243 | 2244 | 父类MessageDatabase的 doStart调用了其load方法: 2245 | 2246 | ```java 2247 | public void load() throws IOException { 2248 | this.indexLock.writeLock().lock(); 2249 | //kahadb 2250 | IOHelper.mkdirs(directory); 2251 | try { 2252 | //启动时删除之前的数据,默认false 2253 | if (deleteAllMessages) { 2254 | getJournal().start(); 2255 | getJournal().delete(); 2256 | getJournal().close(); 2257 | journal = null; 2258 | getPageFile().delete(); 2259 | LOG.info("Persistence store purged."); 2260 | deleteAllMessages = false; 2261 | } 2262 | 2263 | open(); 2264 | store(new KahaTraceCommand().setMessage("LOADED " + new Date())); 2265 | } finally { 2266 | this.indexLock.writeLock().unlock(); 2267 | } 2268 | } 2269 | ``` 2270 | 2271 | open: 2272 | 2273 | ```java 2274 | public void open() throws IOException { 2275 | if( opened.compareAndSet(false, true) ) { 2276 | getJournal().start(); 2277 | try { 2278 | loadPageFile(); 2279 | } catch (Throwable t) { 2280 | //忽略... 2281 | } 2282 | recover(); 2283 | startCheckpoint(); 2284 | } 2285 | } 2286 | ``` 2287 | 2288 | getJournal: 2289 | 2290 | ```java 2291 | public Journal getJournal() throws IOException { 2292 | if (journal == null) { 2293 | journal = createJournal(); 2294 | } 2295 | return journal; 2296 | } 2297 | private Journal createJournal() throws IOException { 2298 | Journal manager = new Journal(); 2299 | manager.setDirectory(directory); 2300 | //默认32MB,保存的消息的最大长度 2301 | manager.setMaxFileLength(getJournalMaxFileLength()); 2302 | //默认false,如果为true,那么将在启动时尝试恢复损坏的数据 2303 | manager.setCheckForCorruptionOnStartup(checkForCorruptJournalFiles); 2304 | manager.setChecksum(checksumJournalFiles || 2305 | checkForCorruptJournalFiles); 2306 | //一次进行批量保存的大小,默认4MB 2307 | manager.setWriteBatchSize(getJournalMaxWriteBatchSize()); 2308 | //默认false,如果true那么把消息日志移动到归档目录而不是删除 2309 | manager.setArchiveDataLogs(isArchiveDataLogs()); 2310 | //貌似用于计算保存的数据的大小 2311 | manager.setSizeAccumulator(journalSize); 2312 | manager.setEnableAsyncDiskSync(isEnableJournalDiskSyncs()); 2313 | //预分配策略,默认整个journal,同步 2314 | manager.setPreallocationStrategy( 2315 | Journal.PreallocationStrategy.valueOf 2316 | (preallocationStrategy.trim().toUpperCase())); 2317 | manager.setJournalDiskSyncStrategy( 2318 | Journal.JournalDiskSyncStrategy.valueOf 2319 | (journalDiskSyncStrategy.trim().toUpperCase())); 2320 | if (getDirectoryArchive() != null) { 2321 | IOHelper.mkdirs(getDirectoryArchive()); 2322 | manager.setDirectoryArchive(getDirectoryArchive()); 2323 | } 2324 | return manager; 2325 | } 2326 | ``` 2327 | 2328 | journal到底是个什么东西?翻译过来就是日志,杂志,Mongo里也有这个东西。ActiveMQ注释上说用于`Manages Datafiles`。 2329 | 2330 | Journal.start: 2331 | 2332 | ```java 2333 | public synchronized void start() throws IOException { 2334 | if (started) { 2335 | return; 2336 | } 2337 | 2338 | long start = System.currentTimeMillis(); 2339 | //缓存文件句柄 2340 | accessorPool = new DataFileAccessorPool(this); 2341 | started = true; 2342 | //用以向文件写数据 2343 | appender = callerBufferAppender ? new CallerBufferingDataFileAppender(this) : new DataFileAppender(this); 2344 | 2345 | File[] files = directory.listFiles(new FilenameFilter() { 2346 | @Override 2347 | public boolean accept(File dir, String n) { 2348 | //directory是当前目录,filePrefix = "db-", fileSuffix = ".log" 2349 | //所以过滤出来的都是db-1.log, db-2.log这种,此类文件用以保存消息 2350 | return dir.equals(directory) && n.startsWith(filePrefix) && n.endsWith(fileSuffix); 2351 | } 2352 | }); 2353 | 2354 | if (files != null) { 2355 | for (File file : files) { 2356 | try { 2357 | String n = file.getName(); 2358 | String numStr = n.substring(filePrefix.length(), n.length()-fileSuffix.length()); 2359 | int num = Integer.parseInt(numStr); 2360 | DataFile dataFile = new DataFile(file, num); 2361 | //全部放进map 2362 | fileMap.put(dataFile.getDataFileId(), dataFile); 2363 | totalLength.addAndGet(dataFile.getLength()); 2364 | } catch (NumberFormatException e) { 2365 | // Ignore file that do not match the pattern. 2366 | } 2367 | } 2368 | 2369 | // Sort the list so that we can link the DataFiles together in the 2370 | // right order. 2371 | LinkedList l = new LinkedList<>(fileMap.values()); 2372 | Collections.sort(l); 2373 | for (DataFile df : l) { 2374 | if (df.getLength() == 0) { 2375 | // possibly the result of a previous failed write 2376 | LOG.info("ignoring zero length, partially initialised journal data file: " + df); 2377 | continue; 2378 | } else if (l.getLast().equals(df) && isUnusedPreallocated(df)) { 2379 | continue; 2380 | } 2381 | dataFiles.addLast(df); 2382 | fileByFileMap.put(df.getFile(), df); 2383 | 2384 | if( isCheckForCorruptionOnStartup() ) { 2385 | lastAppendLocation.set(recoveryCheck(df)); 2386 | } 2387 | } 2388 | } 2389 | 2390 | if (preallocationScope != PreallocationScope.NONE && preallocationStrategy == PreallocationStrategy.OS_KERNEL_COPY) { 2391 | // create a template file that will be used to pre-allocate the journal files 2392 | if (osKernelCopyTemplateFile == null) { 2393 | osKernelCopyTemplateFile = createJournalTemplateFile(); 2394 | } 2395 | } 2396 | 2397 | scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() { 2398 | @Override 2399 | public Thread newThread(Runnable r) { 2400 | Thread schedulerThread = new Thread(r); 2401 | schedulerThread.setName("ActiveMQ Journal Scheduled executor"); 2402 | schedulerThread.setDaemon(true); 2403 | return schedulerThread; 2404 | } 2405 | }); 2406 | 2407 | // init current write file 2408 | if (dataFiles.isEmpty()) { 2409 | nextDataFileId = 1; 2410 | rotateWriteFile(); 2411 | } else { 2412 | currentDataFile.set(dataFiles.getTail()); 2413 | nextDataFileId = currentDataFile.get().dataFileId + 1; 2414 | } 2415 | 2416 | if( lastAppendLocation.get()==null ) { 2417 | DataFile df = dataFiles.getTail(); 2418 | lastAppendLocation.set(recoveryCheck(df)); 2419 | } 2420 | 2421 | // ensure we don't report unused space of last journal file in size metric 2422 | if (totalLength.get() > maxFileLength && lastAppendLocation.get().getOffset() > 0) { 2423 | totalLength.addAndGet(lastAppendLocation.get().getOffset() - maxFileLength); 2424 | } 2425 | 2426 | cleanupTask = scheduler.scheduleAtFixedRate(new Runnable() { 2427 | @Override 2428 | public void run() { 2429 | cleanup(); 2430 | } 2431 | }, DEFAULT_CLEANUP_INTERVAL, DEFAULT_CLEANUP_INTERVAL, TimeUnit.MILLISECONDS); 2432 | 2433 | long end = System.currentTimeMillis(); 2434 | LOG.trace("Startup took: "+(end-start)+" ms"); 2435 | } 2436 | ``` 2437 | 2438 | #### Broker启动 2439 | 2440 | 注意BrokerService和Broker不是一回事。BrokerService用以管理Broker的生命周期。 2441 | 2442 | BrokerService.startBroker方法最终调用了doStartBroker,主要源码如下: 2443 | 2444 | ```java 2445 | private void doStartBroker() { 2446 | broker = getBroker(); 2447 | brokerId = broker.getBrokerId(); 2448 | broker.start(); 2449 | startAllConnectors(); 2450 | //这是个空方法,尼玛 2451 | getBroker().nowMasterBroker(); 2452 | } 2453 | ``` 2454 | 2455 | ##### Broker创建 2456 | 2457 | getBroker: 2458 | 2459 | ```java 2460 | public Broker getBroker() throws Exception { 2461 | if (broker == null) { 2462 | checkStartException(); 2463 | broker = createBroker(); 2464 | } 2465 | return broker; 2466 | } 2467 | protected Broker createBroker() throws Exception { 2468 | regionBroker = createRegionBroker(); 2469 | Broker broker = addInterceptors(regionBroker); 2470 | // Add a filter that will stop access to the broker once stopped 2471 | broker = new MutableBrokerFilter(broker) { 2472 | Broker old; 2473 | @Override 2474 | public void stop() throws Exception { 2475 | old = this.next.getAndSet(new ErrorBroker("Broker has been stopped: " + this) { 2476 | // Just ignore additional stop actions. 2477 | @Override 2478 | public void stop() throws Exception { 2479 | } 2480 | }); 2481 | old.stop(); 2482 | } 2483 | @Override 2484 | public void start() throws Exception { 2485 | if (forceStart && old != null) { 2486 | this.next.set(old); 2487 | } 2488 | getNext().start(); 2489 | } 2490 | }; 2491 | return broker; 2492 | } 2493 | ``` 2494 | 2495 | createRegionBroker主要做了这么几件事: 2496 | 2497 | - 初始化默认的拦截器(Interceptor),有两种: 2498 | 2499 | - VirtualDestinationInterceptor: 默认为启用。VirtualDestination的概念包括Virtual Topics和Composite Destinations。Virtual Topics是为了解决这样一个问题: P2P模式下只允许一个客户端(消费者)进行消费,这就导致负载均衡问题(全部的消息处理都放在 一台机器上)和failover问题(如果这台服务器宕机,那么就完了...)。同时发布订阅模式下Broker会同时把消息推给所有的订阅者,这样也无法解决前面说的两个问题。 2500 | 2501 | Composite Destinations是一个Producer可以同时向多个Destination发送消息。 2502 | 2503 | 具体的实现细节及配置方式见ActiveMQ官方文档: [Virtual Destinations](http://activemq.apache.org/virtual-destinations.html) 2504 | 2505 | - MirroredQueue: 默认不启用。镜像队列是为了解决这样一个问题: 有时我们可能需要监控一个P2P模式中传送的消息到底是什么,MirroredQueue可以使Broker向Consumer发送的消息同时发送到我们制定的监控器上一份。 2506 | 2507 | 参考官方文档: [Mirrored Queues](http://activemq.apache.org/mirrored-queues.html) 2508 | 2509 | - 将以上提到的拦截器组合为CompositeDestinationInterceptor,其intercept方法就是逐一调用包含的拦截器的对应方法。 2510 | 2511 | - 初始化DestinationFactory(DestinationFactoryImpl) 2512 | 2513 | - 构造RegionBroker对象(没有启动jmx的情况下) 2514 | 2515 | - 对RegionBroker进行包装,这个和TransportFilter的原理一样,为了形成链式调用。完整的调用链(非jmx): 2516 | 2517 | ![完整Broker链](images/full_broker_chain.png) 2518 | 2519 | 实际的(即排除默认不启用的): 2520 | 2521 | ![实际Broker链](images/default_broker_chain.png) 2522 | 2523 | 贴一下RegionBroker的构造源码: 2524 | 2525 | ```java 2526 | regionBroker = new RegionBroker(this, getTaskRunnerFactory(), 2527 | getConsumerSystemUsage(),destinationFactory, 2528 | destinationInterceptor,getScheduler(),getExecutor()); 2529 | ``` 2530 | 2531 | ##### start 2532 | 2533 | RegionBroker.start: 2534 | 2535 | ```java 2536 | @Override 2537 | public void start() throws Exception { 2538 | started = true; 2539 | queueRegion.start(); 2540 | topicRegion.start(); 2541 | tempQueueRegion.start(); 2542 | tempTopicRegion.start(); 2543 | int period = this.brokerService 2544 | .getSchedulePeriodForDestinationPurge(); 2545 | if (period > 0) { 2546 | this.scheduler.executePeriodically 2547 | (purgeInactiveDestinationsTask, period); 2548 | } 2549 | } 2550 | ``` 2551 | 2552 | ###### QueueRegion 2553 | 2554 | queueRegion在构造器中创建: 2555 | 2556 | ```java 2557 | //其实就是new了一个QueueRegion对象 2558 | queueRegion = createQueueRegion(memoryManager, taskRunnerFactory, destinationFactory); 2559 | ``` 2560 | 2561 | AbstractRegion.start: 2562 | 2563 | ```java 2564 | @Override 2565 | public final void start() throws Exception { 2566 | started = true; 2567 | //这个获取的其实是已经被持久化的Destination 2568 | Set inactiveDests = getInactiveDestinations(); 2569 | for (Iterator iter = inactiveDests.iterator(); 2570 | iter.hasNext();) { 2571 | ActiveMQDestination dest = iter.next(); 2572 | //重新添加到Broker 2573 | ConnectionContext context = new ConnectionContext(); 2574 | context.setBroker(broker.getBrokerService().getBroker()); 2575 | context.setSecurityContext(SecurityContext.BROKER_SECURITY_CONTEXT); 2576 | context.getBroker().addDestination(context, dest, false); 2577 | } 2578 | destinationsLock.readLock().lock(); 2579 | try{ 2580 | for (Iterator i = destinations.values().iterator(); 2581 | i.hasNext();) { 2582 | Destination dest = i.next(); 2583 | //不知道干了啥 2584 | dest.start(); 2585 | } 2586 | } finally { 2587 | destinationsLock.readLock().unlock(); 2588 | } 2589 | } 2590 | ``` 2591 | 2592 | ###### TopicRegion 2593 | 2594 | 和上面的完全一样。 2595 | 2596 | ###### TempQueueRegion 2597 | 2598 | 和上面一样。 2599 | 2600 | ###### TempTopicRegion 2601 | 2602 | 同上。 2603 | 2604 | ##### Connector启动 2605 | 2606 | 很简单就是调用Broker中全部Connector的start方法,有哪些Connector取决于activemq.xml: 2607 | 2608 | ```xm 2609 | 2610 | 2611 | 2613 | 2615 | 2617 | 2619 | 2621 | 2622 | ``` 2623 | 2624 | 默认支持四种协议,所谓的信息就是怎么把对象转为byte数组在网络上发送。下面就以默认的openwire为例。 2625 | 2626 | TransportConnector.start: 2627 | 2628 | ```java 2629 | @Override 2630 | public void start() throws Exception { 2631 | broker = brokerService.getBroker(); 2632 | //省略设置brokerInfo部分... 2633 | getServer().setAcceptListener(new TransportAcceptListener() { 2634 | @Override 2635 | public void onAccept(final Transport transport) { 2636 | try { 2637 | brokerService.getTaskRunnerFactory().execute(new Runnable() { 2638 | @Override 2639 | public void run() { 2640 | try { 2641 | if (!brokerService.isStopping()) { 2642 | Connection connection = createConnection(transport); 2643 | connection.start(); 2644 | } else { 2645 | throw new BrokerStoppedException 2646 | ("Broker " + brokerService + " is being stopped"); 2647 | } 2648 | } catch (Exception e) { 2649 | String remoteHost = transport.getRemoteAddress(); 2650 | ServiceSupport.dispose(transport); 2651 | onAcceptError(e, remoteHost); 2652 | } 2653 | } 2654 | }); 2655 | } catch (Exception e) { 2656 | String remoteHost = transport.getRemoteAddress(); 2657 | ServiceSupport.dispose(transport); 2658 | onAcceptError(e, remoteHost); 2659 | } 2660 | } 2661 | }); 2662 | getServer().setBrokerInfo(brokerInfo); 2663 | getServer().start(); 2664 | 2665 | DiscoveryAgent da = getDiscoveryAgent(); 2666 | if (da != null) { 2667 | da.registerService(getPublishableConnectString()); 2668 | da.start(); 2669 | } 2670 | if (enableStatusMonitor) { 2671 | this.statusDector = new TransportStatusDetector(this); 2672 | this.statusDector.start(); 2673 | } 2674 | 2675 | LOG.info("Connector {} started", getName()); 2676 | } 2677 | ``` 2678 | 2679 | 可见,TransportConnector的启动实际上是通过Server的启动来完成的。 2680 | 2681 | getServer: 2682 | 2683 | ```java 2684 | public TransportServer getServer() throws IOException, URISyntaxException { 2685 | if (server == null) { 2686 | setServer(createTransportServer()); 2687 | } 2688 | return server; 2689 | } 2690 | protected TransportServer createTransportServer() { 2691 | //uri就是xml配置中的uri属性 2692 | return TransportFactorySupport.bind(brokerService, uri); 2693 | } 2694 | ``` 2695 | 2696 | 最终调用的是TransportFactory的doBind(uri)方法。TransportFactory的查找还是那个套路,实现类是org.apache.activemq.transport.tcp.TcpTransportFactory。 2697 | 2698 | ```java 2699 | @Override 2700 | public TransportServer doBind(final URI location) throws IOException { 2701 | try { 2702 | Map options = 2703 | new HashMap(URISupport.parseParameters(location)); 2704 | ServerSocketFactory serverSocketFactory = createServerSocketFactory(); 2705 | TcpTransportServer server = 2706 | createTcpTransportServer(location, serverSocketFactory); 2707 | server.setWireFormatFactory(createWireFormatFactory(options)); 2708 | IntrospectionSupport.setProperties(server, options); 2709 | Map transportOptions = 2710 | IntrospectionSupport.extractProperties(options, "transport."); 2711 | server.setTransportOption(transportOptions); 2712 | server.bind(); 2713 | return server; 2714 | } catch (URISyntaxException e) { 2715 | throw IOExceptionSupport.create(e); 2716 | } 2717 | } 2718 | ``` 2719 | 2720 | 一目了然。 2721 | 2722 | TcpTransportServer是ServiceSupport的子类,一切还是熟悉的配方: 2723 | 2724 | ```java 2725 | @Override 2726 | protected void doStart() throws Exception { 2727 | //默认true 2728 | if (useQueueForAccept) { 2729 | Runnable run = new Runnable() { 2730 | @Override 2731 | public void run() { 2732 | try { 2733 | while (!isStopped() && !isStopping()) { 2734 | //就是一个LinkedBlockingQueue 2735 | Socket sock = socketQueue.poll(1, TimeUnit.SECONDS); 2736 | if (sock != null) { 2737 | //后面讲运行的时候再说 2738 | handleSocket(sock); 2739 | } 2740 | } 2741 | } 2742 | } 2743 | }; 2744 | socketHandlerThread = new Thread(null, run, 2745 | "ActiveMQ Transport Server Thread Handler: " + toString(), getStackSize()); 2746 | socketHandlerThread.setDaemon(true); 2747 | socketHandlerThread.setPriority(ThreadPriorities.BROKER_MANAGEMENT - 1); 2748 | socketHandlerThread.start(); 2749 | } 2750 | super.doStart(); 2751 | } 2752 | ``` 2753 | 2754 | TransportServerThreadSupport.doStart: 2755 | 2756 | ```java 2757 | protected void doStart() throws Exception { 2758 | LOG.info("Listening for connections at: " + getConnectURI()); 2759 | runner = new Thread(null, this, "ActiveMQ Transport Server: " + toString(), stackSize); 2760 | runner.setDaemon(daemon); 2761 | runner.setPriority(ThreadPriorities.BROKER_MANAGEMENT); 2762 | runner.start(); 2763 | } 2764 | ``` 2765 | 2766 | TcpTransportServer启动了一个线程用于处理连接的socket,这里启动了一个线程用于接收客户端连接,并放到队列中。run方法还是在TcpTransportServer中: 2767 | 2768 | ```java 2769 | @Override 2770 | public void run() { 2771 | if (!isStopped() && !isStopping()) { 2772 | final ServerSocket serverSocket = this.serverSocket; 2773 | if (serverSocket == null) { 2774 | onAcceptError(new IOException()); 2775 | } 2776 | final ServerSocketChannel channel = serverSocket.getChannel(); 2777 | if (channel != null) { 2778 | doRunWithServerSocketChannel(channel); 2779 | } else { 2780 | doRunWithServerSocket(serverSocket); 2781 | } 2782 | } 2783 | } 2784 | ``` 2785 | 2786 | serverSocket默认由javax.net.DefaultServerSocketFactory生成,由反编译得到的代码: 2787 | 2788 | ```java 2789 | public ServerSocket createServerSocket(int i) throws IOException { 2790 | return new ServerSocket(i); 2791 | } 2792 | ``` 2793 | 2794 | 虽然ServerSocket定义了getChannel方法,但是**只有调用ServerSocketChannel.open.socket()得到的ServerSocket调用getChannel返回的才不是null,直接new出来的返回的是null**。不知道此处是否可配置。 2795 | 2796 | doRunWithServerSocketChannel就是很主旋律的nio操作将接收到的连接放进队列或是直接处理(禁用队列的情况下)。 2797 | 2798 | doRunWithServerSocket也是很主旋律的io操作,不说了。 2799 | 2800 | 服务器的启动到这里就完成了。 2801 | 2802 | ## 运行 2803 | 2804 | TcpTransportServer.doHandleSocket 2805 | 2806 | 主要就是构造Transport对象,包装TransportFilter,不同于客户端,Broker端只包装了一个MutexTransport,之后触发AcceptListener的onAccept方法。其在TransportConnector.start中被设置: 2807 | 2808 | ```java 2809 | new TransportAcceptListener() { 2810 | @Override 2811 | public void onAccept(final Transport transport) { 2812 | try { 2813 | brokerService.getTaskRunnerFactory().execute(new Runnable() { 2814 | @Override 2815 | public void run() { 2816 | if (!brokerService.isStopping()) { 2817 | Connection connection = createConnection(transport); 2818 | connection.start(); 2819 | } else { 2820 | throw new BrokerStoppedException 2821 | ("Broker " + brokerService + " is being stopped"); 2822 | } 2823 | } 2824 | }); 2825 | } 2826 | } 2827 | } 2828 | ``` 2829 | 2830 | 此处创建的是TransportConnection,start方法简略版: 2831 | 2832 | ```java 2833 | @Override 2834 | public void start() throws Exception { 2835 | synchronized (this) { 2836 | transport.start(); 2837 | BrokerInfo info = connector.getBrokerInfo().copy(); 2838 | if (connector.isUpdateClusterClients()) { 2839 | info.setPeerBrokerInfos(this.broker.getPeerBrokerInfos()); 2840 | } else { 2841 | info.setPeerBrokerInfos(null); 2842 | } 2843 | dispatchAsync(info); 2844 | connector.onStarted(this); 2845 | } 2846 | } 2847 | ``` 2848 | 2849 | transport仍然是TcpTransport,和客户端是一样的。由于是被包装过的,所以首先执行的是MutexTransport,其start方法只是调用下一层的start方法,所以真正有效的是TcpTransport.doStart,和客户端一样,启动了一个线程用于读取连接的数据。 2850 | 2851 | 消息的处理依然是对transportListener链onCommand方法的调用: 2852 | 2853 | ![transportListener链](images/server_listener_chain.png) 2854 | 2855 | DefaultTransportListener在TransportConnection构造器中被设置,源码: 2856 | 2857 | ```java 2858 | new DefaultTransportListener() { 2859 | @Override 2860 | public void onCommand(Object o) { 2861 | serviceLock.readLock().lock(); 2862 | try { 2863 | Command command = (Command) o; 2864 | if (!brokerService.isStopping()) { 2865 | //TransportConnection实现了CommandVisitor接口 2866 | Response response = service(command); 2867 | if (response != null && !brokerService.isStopping()) { 2868 | dispatchSync(response); 2869 | } 2870 | } 2871 | } finally { 2872 | serviceLock.readLock().unlock(); 2873 | } 2874 | } 2875 | }); 2876 | ``` 2877 | 2878 | ### 消息处理 2879 | 2880 | 核心就在于service方法,简略代码: 2881 | 2882 | ```java 2883 | @Override 2884 | public Response service(Command command) { 2885 | MDC.put("activemq.connector", connector.getUri().toString()); 2886 | Response response = null; 2887 | boolean responseRequired = command.isResponseRequired(); 2888 | int commandId = command.getCommandId(); 2889 | if (!pendingStop) { 2890 | //关键!!! 2891 | response = command.visit(this); 2892 | } else { 2893 | response = new ExceptionResponse(transportException.get()); 2894 | } 2895 | MDC.remove("activemq.connector"); 2896 | return response; 2897 | } 2898 | ``` 2899 | 2900 | 可见,具体是怎么处理的取决于消息的类型,下面选取几个关键的类型示例说明。 2901 | 2902 | #### ConnectionInfo 2903 | 2904 | 此消息代表了客户端的连接。visit调用了TransConnection的processAddConnection: 2905 | 2906 | ```java 2907 | @Override 2908 | public Response processAddConnection(ConnectionInfo info) throws Exception { 2909 | TransportConnectionState state; 2910 | // Make sure 2 concurrent connections by the same ID only generate 1 2911 | // TransportConnectionState object. 2912 | synchronized (brokerConnectionStates) { 2913 | //brokerConnectionStates: Map 2914 | state = (TransportConnectionState) brokerConnectionStates.get(info.getConnectionId()); 2915 | if (state == null) { 2916 | state = new TransportConnectionState(info, this); 2917 | brokerConnectionStates.put(info.getConnectionId(), state); 2918 | } 2919 | state.incrementReference(); 2920 | } 2921 | // If there are 2 concurrent connections for the same connection id, 2922 | // then last one in wins, we need to sync here 2923 | // to figure out the winner. 2924 | synchronized (state.getConnectionMutex()) { 2925 | if (state.getConnection() != this) { 2926 | LOG.debug("Killing previous stale connection: {}", 2927 | state.getConnection().getRemoteAddress()); 2928 | state.getConnection().stop(); 2929 | LOG.debug("Connection {} taking over previous connection: {}", 2930 | getRemoteAddress(), state.getConnection().getRemoteAddress()); 2931 | state.setConnection(this); 2932 | state.reset(info); 2933 | } 2934 | } 2935 | registerConnectionState(info.getConnectionId(), state); 2936 | LOG.debug("Setting up new connection id: {}, address: {}, info: {}", 2937 | new Object[]{ info.getConnectionId(), getRemoteAddress(), info }); 2938 | this.faultTolerantConnection = info.isFaultTolerant(); 2939 | // 初始化ConnectionContext部分忽略 2940 | broker.addConnection(context, info); 2941 | //此消息不需要Response 2942 | return null; 2943 | } 2944 | ``` 2945 | 2946 | 英文源码注释说的很清楚了。 2947 | 2948 | #### SessionInfo 2949 | 2950 | 当客户端生成一个新的session时,便会像Broker发送此消息。调用TransportConnection的processAddSession,源码: 2951 | 2952 | ```java 2953 | @Override 2954 | public Response processAddSession(SessionInfo info) throws Exception { 2955 | ConnectionId connectionId = info.getSessionId().getParentId(); 2956 | TransportConnectionState cs = lookupConnectionState(connectionId); 2957 | // Avoid replaying dup commands 2958 | if (cs != null && !cs.getSessionIds().contains(info.getSessionId())) { 2959 | broker.addSession(cs.getContext(), info); 2960 | try { 2961 | //里面维护有一个map,key为session的id,value为info 2962 | cs.addSession(info); 2963 | } catch (IllegalStateException e) { 2964 | LOG.warn("Failed to add session: {}", info.getSessionId(), e); 2965 | broker.removeSession(cs.getContext(), info); 2966 | } 2967 | } 2968 | return null; 2969 | } 2970 | ``` 2971 | 2972 | 同样无需返回。 2973 | 2974 | #### ProducerInfo 2975 | 2976 | 当创建新的生产者时便会发送此消息,调用processAddProducer: 2977 | 2978 | ```java 2979 | @Override 2980 | public Response processAddProducer(ProducerInfo info) { 2981 | SessionId sessionId = info.getProducerId().getParentId(); 2982 | ConnectionId connectionId = sessionId.getParentId(); 2983 | SessionState ss = cs.getSessionState(sessionId); 2984 | // Avoid replaying dup commands 2985 | if (!ss.getProducerIds().contains(info.getProducerId())) { 2986 | //检查最大Producer限制忽略 2987 | broker.addProducer(cs.getContext(), info); 2988 | //里面还是一个map 2989 | ss.addProducer(info); 2990 | } 2991 | return null; 2992 | } 2993 | ``` 2994 | 2995 | 一目了然。 2996 | 2997 | #### DestinationInfo 2998 | 2999 | 用于向Broker发送增加或删除Destination消息,visit: 3000 | 3001 | ```java 3002 | public Response visit(CommandVisitor visitor) throws Exception { 3003 | if (isAddOperation()) { 3004 | return visitor.processAddDestination(this); 3005 | } else if (isRemoveOperation()) { 3006 | return visitor.processRemoveDestination(this); 3007 | } 3008 | throw new IOException("Unknown operation type: " + getOperationType()); 3009 | } 3010 | ``` 3011 | 3012 | ##### 添加 3013 | 3014 | ```java 3015 | @Override 3016 | public Response processAddDestination(DestinationInfo info) throws Exception { 3017 | TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 3018 | broker.addDestinationInfo(cs.getContext(), info); 3019 | if (info.getDestination().isTemporary()) { 3020 | cs.addTempDestination(info); 3021 | } 3022 | return null; 3023 | } 3024 | ``` 3025 | 3026 | #### 消息处理 3027 | 3028 | processMessage: 3029 | 3030 | ```java 3031 | @Override 3032 | public Response processMessage(Message messageSend) { 3033 | ProducerId producerId = messageSend.getProducerId(); 3034 | //这个对象如果没有就会创建一个,因为在ProducerInfo中只是保存了Producer ID,ProducerBrokerExchange就相当于 //Broker端的Produer对象 3035 | ProducerBrokerExchange producerExchange = 3036 | getProducerBrokerExchange(producerId); 3037 | //过滤重复消息,只要不是重复的都返回true 3038 | if (producerExchange.canDispatch(messageSend)) { 3039 | broker.send(producerExchange, messageSend); 3040 | } 3041 | return null; 3042 | } 3043 | ``` 3044 | 3045 | RegionBroker.send核心代码: 3046 | 3047 | ```java 3048 | public void send(ProducerBrokerExchange producerExchange, Message message) { 3049 | ActiveMQDestination destination = message.getDestination(); 3050 | if (producerExchange.isMutable() || producerExchange.getRegion() == null 3051 | producerExchange.getConnectionContext().getBroker() 3052 | .addDestination(producerExchange.getConnectionContext(), 3053 | destination, isAllowTempAutoCreationOnSend()); 3054 | producerExchange.setRegion(getRegion(destination)); 3055 | producerExchange.setRegionDestination(null); 3056 | } 3057 | 3058 | producerExchange.getRegion().send(producerExchange, message); 3059 | } 3060 | ``` 3061 | 3062 | 从代码中可以看出,客户端创建queue或topic之后Broker并没有随之创建,而是类似于lazy-init。创建Destination: 3063 | 3064 | RegionBroker.addDestination: 3065 | 3066 | ```java 3067 | @Override 3068 | public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) { 3069 | answer = getRegion(destination).addDestination(context, destination, create); 3070 | destinations.put(destination, answer); 3071 | return answer; 3072 | } 3073 | ``` 3074 | 3075 | getRegion: 3076 | 3077 | ```java 3078 | protected Region getRegion(ActiveMQDestination destination) { 3079 | switch (destination.getDestinationType()) { 3080 | case ActiveMQDestination.QUEUE_TYPE: 3081 | return queueRegion; 3082 | case ActiveMQDestination.TOPIC_TYPE: 3083 | return topicRegion; 3084 | case ActiveMQDestination.TEMP_QUEUE_TYPE: 3085 | return tempQueueRegion; 3086 | case ActiveMQDestination.TEMP_TOPIC_TYPE: 3087 | return tempTopicRegion; 3088 | default: 3089 | throw createUnknownDestinationTypeException(destination); 3090 | } 3091 | } 3092 | ``` 3093 | 3094 | 可以看出,ActiveMQ是把queue、topic等类型单独处理的。这样做的好处在于可以单独为每一个Region进行设置。这里就和Broker启动-start一节对应起来了。 3095 | 3096 | ##### Queue 3097 | 3098 | send方法核心代码: 3099 | 3100 | ```java 3101 | @Override 3102 | public void send(final ProducerBrokerExchange producerExchange, final Message message) { 3103 | doMessageSend(producerExchange, message); 3104 | if (sendProducerAck) { 3105 | ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize()); 3106 | context.getConnection().dispatchAsync(ack); 3107 | } 3108 | } 3109 | void doMessageSend(final ProducerBrokerExchange producerExchange, final Message message) { 3110 | if (messages.isCacheEnabled() && !isPersistJMSRedelivered()) { 3111 | //异步执行保存 3112 | result = store.asyncAddQueueMessage(context, message, isOptimizeStorage()); 3113 | result.addListener(new PendingMarshalUsageTracker(message)); 3114 | } else { 3115 | //同步执行 3116 | store.addMessage(context, message); 3117 | } 3118 | } 3119 | ``` 3120 | 3121 | KahaDBStore.addMessage: 3122 | 3123 | ```java 3124 | @Override 3125 | public void addMessage(final ConnectionContext context, final Message message) { 3126 | final KahaAddMessageCommand command = new KahaAddMessageCommand(); 3127 | //配置command忽略 3128 | org.apache.activemq.util.ByteSequence packet = wireFormat.marshal(message); 3129 | command.setMessage(new Buffer(packet.getData(), packet.getOffset(), packet.getLength())); 3130 | store(command, isEnableJournalDiskSyncs() && message.isResponseRequired(), 3131 | new IndexAware() {//忽略...}, null); 3132 | } 3133 | ``` 3134 | 3135 | 可见,实际存储的仍然是按照我们使用的协议序列化之后的byte数组。store: 3136 | 3137 | ```java 3138 | public Location store(JournalCommand data, boolean sync, IndexAware before, Runnable after, 3139 | Runnable onJournalStoreComplete) { 3140 | ByteSequence sequence = toByteSequence(data); 3141 | Location location; 3142 | //在这里通过FileAppender真正向文件写入数据 3143 | location = onJournalStoreComplete == null ? journal.write(sequence, sync) : 3144 | journal.write(sequence, onJournalStoreComplete) ; 3145 | //当数据写入完成之后进行设置index工作,Vistor模式 3146 | process(data, location, before); 3147 | return location; 3148 | } 3149 | ``` 3150 | 3151 | 以消息写入(KahaAddMessageCommand)为例: 3152 | 3153 | ```java 3154 | protected void process(final KahaAddMessageCommand command, final Location location, 3155 | final IndexAware runWithIndexLock) { 3156 | if (command.hasTransactionInfo()) { 3157 | //如果有事务存在,那么现将保存操作保存的list,等待commit命令同时提交 3158 | //注意此时数据实际上已经写入了,只是没有建立索引 3159 | List inflightTx = getInflightTx(command.getTransactionInfo()); 3160 | inflightTx.add(new AddOperation(command, location, runWithIndexLock)); 3161 | } else { 3162 | this.indexLock.writeLock().lock(); 3163 | try { 3164 | //直接建立索引 3165 | pageFile.tx().execute(new Transaction.Closure() { 3166 | @Override 3167 | public void execute(Transaction tx) throws IOException { 3168 | long assignedIndex = updateIndex(tx, command, location); 3169 | if (runWithIndexLock != null) { 3170 | runWithIndexLock.sequenceAssignedWithIndexLocked(assignedIndex); 3171 | } 3172 | } 3173 | }); 3174 | } finally { 3175 | this.indexLock.writeLock().unlock(); 3176 | } 3177 | } 3178 | } 3179 | ``` 3180 | 3181 | 再回到Queue.send,消息保存后,如果需要返回相应,那么即执行返回ACK操作。ACK返回调用了TransportConnection.dispatchAsync执行异步发送: 3182 | 3183 | ```java 3184 | @Override 3185 | public void dispatchAsync(Command message) { 3186 | if (!stopping.get()) { 3187 | //在start方法中初始化 3188 | if (taskRunner == null) { 3189 | //直接同步发送,一般不会到这里 3190 | dispatchSync(message); 3191 | } else { 3192 | synchronized (dispatchQueue) { 3193 | dispatchQueue.add(message); 3194 | } 3195 | try { 3196 | //唤醒线程 3197 | taskRunner.wakeup(); 3198 | } catch (InterruptedException e) { 3199 | Thread.currentThread().interrupt(); 3200 | } 3201 | } 3202 | } 3203 | } 3204 | ``` 3205 | 3206 | ##### Topic 3207 | 3208 | 和Queue的区别在于doMessageSend方法: 3209 | 3210 | ```java 3211 | synchronized void doMessageSend(final ProducerBrokerExchange producerExchange, final Message message) { 3212 | final ConnectionContext context = producerExchange.getConnectionContext(); 3213 | message.getMessageId().setBrokerSequenceId(getDestinationSequenceId()); 3214 | Future result = null; 3215 | //直接异步保存 3216 | result = topicStore.asyncAddTopicMessage(context, message,isOptimizeStorage()); 3217 | //最大的不同: Topic收到消息后直接推给订阅者,Queue并不会 3218 | dispatch(context, message); 3219 | } 3220 | ``` 3221 | 3222 | dispatch实际调用dispatchPolicy.dispatch,默认是SimpleDispatchPolicy: 3223 | 3224 | ```java 3225 | public boolean dispatch(MessageReference node, MessageEvaluationContext msgContext, 3226 | List consumers) { 3227 | int count = 0; 3228 | for (Subscription sub : consumers) { 3229 | // Don't deliver to browsers 3230 | if (sub.getConsumerInfo().isBrowser()) { 3231 | continue; 3232 | } 3233 | // Only dispatch to interested subscriptions 3234 | //就是根据ConsumerId判断 3235 | if (!sub.matches(node, msgContext)) { 3236 | //空实现 3237 | sub.unmatched(node); 3238 | continue; 3239 | } 3240 | 3241 | sub.add(node); 3242 | count++; 3243 | } 3244 | return count > 0; 3245 | } 3246 | ``` 3247 | 3248 | add即是根据设置的同步还是异步调用TransportConnection.dispatchAsyc/dispatchSync向订阅者发送数据。 3249 | 3250 | ##### TempQueue & TempTopic 3251 | 3252 | 只存在于连接保持的时候,不多说了。 3253 | 3254 | #### MessagePull 3255 | 3256 | 前面提到了,Queue接收到消息时并不会主动向Consumer发送消息,而是需要Consumer向Broker发送此命令让Broker发消息。visit方法调用TransportConnection的processMessagePull: 3257 | 3258 | ```java 3259 | @Override 3260 | public Response processMessagePull(MessagePull pull) throws Exception { 3261 | return broker.messagePull(lookupConnectionState(pull.getConsumerId()).getContext(), pull); 3262 | } 3263 | @Override 3264 | public Response messagePull(ConnectionContext context, MessagePull pull) throws Exception { 3265 | ActiveMQDestination destination = pull.getDestination(); 3266 | return getRegion(destination).messagePull(context, pull); 3267 | } 3268 | ``` 3269 | 3270 | 核心逻辑在PrefetchSubscription.dispatchPending,简略代码: 3271 | 3272 | ```java 3273 | public void dispatchPending() throws IOException { 3274 | List slowConsumerTargets = null; 3275 | synchronized(pendingLock) { 3276 | while (pending.hasNext() && !isFull() && count < numberToDispatch) { 3277 | MessageReference node = pending.next(); 3278 | if (node == null) { 3279 | break; 3280 | } 3281 | synchronized(dispatchLock) { 3282 | pending.remove(); 3283 | //向Consumer发送消息(TransportConnection.dispatchAsyc/Sync) 3284 | dispatch(node); 3285 | } 3286 | } 3287 | } 3288 | } 3289 | ``` 3290 | 3291 | pending是一个PendingMessageCursor对象,实现类应该是QueueStorePrefetch。这个东西应该是用于批量从文件获取数据,内部还维护有一个List作为缓存。hasNext: 3292 | 3293 | ```java 3294 | @Override 3295 | public final synchronized boolean hasNext() { 3296 | if (batchList.isEmpty()) { 3297 | try { 3298 | //从文件获取 3299 | fillBatch(); 3300 | } catch (Exception e) {} 3301 | } 3302 | //从list生成Iterator 3303 | ensureIterator(); 3304 | return this.iterator.hasNext(); 3305 | } 3306 | ``` 3307 | 3308 | QueueStorePrefetch.doFillBatch: 3309 | 3310 | ```java 3311 | @Override 3312 | protected void doFillBatch() throws Exception { 3313 | //是否达到内存使用限制 3314 | hadSpace = this.hasSpace(); 3315 | if (!broker.getBrokerService().isPersistent() || hadSpace) { 3316 | //关键在这里 3317 | this.store.recoverNextMessages(this.maxBatchSize, this); 3318 | dealWithDuplicates(); // without the index lock 3319 | } 3320 | } 3321 | ``` 3322 | 3323 | #### 事务处理 3324 | 3325 | ##### 开启 3326 | 3327 | ```java 3328 | @Override 3329 | public Response processBeginTransaction(TransactionInfo info) throws Exception { 3330 | TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 3331 | context = null; 3332 | if (cs != null) { 3333 | context = cs.getContext(); 3334 | } 3335 | // Avoid replaying dup commands 3336 | if (cs.getTransactionState(info.getTransactionId()) == null) { 3337 | //map的put操作 3338 | cs.addTransactionState(info.getTransactionId()); 3339 | broker.beginTransaction(context, info.getTransactionId()); 3340 | } 3341 | return null; 3342 | } 3343 | ``` 3344 | 3345 | 此处的broker是被包装过的,调用链参见Broker启动-Broker启动一节。事务的真正实现位于TransactionBroker 3346 | 3347 | ```java 3348 | public void beginTransaction(ConnectionContext context, TransactionId xid) throws Exception { 3349 | // the transaction may have already been started. 3350 | if (xid.isXATransaction()) { 3351 | XATransaction transaction = null; 3352 | synchronized (xaTransactions) { 3353 | transaction = xaTransactions.get(xid); 3354 | if (transaction != null) { 3355 | return; 3356 | } 3357 | transaction = new XATransaction(transactionStore, (XATransactionId)xid, this, 3358 | context.getConnectionId()); 3359 | xaTransactions.put(xid, transaction); 3360 | } 3361 | } else { 3362 | Map transactionMap = context.getTransactions(); 3363 | Transaction transaction = transactionMap.get(xid); 3364 | if (transaction != null) { 3365 | throw new JMSException("Transaction '" + xid + "' has already been started."); 3366 | } 3367 | transaction = new LocalTransaction(transactionStore, (LocalTransactionId)xid, context); 3368 | transactionMap.put(xid, transaction); 3369 | } 3370 | } 3371 | ``` 3372 | 3373 | 可以看出,只是分为JTA事务和本地事务两种情况创建了一个对应的事物对象保存在map中。 3374 | 3375 | ##### RollBack 3376 | 3377 | TransportConnection.processRollbackTransaction: 3378 | 3379 | ```java 3380 | @Override 3381 | public Response processRollbackTransaction(TransactionInfo info) { 3382 | TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 3383 | context = cs.getContext(); 3384 | //移除TransactionState 3385 | cs.removeTransactionState(info.getTransactionId()); 3386 | broker.rollbackTransaction(context, info.getTransactionId()); 3387 | return null; 3388 | } 3389 | ``` 3390 | 3391 | 最终调用LocalTransaction.rollback(以本地事务为例): 3392 | 3393 | ```java 3394 | @Override 3395 | public void rollback() { 3396 | setState(Transaction.FINISHED_STATE); 3397 | context.getTransactions().remove(xid); 3398 | synchronized (transactionStore) { 3399 | transactionStore.rollback(getTransactionId()); 3400 | fireAfterRollback(); 3401 | } 3402 | } 3403 | ``` 3404 | 3405 | transactionStore来源于: 3406 | 3407 | ```java 3408 | broker = new TransactionBroker(broker, getPersistenceAdapter().createTransactionStore()); 3409 | //KahaDBStore.createTransactionStore: 3410 | this.transactionStore = new KahaDBTransactionStore(this); 3411 | ``` 3412 | 3413 | 最终调用上面提到的store方法保存了KahaRollbackCommand。最终执行保存的是DataFileAppender.storeItem方法。**此处使用了一个批处理以提高文件读写的速度。** 3414 | 3415 | 但是为什么写入一个命令就能完成commit/rollback的操作,可能需要探究KahaDB的实现原理,此处是个坑。。。 3416 | 3417 | ### Response发送 3418 | 3419 | dispatchSync最终调用processDispatch,简略版源码: 3420 | 3421 | ```java 3422 | protected void processDispatch(Command command) { 3423 | MessageDispatch messageDispatch = (MessageDispatch) 3424 | (command.isMessageDispatch() ? command : null); 3425 | if (!stopping.get()) { 3426 | if (messageDispatch != null) { 3427 | broker.preProcessDispatch(messageDispatch); 3428 | } 3429 | dispatch(command); 3430 | } 3431 | } 3432 | ``` 3433 | 3434 | dispatch方法最终调用TcpTransport的oneway方法,通过socket向客户端返回相应,完成交互过程。 3435 | 3436 | -------------------------------------------------------------------------------- /notes/images/ActiveMQDispatcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/ActiveMQDispatcher.png -------------------------------------------------------------------------------- /notes/images/MessageDispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/MessageDispatch.png -------------------------------------------------------------------------------- /notes/images/arrayternarytrie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/arrayternarytrie.png -------------------------------------------------------------------------------- /notes/images/arraytrie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/arraytrie.png -------------------------------------------------------------------------------- /notes/images/cache_level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/cache_level.png -------------------------------------------------------------------------------- /notes/images/compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/compact.png -------------------------------------------------------------------------------- /notes/images/component_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/component_structure.png -------------------------------------------------------------------------------- /notes/images/default_broker_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/default_broker_chain.png -------------------------------------------------------------------------------- /notes/images/demo_flow_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/demo_flow_chart.png -------------------------------------------------------------------------------- /notes/images/eat_what_you_kill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/eat_what_you_kill.png -------------------------------------------------------------------------------- /notes/images/full_broker_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/full_broker_chain.png -------------------------------------------------------------------------------- /notes/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/lock.png -------------------------------------------------------------------------------- /notes/images/onCommand_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/onCommand_chain.png -------------------------------------------------------------------------------- /notes/images/producer_init.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/producer_init.jpg -------------------------------------------------------------------------------- /notes/images/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/task.png -------------------------------------------------------------------------------- /notes/images/task_runner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/task_runner.png -------------------------------------------------------------------------------- /notes/images/transport_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/transport_chain.png -------------------------------------------------------------------------------- /notes/images/uncompact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seaswalker/activemq-notes/4ab43d740eac583516d1877f62e7fa28d8288bf8/notes/images/uncompact.png -------------------------------------------------------------------------------- /notes/note.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 3 | 单开此文件的目的是原先的笔记记录与较早之前,现在看起来不是那么尽如人意。在这里用另外一个全新的角度说明一些重要问题。 4 | 5 | # 组件结构 6 | 7 | 首先用最简单的启动消费者的方法开始: 8 | 9 | ```java 10 | ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, 11 | ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616"); 12 | Connection connection = connectionFactory.createConnection(); 13 | connection.start(); 14 | Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); 15 | Destination destination = session.createQueue("foo.bar"); 16 | MessageConsumer consumer = session.createConsumer(destination); 17 | while (true) { 18 | consumer.receive(); 19 | } 20 | ``` 21 | 22 | 用流程图来表示这一过程: 23 | 24 | ![demo流程](images/demo_flow_chart.png) 25 | 26 | 进一步,Connection、Session、Consumer以及MessageListener的存储结构如下图: 27 | 28 | ![存储结构](images/component_structure.png) 29 | 30 | ## ConsumerId 31 | 32 | 下面看一下这个消费者ID究竟是个什么东西,创建的入口位于ActiveMQSession的createConsumer方法: 33 | 34 | ```java 35 | public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal, MessageListener messageListener) { 36 | //... 37 | return new ActiveMQMessageConsumer(this, getNextConsumerId(), activemqDestination, null, messageSelector, 38 | prefetch, prefetchPolicy.getMaximumPendingMessageLimit(), noLocal, false, isAsyncDispatch(), messageListener); 39 | } 40 | ``` 41 | 42 | 由getNextConsumerId()方法创建: 43 | 44 | ```java 45 | protected ConsumerId getNextConsumerId() { 46 | return new ConsumerId(info.getSessionId(), consumerIdGenerator.getNextSequenceId()); 47 | } 48 | ``` 49 | 50 | consumerIdGenerator其实就是对一个long型数字进行累加。 51 | 52 | ## ActiveMQDispatcher 53 | 54 | 从上面的存储结构图中可以看出,Connection同样记录了从ConsumerId到ActiveMQDispatcher的映射,其实在这里**ActiveMQDispatcher就是ActiveMQSession**,类图: 55 | 56 | ![ActiveMQDispatcher](images/ActiveMQDispatcher.png) 57 | 58 | 此接口只有一个方法: 59 | 60 | ```java 61 | void dispatch(MessageDispatch messageDispatch); 62 | ``` 63 | 64 | 下面看看被分发的MessageDispatch是个什么东西: 65 | 66 | ![MessageDispatch](images/MessageDispatch.png) 67 | 68 | # 消息是如何接收的 69 | 70 | 有了上面存储结构说明的铺垫,这一部分将容易理解许多。 71 | 72 | TcpTransport的父类TransportThreadSupport的doStart方法创建了一个线程,专门用于接收服务器传来的各种东西,包括Queue消息、Topic消息以及控制命令。 73 | 74 | 同时MQ维护了一条调用链,用于依次处理收到的东西,关于这条链是如何构成的,参考以前的笔记。 75 | 76 | 这里我们重点关注调用链的末端--ActiveMQConnection的onCommand方法: 77 | 78 | ```java 79 | @Override 80 | public void onCommand(final Object o) { 81 | final Command command = (Command)o; 82 | if (!closed.get() && command != null) { 83 | try { 84 | command.visit(new CommandVisitorAdapter() { 85 | @Override 86 | public Response processMessageDispatch(MessageDispatch md) { 87 | ActiveMQDispatcher dispatcher = dispatchers.get(md.getConsumerId()); 88 | dispatcher.dispatch(md); 89 | return null; 90 | } 91 | } 92 | } 93 | } 94 | //... 95 | } 96 | ``` 97 | 98 | 这里的dispatcher便是ActiveMQSession,可以得出一个结论:**一个Session中所有消费者/订阅者的消息均由此Session负责分发**,同时在dispatchers数据结构中,一个ActiveMQSession 99 | 会以value的形式存在于多个K-V对中。 100 | 101 | ActiveMQSession的dispatch方法: 102 | 103 | ```java 104 | @Override 105 | public void dispatch(MessageDispatch messageDispatch) { 106 | executor.execute(messageDispatch); 107 | } 108 | ``` 109 | 110 | execute方法的实现位于ActiveMQSessionExecutor: 111 | 112 | ```java 113 | void execute(MessageDispatch message) throws InterruptedException { 114 | if (!session.isSessionAsyncDispatch() && !dispatchedBySessionPool) { 115 | dispatch(message); 116 | } else { 117 | messageQueue.enqueue(message); 118 | wakeup(); 119 | } 120 | } 121 | ``` 122 | 123 | 很明显,这里的分发支持同步和异步两种模式,由ActiveMQConnection的dispatchAsync属性进行控制,默认为true,在Spring中我们可以通过以下配置进行改变: 124 | 125 | ```xml 126 | 127 | 128 | 129 | ``` 130 | 131 | 这里我们以较为复杂的异步模式进行说明。 132 | 133 | ## 消息队列 134 | 135 | messageQueue属性在ActiveMQSessionExecutor的构造器中初始化: 136 | 137 | ```java 138 | ActiveMQSessionExecutor(ActiveMQSession session) { 139 | this.session = session; 140 | if (this.session.connection != null 141 | && this.session.connection.isMessagePrioritySupported()) { 142 | this.messageQueue = new SimplePriorityMessageDispatchChannel(); 143 | }else { 144 | this.messageQueue = new FifoMessageDispatchChannel(); 145 | } 146 | } 147 | ``` 148 | 149 | 如果我们进行以下的配置: 150 | 151 | ```xml 152 | 153 | 154 | 155 | ``` 156 | 157 | 即表示启用消息优先级机制,此时将使用优先级队列进行消息分发,否则使用简单的先进先出队列,默认为关闭。 158 | 159 | 消息优先级由JMS标准定义,其实就是一个0-9的数字,默认为4. 160 | 161 | 以ActiveMQTextMessage为例,我们可以通过调用其setPriority方法设置其优先级。 162 | 163 | 从SimplePriorityMessageDispatchChannel本质上是一个LinkedList数组,**每一个优先级都有一个LinkedList与之对应**。其构造器源码: 164 | 165 | ```java 166 | private final LinkedList[] lists; 167 | public SimplePriorityMessageDispatchChannel() { 168 | this.lists = new LinkedList[MAX_PRIORITY]; 169 | for (int i = 0; i < MAX_PRIORITY; i++) { 170 | lists[i] = new LinkedList(); 171 | } 172 | } 173 | ``` 174 | 175 | 出队操作由removeFirst方法完成: 176 | 177 | ```java 178 | private final MessageDispatch removeFirst() { 179 | if (this.size > 0) { 180 | for (int i = MAX_PRIORITY - 1; i >= 0; i--) { 181 | LinkedList list = lists[i]; 182 | if (!list.isEmpty()) { 183 | this.size--; 184 | return list.removeFirst(); 185 | } 186 | } 187 | } 188 | return null; 189 | } 190 | ``` 191 | 192 | 优先从高优先级的LinkedList中获取。 193 | 194 | ## 分发 195 | 196 | ActiveMQSessionExecutor.wakeUp方法: 197 | 198 | ```java 199 | public void wakeup() { 200 | if (!dispatchedBySessionPool) { 201 | if (session.isSessionAsyncDispatch()) { 202 | TaskRunner taskRunner = this.taskRunner; 203 | if (taskRunner == null) { 204 | synchronized (this) { 205 | if (this.taskRunner == null) { 206 | if (!isRunning()) { 207 | // stop has been called 208 | return; 209 | } 210 | this.taskRunner = 211 | session.connection.getSessionTaskRunner().createTaskRunner(this, 212 | "ActiveMQ Session: " + session.getSessionId()); 213 | } 214 | taskRunner = this.taskRunner; 215 | } 216 | } 217 | taskRunner.wakeup(); 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | 当我们设置了MessageListener是dispatchedBySessionPool便为true,这一点可以从ActiveMQSession的setMessageListener方法得以证明: 224 | 225 | ```java 226 | @Override 227 | public void setMessageListener(MessageListener listener) throws JMSException { 228 | //... 229 | if (listener != null) { 230 | executor.setDispatchedBySessionPool(true); 231 | } 232 | } 233 | ``` 234 | 235 | 这可以得出一个很重要的结论: 236 | 237 | **MessageListener和其它形式的消费行为是互斥的**,换句话说,一旦MessageListener被设置(对于Session来说),那么普通的Consumer以及Subscriber都将失效。 238 | 239 | ### MessageListener 240 | 241 | 这个地方需要着重说明一点:**Session和Consumer均可以设置MessageListener**,MessageListener和普通的阻塞消费者是两种**互斥**的方式。 242 | 243 | ### 其它 244 | 245 | #### TaskRunner 246 | 247 | TaskRunner接口是ActiveMQ对线程执行特定的任务这一行为的抽象。ActiveMQ创建了一个DedicatedTaskRunner对象用于Session阶段的消息分发。 248 | 249 | ![TaskRunner](images/task_runner.png) 250 | 251 | DedicatedTaskRunner的原理从其构造器中便可以看出: 252 | 253 | ```java 254 | public DedicatedTaskRunner(final Task task, String name, int priority, boolean daemon) { 255 | this.task = task; 256 | thread = new Thread(name) { 257 | @Override 258 | public void run() { 259 | try { 260 | runTask(); 261 | } finally { 262 | LOG.trace("Run task done: {}", task); 263 | } 264 | } 265 | }; 266 | thread.setDaemon(daemon); 267 | thread.setName(name); 268 | thread.setPriority(priority); 269 | thread.start(); 270 | } 271 | ``` 272 | 273 | #### Task 274 | 275 | Task接口是ActiveMQ对任务的抽象: 276 | 277 | ![Task](images/task.png) 278 | 279 | 这里将任务抽象为一次又一次的事件循环,iterate方法将持续被调用,直到返回false。 280 | 281 | #### Session级别分发 282 | 283 | 核心逻辑位于ActiveMQSessionExecutor的iterate方法: 284 | 285 | ```java 286 | public boolean iterate() { 287 | // Deliver any messages queued on the consumer to their listeners. 288 | for (ActiveMQMessageConsumer consumer : this.session.consumers) { 289 | if (consumer.iterate()) { 290 | return true; 291 | } 292 | } 293 | 294 | // No messages left queued on the listeners.. so now dispatch messages 295 | // queued on the session 296 | MessageDispatch message = messageQueue.dequeueNoWait(); 297 | if (message == null) { 298 | return false; 299 | } else { 300 | dispatch(message); 301 | return !messageQueue.isEmpty(); 302 | } 303 | } 304 | ``` 305 | 306 | 消费者(Subcriber也是消费者)对于消息的处理同样有两种情况: 307 | 308 | - MessageListener: 直接由Session分发线程调用其onMessage方法。 309 | 310 | - 如果没有设置MessageListener,那么和Session分发的逻辑完全相同,消息首先被加入到一个FIFO或者优先级队列中,之后喜闻乐见的notify我们正在等待消息的消费者。 311 | 312 | 所以这里Session消息分发的逻辑可以总结为: 313 | 314 | - 如果任何一个消费者中尚有没有被消费的消息,那么将这些消息传递给MessageListener。 315 | 316 | - 如果第一条不满足,那么将Session收到的消息向下分发给消费者,这里分发的逻辑便是上面提到的。 317 | 318 | 到这里可能会有疑问: 既然只有当没有设置MessageListener时消息才会被放到队列中,那么上面将未消费的消息传递给MessageListener是什么情况呢? 319 | 320 | 其实从代码来看,只有满足下列条件时才会直接传递给MessageListener,ActiveMQMessageConsumer.dispatch: 321 | 322 | ```java 323 | @Override 324 | public void dispatch(MessageDispatch md) { 325 | if (listener != null && unconsumedMessages.isRunning()) { 326 | //调用MessageListener.onMessage方法 327 | } 328 | } 329 | ``` 330 | 331 | 猜测需要处理连接已经建立(即已经开始监听消息),但是消费者还没启动完毕这个时间空隙收到的消息。 332 | 333 | 如果所有的Consumer都没有未消费的消息,那么Session将通过ActiveMQSessionExecutor的dispatch方法将消息分派给相应的Consumer: 334 | 335 | ```java 336 | void dispatch(MessageDispatch message) { 337 | // TODO - we should use a Map for this indexed by consumerId 338 | for (ActiveMQMessageConsumer consumer : this.session.consumers) { 339 | ConsumerId consumerId = message.getConsumerId(); 340 | if (consumerId.equals(consumer.getConsumerId())) { 341 | consumer.dispatch(message); 342 | break; 343 | } 344 | } 345 | } 346 | ``` 347 | 348 | 这里就是一个简单的遍历对比Consumer ID,至此,整个消息的接收流程已经很清晰了,至于ActiveMQ采取何种策略决定发送到的消费者,这里不再深究,估计可以Round robin之类的。 349 | 350 | # DefaultMessageListenerContainer 351 | 352 | 下面来看一下Spring对JMS的封装,下面是我们常用的一种配置: 353 | 354 | ```xml 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | ``` 365 | 366 | 理解此类的关键是理解concurrentConsumers和缓存级别的意义。 367 | 368 | ## consumer数量 369 | 370 | 简单来说,正如属性名那样,此字段的含义其实就是一个Container中消费者的数量,在DefaultMessageListenerContainer中每个消费者用内部类AsyncMessageListenerInvoker实现。 371 | 372 | concurrentConsumers决定了消费者数量的下限,maxConcurrentConsumers决定了消费者数量的上限,当有消息到来时DefaultMessageListenerContainer会尝试提升消费者的数量,直到maxConcurrentConsumers。 373 | 374 | ## 缓存级别 375 | 376 | 为什么有缓存级别这个东西呢? 377 | 378 | 一个Consumer的创建需要Connection、Sesssion这两个必不可少的组件,那么每次收到消息时是新创建一个组件还是对一个进行重用?这便是缓存的意义。所有的缓存级别用一张偷来的图表示: 379 | 380 | ![缓存级别](images/cache_level.png) 381 | 382 | 那么默认的缓存级别是什么呢? 383 | 384 | 与容器中是否有事物管理器有关,如果有,那么使用最低的NONE级别,如果没有,那么使用最高的缓存Consumer级别。 385 | 386 | ## 初始化 387 | 388 | 这一步所做的可以总结为三个方面: 389 | 390 | 1. 如果缓存级别设为CACHE_AUTO,那么动态调整级别。 391 | 2. 线程池的初始化。 392 | 3. AsyncMessageListenerInvoker初始化。 393 | 394 | 下面来通过源码对其初始化过程进行分析。DefaultMessageListenerContainer实现了Spring的InitializingBean接口,AbstractJmsListeningContainer实现了接口的afterPropertiesSet方法, 395 | 并最终调用了initialize方法,实际的初始化工作将由此方法来完成。 396 | 397 | DefaultMessageListenerContainer.initialize: 398 | 399 | ```java 400 | @Override 401 | public void initialize() { 402 | // Adapt default cache level. 403 | if (this.cacheLevel == CACHE_AUTO) { 404 | this.cacheLevel = (getTransactionManager() != null ? CACHE_NONE : CACHE_CONSUMER); 405 | } 406 | 407 | // 线程池创建 408 | synchronized (this.lifecycleMonitor) { 409 | if (this.taskExecutor == null) { 410 | this.taskExecutor = createDefaultTaskExecutor(); 411 | } 412 | } 413 | 414 | // 导致下面doInitialize方法的调用 415 | super.initialize(); 416 | } 417 | ``` 418 | 419 | 线程池其实是一个SimpleAsyncTaskExecutor对象,其实现很有意思,**对于每一个提交的任务都创建一个新的线程来执行**,这也就说明,对线程个数的控制不是通过线程池来完成的。 420 | 421 | DefaultMessageListenerContainer.doInitialize: 422 | 423 | ```java 424 | @Override 425 | protected void doInitialize() throws JMSException { 426 | synchronized (this.lifecycleMonitor) { 427 | for (int i = 0; i < this.concurrentConsumers; i++) { 428 | scheduleNewInvoker(); 429 | } 430 | } 431 | } 432 | ``` 433 | scheduleNewInvoker方法用于创建一个AsyncMessageListenerInvoker对象。 434 | 435 | ## 启动 436 | 437 | DefaultMessageListenerContainer实现了SmartLifecycle接口,启动流程便通过此接口的相关方法触发。 438 | 439 | AbstractJmsListeningContainer.doStart: 440 | 441 | ```java 442 | protected void doStart() throws JMSException { 443 | // Lazily establish a shared Connection, if necessary. 444 | if (sharedConnectionEnabled()) { 445 | establishSharedConnection(); 446 | } 447 | 448 | // Reschedule paused tasks, if any. 449 | synchronized (this.lifecycleMonitor) { 450 | this.running = true; 451 | this.lifecycleMonitor.notifyAll(); 452 | resumePausedTasks(); 453 | } 454 | 455 | // Start the shared Connection, if any. 456 | if (sharedConnectionEnabled()) { 457 | startSharedConnection(); 458 | } 459 | } 460 | ``` 461 | 462 | 分为以下三部分进行说明。 463 | 464 | ### 建立连接 465 | 466 | 如果缓存级别不低于`CACHE_CONNECTION`,那么将在此时创建与ActiveMQ服务器的连接,原因很简单:因为在这个Container的声明周期内都是使用这一个连接。establishSharedConnection通过AbstractJmsListeningContainer 467 | 的createSharedConnection方法实现: 468 | 469 | ```java 470 | protected Connection createSharedConnection() { 471 | Connection con = createConnection(); 472 | prepareSharedConnection(con); 473 | return con; 474 | } 475 | ``` 476 | 477 | createConnection的实现就是通过jms的ConnectionFactory创建连接的过程,prepareSharedConnection也很简单: 478 | 479 | ```java 480 | protected void prepareSharedConnection(Connection connection) { 481 | String clientId = getClientId(); 482 | if (clientId != null) { 483 | connection.setClientID(clientId); 484 | } 485 | } 486 | ``` 487 | 488 | 就是设置了一个客户端ID。 489 | 490 | ### 启动线程池 491 | 492 | 这一步其实就是将初始化流程中创建的AsyncMessageListenerInvoker交给线程池执行。 493 | 494 | AbstractJmsListeningContainer.resumePausedTasks: 495 | 496 | ```java 497 | protected void resumePausedTasks() { 498 | synchronized (this.lifecycleMonitor) { 499 | if (!this.pausedTasks.isEmpty()) { 500 | for (Iterator it = this.pausedTasks.iterator(); it.hasNext();) { 501 | Object task = it.next(); 502 | doRescheduleTask(task); 503 | it.remove(); 504 | } 505 | } 506 | } 507 | } 508 | ``` 509 | 510 | pausedTasks中其实就是我们之前创建的AsyncMessageListenerInvoker对象集合,为什么这里没有必要展开。 511 | 512 | DefaultMessageListenerContainer.doRescheduleTask: 513 | 514 | ```java 515 | @Override 516 | protected void doRescheduleTask(Object task) { 517 | this.taskExecutor.execute((Runnable) task); 518 | } 519 | ``` 520 | 521 | 从上面的分析可以看出,这里将导致concurrentConsumers个线程的创建,线程执行的逻辑显然是AsyncMessageListenerInvoker的run方法,具体在后面展开。 522 | 523 | ### 连接启动 524 | 525 | startSharedConnection方法其实就是简单的调用了javax.jms.Connection的start方法,没什么好说的。 526 | 527 | ## 运行流程 528 | 529 | 这里我们以sessionTransacted应答模式为例进行展开。 530 | 531 | DefaultMessageListenerContainer.AsyncMessageListenerInvoker的run方法最终调用了其invokeListener方法,这边是其处理的核心逻辑所在: 532 | 533 | ```java 534 | private boolean invokeListener() throws JMSException { 535 | initResourcesIfNecessary(); 536 | boolean messageReceived = receiveAndExecute(this, this.session, this.consumer); 537 | this.lastMessageSucceeded = true; 538 | return messageReceived; 539 | } 540 | ``` 541 | 542 | ### Session/Consumer初始化 543 | 544 | 这里主要是针对不同的缓存级别初始化Session、Consumer等组件。initResourcesIfNecessary: 545 | 546 | ```java 547 | private void initResourcesIfNecessary() throws JMSException { 548 | if (getCacheLevel() <= CACHE_CONNECTION) { 549 | updateRecoveryMarker(); 550 | } else { 551 | if (this.session == null && getCacheLevel() >= CACHE_SESSION) { 552 | updateRecoveryMarker(); 553 | this.session = createSession(getSharedConnection()); 554 | } 555 | if (this.consumer == null && getCacheLevel() >= CACHE_CONSUMER) { 556 | this.consumer = createListenerConsumer(this.session); 557 | synchronized (lifecycleMonitor) { 558 | registeredWithDestination++; 559 | } 560 | } 561 | } 562 | } 563 | ``` 564 | 565 | 很容易理解,底层的创建就是ActiveMQ实现。 566 | 567 | ### onMessage 568 | 569 | 核心逻辑为AbstractMessageListenerContainer.doExecuteListener: 570 | 571 | ```java 572 | protected void doExecuteListener(Session session, Message message) { 573 | try { 574 | invokeListener(session, message); 575 | } catch (JMSException ex) { 576 | rollbackOnExceptionIfNecessary(session, ex); 577 | throw ex; 578 | } catch (RuntimeException ex) { 579 | rollbackOnExceptionIfNecessary(session, ex); 580 | throw ex; 581 | } catch (Error err) { 582 | rollbackOnExceptionIfNecessary(session, err); 583 | throw err; 584 | } 585 | commitIfNecessary(session, message); 586 | } 587 | ``` 588 | 589 | invokeListener便是调用我们设置的MessageListener,无需多说,我们看看rollbackOnExceptionIfNecessary干了什么: 590 | 591 | ```java 592 | protected void rollbackOnExceptionIfNecessary(Session session, Throwable ex) { 593 | if (session.getTransacted()) { 594 | JmsUtils.rollbackIfNecessary(session); 595 | } else if (isClientAcknowledge(session)) { 596 | session.recover(); 597 | } 598 | } 599 | ``` 600 | 601 | JmsUtils里面也很简单: 602 | 603 | ```java 604 | public static void rollbackIfNecessary(Session session) { 605 | session.rollback(); 606 | } 607 | ``` 608 | 609 | ### 总结 610 | 611 | 如果我们直接使用ActiveMQ的原生API,那么对于sessionTransacted类型的应答机制我们需要手动调用Session的commit或rollback方法,但是Spring的DefaultMessageListenerContainer帮我们做了这项任务。即: 612 | 613 | 如果我们的业务逻辑没有抛出异常,那么Spring将为我们自动commit,反之,自动rollback。 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | skywalker 8 | activemq-notes 9 | 1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 4.3.2.RELEASE 14 | 5.15.16 15 | 16 | 17 | 18 | 19 | 20 | org.springframework 21 | spring-context 22 | ${spring-version} 23 | 24 | 25 | org.springframework 26 | spring-web 27 | ${spring-version} 28 | 29 | 30 | org.springframework 31 | spring-jdbc 32 | ${spring-version} 33 | 34 | 35 | org.springframework 36 | spring-tx 37 | ${spring-version} 38 | 39 | 40 | org.springframework 41 | spring-jms 42 | ${spring-version} 43 | 44 | 45 | 46 | 47 | org.apache.activemq 48 | activemq-client 49 | ${activemq-version} 50 | 51 | 52 | org.apache.activemq 53 | activemq-pool 54 | ${activemq-version} 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-compiler-plugin 63 | 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/mq/Bootstrap.java: -------------------------------------------------------------------------------- 1 | package mq; 2 | 3 | import org.springframework.context.support.ClassPathXmlApplicationContext; 4 | 5 | /** 6 | * 启动测试. 7 | * 8 | * @author zhao.xudong 9 | */ 10 | public class Bootstrap { 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-activemq.xml"); 14 | SimpleProducer producer = context.getBean(SimpleProducer.class); 15 | producer.produce(); 16 | System.out.println(producer); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mq/SessionListener.java: -------------------------------------------------------------------------------- 1 | package mq; 2 | 3 | import org.apache.activemq.ActiveMQConnectionFactory; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.annotation.PostConstruct; 7 | import javax.jms.*; 8 | 9 | /** 10 | * {@link org.apache.activemq.ActiveMQSession#setMessageListener(MessageListener)}到底是什么效果? 11 | * 12 | * @author skywalker 13 | */ 14 | @Component 15 | public class SessionListener { 16 | 17 | @PostConstruct 18 | public void receive() { 19 | ConnectionFactory connectionFactory; // Connection :JMS 客户端到JMS 20 | Connection connection = null; // Session: 一个发送或接收消息的线程 21 | Session session; // Destination :消息的目的地;消息发送给谁. 22 | Destination destination; // MessageProducer:消息发送者 23 | // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar 24 | connectionFactory = new ActiveMQConnectionFactory("admin", 25 | "admin", "tcp://localhost:61616"); 26 | try { 27 | connection = connectionFactory.createConnection(); 28 | connection.start(); 29 | session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); 30 | 31 | session.setMessageListener(new MessageListener() { 32 | @Override 33 | public void onMessage(Message message) { 34 | 35 | } 36 | }); 37 | 38 | destination = session.createQueue("foo.bar"); 39 | MessageConsumer consumer = session.createConsumer(destination); 40 | Message message; 41 | while ((message = consumer.receive()) != null) { 42 | System.out.println("收到消息: " + message); 43 | } 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } finally { 47 | try { 48 | if (null != connection) 49 | connection.close(); 50 | } catch (Throwable ignore) { 51 | } 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mq/SimpleListener.java: -------------------------------------------------------------------------------- 1 | package mq; 2 | 3 | import org.apache.activemq.command.ActiveMQTextMessage; 4 | 5 | import javax.jms.JMSException; 6 | import javax.jms.Message; 7 | import javax.jms.MessageListener; 8 | 9 | /** 10 | * 测试用消费者. 11 | * 12 | * @author skywalker 13 | */ 14 | public class SimpleListener implements MessageListener { 15 | 16 | @Override 17 | public void onMessage(Message message) { 18 | try { 19 | String data = ((ActiveMQTextMessage) message).getText(); 20 | /*if (data.equals("消息: 2")) { 21 | System.out.println(Thread.currentThread().getId() + "抛异常,消息: " + data); 22 | throw new RuntimeException(); 23 | }*/ 24 | System.out.println(Thread.currentThread().getId() + "收到消息: " + data); 25 | } catch (JMSException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/mq/SimpleProducer.java: -------------------------------------------------------------------------------- 1 | package mq; 2 | 3 | import org.apache.activemq.command.ActiveMQTopic; 4 | import org.springframework.jms.core.JmsTemplate; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.Resource; 8 | import javax.jms.*; 9 | 10 | /** 11 | * 简单的生产者. 12 | * 13 | * @author zhao.xudong 14 | */ 15 | @Component 16 | public class SimpleProducer { 17 | 18 | @Resource 19 | private JmsTemplate jmsTemplate; 20 | @Resource(name = "testQueue") 21 | private Destination destination; 22 | @Resource(name = "testTopic") 23 | private ActiveMQTopic topic; 24 | @Resource(name = "virtualTopic") 25 | private ActiveMQTopic virtualTopic; 26 | int i = 0; 27 | 28 | public void produce() throws InterruptedException { 29 | Thread.sleep(3000); 30 | for (; i < 6; i++) { 31 | jmsTemplate.send(virtualTopic, session -> session.createTextMessage("消息: " + i)); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/mq/VirtualTopicListener.java: -------------------------------------------------------------------------------- 1 | package mq; 2 | 3 | import org.apache.activemq.command.ActiveMQTextMessage; 4 | 5 | import javax.jms.JMSException; 6 | import javax.jms.Message; 7 | import javax.jms.MessageListener; 8 | 9 | /** 10 | * 虚拟Topic消费者. 11 | * 12 | * @author skywalker 13 | */ 14 | public class VirtualTopicListener implements MessageListener { 15 | 16 | @Override 17 | public void onMessage(Message message) { 18 | try { 19 | String data = ((ActiveMQTextMessage) message).getText(); 20 | System.out.println(Thread.currentThread().getId() + "抛异常,消息: " + data); 21 | throw new RuntimeException(); 22 | } catch (JMSException e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/activemq.properties: -------------------------------------------------------------------------------- 1 | activemq.username=admin 2 | activemq.password=admin 3 | activemq.broker=tcp://localhost:61616 4 | activemq.queue=test_queue 5 | activemq.topic=test_topic 6 | activemq.virtualTopic=VirtualTopic.test_topic 7 | activemq.virtualTopic.consumer=Consumer.A.VirtualTopic.test_topic -------------------------------------------------------------------------------- /src/main/resources/spring-activemq.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | classpath:activemq.properties 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | --------------------------------------------------------------------------------