This article assumes that the reader understands the Apache Ignite, has read the official documentation of the Ignite service grid, or has used a Ignite service grid, and this article also assumes that the reader understands the relevant uses of Java's completionstage. The ignite version covered in this article is 2.4.0.
Using the Apache Ignite service grid as a microservices development framework, the service is typically defined and implemented as follows:
Service Interface:
public interface MyService { public String sayHello(String to);}
This article will implement the following style of service to make it asynchronous:
Asynchronous Service Interfaces:
public interface MyServiceAsync { public CompletionStage<String> sayHello(String to);}
Currently ignite does not have remote support for asynchronous service methods such as the top. When the caller and the service are deployed to the same node, Ignite initiates a local method call, which is not a problem, but when the service is deployed and the caller is on a different node, ignite by initiating a distributed task, the call is posted to the service deployment node by means of a message. Because the service implementation is asynchronous, it usually returns an incomplete state of the completionstage, and subsequent when the real complete, the completionstage of the caller is not notify, that is, the caller can never get a real call result.
In order to support Completionstage's remote state delivery, we need to make the following changes to ignite:
Org/apache/ignite/internal/processors/service/gridserviceproxy.java
...//Line 192if (CompletionStage.class.isAssignableFrom (Mtd.getreturntype ())) {Completablefuture<object> cs = New Completablefuture<> (); Call Async and notify completion stage Ctx.closure (). Callasyncnofailover (Gridclosurecallmode.broadcast, New Serviceproxycallable (Mtd.getname (), Name, Mtd.getparametertypes (), args), Collections.singleton (node), False, Waittimeout, true). Listen (f, {if (F.error ()! = null) { Cs.completeexceptionally (F.error ()); }else if (f.iscancelled ()) {Cs.cancel (false); } if (F.isdone ()) {try {Object result = F.get (); if (result! = null && IgniteException.class.isAssignableFrom (Result.getclass ())) { Cs.completeexceptionally ((igniteexception) result);}else {cs.complete (F.get ()); }} catch (Ignitecheckedexception e) {cs.completeexceptionally (e); } } }); return CS;} ...
This code does the following: detects when the service method return value is a completionstage, creates a completablefuture to be returned to the caller as a proxy object. The result of the remote invocation of the service is then monitored, and the result is used to update the completablefuture. Here, the transformation of the service proxy on the caller is complete. Next, we need to transform the service node at the end:
Org/apache/ignite/internal/processors/job/gridjobworker.java (this finally block from line 618), before transformation:
finally { // Finish here only if not held by this thread. if (!HOLD.get()) finishJob(res, ex, sndRes); else // Make sure flag is not set for current thread. // This may happen in case of nested internal task call with continuation. HOLD.set(false); ctx.job().currentTaskSession(null); if (reqTopVer != null) GridQueryProcessor.setRequestAffinityTopologyVersion(null); }}
After transformation:
finally {if (res! = null && CompletionStage.class.isAssignableFrom (Res.getclass ())) {Final Boolean send Result = Sndres; Final igniteexception igexception = ex; @SuppressWarnings ("unchecked") completionstage<object> cs = (completionstage<object>) res; Cs.exceptionally (t->{return new igniteexception (t); }). thenaccept (r->{if (! Hold.get ()) {igniteexception E = igexception; Finishjob (R, E, Sendresult); } else//Make sure flag are not set for the current thread. This is happen in case of nested internal task call with continuation. Hold.set (FALSE); Ctx.job (). Currenttasksession (NULL); if (reqtopver! = null) gridqueryprocessor.setrequestaffinitytopologyversion (NULL); }); The else {//Finish here is only if the held by this thread. if (! Hold.get ()) FinishjoB (Res, EX, sndres); else/Make sure flag are not set for the current thread. This is happen in case of nested internal task call with continuation. Hold.set (FALSE); Ctx.job (). Currenttasksession (NULL); if (reqtopver! = null) gridqueryprocessor.setrequestaffinitytopologyversion (NULL); }}
The thing to do here is: when we get the execution results on the service Deployment node, if we find that the service return result is a completionstage, then deal with the completionstage exceptionally and thenaccept, Send the results to the caller of remote.
So, with a simple retrofit, we have the ability to handle asynchronous service method calls in ignite. Below we implement a service to see the retrofit results:
Service definition and implementation:
import java.util.concurrent.CompletionStage;import org.apache.ignite.services.Service;public interface MyService extends Service { public CompletionStage<String> sayHelloAsync(String to); public String sayHelloSync(String to);}
import java.util.concurrent.CompletionStage;public class MyServiceImpl implements MyService { private ScheduledExecutorService es; @Override public void init(ServiceContext ctx) throws Exception { es = Executors.newSingleThreadScheduledExecutor(); } @Override public CompletionStage<String> sayHelloAsync(String to){ CompletableFuture<String> ret = new CompletableFuture<>(); //return "async hello $to" after 3 secs es.schedule(()->ret.complete("async hello " + to), 3, TimeUnit.SECONDS); return ret; } @Override public String sayHelloSync(String to){ return "sync hello " + to; } ...}
Then deploy the services in service grid:
...ServiceConfiguration sConf = new ServiceConfiguration();sConf.setName("myservice.version.1");sConf.setService(new MyServiceImpl());sConf.setMaxPerNodeCount(2);sConf.setTotalCount(4);ignite.services().deploy(sConf);...
Then start a client node to make the service call:
MyService service = ignite.services().serviceProxy("myservice.version.1", MyService.class, false);//test async serviceservice.sayHelloAsync("nathan").thenAccept(r->{ System.out.println(r);});//test sync serviceSystem.out.println(service.sayHelloSync("nathan"));...
Output Result:
sync hello nathanasync hello nathan
You can see that the result of sync is output first, and the result of async is output after about 3 seconds.
Apache Ignite Retrofit (i)--Service Async support