Skip to content

Netifi Spring Client

Netifi seamlessly integrates with Spring Boot to make building cloud-native applications and microservices easy.

This page will walk you through defining a Netifi contract, implementing the contract as a Netifi Spring Boot service and creating a client to call the service, as part of a multi-module Gradle project.

This page provides two methods for getting up and running with Netifi and Spring Boot:

  1. Start from Scratch—Define the service contract in protobuf, implement the service code, and implement the client that calls the service.
  2. Clone Netifi Spring QuickStart—Get up and running quickly with a pre-written example.

Netifi Client Annotations

Netifi’s Spring integration makes it easy to autowire Netifi clients into Spring-enabled projects. Netifi’s Spring module offers three type of annotations that specify different types of interaction between client and server:

@Destination
sends data to a particular application with the specified destination tag
@Group
sends data to one application chosen by the Broker from those with the specified group tag
@Broadcast
sends data to all applications that have the specified group tag, then accepts the first response received

The following sections describe the behavior of these annotations in greater detail:

@Destination Client Annotation

This annotation allows you to autowire a client so that it sends data to the one application with the specified destination tag:

@Destination(destination = "destination-name", group = "group.name")
Example showing the @Destination annotation
@Component
public class ClientRunner implements CommandLineRunner {
  private static final Logger logger = LogManager.getLogger(ClientRunner.class);

  @Destination(destination = "service-hello", group = "hello.service.group")
  private HelloServiceClient client;

  final HelloWorldService helloWorldClient;

  public ClientRunner(
    @Destination(destination = "service-hello-world", group = "hello.service.group")
    HelloWorldService helloWorldClient
  ) {
    this.helloWorldClient = helloWorldClient;
  }

  @Override
  public void run(String... args) throws Exception {
    // Service implementation here!
  }
}

The @Destination annotation in this example allows your client to instantiate its client and service objects in such a way that they make their calls to a particular service instance, by specifying that service’s destination and group Netifi tags.

@Group Client Annotation

The @Group annotation is similar to @Destination, but allows your application to connect to and communicate with the Netifi broker-chosen application in the specified application group, rather than with a previously-chosen and hardwired application instance:

@Group("group.name")
Example showing the @Group annotation
@Component
public class ClientRunner implements CommandLineRunner {
  private static final Logger logger = LogManager.getLogger(ClientRunner.class);

  @Group("quickstart.services.helloservices")
  private HelloService client;

  @Override
  public void run(String... args) throws Exception {
    // Service implementation here!
  }
}

As in the previous example, in the code above you use this annotation on top of the required field, so the underlying mechanism can find appropriate implementation and inject it during the bean construction. Since your destination is an unspecified member of a group of microservices, you omit the destination parameter, and so the declaration is a bit shorter.

Note

With @Group you send messages to only one instance in the group. The Netifi Broker selects which instance by using predictive load balancing.

@Broadcast Client Annotation

The final type of annotation is @Broadcast which allows sending messages to all instances in the specified group:

@Broadcast("group.name")
Example showing the @Broadcast annotation
@Component
public class ClientRunner implements CommandLineRunner {
  private static final Logger logger = LogManager.getLogger(ClientRunner.class);

  @Broadcast("quickstart.services.helloservices")
  private HelloServiceClient broadcastClient;

  @Override
  public void run(String... args) throws Exception {
    // Service implementation here!
  }
}

Getting Started from Scratch

Follow the steps below to implement the service. The basic steps to implementing a Netifi Spring Boot service are:

  1. Define the service contract in Protobuf IDL.
  2. Implement the service code.
  3. Implement the service client that calls the service.

If you have any issues you can compare your code against the Netifi Spring QuickStart project repository.

Project Setup

First, create a root Gradle build file containing the following:

build.gradle
plugins {
  id 'org.springframework.boot' version '2.1.3.RELEASE' apply false
  id 'com.google.protobuf' version '0.8.8' apply false
}

subprojects {
  apply plugin: 'java'
  apply plugin: 'idea'
  apply plugin: 'com.google.protobuf'
  apply plugin: 'io.spring.dependency-management'

  group 'com.netifi.quickstart'
  version '1.6.4'

  ext {
    protobufVersion = '3.6.1'
    netifiVersion = '1.6.4'
    rsocketRpcVersion = '0.2.13.3'
    rsocketVersion = '0.11.17.2'
    netifiSpringVersion = '1.6.4'
  }

  dependencyManagement {

          dependencies {
              dependency "com.netifi:netifi-spring-boot-starter:${netifiSpringVersion}"
              dependency "com.google.protobuf:protobuf-java:${protobufVersion}"
              dependency "com.google.protobuf:protoc:${protobufVersion}"
              dependency "io.rsocket.rpc:rsocket-rpc-protobuf:${rsocketRpcVersion}"
              dependency "io.rsocket.rpc:rsocket-rpc-core:${rsocketRpcVersion}"
              dependency "io.rsocket:rsocket-core:${rsocketVersion}"
              dependency "io.rsocket:rsocket-transport-netty:${rsocketVersion}"
          }
  }

  repositories {
    jcenter()
    maven {
      url 'https://sonatype.netifiinc.com/repository/netifi-oss/'
    }
  }
}

Define the Service Contract

You implement Netifi service contracts in Protobuf IDL and build them into code by using a special RSocket Protobuf compiler plugin.

  1. Create a Gradle project, named service-idl, with the following build file:

    service-idl/build.gradle
    dependencies {
      implementation "io.rsocket.rpc:rsocket-rpc-core"
      implementation "com.google.protobuf:protobuf-java"
    }
    
    sourceSets {
      main {
        proto { srcDir 'src/main/proto' }
      }
    
      test {
        proto { srcDir 'src/test/proto' }
      }
    }
    
    protobuf {
      generatedFilesBaseDir = "${projectDir}/src/generated"
    
      protoc {
        artifact = "com.google.protobuf:protoc"
      }
    
      plugins {
        rsocketRpc {
          artifact = "io.rsocket.rpc:rsocket-rpc-protobuf"
        }
      }
    
      generateProtoTasks {
        ofSourceSet('main')*.plugins {
           rsocketRpc {}
        }
      }
    }
    
    idea {
      module {
        sourceDirs += file("src/main/proto")
        sourceDirs += file("src/generated/main/java")
        sourceDirs += file("src/generated/main/rsocketRpc")
    
        generatedSourceDirs += file('src/generated/main/java')
        generatedSourceDirs += file('src/generated/main/rsocketRpc')
      }
    }
    
    clean {
      delete 'src/generated'
    }
    
  2. Define your service in a .proto file under the following directory structure service-idl/src/main/proto/package-name.

    For the purposes of this example assume that you are defining the service.proto contract under the com.netifi.quickstart.service.protobuf package as follows:

    service.proto
    syntax = "proto3";
    
    package com.netifi.quickstart.service;
    
    option java_package = "com.netifi.quickstart.service.protobuf";
    option java_outer_classname = "ServiceProto";
    option java_multiple_files = true;
    
    service HelloService {
      // Returns a Hello World Message
      rpc SayHello (HelloRequest) returns (HelloResponse) {}
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloResponse {
      string message = 1;
    }
    

    Note

    Pay special attention to the java_package option you define in the contract. This must match the package of your .proto file.

  3. You can now build the code that implements the contract by running the following Gradle command:

    ./gradlew build

Implement the Service

Now that you have the generated code that defines your Netifi service contract, create a new Gradle module to hold the service implementation.

  1. Create a Gradle project, named service, with the following build file:

    service/build.gradle
    plugins {
      id 'org.springframework.boot'
    }
    
    springBoot {
      mainClassName = 'com.netifi.quickstart.service.Main'
    }
    
    dependencies {
      implementation project(':service-idl') // This should reference your service contract project
      implementation "com.netifi:netifi-spring-boot-starter"
    }
    

    Notice that this service project depends on the service-idl project which defines the contract and that Netifi Spring integration is achieved by adding a dependency on the netifi-spring-boot-starter library.

  2. Create a standard main Spring Boot application class as follows:

    @SpringBootApplication
    public class Main {
    
      public static void main(String... args) {
        SpringApplication.run(Main.class, args);
      }
    }
    

    There is nothing special you need to add to enable Netifi integration. Netifi will be auto-configured because you added the netifi-spring-boot-starter library to the classpath.

  3. Implement the service contract, using the generated code, by creating a class called DefaultHelloService that implements the service interface, HelloService, that you generated from the IDL prototype:

    @Component
    public class DefaultHelloService implements HelloService {
    private static final Logger logger = LogManager.getLogger(DefaultHelloService.class);
    
    @Override
    public Mono<HelloResponse> sayHello(HelloRequest message, ByteBuf metadata) {
      logger.info("received a message -> {}", message.getName());
      return Mono.just(
        HelloResponse.newBuilder()
                     .setMessage("Hello, " + message.getName() + "! from " + METHOD_SAY_HELLO)
                     .build());
    }
    

    Notice how the class is annotated with the @Component annotation. This will cause the Netifi service to be scanned and registered automatically during application startup.

  4. Add a Spring application.properties file to src/main/resources with the following properties:

    netifi.client.broker.hostname=localhost
    netifi.client.broker.port=8001
    
    netifi.client.access.key=9007199254740991
    netifi.client.access.token=kTBDVtfRBO4tHOnZzSyY5ym2kfY=
    
    netifi.client.group=quickstart.services.helloservices
    

    For a description of these properties, please see the Configuration Properties section below.

Congratulations!

That is all there is to implementing a Netifi service using Spring Boot. Next you will create a Netifi client to call your service.

Implement the Service Client

Now that you have implemented a Spring Boot Netifi service, create a Netifi client to call the service and retrieve some data.

  1. Create a Gradle project, named client, with the following build file:

    client/build.gradle
    plugins {
      id 'org.springframework.boot'
    }
    
    springBoot {
      mainClassName = 'com.netifi.quickstart.client.Main'
    }
    
    dependencies {
      implementation project(':service-idl') // This should reference your service contract project
      implementation "com.netifi:netifi-spring-boot-starter"
    }
    

    Notice how both this project and the service project depend on the service-idl project that defines the contract. This is important as it forms the basis for how services and clients communicate within the Netifi ecosystem.

  2. Create a standard main Spring Boot application class as follows:

    @SpringBootApplication
    public class Main {
    
      public static void main(String... args) {
        SpringApplication.run(Main.class, args);
      }
    }
    

    There is nothing special to add to enable Netifi integration. Netifi will be auto-configured because you added the netifi-spring-boot-starter library to the classpath.

  3. As you are not in need of Spring Boot’s HTTP capabilities, create a simple CommandLineRunner implementation that will execute on application startup:

    @Component
    public class ClientRunner implements CommandLineRunner {
      private static final Logger logger = LogManager.getLogger(ClientRunner.class);
    
      @Group("quickstart.services.helloservices")
      private HelloServiceClient client;
    
      @Override
      public void run(String... args) throws Exception {
        // Create Request to HelloService
        HelloRequest request = HelloRequest.newBuilder()
                                           .setName("World")
                                           .build();
    
        logger.info("Sending 'World' to HelloService...");
    
        // Call the HelloService
        logger.info(client.sayHello(request).block());
    
        // Exit
        System.exit(0);
      }
    }
    

    Notice the client, from the service-idl project, is automatically injected into the class via the @Group annotation. You configured the @Group annotation to send data to the quickstart.services.helloservices group. This is the group that you configured in the service project.

  4. Add a Spring application.properties file to src/main/resources with the following properties:

    client/src/main/resources/application.properties
    netifi.client.broker.hostname=localhost
    netifi.client.broker.port=8001
    
    netifi.client.access.key=9007199254740991
    netifi.client.access.token=kTBDVtfRBO4tHOnZzSyY5ym2kfY=
    
    netifi.client.group=quickstart.clients
    

    For a description of these properties, please see the Configuration Properties section below.

Congratulations!

You now have a service, with a defined contract that can be shared, and a client that calls the service and retrieves data.

Running the Example

Now that you have created the project, run it by following the steps below:

  1. Start a Netifi Broker by following the instructions on the Netifi Broker Getting Started page.

  2. In another terminal, run the following Gradle command to start the service:

    ./gradlew :service:bootRun

  3. In another terminal, run the following Gradle command to start the client:

    ./gradlew :client:bootRun

Getting Started Using Netifi Spring QuickStart

If you want to get up and running quickly you can start by with the Netifi Spring QuickStart project:

  1. Clone the Netifi Spring QuickStart project from GitHub:

  2. Run the following command to generate the code from the service contract definition and build the quickstart.

    ./gradlew build

Running the Example

Now that you have created the project you can run it by following the steps below:

  1. Start a Netifi Broker by following the instructions on the Netifi Broker Getting Started page.

  2. In another terminal, run the following Gradle command to start the service:

    ./gradlew :service:bootRun

  3. In another terminal, run the following Gradle command to start the client:

    ./gradlew :client:bootRun

Configuration Properties

The following table contains the properties that are available for configuration in the Spring Boot application properties file of your application.

Property Description Required Default
netifi.client.broker.hostname Hostname of the Netifi Broker no localhost
netifi.client.broker.port TCP port of the Netifi Broker no 8001
netifi.client.access.key Access key used to authenticate with the Netifi Broker yes n/a
netifi.client.access.token Access token used to authenticate with the Netifi Broker yes n/a
netifi.client.group Netifi group name to assign to this client / service yes n/a
netifi.client.destination Netifi destination name to assign to this client / service instance no Generated UUID
netifi.client.pool-size Number of Netifi Brokers the client connects to by default no # of Processor Cores
netifi.client.ssl.disabled Disables SSL/TLS connections to the Netifi Broker no false

What Next?

In order to learn how to use Netifi-Spring integration in a real-world scenario, please see the following demo.