View on GitHub

ScaleCube Reactive Microservices

The Future is reactive - The Future is right here!

Consuming Services

We have seen how to define a service interfaces and how to implement them, now we need to consume them. The service interface contains everything ScaleCube services needs to know on how to go about invoking a service.

A service implementation may be located anywhere at the cluster and or may appear with number of instances. ScaleCube service ServiceCall solve this for you. no matter where and how much service instances you have it gives you the full control to consume them.

A service consumer is a member of the service cluster sharing same Cluster Membership / Failure Detection / Gossip discovery group. This can be achieved by joining one of the known cluster members AKA .seeds() nodes.

   
    // Create microservice consumer
    Microservices consumer = Microservices.builder()
		.discovery(options -> options.seeds(....))
		.startAwait();

Using the service interface class we can request from the consumer a ServiceCall for a given .api(GreetingService.class). The call to the api method will build for us a service proxy with relevant descriptors to address the specific service in the cluster.

  // Get a proxy to the service API.
  ServiceCall serviceCall = consumer.call().create();
  
  // Creates a service proxy passing the service interface class
  GreetingService greetingService = serviceCall.api(GreetingService.class);

Using the service proxy to call the concrete service is as trivial as a simple method call. The service proxy will locate the service instance in the cluster and will route to them the requests. Services supports java Reactor Project Mono for a async request response and Flux for a reactive streams pattern.

  // Call service and when complete print the greeting.
  GreetingRequest req = new GreetingRequest("Joe");
  
  Publisher<GreetingResponse> publisher = greetingService.greeting(req);
  GreetingResponse response = 
		Mono.from(publisher).subscribe(result -> {
			System.out.println(result.greeting());
		});
  
  Flux<GreetingResponse> stream = greetingService.greetings(req)
   .subscribe(onNext -> {
      System.out.println(onNext.getResult())
  });

By default the scale-cube services provides a Round-robin service instance selection. Thus for each service request the service proxy will use the currently available service endpoints and will invoke one message to one service instance at a time so its balanced across all currently-live-service-instances. Using the .router(...) its also possible to control the endpoint selection logic.

Service Proxy Options:
.router(...) option you can choose from available selection logic or provide a custom one.
.timeout(Duration) option you can specify a timeout for waiting for service response the default is 30 secounds.

  // Get a proxy to a service API via a router.
    CanaryService service = gateway.call()
        .router(Routers.getRouter(CanaryTestingRouter.class))
        .create()
        .api(CanaryService.class);