Service Fabric on Linux

  • 8/24/2018

Other Service Types and Frameworks

Using Service Fabric Java SDK to implement other service types, including stateful services, actor services, and guest application services, is very similar to using the .NET SDK. Of course, because of language differences, the Java SDK is adapted to work more naturally for Java developers.

The next few sections provide a quick glimpse into how Java SDK supports different service types. To try out different service types, simply create a Service Fabric application and add a service with the type. The scaffolded code gives you quick examples on basic usage of the corresponding service types. You’ll see a great similarity between the Java code and the .NET code.

Stateful Services

Interacting with state managers in Java SDK is slightly different from the .NET SDK. The Java SDK uses a CompletableFuture<T> type, which can be taken as an approximation of the .NET Task<T> type. Or, if you are familiar with promises, you can take it as an implementation of a promise. When you create a new stateful service, the SDK scaffolds a default runAsync method implementation, as shown in the following snippet:

@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
    Transaction tx = stateManager.createTransaction();
    return this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap")
        .thenCompose((map) -> {
        return map.computeAsync(tx, "counter", (k, v) -> {
            if (v == null)
                return 1L;
            else
                return ++v;
        }, Duration.ofSeconds(4), cancellationToken).thenApply((l) -> {
            return tx.commitAsync().handle((r, x) -> {
                if (x != null) {
                    logger.log(Level.SEVERE, x.getMessage());
                }
                try {
                    tx.close();
                } catch (Exception e) {
                    logger.log(Level.SEVERE, e.getMessage());
                }
                return null;
            });
        });
    });
}

This code first creates a new transaction. Then, it tries to get or add a microsoft.servicefabric.data.collections.ReliableHashMap<K,V> instance, which is equivalent to IReliableDictionary<K,V> in the .NET SDK. Finally, it tries to create or update the "counter" entry in the map and commits the transaction.

Actor Services

Actor services with Java SDK work in the same way as .NET-based actor services. To construct and use an actor service in Java, you need the same set of artifacts as in the .NET SDK:

  • Actor interface An actor interface is defined as a regular Java interface that inherits a default Actor interface—for example:

    public interface MyActor extends Actor {
        @Readonly
        CompletableFuture<Integer> getCountAsync();
        CompletableFuture<?> setCountAsync(int count);
    }
  • Actor implementation An actor implementation inherits from a microsoft.servicefabric.actors.FabricActor base class and implements the actor interface. The following snippet shows that the actor implementation in Java corresponds almost line by line with the .NET implementation:

    @ActorServiceAttribute(name = "MyActorActorService")
    @StatePersistenceAttribute(statePersistence = StatePersistence.Persisted)
    public class MyActorImpl extends FabricActor implements MyActor {
        private Logger logger = Logger.getLogger(this.getClass().getName());
        public MyActorImpl(FabricActorService actorService, ActorId actorId){
            super(actorService, actorId);
        }
        @Override
        protected CompletableFuture<?> onActivateAsync() {
            logger.log(Level.INFO, "onActivateAsync");
            return this.stateManager().tryAddStateAsync("count", 0);
        }
        @Override
        public CompletableFuture<Integer> getCountAsync() {
            logger.log(Level.INFO, "Getting current count value");
            return this.stateManager().getStateAsync("count");
        }
        @Override
        public CompletableFuture<?> setCountAsync(int count) {
            logger.log(Level.INFO, "Setting current count value {0}", count);
            return this.stateManager().addOrUpdateStateAsync("count", count,
                (key, value) -> count > value ? count : value);
        }
    }
  • Actor proxy for a client to connect to an actor service The Java SDK provides a microsoft.servicefabric.actors.ActorProxyBase class, through which you can create actor proxies for your actor interface:

    MyActor actorProxy = ActorProxyBase.create(MyActor.class, new ActorId("From Actor 1"),
        new URI("fabric:/ActorApplicationApplication/MyActorActorService"));
    int count = actorProxy.getCountAsync().get();
    System.out.println("From Actor:" + ActorExtensions.getActorId(actorProxy)
        + " CurrentValue:" + count);
    actorProxy.setCountAsync(count+1);

Guest Binary Services

As on the Windows platform, you can package a guest binary and host it on Service Fabric as a stateless service. The following steps show you how to package a Python-based application as a guest binary service. In this simple example, you’ll create a web server using Flask and then host the Python application as a stateless service on your local Service Fabric cluster.

  1. Create a new Service Fabric application named GuestPythonApplication with a guest binary service named FlaskWebServer.

  2. When asked for guest binary details, simply click the Finish button. You’ll add these files manually later.

  3. Use the following code, which uses Flask to implement a very simple web server, to create a new flaskserver.py file in the GuestPythonApplicationApplication\FlaskWebServerPkg\Code folder:

    from flask import Flask
    app = Flask("myweb")
    @app.route("/")
    def hello():
      return "Hello from Flask!"
    app.run(host='0.0.0.0', port=8183, debug = False)
  4. In the same folder, add a new launch.sh file to launch the web server. The following script first installs the flask module using pip. Then, it locates the path of the current script and feeds the correct server file path to Python. Strictly speaking, installing Flask should have been done in a setup entry point because it’s a host environment configuration step. I’ll leave this exercise to interested readers.

    #!/bin/bash
    sudo python -m pip install flask >> ../log/flask-install.txt 2>&1
    pushd $(dirname "${0}") > /dev/null
    BASEDIR=$(pwd -L)
    popd > /dev/null
    logger ${BASEDIR}
    python ${BASEDIR}/flaskserver.py
  5. Update the ServiceManifest.xml file as follows:

    <?xml version="1.0" encoding="utf-8" ?>
    <ServiceManifest Name="FlaskWebServerPkg" Version="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <Description>Service that implements a FlaskWebServer service</Description>
      <ServiceTypes>
        <StatelessServiceType ServiceTypeName="FlaskWebServerType" UseImplicitHost="true"/>
      </ServiceTypes>
      <CodePackage Name="Code" Version="1.0.0">
        <EntryPoint>
          <ExeHost>
            <Program>launch.sh</Program>
            <Arguments />
            <WorkingFolder>CodePackage</WorkingFolder>
          </ExeHost>
        </EntryPoint>
        <EnvironmentVariables></EnvironmentVariables>
      </CodePackage>
      <ConfigPackage Name="Config" Version="1.0.0" />
      <DataPackage Name="Data" Version="1.0.0" />
      <Resources>
          <Endpoints>
              <Endpoint Name="ServiceEndpoint" Protocol="http" Port="8183" Type="Input"/>
          </Endpoints>
      </Resources>
     </ServiceManifest>
  6. Build and deploy the application.

  7. Using a web browser, navigate to http://localhost:8183/. You should see a “Hello from Flask!” message.