JBoss Messaging has a fully pluggable and highly flexible transport layer and defines its own Service Provider Interface (SPI) to make plugging in a new transport provider relatively straightforward.
In this chapter we'll describe the concepts required for understanding JBoss Messaging transports and where and how they're configured.
One of the most important concepts in JBoss Messaging transports is the acceptor. Let's dive straight in and take a look at an acceptor defined in xml in the configuration file jbm-configuration.xml.
<acceptors> <acceptor name="netty"> <factory-class> org.jboss.messaging.integration.transports.netty.NettyAcceptorFactory </factory-class> <param key="jbm.remoting.netty.port" value="5446" type="Integer"/> </acceptor> </acceptors>
Acceptors are always defined inside an acceptors element. There can be one or more acceptors defined in the acceptors element. There's no upper limit to the number of acceptors per server.
Each acceptor defines a way in which connections can be made to the JBoss Messaging server.
In the above example we're defining an acceptor that uses Netty to listen for connections at port 5446.
The acceptor element contains a sub-element factory-class, this element defines the factory used to create acceptor instances. In this case we're using Netty to listen for connections so we use the Netty implementation of an AcceptorFactory to do this. Basically, the factory-class element determines which pluggable transport we're going to use to do the actual listening.
The acceptor element can also be configured with zero or more param sub-elements. Each param element defines a key-value pair. These key-value pairs are used to configure the specific transport, the set of valid key-value pairs depends on the specific transport be used and are passed straight through to the underlying transport.
Examples of key-value pairs for a particular transport would be, say, to configure the IP address to bind to, or the port to listen at.
Keys are always strings and values can be of type Long, Integer, String or Boolean.
Whereas acceptors are used on the server to define how we accept connections, connectors are used by a client to define how it connects to a server.
Let's look at a connector defined in our jbm-configuration.xml file:
<connectors> <connector name="netty"> <factory-class> org.jboss.messaging.integration.transports.netty.NettyConnectorFactory </factory-class> <param key="jbm.remoting.netty.port" value="5446" type="Integer"/> </connector> </connectors>
Connectors can be defined inside an connectors element. There can be one or more connectors defined in the connectors element. There's no upper limit to the number of connectors per server.
You make ask yourself, if connectors are used by the client to make connections then why are they defined on the server? There are a couple of reasons for this:
Sometimes the server acts as a client itself when it connects to another server, for example when one server is bridged to another, or when a server takes part in a cluster. In this cases the server needs to know how to connect to other servers. That's defined by connectors.
If you're using JMS and the server side JMS service to instantiate JMS ConnectionFactory instances and bind them in JNDI, then when creating the JBossConnectionFactory it needs to know what server that connection factory will create connections to.
That's defined by the connector-ref element in the jbm-jms.xmlfile on the server side. Let's take a look at a snipped from a jbm-jms.xml file that shows a JMS connection factory that references our netty connector defined in our jbm-configuration.xml file:
<connection-factory name="ConnectionFactory"> <connector-ref connector-name="netty"/> <entries> <entry name="ConnectionFactory"/> <entry name="XAConnectionFactory"/> </entries> </connection-factory>
How do we configure a core ClientSessionFactory with the information that it needs to connect with a server?
Connectors are also used indirectly when directly configuring a core ClientSessionFactory to directly talk to a server. Although in this case there's no need to define such a connector in the server side configuration, instead we just create the parameters and tell the ClientSessionFactory which connector factory to use.
Here's an example of creating a ClientSessionFactory which will connect directly to the acceptor we defined earlier in this chapter, it uses the standard Netty TCP transport and will try and connect on port 5446 to localhost (default):
Map<String, Object> connectionParams = new HashMap<String, Object>(); connectionParams.put(org.jboss.messaging.integration.transports.netty.PORT_PROP_NAME, 5446); TransportConfiguration transportConfiguration = new TransportConfiguration( "org.jboss.messaging.integration.transports.netty.NettyConnectorFactory", connectionParams); ClientSessionFactory sessionFactory = new ClientSessionFactory(transportConfiguration); ClientSession session = sessionFactory.createSession(...); etc
Similarly, if you're using JMS, you can configure the JMS connection factory directly on the client side without having to define a connector on the server side or define a connection factory in jbm-jms.xml:
Map<String, Object> connectionParams = new HashMap<String, Object>(); connectionParams.put(org.jboss.messaging.integration.transports.netty.PORT_PROP_NAME, 5446); TransportConfiguration transportConfiguration = new TransportConfiguration( "org.jboss.messaging.integration.transports.netty.NettyConnectorFactory", connectionParams); ConnectionFactory connectionFactory = new JBossConnectionFactory(transportConfiguration); Connection jmsConnection = connectionFactory.createConnection(); etc
Out of the box, JBoss Messaging currently uses Netty, a high performance low level network library.
Our Netty transport can be configured in several different ways; to use old (blocking) Java IO, or NIO (non-blocking), also to use straightforward TCP sockets, SSL, or to tunnel over HTTP or HTTPS, on top of that we also provide a servlet transport.
We believe this caters for the vast majority of transport requirements.
Netty TCP is a simple unencrypted TCP sockets based transport. Netty TCP can be configured to use old blocking Java IO or non blocking Java NIO. We recommend you use the default Java NIO for better scalability.
If you're running connections across an untrusted network please bear in mind this transport is unencrypted. You may want to look at the SSL or HTTPS configurations.
With the Netty TCP transport all connections are initiated from the client side. I.e. the server does not initiate any connections to the client. This works well with firewall policies that typically only allow connections to be initiated in one direction.
All the valid Netty transport keys are defined in the class org.jboss.messaging.integration.transports.netty.TransportConstants. The parameters can be used either with acceptors or connectors. The following parameters can be used to configure Netty for simple TCP:
jbm.remoting.netty.usenio. If this is true then Java non blocking NIO will be used. If set to false than old blocking Java IO will be used.
We highly recommend that you use non blocking Java NIO. Java NIO does not maintain a thread per connection so can scale to many more concurrent connections than with old blocking IO. We recommend the usage of Java 6 for NIO and the best scalability. The default value for this property is true.
jbm.remoting.netty.host. This specified the host name or ip address to connect to (when configuring a connector) or to listen on (when configuring an acceptor). The default value for this property is localhost. Note that if you want your servers accessible from other nodes, don't bind to localhost!
jbm.remoting.netty.port. This specified the port to connect to (when configuring a connector) or to listen on (when configuring an acceptor). The default value for this property is 5445.
jbm.remoting.netty.tcpnodelay. If this is true then Nagle's algorithm will be enabled. The default value for this property is true.
jbm.remoting.netty.tcpsendbuffersize. This parameter determines the size of the TCP send buffer in bytes. The default value for this property is 32768 bytes (32KiB).
TCP buffer sizes should be tuned according to the bandwidth and latency of your network. Here's a good link that explains the theory behind this.
In summary TCP send/receive buffer sizes should be calculated as:
buffer_size = bandwidth * RTT.
Where bandwidth is in bytes per second and network round trip time (RTT) is in seconds. RTT can be easily measured using the ping utility.
For fast networks you may want to increase the buffer sizes from the defaults.
jbm.remoting.netty.tcpreceivebuffersize. This parameter determines the size of the TCP receive buffer in bytes. The default value for this property is 32768 bytes (32KiB).
Netty SSL is similar to the Netty TCP transport but it provides additional security by encrypting TCP connections using the Secure Sockets Layer SSL
Please see the examples for a full working example of using Netty SSL.
Netty SSL uses all the same properties as Netty TCP but adds the following additional properties:
jbm.remoting.netty.sslenabled. Must be true to enable SSL.
jbm.remoting.netty.keystorepath. This is the path to the SSL key store on the client which holds the client certificates.
jbm.remoting.netty.keystorepassword. This is the password for the client certificate key store on the client.
jbm.remoting.netty.truststorepath. This is the path to the trusted client certificate store on the server.
jbm.remoting.netty.truststorepassword. This is the password to the trusted client certificate store on the server.
Netty HTTP tunnels packets over the HTTP protocol. It can be useful in scenarios where firewalls only allow HTTP traffice to pass.
Please see the examples for a full working example of using Netty HTTP.
Netty HTTP uses the same properties as Netty TCP but adds the following additional properties:
jbm.remoting.netty.httpenabled. Must be true to enable HTTP.
jbm.remoting.netty.httpclientidletime. How long a client can be idle before sending an empty http request to keep the connection alive
jbm.remoting.netty.httpclientidlescanperiod. How often, in milliseconds, to scan for idle clients
jbm.remoting.netty.httpresponsetime. How long the server can wait before sending an empty http response to keep the connection alive
jbm.remoting.netty.httpserverscanperiod. How often, in milliseconds, to scan for clients needing responses
jbm.remoting.netty.httprequiressessionid. If true the client will wait after the first call to receive a session id. Used the http connector is connecting to servlet acceptor (not recommended)
We also provide a Netty servlet transport for use with JBoss Messaging. The servlet transport allows JBoss Messaging traffic to be tunneled over HTTP to a servlet running in a servlet engine which then redirects it to an in-VM JBoss Messaging server.
The servlet transport differs from the Netty HTTP transport in that, with the HTTP transport JBoss Messaging effectively acts a web server listening for HTTP traffic on, e.g. port 80 or 8080, whereas with the servlet transport JBM traffic is proxied through a servlet engine which may already be serving web site or other applications. This allows JBoss Messaging to be used where corporate policies may only allow a single web server listening on an HTTP port, and this needs to serve all applications including messaging.
Please see the examples for a full working example of the servlet transport being used.
To configure a servlet engine to work the Netty Servlet transport we need to do the following things:
Deploy the servlet. Here's an example web.xml describing a web application that uses the servlet:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <context-param> <param-name>serverChannelName</param-name> <param-value>org.jboss.jbm</param-value> </context-param> <context-param> <param-name>streaming</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>reconnectTimeout</param-name> <param-value>3000</param-value> </context-param> <listener> <listener-class> org.jboss.netty.channel.socket.http.HttpTunnelingSessionListener </listener-class> </listener> <listener> <listener-class> org.jboss.netty.channel.socket.http.HttpTunnelingContextListener </listener-class> </listener> <servlet> <servlet-name>NettyServlet</servlet-name> <servlet-class> org.jboss.netty.channel.socket.http.HttpTunnelingServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>NettyServlet</servlet-name> <url-pattern>/JBMServlet</url-pattern> </servlet-mapping> </web-app>
We also need to add a special Netty invm acceptor on the server side configuration.
Here's a snippet from the jbm-configuration.xml file showing that acceptor being defined:
<acceptors> <acceptor name="netty-invm"> <factory-class> org.jboss.messaging.integration.transports.netty.NettyAcceptorFactory </factory-class> <param key="jbm.remoting.netty.useinvm" value="true" type="Boolean"/> <param key="jbm.remoting.netty.host" value="org.jboss.jbm" type="String"/> </acceptor> </acceptors>
Lastly we need a connector for the client, this again will be configured in the jbm-configuration.xml file as such:
<connectors> <connector name="netty-servlet"> <factory-class> org.jboss.messaging.integration.transports.netty.NettyConnectorFactory </factory-class> <param key="jbm.remoting.netty.host" value="localhost" type="String"/> <param key="jbm.remoting.netty.port" value="8080" type="Integer"/> <param key="jbm.remoting.netty.useservlet" value="true" type="Boolean"/> <param key="jbm.remoting.netty.servletpath" value="/messaging/JBMServlet" type="String"/> </connector> </connectors>
Heres a list of the context params and what they are used for
serverChannelName - This is the name of the netty acceptor that the servlet will forward its packets too. You can see it matches the name of the jbm.remoting.netty.host param.
streaming - set to true means that all packets will be streamed as one continuous request rather than one request per packet.
reconnectTimeout - How long in milliseconds the servlet will await for the client to reconnect after a connection being closed by the web server
The servlet pattern configured in the web.xml is the path of the URL that is used. The connector param jbm.remoting.netty.servletpath on the connector config must match this using the application context of the web app if there is one.