OnixS Java CME iLink 3 Binary Order Entry Handler is a Java class library that encapsulates the CME iLink 3 order entry protocol.
The Handler provides the following services:
The following image is a high-level overview of the handler architecture:
All handler classes are located in the biz.onixs.cme.ilink3.handler package.
The typical way of using the handler is as follows:
Check also following chapters:
A iLink 3 session is defined as a bi-directional stream of ordered messages between two parties within a continuous sequence number series. It is represented by the biz.onixs.cme.ilink3.handler.Session class.
Each iLink 3 session is identified by a Universally Unique ID (UUID).
Constructing Session
To create a session the appropriate constructor must be used:
Example
29 30 31 32 33 34 35 | final int MarketSegmentId = 54 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); |
Each iLink 3 session is represented with a unique 64-bit identifier (UUID). CME Group recommends using the system timestamp, which represents the number of microseconds since epoch (Jan 1, 1970) as the timestamp. Business messages received from the exchange are recoverable for the sequence number + UUID combination.
The Session generates the UUID automatically, when it is created or when biz.onixs.cme.ilink3.handler.Session.reset(boolean) method is called.
Additionally, one can pass a value manually to the biz.onixs.cme.ilink3.handler.Session.Session(long, SessionSettings, int, boolean, SessionStorageType) constructor.
More details can be found in the Universally Unique Identifier (UUID) article.
Re-Setting UUID
To reset the UUID intra-session on the same trading day, use the reset(false) method.
To reset the UUID before the first connection of the week (so-called “Weekly Reset”), use the reset(true) method.
Note: The biz.onixs.cme.ilink3.handler.Session.reset(boolean) method can be called only when the session is disconnected.
To establish an iLink 3 connection, the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method must be used.
To handle network errors during the connection establishment, one needs to catch LogonException exceptions from the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method and try to connect again.
To terminate the session, the biz.onixs.cme.ilink3.handler.Session.terminate() method must be used.
Reconnection Facility
When an session is in the active state and there are some network issues the handler will try to restore the connection in accordance with biz.onixs.cme.ilink3.handler.session.SessionSettings.setReconnectAttempts(int) and biz.onixs.cme.ilink3.handler.session.SessionSettings.setReconnectInterval(long) settings.
Note: Reconnection facility works for already established connections. In other cases, you need to manually handle
biz.onixs.cme.ilink3.handler.exceptions.LogonException exceptions of biz.onixs.cme.ilink3.handler.Session.logon(String, int) method and try to connect again.
Example
30 31 32 33 34 35 36 37 38 39 40 41 42 43 | final int MarketSegmentId = 54 ; final String Host = "Localhost" ; final int Port = 64124 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); // Sends the Negotiation/Establishment messages and waits for the Negotiation/Establishment acknowledgments. session.logon(Host, Port); // Message exchanges and processing logic goes here. // Sends the Terminate message and waits for the confirming Terminate. session.terminate(); |
Sending Messages to a Counterparty
To send a message to a counterparty, the biz.onixs.cme.ilink3.handler.Session.send(IMessage) method must be used.
The session should be fully established before sending any messages. This method is asynchronous, so the handler tries to send an SBE message in the primary application thread and only after a failure, stores it in the sending queue for the next process in the sending thread.
The Handler sets all session-level fields automatically (e.g., SeqNum, SendingTimeEpoch).
Receiving Messages from CounterParty
To receive incoming application-level messages it is necessary to implement the biz.onixs.cme.ilink3.handler.session.InboundApplicationMessageListener interface and register the handler with the biz.onixs.cme.ilink3.handler.Session.setInboundApplicationMessageListener(InboundApplicationMessageListener) method.
To receive incoming session-level messages it is necessary to implement the biz.onixs.cme.ilink3.handler.session.InboundSessionMessageListener interface and register the handler with the biz.onixs.cme.ilink3.handler.Session.setInboundSessionMessageListener(InboundSessionMessageListener) method.
Example
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 67 68 69 70 | private static class MessageProcessor implements InboundApplicationMessageListener, InboundSessionMessageListener { @Override public void onInboundApplicationMessage( final Object sender, final InboundApplicationMessageArgs args) { System.out.println( "Incoming application-level message: " + args.getMsg()); } @Override public void onInboundSessionMessage( final Object sender, final InboundSessionMessageArgs args) { System.out.println( "Incoming session-level message: " + args.getMsg()); } } public static void main( final String[] args) { final Handler handler = Handler.init(); // START SNIPPET: SNIPPET final int MarketSegmentId = 54 ; final String Host = "Localhost" ; final int Port = 64124 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); final MessageProcessor processor = new MessageProcessor(); // Register message listeners session.setInboundApplicationMessageListener(processor); session.setInboundSessionMessageListener(processor); // Sends the Negotiation/Establishment messages and waits for the Negotiation/Establishment acknowledgments. session.logon(Host, Port); // Message exchanges and processing logic goes here. final IMessage message = createOrder(); session.send(message); // Sends the Terminate message and waits for the confirming Terminate. session.terminate(); |
Understanding Message Sequencing
Each iLink 3 Session establishes independent incoming and outgoing message sequence numbers series.
Sequence numbers are initialized at the start of an iLink 3 logical session, starting at 1 and incremented by each application-level message through the session.
A single iLink 3 logical session with the same Universally Unique Identifier (UUID) can exist across multiple sequential (not concurrent) physical connections. The client can connect and disconnect multiple times while maintaining the same iLink 3 logical session.
Once the biz.onixs.cme.ilink3.handler.Session.terminate() method is called, the physical connection is terminated, but the logical session does not end its lifetime. It is possible to continue the session later, using the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method again.
Manipulating Message Sequence Numbers
Monitoring of the sequence numbers enables parties to synchronize applications gracefully, while reconnecting during an iLink 3 session. The expected sequence number of the next incoming message can be get using the biz.onixs.cme.ilink3.handler.Session.getInSeqNum() method and set using the biz.onixs.cme.ilink3.handler.Session.setInSeqNum(long) method.
The sequence number of the next outgoing message can be get using the biz.onixs.cme.ilink3.handler.Session.getOutSeqNum() method and set using the biz.onixs.cme.ilink3.handler.Session.setOutSeqNum(long) method.
Resetting inbound and outbound message sequence numbers back to 1, for whatever reason, constitutes the beginning of a new iLink 3 session. The biz.onixs.cme.ilink3.handler.Session.reset(boolean) method must be used to back up the previous log files and reset the sequence numbers to 1. In such cases, a new UUID is generated.
Note: Both biz.onixs.cme.ilink3.handler.Session.setInSeqNum(long) and biz.onixs.cme.ilink3.handler.Session.setOutSeqNum(long) properties can be used to set expected sequence numbers manually before a FIX connection is established. However, it is NOT recommended to use these properties to reset the message sequence numbers to 1 for the session. Instead, method biz.onixs.cme.ilink3.handler.Session.reset(boolean) must be used.
The biz.onixs.sbe.IMessage interface represents a Simple Binary Encoding (SBE) message.
The details about SBE can be found at the iLink 3 - Simple Binary Encoding page.
If message buffer was reused to reduce GC load, there is a need to clear it before creating a new message. The following example shows how to calculate required message size:
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 | public static int calculateMessageSize( final MessageSchema messageSchema, final int templateId, final Map<Integer, Integer> groupSizes) { int size = 0 ; final int framingHeaderSize = messageSchema.getFrameHeaderSize(); final int binaryEncodingHeaderSize = messageSchema.getHeaderType().getFixedSize(); size += framingHeaderSize + binaryEncodingHeaderSize; final UnifiedMetaData msgDef = messageSchema.getMessages().get(templateId); size += msgDef.getBlockLength(); for (UnifiedMetaData groupDef : msgDef.getGroups()) { final int groupSize = groupSizes.getOrDefault(groupDef.getTag(), 0 ); size += groupDef.getResolvedDimensionType().getFixedSize() + groupDef.getBlockLength() * groupSize; } return size; } public static biz.onixs.sbe.IMessage createMessage( final MessageSchema messageSchema, final int templateId, final Map<Integer, Integer> groupSizes, final ByteDecoder decoder, final byte [] buffer, final int offset) throws Exception { final int size = calculateMessageSize(messageSchema, templateId, groupSizes); Arrays.fill(buffer, offset, offset + size, ( byte ) 0 ); return decoder.encodePreCreatedMessage(buffer, offset, size, templateId); } |
Each message or repeating group implements biz.onixs.sbe.IFieldSet interface. This interface has a couple of getXX/tryGetXX/setXX methods for primitive types, build-in composites and repeating groups. The XX suffix means type-denoting suffix that qualifies type of the data that is used by appropriate method. For instance: biz.onixs.sbe.IFieldSet.setShort(int, int), biz.onixs.sbe.IFieldSet.getLong(int), biz.onixs.sbe.IFieldSet.tryGetLong(int, AtomicLong), etc.
To access raw byte values there are biz.onixs.sbe.IFieldSet.getBytes(int), biz.onixs.sbe.IFieldSet.setBytes(int, byte:A), biz.onixs.sbe.IFieldSet.tryGetBytes(int); and biz.onixs.sbe.IFieldSet.getValuePtr(int), biz.onixs.sbe.IFieldSet.tryGetValuePtr(int, ValuePtr) methods.
IFieldSet.getXX(..) methods that return an object allocates memory and is not GC-friendly. IFieldSet.tryGetXX(..) methods accept a returning object as a parameter and do not allocate memory. Exceptions are IFieldSet.tryGetXX(..) methods which returns immutable objects like strings.
If field type can be implicitly converted to getter type, such getter will return a value or throw an exception otherwise. So biz.onixs.sbe.IFieldSet.getLong(int) can be called for fields with type byte, short, int and long; and so on.
If setter type can be implicitly converted to the field type, such setter will set a value of the field or throw an exception otherwise. So biz.onixs.sbe.IFieldSet.setBytes(int, byte:A) can be called for fields with type byte, short, int and long; and so on.
To reset values of optional fields, there is an biz.onixs.sbe.IFieldSet.reset(int) method. If the field is optional, this method will set it to the null value that is specified in the template. If the field is required, this method will set the field to the default (zero) value.
The biz.onixs.sbe.IGroup interface represents a repeating group. Each group object implements an iterator pattern to iterate via group entries and also implements biz.onixs.sbe.IFieldSet interface to access fields of the current repeating group entry. Repeating group object can be obtained via biz.onixs.sbe.IFieldSet.getGroup(int) method.
Example of reading data from the repeating group
35 36 37 38 39 40 41 | final IGroup group = message.getGroup(TAG_GROUP); group.rewind(); while (group.next()) { final int value1 = group.getInt(TAG1); final String value2 = group.getString(TAG2); final ScaledDecimal value3 = group.getDecimal(TAG3); } |
Example of writing data to the repeating group.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | final IGroup group = message.getGroup(TAG_GROUP); group.setLength( 2 ); // two repeating group entries group.rewind(); group.next(); // first entry group.setInt(TAG1, 10 ); group.setString(TAG2, "A1" ); group.setDecimal(TAG3, new ScaledDecimal( 10 , - 1 )); group.next(); // second entry group.setInt(TAG1, 20 ); group.setString(TAG2, "A2" ); group.setDecimal(TAG3, new ScaledDecimal( 20 , - 1 )); |
The biz.onixs.sbe.ByteDecoder class represents a SBE codec used to decode and encode SBE messages. This class is not thread safe, so each thread should use its own codec object.
ByteDecoder has methods biz.onixs.sbe.ByteDecoder.encode(byte:A, int, int, int) and biz.onixs.sbe.ByteDecoder.decode(byte:A, int, int) which creates a new message objects.
To reduce GC load there are methods biz.onixs.sbe.ByteDecoder.encodePreCreatedMessage(byte:A, int, int, int) and biz.onixs.sbe.ByteDecoder.decodePreCreatedMessage(byte:A, int, int) which initializes and returns already created message objects. If there is a need to pass message object to other thread for processing, an biz.onixs.sbe.IMessage.clone() method should be called before another call to biz.onixs.sbe.ByteDecoder.encodePreCreatedMessage(byte:A, int, int, int) or biz.onixs.sbe.ByteDecoder.decodePreCreatedMessage(byte:A, int, int).
An exception is used as a fundamental error-reporting mechanism. In the event of any error the biz.onixs.cme.ilink3.handler.exceptions.HandlerException exception is thrown.
The handler requires a valid license key file for its execution. If there is no license file available or the handler is not able to locate it, the biz.onixs.cme.ilink3.handler.exceptions.LicenseException exception will be thrown.
The pre-defined license file name is “OnixS.lic”. The license file is loaded during handler initialization as described in the Unified Resource Loading .
The default license file name can be changed using LicenseFile parameter in the Properties File or property of the Settings Class or using biz.onixs.cme.ilink3.handler.HandlerSettings.setLicenseFile(String) method.
The following table explains different options to keep the license file.
License File Location | License File Name | LicenseFile Property | Note |
---|---|---|---|
application classpath | OnixS.lic | no change required | |
current directory | *.lic | no change required | Any file with the “.lic” extension in the current directory is checked automatically. |
user home directory | *.lic | no change required | Any file with the “.lic” extension in the user home is checked automatically. |
application classpath | AltName.ext | AltName.ext | |
absolute path “/foo/bar” | AltName.ext | /foo/bar/AltName.ext | |
relative path “foo/bar” | AltName.ext | foo/bar/AltName.ext |
There are a number of parameters (settings) which control the behavior of the handler.
To specify a configuration for the handler, one can use the biz.onixs.cme.ilink3.handler.session.SessionSettings class interface to set it programmatically.
Example
Configure the Handler by the biz.onixs.cme.ilink3.handler.session.SessionSettings class:
29 30 31 32 33 34 35 | final int MarketSegmentId = 54 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); |
By default, any external resource is looked for at the following places and in the following order (if other is not set explicitly): 1. Classpath 1. Absolute file path 1. Current directory 1. User home directory
If it can’t be found at the 1st location then the 2nd one is checked, etc.
This section explains how logging services are organized in the handler. It depicts the structure of logs, as well as how logging can be customized.
By default, the handler stores all incoming and outgoing messages, session state data, as well as the handler primary log in separate files. It uses file system directory specified by the “Log.Directory” configuration parameter.
However, it is possible to store all session-related logging data in memory. For these purposes, storageType parameter of the biz.onixs.cme.ilink3.handler.Session.Session(long, SessionSettings, boolean, SessionStorageType) constructor is available. Once biz.onixs.cme.ilink3.handler.storage.SessionStorageType.MemoryBasedStorage value will be specified as value for the storageType parameter, session-related data will be logged into memory.
Also, you can use the asynchronous File-Based Session Storage which combines good performance with the File-Based Session Storage functionality, in this case, the biz.onixs.cme.ilink3.handler.storage.SessionStorageType.AsyncFileBased value should be specified as value for the storageType parameter.
For each iLink3 session with file-based session storage the following files are also created:
Where:
When the biz.onixs.cme.ilink3.handler.Session object with a file-based session storage is created anew after an handler restart, the iLink 3 Session state (the sequence numbers of last received and sent messages, the previously sent messages, etc) is restored from these files.
To start an iLink 3 session as a new one (so-called “clean start”), it is necessary to remove the session files from the previous runs.
Connecting parties must bi-laterally agree as to when sessions are to be started/stopped, and log files are backed up based upon the individual system and time zone requirements.
When the iLink 3 session is finished, the session files are not needed anymore. They can be backed up, and later a new iLink 3 session with the new UUID will start sequence numbers from 1. The usual CME practice is to back up the log files at the end of each business week (so-called “End Of Week procedure”) to start the sequence numbers from 1 at the beginning of the next week.
Note: To reset the sequence numbers to 1 and back up, the log files biz.onixs.cme.ilink3.handler.Session.reset(boolean) method must be used. This method can be called only when the session is disconnected.
By default, the handler logs all important aspects of its activity while working. The SLF4J (Simple Logging Facade for Java) is used by Handler internally. The SLF4J detects and uses the concrete logging implementation configured by the user. By default, the Logback logging implementation is recommended.
Please see Logging section for more details.
This section provides information about more advanced development techniques that are supported by Java CME iLink 3 Handler.
This section provides information about more advanced development techniques related to the iLink 3 session.
During the iLink 3 session lifetime, state changes occur. The biz.onixs.cme.ilink3.handler.Session class exposes the biz.onixs.cme.ilink3.handler.Session.getState() method to determine in which state it currently resides.
The biz.onixs.cme.ilink3.handler.SessionState class reflects all possible iLink 3 session states that can occur during its lifetime, as well as what a specific state means.
Tracking Session State Change
To be notified about the changes in the session state implement the biz.onixs.cme.ilink3.handler.session.StateChangeListener class and register it as event handler with the biz.onixs.cme.ilink3.handler.Session.addStateChangeListener(StateChangeListener) method.
Example
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | public class SessionStateChangeListener implements StateChangeListener { public static void main( final String[] args) { final Handler handler = Handler.init(); final int MarketSegmentId = 54 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); session.addStateChangeListener( new SessionStateChangeListener()); handler.shutdown(); } @Override public void onStateChange( final Object sender, final StateChangeArgs args) { System.out.println( "Prev session state: " + args.getPrevState()); System.out.println( "New session state: " + args.getNewState()); } } |
The events and their listeners of the biz.onixs.cme.ilink3.handler.Session class are listed below:
Avoid time-consuming tasks and session management method calls (logon, terminate, reset, etc.) in the session event listener calling thread. |
Example
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | public class SessionEventListener implements StateChangeListener, InboundSessionMessageListener, InboundApplicationMessageListener, OutboundSessionMessageListener, OutboundApplicationMessageListener, WarningListener, ErrorListener { public static void main( final String[] args) { final Handler handler = Handler.init(); final int MarketSegmentId = 54 ; final SessionSettings settings = new SessionSettings() .setSession( "SessionId" ) .setSecretKey( "secretKey" ) .setAccessKey( "accessKey" ) .setFirm( "firmId" ); final Session session = new Session(settings, MarketSegmentId); final SessionEventListener listener = new SessionEventListener(); session.addStateChangeListener(listener); session.setInboundSessionMessageListener(listener); session.setInboundApplicationMessageListener(listener); session.setOutboundSessionMessageListener(listener); session.setOutboundApplicationMessageListener(listener); session.setWarningListener(listener); session.setErrorListener(listener); handler.shutdown(); } @Override public void onStateChange( final Object sender, final StateChangeArgs args) { System.out.println( "New session state: " + args.getNewState()); } @Override public void onInboundApplicationMessage( final Object sender, final InboundApplicationMessageArgs args) { System.out.println( "Incoming application-level message: " + args.getMsg()); } @Override public void onInboundSessionMessage( final Object sender, final InboundSessionMessageArgs args) { System.out.println( "Incoming session-level message: " + args.getMsg()); } @Override public void onOutboundApplicationMessage( final Object sender, final OutboundApplicationMessageArgs args) { System.out.println( "Outgoing application-level message: " + args.getMsg()); } @Override public void onOutboundSessionMessage( final Object sender, final OutboundSessionMessageArgs args) { System.out.println( "Outbound session-level message: " + args.getMsg()); } @Override public void onWarning( final Object sender, final WarningArgs args) { System.out.println( "Session warning: " + args.getReason()); } @Override public void onError( final Object sender, final ErrorArgs args) { System.out.println( "Session error: " + args.getReason()); } } |
Session class exposes biz.onixs.cme.ilink3.handler.Session.reset(boolean) method to reset both incoming and outgoing message local sequence numbers to 1, back up the previous log files and generate a new UUID.
Note: The biz.onixs.cme.ilink3.handler.Session.reset(boolean) member can be called only when the session is disconnected.
Memory-based session storage is typically used to maintain a high-performance FIX session when persisting of session state and messages in the file system is not required.
To create such a session storageType parameter of session constructor should be set to biz.onixs.cme.ilink3.handler.storage.SessionStorageType.MemoryBasedStorage. Please see below:
37 38 | final Session session = new Session(settings, MarketSegmentId, true , SessionStorageType.MemoryBasedStorage); |
Asynchronous File-Based Session Storage combines the good-performance with the File-Based Session Storage functionality. This storage has the same abilities as File-Based Session Storage except for the asynchronous file operations. Each this storage creates the separated thread, which will be used to perform all operations with the file system.
To create such a session storageType parameter of session constructor should be set to biz.onixs.cme.ilink3.handler.storage.SessionStorageType.AsyncFileBased. Please see below:
37 | final Session session = new Session(settings, MarketSegmentId, true , SessionStorageType.AsyncFileBased); |
To simplify development and testing, the Handler’s SDK includes the Gateway Emulator. The Emulator simulates the CME Gateway. It can accept a connection, establish a FIXP session, and send/receive session-level/application-level messages.
Example
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | public class GatewayEmulator { public static void main( final String[] args) throws Throwable { // Initializes the Handler. Handler.init(); // Creates a TestUtility object, which provides tools for test setup and interactions. final TestUtility testUtility = new TestUtility(); // Creates an Acceptor object (which extends TestAcceptorTask), responsible for handling incoming connections. final Acceptor acceptor = new Acceptor(testUtility); // Launches the Acceptor in a separate thread, allowing it to run concurrently. testUtility.launchEmulatorTask(acceptor); // Creates SessionSettings. final SessionSettings settings = testUtility.getSessionSettings().setSession( "SN" ).setFirm( "007" ); // Creates a Session object using the configured settings. final Session initiator = new Session(settings, 1 ); // Establishes a connection between the 'initiator' Session and the Acceptor's emulator. testUtility.connectToEmulator(initiator, acceptor.getListenPort()); // Create an order and sends it to gateway emulator. final IMessage order = testUtility.createOrder( 1 ); initiator.send(order); // Initiates a termination sequence from the 'initiator' Session. initiator.terminate(); // Shuts down the Handler, releasing resources. Handler.getInstance().shutdown(); // Waits for the Acceptor task and other potential test tasks to complete. testUtility.waitTestTasks(); // Asserts the results of all completed test tasks. testUtility.checkTestTasks(); } private static class Acceptor extends TestAcceptorTask { public Acceptor( final TestUtility testUtility) { super (testUtility); } @Override protected void runEmulatorTestSteps() throws Throwable { // Creates an Emulator object configured as an acceptor. final Emulator emulator = testUtility.createEmulatorAcceptor(); // Accepts an incoming TCP connection and prepares for message exchange. emulator.acceptConnection( this ); // Accepts a "Negotiate" message from the initiator. emulator.acceptNegotiation(); // Accepts an "Establish" message, finalizing the session setup. emulator.acceptEstablishment( 1 ); // Receives the order. final IMessage order = emulator.receiveOrder( 1 ); // Accepts a "Terminate" message, ending the session. emulator.acceptTerminate(); // Asserts that the Emulator's connection is now closed. Assertions.assertTrue(emulator.isConnectionClosed()); } } } |
The audit trail generator tool is included in the distribution package and is located in the ‘tools’ directory. This tool can be used to generate the CME audit trail files. Please see Tools :: Audit Trail Generator section for more details.
This section summarizes our findings and recommends best practices to tune the different layers of the handler.
Please note that the exact benefits and effects each of these configuration choices, that will be highly dependent upon the specific applications and workloads. In this way, so we strongly recommend experimenting with the different configuration options with your workload before deploying them in a production environment.
Selecting right storage
Using MemoryBasedStorage instead of FileBasedStorage boosts performance since SBE messages are stored directly in memory. Alternatively, it’s possible to use custom pluggable storage (using PluggableStorage) which does nothing on SBE message-related operations as soon as no resend requests are to be supported.
It is also recommended to use AsyncFileBasedStorage which provides same functionality as FileBasedStorage with good-performance.
Manipulating Threads Affinity
By default, threads are used by a session to send and receive SBE messages; they can be executed on any of available processors/cores. Specifying CPU affinity for each session thread may give a significant performance boost. It can be configured via the biz.onixs.cme.ilink3.handler.session.SessionSettings.setReceivingThreadAffinity(int:A) and biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendingThreadAffinity(int:A) methods.
Using send spinning timeout
The biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendSpinningTimeout(long) method can be used to decrease the latency of the data sending.
If the value is zero (by default) and the outgoing message cannot be sent immediately it is placed to the outgoing queue.
If the value greater than zero, the biz.onixs.cme.ilink3.handler.Session.send(IMessage) method waits for the socket sending buffer availability in the spin loop mode before placing the message to the outgoing queue (to be sent later by the sending thread). The method parameter specifies the spin loop period in nanoseconds. biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendSpinningTimeout(long) using makes sense when your session sends SBE messages frequently, in this case, waiting in the loop is cheaper than the thread context switch. However please note that the spin wait increases the CPU usage so the spin wait period should not be too long.
Using session warmup
The biz.onixs.cme.ilink3.handler.Session.warmUp(IMessage) method can be used to warm up the sending path.
It makes sense if your session sends SBE messages infrequently, in this case, the sending path and associated data structures will not be in a cache and this can increase the send latency. You can periodically (a recommended period is 500 usec and less) call the biz.onixs.cme.ilink3.handler.Session.warmUp(IMessage) to avoid cache misses and keep the sending path fast.
Reusing FIX message instance for incoming messages
Object creation is an expensive operation in Java, with an impact on both performance and memory consumption. The cost varies depending on the amount of initialization that needs to be performed when the object is to be created. The handler exposes an ability to reuse the incoming message by Session. We highly recommend to turn on biz.onixs.cme.ilink3.handler.session.SessionSettings.setInboundMessageReuse(boolean) to true in order to minimize the excess object creation and garbage collection overhead.
Reusing SBE message instance
A common strategy is to use the same message instance for sending multiple times. As soon as a message of a certain type is constructed, it is updated with common fields for the subsequent re-use. Afterwards, before sending it to the counterparty, it is updated with mutable fields (like price or quantity) exactly for the case and sent to the counterparty.
As mutable fields are updated, each time message is about to be sent to the counterparty.
This section summarizes our findings and recommends best practices to tune the different layers of the handler for high-throughput workloads. Please note that the exact benefits and effects of each of these configuration choices will be highly dependent upon the specific applications and workloads, so we strongly recommend experimenting with the different configuration options with your workload, before deploying them in a production environment.
There are many settings in the Session class and this section covers the only subset that relates to high-throughput workloads.
There are many options in the protocol stack that can affect the efficiency of the data delivery. You must understand the characteristics of the version of the stacks you are running, and that they are compatible with the versions and options on the other stacks.
Enable the Nagle’s algorithm (disable TCP_NODELAY)
Nagle’s algorithm is very useful for minimizing network overhead by concatenating packets together. We strongly recommend enabling Nagle’s algorithm for high-throughput workloads. Please consider using biz.onixs.cme.ilink3.handler.session.SessionSettings.setSocketTcpNoDelay(boolean) method to set it to true.
OnixS provides online support resources to customers and development partners.
Jira is an issue logging and tracking system with release version control, records of issues/fixes and enhancements, and access to useful resource and FAQ information.
We recommend using Jira for issue logging/tracking. Registration requests for Jira access and technical support are also available via email at support@onixs.biz.