Architecture diagram
The architecture of activeMQ is shown in the figure, leaving aside the right part, we can see that there are three parts from top to bottom:
1. connector: connector
2. region: mainly divided into topic and queue, dealing with related content separately
3. message store: storage
DEMO
Let's start with IDERunner, a test class for broker, and look at the broker source code, which is shown below
1 // configuration management broker,connector 2 BrokerService brokerService = new BrokerService(); 3 // To configure---------------------------------------------------------- 4 // Set up broker Bound address address 5 String bindAddress = "tcp://0.0.0.0:61616?trace=" + TRANSPORT_TRACE + "&transport.wireFormat.maxFrameSize=104857600"; 6 // Add Connector 7 // 1,Will be based on bindAddress Of schema structure connector Connector, build TransportServer(If it is Http,Then use Jetty Of connector Implement) 8 brokerService.addConnector(bindAddress); 9 // broker Whether to persist messages 10 // 1,true Indicates open persistence 11 brokerService.setPersistent(true); 12 // Whether to use jmx 13 brokerService.setUseJmx(false); 14 // Allow alert demotion 15 brokerService.setAdvisorySupport(false); 16 // start-up----------------------------------------------------------- 17 // start-up brokerService 18 brokerService.start(); 19 // Waiting for End------------------------------------------------------- 20 // Main Thread await,Until brokerService Status is stop 21 brokerService.waitUntilStopped();
Example 8 lines of code with 21 lines of comments.
Source Parsing
addConnector
Line 2 builds the BrokerService object, and line 4 addConnector builds the connector, which creates the TransportConnector and adds it to the BrokerService.
1 public TransportConnector addConnector(URI bindAddress) throws Exception { 2 return addConnector(createTransportConnector(bindAddress)); 3 }
Enter the createTransportConnector method and build TransportServer via Communism before constructing TransportConnector
1 protected TransportConnector createTransportConnector(URI brokerURI) throws Exception { 2 TransportServer transport = TransportFactorySupport.bind(this, brokerURI); 3 return new TransportConnector(transport); 4 }
Enter the bind method again, select TransportFactory, and call its doBind method to complete the creation of TransportServer.For example, HttpTransportServer is created by HttpTransportFactory.
1 public static TransportServer bind(BrokerService brokerService, URI location) throws IOException { 2 TransportFactory tf = TransportFactory.findTransportFactory(location); // according to schema find factory 3 if( brokerService!=null && tf instanceof BrokerServiceAware) { 4 ((BrokerServiceAware)tf).setBrokerService(brokerService); 5 } 6 try { 7 if( brokerService!=null ) { 8 SslContext.setCurrentSslContext(brokerService.getSslContext()); 9 } 10 return tf.doBind(location); // structure Transportserver 11 } finally { 12 SslContext.setCurrentSslContext(null); 13 } 14 }
In general, the addConnector method is to create a TransportConnector that contains TransportServer for handling communications (such as Http communications implemented by HttpTransportServer).
start
Looking at line 18 again, brokerService calls the start method, which enters the method, the core of which is to start the broker
startBroker(startAsync)
1 private void startBroker(boolean async) throws Exception { 2 if (async) { 3 new Thread("Broker Starting Thread") { 4 @Override 5 public void run() { 6 // ... 7 doStartBroker(); 8 // ... 9 } 10 }.start(); 11 } else { 12 doStartBroker(); 13 } 14 }
Follow up on doStartBroker
1 // ... 2 broker = getBroker(); // Obtain broker Instance object 3 // ... 4 broker.start(); // start-up broker, region, destination 5 // ... 6 startAllConnectors(); // start-up connectors Connector 7 // ...
The second line of the getBroker method creates the Broker's implementation class RegionBroker, which contains region implementations such as QueueRegion and TopicRegion.
Line 4 start method starts broker and all region s
1 public void start() throws Exception { 2 started = true; // modify broker Status of 3 // start-up region,region All under destination Will also start 4 queueRegion.start(); // start-up queue region 5 topicRegion.start(); // start-up topic region 6 tempQueueRegion.start(); // start-up temp queue region 7 tempTopicRegion.start(); // start-up temp topic region 8 // ... 9 }
Next line 6, the startAllConnectors method starts all the TransportConnector s we created
1 // ... 2 for (Iterator<TransportConnector> iter = getTransportConnectors().iterator(); iter.hasNext();) { 3 TransportConnector connector = iter.next(); 4 al.add(startTransportConnector(connector)); // Start Connector 5 } 6 // ...
Enter the startTransportConnector method, which calls the Connector's start
1 public TransportConnector startTransportConnector(TransportConnector connector) throws Exception { 2 // ... 3 connector.start(); 4 return connector; 5 }
start method to follow up TransportConnector
1 public void start() throws Exception { 2 broker = brokerService.getBroker(); 3 // ... 4 // Add to transport Monitor 5 // from jetty Connector callback current listener Of onAccept Method 6 getServer().setAcceptListener(new TransportAcceptListener() { 7 @Override 8 public void onAccept(final Transport transport) { 9 // ... 10 try { 11 brokerService.getTaskRunnerFactory().execute(new Runnable() { 12 @Override 13 public void run() { 14 try { 15 if (!brokerService.isStopping()) { 16 // Receive Client transport Connect 17 Connection connection = createConnection(transport); // Establish transport Of connection 18 connection.start(); // Start the connection 19 } else { 20 // ... 21 } 22 } catch (Exception e) { 23 // ... 24 } 25 } 26 }); 27 } catch (Exception e) { 28 // ... 29 } 30 } 31 // ... 32 }); 33 getServer().setBrokerInfo(brokerInfo); 34 getServer().start(); // transport server 35 // ... 36 }
The core of the start method has two pieces
1. A Transport listener is set up, that is, if a client initiates a transport connection, the listener will be triggered.
2. Start TransportServer
Let's first see what TransportServer.doStart does (take HttpTransportServer for example)
1 protected void doStart() throws Exception { 2 createServer(); // Establish jetty Of server object 3 // ... 4 ServletHolder holder = new ServletHolder(); 5 holder.setServlet(new HttpTunnelServlet()); 6 contextHandler.addServlet(holder, "/"); // All Mapping Paths 7 // ... 8 server.start(); // start-up jetty Of server 9 // ... 10 }
The main purpose is to use Jetty's server for HTTP services, where an HttpTunnelServlet is created to process requests.When a request comes in from the HttpTunnelServlet, it is wrapped as a Transport, which triggers the TransportServerListener we just mentioned in the first step and creates a Connection into the createConnection method
1 protected Connection createConnection(Transport transport) throws IOException { 2 //... 3 TransportConnection answer = new TransportConnection(this, transport, broker, disableAsyncDispatch ? null 4 : taskRunnerFactory, brokerService.getTaskRunnerFactory()); 5 // ... 6 return answer; 7 }
Follow the TransportConnection construction method, which does two things
1. The service method handles command s, such as sending a Message that calls the broker's send, and the broker will find a corresponding region -> destination -> Message store -> addMessage to process
2. dispatchSync distributes messages
1 public TransportConnection(TransportConnector connector, final Transport transport, Broker broker, 2 TaskRunnerFactory taskRunnerFactory, TaskRunnerFactory stopTaskRunnerFactory) { 3 // ... 4 this.transport.setTransportListener(new DefaultTransportListener() { 5 @Override 6 public void onCommand(Object o) { 7 // ... 8 try { 9 // ... 10 Command command = (Command) o; 11 if (!brokerService.isStopping()) { 12 Response response = service(command); 13 if (response != null && !brokerService.isStopping()) { 14 // Received Message 15 dispatchSync(response); 16 } 17 } else { 18 // ... 19 } 20 } finally { 21 // ... 22 } 23 } 24 // ... 25 }); 26 // ... 27 }