Chapter 8. Configuring the Messaging Transports This section describes the concepts critical to understanding JBoss EAP messaging transports, specifically connectors and acceptors. Acceptors are used on the server to define how it can accept connections, while connectors are used by the client to define how it connects to a server. Each concept is discussed in turn and then a practical example shows how clients can make connections to a JBoss EAP messaging server, using JNDI or the Core API. 8.1. Acceptor and Connector Types There are three main types of acceptor and connector defined in the configuration of JBoss EAP. in-vm : In-vm is short for Intra Virtual Machine. Use this connector type when both the client and the server are running in the same JVM, for example, Message Driven Beans (MDBs) running in the same instance of JBoss EAP. http : Used when client and server are running in different JVMs. Uses the undertow subsystem's default port of 8080 and is thus able to multiplex messaging communications over HTTP. Red Hat recommends using the http connector when the client and server are running in different JVMs due to considerations such as port management, especially in a cloud environment. remote : Remote transports are Netty-based components used for native TCP communication when the client and server are running in different JVMs. An alternative to http when it cannot be used. A client must use a connector that is compatible with one of the server's acceptors. For example, only an in-vm-connector can connect to an in-vm-acceptor , and only a http-connector can connect to an http-acceptor , and so on. You can have the management CLI list the attributes for a given acceptor or connector type using the read-children-attributes operation. For example, to see the attributes of all the http-connectors for the default messaging server you would enter: The attributes of all the http-acceptors are read using a similar command: The other acceptor and connector types follow the same syntax. Just provide child-type with the acceptor or connector type, for example, remote-connector or in-vm-acceptor . 8.2. Acceptors An acceptor defines which types of connection are accepted by the JBoss EAP integrated messaging server. You can define any number of acceptors per server. The sample configuration below is modified from the default full-ha configuration profile and provides an example of each acceptor type. <subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> ... <http-acceptor name="http-acceptor" http-listener="default"/> <remote-acceptor name="legacy-messaging-acceptor" socket-binding="legacy-messaging"/> <in-vm-acceptor name="in-vm" server-id="0"/> ... </server> </subsystem> In the above configuration, the http-acceptor is using Undertow's default http-listener which listens on JBoss EAP's default http port, 8080. The http-listener is defined in the undertow subsystem: <subsystem xmlns="urn:jboss:domain:undertow:10.0"> ... <server name="default-server"> <http-listener name="default" redirect-socket="https" socket-binding="http"/> ... </server> ... </subsystem> Also note how the remote-acceptor above uses the socket-binding named legacy-messaging , which is defined later in the configuration as part of the server's default socket-binding-group . <server xmlns="urn:jboss:domain:8.0"> ... <socket-binding-group name="standard-sockets" default-interface="public" port-offset="USD{jboss.socket.binding.port-offset:0}"> ... <socket-binding name="legacy-messaging" port="5445"/> ... </socket-binding-group> </server> In this example, the legacy-messaging socket-binding binds JBoss EAP to port 5445 , and the remote-acceptor above claims the port on behalf of the messaging-activemq subsystem for use by legacy clients. Lastly, the in-vm-acceptor uses a unique value for the server-id attribute so that this server instance can be distinguished from other servers that might be running in the same JVM. 8.3. Connectors A connector defines how to connect to an integrated JBoss EAP messaging server, and is used by a client to make connections. You might wonder why connectors are defined on the server when they are actually used by the client. The reasons for this include: In some instances, the server might act as a client when it connects to another server. For example, one server might act as a bridge to another, or it might want to participate in a cluster. In such cases, the server needs to know how to connect to other servers, and that is defined by connectors. A server can provide connectors using a ConnectionFactory which is looked up by clients using JNDI, so creating connection to the server is simpler. You can define any number of connectors per server. The sample configuration below is based on the full-ha configuration profile and includes connectors of each type. <subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> ... <http-connector name="http-connector" endpoint="http-acceptor" socket-binding="http" server-name="messaging-server-1"/> <remote-connector name="legacy-remoting-connector" socket-binding="legacy-remoting"/> <in-vm-connector name="in-vm" server-id="0"/> ... </server> </subsystem> Like the http-acceptor from the full-ha profile, the http-connector uses the default http-listener defined by the undertow subsystem. The endpoint attribute declares which http-acceptor to connect to. In this case, the connector will connect to the default http-acceptor . JBoss EAP 7.1 introduced a new server-name attribute for the http-connector . This new attribute is optional, but it is required to be able to connect to the correct http-acceptor on a remote server that is running more than one ActiveMQ Artemis instance. If this attribute is not defined, the value is resolved at runtime to be the name of the parent ActiveMQ Artemis server in which the connector is defined. Also, note that the remote-connector references the same socket-binding as its remote-acceptor counterpart. Lastly, the in-vm-connector uses the same value for server-id as the in-vm-acceptor since they both run inside the same server instance. Note If the bind address for the public interface is set to 0.0.0.0 , you will see the following warning in the log when you start the JBoss EAP server: This is because a remote connector cannot connect to a server using the 0.0.0.0 address and the messaging-activemq subsystem tries to replace it with the server's host name. The administrator should configure the remote connector to use a different interface address for the socket binding. 8.4. Configuring Acceptors and Connectors There are a number of configuration options for connectors and acceptors. They appear in the configuration as child <param> elements. Each <param> element includes a name and value attribute pair that is understood and used by the default Netty-based factory class responsible for instantiating a connector or acceptor. In the management CLI, each remote connector or acceptor element includes an internal map of the parameter name and value pairs. For example, to add a new param to a remote-connector named myRemote use the following command: Retrieve parameter values using a similar syntax. You can also include parameters when you create an acceptor or connector, as in the example below. Table 8.1. Transport Configuration Properties Property Description batch-delay Before writing packets to the transport, the messaging server can be configured to batch up writes for a maximum of batch-delay in milliseconds. This increases the overall throughput for very small messages by increasing average latency for message transfer. The default is 0. direct-deliver When a message arrives on the server and is delivered to waiting consumers, by default, the delivery is done on the same thread on which the message arrived. This gives good latency in environments with relatively small messages and a small number of consumers but reduces the throughput and latency. For highest throughput you can set this property as false . The default is true . http-upgrade-enabled Used by an http-connector to specify that it is using HTTP upgrade and therefore is multiplexing messaging traffic over HTTP. This property is set automatically by JBoss EAP to true when the http-connector is created and does not require an administrator. http-upgrade-endpoint Specifies the http-acceptor on the server-side to which the http-connector will connect. The connector will be multiplexed over HTTP and needs this info to find the relevant http-acceptor after the HTTP upgrade. This property is set automatically by JBoss EAP when the http-connector is created and does not require an administrator. local-address For a http or a remote connector, this is used to specify the local address which the client will use when connecting to the remote address. If a local address is not specified then the connector will use any available local address. local-port For a http or a remote connector, this is used to specify which local port the client will use when connecting to the remote address. If the local-port default is used (0) then the connector will let the system pick up an ephemeral port. Valid port values are 0 to 65535 . nio-remoting-threads If configured to use NIO, the messaging will by default use a number of threads equal to three times the number of cores (or hyper-threads) as reported by Runtime.getRuntime().availableProcessors() for processing incoming packets. To override this value, you can set a custom value for the number of threads. The default is -1 . tcp-no-delay If this is true then Nagle's algorithm will be enabled. This algorithm helps improve the efficiency of TCP/IP networks by reducing the number of packets sent over a network. The default is true . tcp-send-buffer-size This parameter determines the size of the TCP send buffer in bytes. The default is 32768 . tcp-receive-buffer-size This parameter determines the size of the TCP receive buffer in bytes. The default is 32768 . use-nio-global-worker-pool This parameter will ensure all Jakarta Messaging connections share a single pool of Java threads, rather than each connection having its own pool. This serves to avoid exhausting the maximum number of processes on the operating system. The default is true . 8.5. Connecting to a Server If you want to connect a client to a server, you have to have a proper connector. There are two ways to do that. You could use a ConnectionFactory which is configured on the server and can be obtained via JNDI lookup. Alternatively, you could use the ActiveMQ Artemis core API and configure the whole ConnectionFactory on the client side. 8.5.1. Jakarta Messaging Connection Factories Clients can use JNDI to look up ConnectionFactory objects which provide connections to the server. Connection Factories can expose each of the three types of connector: A connection-factory referencing a remote-connector can be used by a remote client to send messages to or receive messages from the server (assuming the connection-factory has an appropriately exported entry). A remote-connector is associated with a socket-binding that tells the client using the connection-factory where to connect. A connection-factory referencing an in-vm-connector is suitable to be used by a local client to either send messages to or receive messages from a local server. An in-vm-connector is associated with a server-id which tells the client using the connection-factory where to connect, since multiple messaging servers can run in a single JVM. A connection-factory referencing a http-connector is suitable to be used by a remote client to send messages to or receive messages from the server by connecting to its HTTP port before upgrading to the messaging protocol. A http-connector is associated with the socket-binding that represents the HTTP socket, which by default is named http . Since Jakarta Messaging 2.0, a default Jakarta Messaging connection factory is accessible to Jakarta EE applications under the JNDI name java:comp/DefaultJMSConnectionFactory . The messaging-activemq subsystem defines a pooled-connection-factory that is used to provide this default connection factory. Below are the default connectors and connection factories that are included in the full configuration profile for JBoss EAP: <subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> [...] <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor" /> <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput"> <param name="batch-delay" value="50"/> </http-connector> <in-vm-connector name="in-vm" server-id="0"/> [...] <connection-factory name="InVmConnectionFactory" connectors="in-vm" entries="java:/ConnectionFactory" /> <pooled-connection-factory name="activemq-ra" transaction="xa" connectors="in-vm" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory"/> [...] </server> </subsystem> The entries attribute of a factory specifies the JNDI names under which the factory will be exposed. Only JNDI names bound in the java:jboss/exported namespace are available to remote clients. If a connection-factory has an entry bound in the java:jboss/exported namespace a remote client would look-up the connection-factory using the text after java:jboss/exported . For example, the RemoteConnectionFactory is bound by default to java:jboss/exported/jms/RemoteConnectionFactory which means a remote client would look-up this connection-factory using jms/RemoteConnectionFactory . A pooled-connection-factory should not have any entry bound in the java:jboss/exported namespace because a pooled-connection-factory is not suitable for remote clients. 8.5.2. Connecting to the Server Using JNDI If the client resides within the same JVM as the server, it can use the in-vm connector provided by the InVmConnectionFactory . Here is how the InvmConnectionFactory is typically configured, as found for example in standalone-full.xml . <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm" /> Note the value of the entries attribute. Clients using the InVmConnectionFactory should drop the leading java:/ during lookup, as in the following example: InitialContext ctx = new InitialContext(); ConnectionFactory cf = (ConnectionFactory)ctx.lookup("ConnectionFactory"); Connection connection = cf.createConnection(); Remote clients use the RemoteConnectionFactory , which is usually configured as below: <connection-factory name="RemoteConnectionFactory" scheduled-thread-pool-max-size="10" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/> Remote clients should ignore the leading java:jboss/exported/ of the value for entries , following the example of the code snippet below: final Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory"); env.put(Context.PROVIDER_URL, "http-remoting://remotehost:8080"); InitialContext remotingCtx = new InitialContext(env); ConnectionFactory cf = (ConnectionFactory) remotingCtx.lookup("jms/RemoteConnectionFactory"); Note the value for the PROVIDER_URL property and how the client is using the JBoss EAP http-remoting protocol. Note also how the client is using the org.wildfly.naming.client.WildFlyInitialContextFactory , which implies the client has this class and its encompassing client JAR somewhere in the classpath. For maven projects, this can be achieved by including the following dependency: <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId>wildfly-jms-client-bom</artifactId> <type>pom</type> </dependency> </dependencies> 8.5.3. Connecting to the Server Using the Core API You can use the Core API to make client connections without needing a JNDI lookup. Clients using the Core API require a client JAR in their classpath, just as JNDI-based clients. ServerLocator Clients use ServerLocator instances to create ClientSessionFactory instances. As their name implies, ServerLocator instances are used to locate servers and create connections to them. In Jakarta Messaging terms think of a ServerLocator in the same way you would a Jakarta Messaging Connection Factory. ServerLocator instances are created using the ActiveMQClient factory class. ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(InVMConnectorFactory.class.getName())); ClientSessionFactory Clients use a ClientSessionFactory to create ClientSession instances, which are basically connections to a server. In Jakarta Messaging terms think of them as Jakarta Messaging connections. ClientSessionFactory instances are created using the ServerLocator class. ClientSessionFactory factory = locator.createClientSessionFactory(); ClientSession A client uses a ClientSession for consuming and producing messages and for grouping them in transactions. ClientSession instances can support both transactional and non transactional semantics and also provide an XAResource interface so messaging operations can be performed as part of the Jakarta Transactions operation. ClientSession instances group ClientConsumers and ClientProducers . ClientSession session = factory.createSession(); The simple example below highlights some of what was just discussed: ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA( new TransportConfiguration( InVMConnectorFactory.class.getName())); // In this simple example, we just use one session for both // producing and consuming ClientSessionFactory factory = locator.createClientSessionFactory(); ClientSession session = factory.createSession(); // A producer is associated with an address ... ClientProducer producer = session.createProducer("example"); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("Hello"); // We need a queue attached to the address ... session.createQueue("example", "example", true); // And a consumer attached to the queue ... ClientConsumer consumer = session.createConsumer("example"); // Once we have a queue, we can send the message ... producer.send(message); // We need to start the session before we can -receive- messages ... session.start(); ClientMessage msgReceived = consumer.receive(); System.out.println("message = " + msgReceived.getBodyBuffer().readString()); session.close(); 8.6. Messaging through a load balancer When using JBoss EAP as a load balancer, clients can call messaging servers behind either a static Undertow HTTP load balancer, or behind a mod_cluster load balancer. Configurations to support messaging clients calling messaging servers through a static load balancer must meet the following requirements: When using JBoss EAP as a load balancer, you must configure the load balancer using HTTP or HTTPS. AJP is not supported for messaging load balancers. For details about configuring Undertow as a static load balancer, see Configure Undertow as a Static Load Balancer in the JBoss EAP Configuration Guide . If JNDI lookups occur on the messaging servers behind the load balancer, you must configure the back-end messaging workers . Clients connecting to the load balancer must reuse the initial connections to the load balancer to ensure they communicate with the same server. Clients connecting to a load balancer must not use the cluster topology to connect to the load balancer. Using the cluster topology might result in messages being sent to a different server, which might result in disruptions to transaction processing. For details about configuring Undertow as a load balancer using mod_cluster, Configure Undertow as a Load Balancer Using mod_cluster in the JBoss EAP Configuration Guide . Configuration of messaging clients to communicate through a load balancer Clients that connect to a load balancer must be configured to re-use the initial connection rather than using the cluster topology to connect to the load balancer. Re-using the initial connection ensures that the client connects to the same server. Using the cluster topology might result in messages being directed to a different server, which might result in disruptions to transaction processing. A connection factory or pooled connection factory that is used to connect to a load balancer must be configured with the attribute use-topology-for-load-balancing set to false. The following example illustrates how to define this configuration in the CLI. Configuring back-end workers You must configure back-end messaging workers only if you plan to do JNDI lookups behind the load balancer. Create a new outbound socket binding that points to the load-balancing server. Create an HTTP connector that references the load-balancing server socket binding. Add the HTTP connector to the connection factory used by the client. Make sure you configure the clients to re-use the initial connection:

Commands:
/subsystem=messaging-activemq/server=default:read-children-resources(child-type=http-connector,include-runtime=true)
/subsystem=messaging-activemq/server=default:read-children-resources(child-type=http-acceptor,include-runtime=true)
<subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> <http-acceptor name="http-acceptor" http-listener="default"/> <remote-acceptor name="legacy-messaging-acceptor" socket-binding="legacy-messaging"/> <in-vm-acceptor name="in-vm" server-id="0"/> </server> </subsystem>
<subsystem xmlns="urn:jboss:domain:undertow:10.0"> <server name="default-server"> <http-listener name="default" redirect-socket="https" socket-binding="http"/> </server> </subsystem>
<server xmlns="urn:jboss:domain:8.0"> <socket-binding-group name="standard-sockets" default-interface="public" port-offset="USD{jboss.socket.binding.port-offset:0}"> <socket-binding name="legacy-messaging" port="5445"/> </socket-binding-group> </server>
<subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> <http-connector name="http-connector" endpoint="http-acceptor" socket-binding="http" server-name="messaging-server-1"/> <remote-connector name="legacy-remoting-connector" socket-binding="legacy-remoting"/> <in-vm-connector name="in-vm" server-id="0"/> </server> </subsystem>
AMQ121005: Invalid "host" value "0.0.0.0" detected for "connector" connector. Switching to <HOST_NAME>. If this new address is incorrect please manually configure the connector to use the proper one.
/subsystem=messaging-activemq/server=default/remote-connector=myRemote:map-put(name=params,key=foo,value=bar)
/subsystem=messaging-activemq/server=default/remote-connector=myRemote:map-get(name=params,key=foo) { "outcome" => "success", "result" => "bar" }
/subsystem=messaging-activemq/server=default/remote-connector=myRemote:add(socket-binding=mysocket,params={foo=bar,foo2=bar2})
<subsystem xmlns="urn:jboss:domain:messaging-activemq:4.0"> <server name="default"> [...] <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor" /> <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput"> <param name="batch-delay" value="50"/> </http-connector> <in-vm-connector name="in-vm" server-id="0"/> [...] <connection-factory name="InVmConnectionFactory" connectors="in-vm" entries="java:/ConnectionFactory" /> <pooled-connection-factory name="activemq-ra" transaction="xa" connectors="in-vm" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory"/> [...] </server> </subsystem>
<connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm" />
InitialContext ctx = new InitialContext(); ConnectionFactory cf = (ConnectionFactory)ctx.lookup("ConnectionFactory"); Connection connection = cf.createConnection();
<connection-factory name="RemoteConnectionFactory" scheduled-thread-pool-max-size="10" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
final Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory"); env.put(Context.PROVIDER_URL, "http-remoting://remotehost:8080"); InitialContext remotingCtx = new InitialContext(env); ConnectionFactory cf = (ConnectionFactory) remotingCtx.lookup("jms/RemoteConnectionFactory");
<dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId>wildfly-jms-client-bom</artifactId> <type>pom</type> </dependency> </dependencies>
ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(InVMConnectorFactory.class.getName()));
ClientSessionFactory factory = locator.createClientSessionFactory();
ClientSession session = factory.createSession();
ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA( new TransportConfiguration( InVMConnectorFactory.class.getName())); // In this simple example, we just use one session for both // producing and consuming ClientSessionFactory factory = locator.createClientSessionFactory(); ClientSession session = factory.createSession(); // A producer is associated with an address ClientProducer producer = session.createProducer("example"); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("Hello"); // We need a queue attached to the address session.createQueue("example", "example", true); // And a consumer attached to the queue ClientConsumer consumer = session.createConsumer("example"); // Once we have a queue, we can send the message producer.send(message); // We need to start the session before we can -receive- messages session.start(); ClientMessage msgReceived = consumer.receive(); System.out.println("message = " + msgReceived.getBodyBuffer().readString()); session.close();
/subsystem=messaging-activemq/pooled-connection-factory=remote-artemis:write-attribute(name=use-topology-for-load-balancing, value=false)
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=balancer-binding:add(host=load_balance.example.com,port=8080)
/subsystem=messaging-activemq/server=default/http-connector=balancer-connector:add(socket-binding=balancer-binding, endpoint=http-acceptor)
/subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory:write-attribute(name=connectors,value=[balancer-connector])
/subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory:write-attribute(name=use-topology-for-load-balancing,value=false)