Interface NetworkService

  • All Superinterfaces:
    Service

    @DefaultServiceFactory(NetworkServiceFactory.class)
    public interface NetworkService
    extends Service
    « start hereEntry point to TCP-based client/server communication API.

    Overview

    NetworkService provides an abstraction layer on top of sockets API for building connection-oriented communication protocols.

    Service Configuration

    NetworkService can be configured and registered within the HekateBootstrap via the NetworkServiceFactory class as in the example below:

    
    // Prepare network service factory.
    NetworkServiceFactory factory = new NetworkServiceFactory()
        // Configure options.
        .withPort(10012) // Cluster node network port.
        .withPortRange(100) // ... if port is busy then auto-increment it
        .withNioThreads(10) // Number of threads to serve NIO operations
        .withConnectTimeout(5000) // Timeout of connecting to a remote peer
        .withHeartbeatInterval(500) // Heartbeat interval for each socket connection to keep alive
        .withHeartbeatLossThreshold(4); // Maximum amount of lost heartbeats
    // ... other options ...
    
    // Start node.
    Hekate hekate = new HekateBootstrap()
        .withService(factory)
        .join();
    
    // Access the service.
    NetworkService network = hekate.network();
    
    Note: This example requires Spring Framework integration (see HekateSpringBootstrap).
    
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:h="http://www.hekate.io/spring/hekate-core"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.hekate.io/spring/hekate-core
            http://www.hekate.io/spring/hekate-core.xsd">
    
        <h:node id="hekate">
            <!-- Network service. -->
            <h:network host="any-ip4" port="10012" port-range="100"
                nio-threads="10"
                heartbeat-interval-ms="500"
                heartbeat-loss-threshold="4"
                connect-timeout-ms="5000">
                <!-- ...other options... -->
            </h:network>
    
            <!-- ...other services... -->
        </h:node>
    </beans>
    
    Note: This example requires Spring Framework integration (see HekateSpringBootstrap).
    
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="hekate" class="io.hekate.spring.bean.HekateSpringBootstrap">
            <property name="services">
                <list>
                    <!-- Network service. -->
                    <bean class="io.hekate.network.NetworkServiceFactory">
                        <property name="port" value="10012"/>
                        <property name="portRange" value="100"/>
                        <property name="nioThreads" value="10"/>
                        <property name="heartbeatInterval" value="500"/>
                        <property name="heartbeatLossThreshold" value="4"/>
                        <property name="connectTimeout" value="5000"/>
    
                        <!-- ...other options... -->
                    </bean>
    
                    <!-- ...other services... -->
                </list>
            </property>
        </bean>
    </beans>
    

    Please see the documentation of NetworkServiceFactory class for more details about the available configuration options.

    Connectors

    Communication units in the NetworkService are represented by the NetworkConnector interface. This interface provides API for creating client connections based on the connector options as well as accepting connections from remote clients (optional).

    The client side of a connector API is represented by the NetworkClient interface. Each client manages a single socket connection and provides API for connecting to remote endpoints and sending/receiving messages to/from them. Instances if this interface can be be obtained from NetworkConnector as illustrated in the example below.

    The server side of a connector API is represented by the NetworkServerHandler interface. This interface provides callback methods that get notified upon various events of remote clients (connects, disconnects, new messages, etc). Instances of this interface can be registered via NetworkConnectorConfig.setServerHandler(NetworkServerHandler) method as illustrated in the example below.

    Note: NetworkServerHandler is optional and if not specified for particular NetworkConnector then such connector will act in a pure client mode and will not be able to accept connections from remote addresses.

    Connectors Configuration

    NetworkConnector configuration is represented by the NetworkConnectorConfig class. Please see its documentation for the complete list of all available configuration options.

    Instances of this class can be registered within the NetworkService via NetworkServiceFactory.setConnectors(List) method.

    Protocol Identifier

    Each connector must have a protocol identifier. This identifier is used by the NetworkService to select which NetworkConnector should be responsible for processing each particular connection from a remote NetworkClient. When NetworkClient established a new connection to a remote NetworkService it submits its protocol identifier as part of an initial handshake message. This identifier is used by the remote NetworkService to select a NetworkConnector instance that is configured with exactly the same protocol identifier. If such instance can be found then all subsequent communication events will be handled by its NetworkServerHandler. If such instance can't be found then connection will be rejected.

    SSL Encryption

    It is possible to configure NetworkService to use secure communications by setting SSL configuration. Please see the documentation of the NetworkSslConfig class for available configuration options.

    Note that SSL encryption will be applied to all network communications at the cluster node level, thus it is important to make sure that all nodes in the cluster are configured to use SSL encryption. Mixed mode, when some nodes do use SSL and some do not use it, is not supported. In such case non-SSL nodes will not be able to connect to SSL-enabled nodes and vice versa.

    Protocol identifier must be specified within the NetworkConnector configuration via NetworkConnectorConfig.setProtocol(String) method.

    Data Serialization

    Data serialization and deserialization within connectors is handled by the Codec interface. Instances of this interface can be specified for each connector independently via NetworkConnectorConfig.setMessageCodec(CodecFactory) method. If not specified the the default general purpose codec of a Hekate instance will be used (see HekateBootstrap.setDefaultCodec(CodecFactory)).

    Please see the documentation of Codec interface for more details about data serialization.

    Thread Management

    NetworkService manages a core NIO thread pool of NetworkServiceFactory.setNioThreads(int) size. This thread pools is used to process all incoming and outgoing connections by default.

    It is also possible to configure each NetworkConnector to use its own thread pool via NetworkConnectorConfig.setNioThreads(int) option. In such case all incoming and outgoing connections of that connector will be handled by a dedicated thread pool of the specified size and will not interfere with NetworkService's core thread nor with thread pool of any other connector.

    Whenever a new connection is created by the connector (either client connection or NetworkServerHandler.onConnect(Object, NetworkEndpoint) server connection}) it obtains a worker thread from the NetworkConnector's thread pool and uses this thread to process all of the NIO events. Due to the event-based nature of NIO each thread can handle multiple connections and doesn't require a one-to-one relationship between the pool size and the amount of active connections. Typically thread pool size must be much less than the number of active connections. When connection gets closed it unregisters itself from its worker thread.

    Example

    The code example below shows how NetworkService can be used to implement client/server communications. For the sake of brevity this example uses the default Java serialization and messages of String type. For real world applications it is recommended to implement custom message classes and provide a more optimized implementation of Codec in order to increase communication speed and to support a more complex application logic.

    Server Example

    1) Prepare server handler.

    
    // Prepare server handler (String - base type of exchanged messages)
    public class ExampleHandler implements NetworkServerHandler<String> {
        @Override
        public void onConnect(String loginMsg, NetworkEndpoint<String> client) {
            System.out.println("Got new connection: " + loginMsg);
    
            // Initialize connection context object.
            client.setContext(new AtomicInteger());
        }
    
        @Override
        public void onMessage(NetworkMessage<String> netMsg, NetworkEndpoint<String> from) throws IOException {
            String msg = netMsg.decode();
    
            System.out.println("Message from client: " + msg);
    
            AtomicInteger counter = (AtomicInteger)from.getContext();
    
            // Send reply.
            from.send(msg + " processed (total=" + counter.incrementAndGet() + ')');
        }
    
        @Override
        public void onDisconnect(NetworkEndpoint<String> client) {
            System.out.println("Closed connection.");
        }
    }
    

    2) Prepare connector configuration.

    
    NetworkConnectorConfig<String> connCfg = new NetworkConnectorConfig<>();
    
    connCfg.setProtocol("example.protocol"); // Protocol identifier.
    connCfg.setMessageCodec(new JdkCodecFactory<>()); // Use default Java serialization.
    connCfg.setServerHandler(new ExampleHandler()); // Server handler.
    

    3) Start new node.

    
    // Prepare network service factory and register the connector configuration.
    NetworkServiceFactory factory = new NetworkServiceFactory()
        .withPort(10023) // Fixed port for demo purposes.
        .withConnector(connCfg);
    
    // Register the network service factory and start new node.
    Hekate hekate = new HekateBootstrap()
        .withService(factory)
        .join();
    

    Client Example

    Note: This example uses the same connector configuration as in the server example.

    1) Instantiate a new client and connect to the server.

    
    // Get connector by its protocol identifier.
    NetworkConnector<String> connector = hekate.network().connector("example.protocol");
    
    NetworkClient<String> client = connector.newClient();
    
    InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 10023);
    
    // Asynchronously connect, send a login message (optional) and start receiving messages.
    client.connect(serverAddress, "example login", (message, from) ->
        System.out.println("Message from server: " + message)
    );
    

    2) Start sending messages (can be done even if connection establishment is still in progress).

    
    // Send some messages.
    for (int i = 0; i < 10; i++) {
        client.send("Example message", (msg, err) -> {
            // Check error.
            if (err == null) {
                System.out.println("Successfully sent: " + msg);
            } else {
                System.err.println("Failed to send: " + err);
            }
        });
    }
    
    // Disconnect and await for completion.
    client.disconnect().get();
    

    See Also:
    NetworkServiceFactory
    • Method Detail

      • hasConnector

        boolean hasConnector​(String protocol)
        Returns true if this service has a connector with the specified protocol name.
        Parameters:
        protocol - Protocol name (see NetworkConnectorConfig.setProtocol(String)).
        Returns:
        true if connector exists.
      • ping

        void ping​(InetSocketAddress address,
                  NetworkPingCallback callback)
        Asynchronously checks if connection can be established with a NetworkService at the specified address and notifies the provided callback on operation result.

        Example:

        
        hekate.network().ping(new InetSocketAddress("127.0.0.1", 10012), (address, result) -> {
            switch (result) {
                case SUCCESS: {
                    System.out.println("Node is alive at " + address);
        
                    break;
                }
                case FAILURE: {
                    System.out.println("No node at " + address);
        
                    break;
                }
                case TIMEOUT: {
                    System.out.println("Ping timeout " + address);
        
                    break;
                }
                default: {
                    throw new IllegalArgumentException("Unsupported ping result: " + result);
                }
            }
        });
        

        Parameters:
        address - Address.
        callback - Callback to be notified.
        See Also:
        NetworkPingResult