Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa è la prima di una serie di esercitazioni. Al termine sarà disponibile un'applicazione Voting con un front-end Web Java che salva i risultati delle votazioni in un servizio back-end con stato in Azure Service Fabric. Questa serie di esercitazioni richiede che sia disponibile un computer per sviluppatori Mac OSX o Linux funzionante. Se non si vuole creare manualmente l'applicazione di voto, è possibile scaricare il codice sorgente per l'applicazione completata e passare a Procedura dettagliata per l'applicazione di esempio di voto. Prendere inoltre in considerazione la procedura di avvio rapido per Java Reliable Services.
In questa serie di esercitazioni si apprenderà come:
- Compilare un'applicazione Reliable Services di Service Fabric per Java
- Distribuire ed eseguire il debug dell'applicazione in un cluster locale
- Distribuire un'applicazione in un cluster di Azure
- Configurare il monitoraggio e la diagnostica per l'applicazione
- Configurare CI/CD
Nella prima parte della serie si apprenderà come:
- Creare un servizio affidabile con stato gestito in Java
- Creare un servizio di applicazione web Java senza stato
- Usare la comunicazione remota per comunicare con il servizio con stato
- Distribuire un'applicazione in un cluster di Service Fabric locale
Prerequisiti
Prima di iniziare questa esercitazione:
- Se non hai una sottoscrizione di Azure, crea un account gratuito.
- Configurare l'ambiente di sviluppo per Mac o Linux. Seguire le istruzioni per installare il plug-in Eclipse, Gradle, Service Fabric SDK e l'interfaccia della riga di comando di Service Fabric (sfctl).
Creare il servizio front-end Java senza stato
Creare prima di tutto il front-end Web dell'applicazione Voting. Un'interfaccia utente Web basata su AngularJS invia richieste al servizio Java senza stato, che esegue un server HTTP leggero. Questo servizio elabora ogni richiesta e invia una chiamata di procedura remota al servizio con stato per archiviare i voti.
Apri Eclipse.
Creare un progetto con File>>>>.
Nella finestra di dialogo Creazione guidata progetto ServiceFabric assegna il nome Voting al progetto e seleziona Avanti.
Nella pagina Aggiungi servizio selezionare Servizio senza stato e assegnare al servizio il nome VotingWeb. Selezionare Fine per creare il progetto.
Eclipse crea un'applicazione e un progetto di servizio e li visualizza in Esplora pacchetti.
La tabella fornisce una breve descrizione di ogni elemento in Esplora pacchetti dello screenshot precedente.
| Elemento di Package Explorer | Descrizione |
|---|---|
| PublishProfiles | Contiene file JSON che descrivono i dettagli del profilo dei cluster locali e di Azure Service Fabric. Il contenuto di questi file viene usato dal plug-in durante la distribuzione dell'applicazione. |
| Scripts | Contiene script helper che possono essere usati dalla riga di comando per gestire rapidamente l'applicazione con un cluster. |
| Applicazione di Voto | Contiene l'applicazione di Service Fabric di cui viene eseguito il push nel cluster di Service Fabric. |
| VotingWeb | Contiene i file di origine del servizio senza stato front-end insieme al file di compilazione Gradle correlato. |
| build.gradle | File Gradle usato per gestire il progetto. |
| settings.gradle | Contiene i nomi dei progetti Gradle in questa cartella. |
Aggiungere HTML e JavaScript al servizio VotingWeb
Per aggiungere un'interfaccia utente che può essere renderizzata dal servizio stateless, aggiungere un file HTML. Questo file HTML viene quindi reso dal server HTTP leggero incorporato nel servizio Java stateless.
Espandere la directory VotingApplication per raggiungere la directory VotingApplication/VotingWebPkg/Code .
Fare clic con il tasto destro sulla directory Codice e selezionare Nuova>Cartella.
Assegnare alla cartella il nome wwwroot e selezionare Fine.
Aggiungere un file al wwwroot denominato index.html e incollare il contenuto seguente in questo file.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.4/ui-bootstrap-tpls.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<body>
<script>
var app = angular.module('VotingApp', ['ui.bootstrap']);
app.controller("VotingAppController", ['$rootScope', '$scope', '$http', '$timeout', function ($rootScope, $scope, $http, $timeout) {
$scope.votes = [];
$scope.refresh = function () {
$http.get('getStatelessList')
.then(function successCallback(response) {
$scope.votes = Object.assign(
{},
...Object.keys(response.data) .
map(key => ({[decodeURI(key)]: response.data[key]}))
)
},
function errorCallback(response) {
alert(response);
});
};
$scope.remove = function (item) {
$http.get("removeItem", {params: { item: encodeURI(item) }})
.then(function successCallback(response) {
$scope.refresh();
},
function errorCallback(response) {
alert(response);
});
};
$scope.add = function (item) {
if (!item) {return;}
$http.get("addItem", {params: { item: encodeURI(item) }})
.then(function successCallback(response) {
$scope.refresh();
},
function errorCallback(response) {
alert(response);
});
};
}]);
</script>
<div ng-app="VotingApp" ng-controller="VotingAppController" ng-init="refresh()">
<div class="container-fluid">
<div class="row">
<div class="col-xs-8 col-xs-offset-2 text-center">
<h2>Service Fabric Voting Sample</h2>
</div>
</div>
<div class="row">
<div class="col-xs-offset-2">
<form style="width:50% ! important;" class="center-block">
<div class="col-xs-6 form-group">
<input id="txtAdd" type="text" class="form-control" placeholder="Add voting option" ng-model="item" />
</div>
<button id="btnAdd" class="btn btn-default" ng-click="add(item)">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
Add
</button>
</form>
</div>
</div>
<hr />
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="row">
<div class="col-xs-4">
Click to vote
</div>
</div>
<div class="row top-buffer" ng-repeat="(key, value) in votes">
<div class="col-xs-8">
<button class="btn btn-success text-left btn-block" ng-click="add(key)">
<span class="pull-left">
{{key}}
</span>
<span class="badge pull-right">
{{value}} Votes
</span>
</button>
</div>
<div class="col-xs-4">
<button class="btn btn-danger pull-right btn-block" ng-click="remove(key)">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
Remove
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Aggiornare il file VotingWeb.java
Nel sottoprogetto VotingWeb aprire il file VotingWeb/src/statelessservice/VotingWeb.java . VotingWeb è il gateway nel servizio senza stato ed è responsabile della configurazione del listener di comunicazione per l'API front-end.
Sostituire il metodo createServiceInstanceListeners esistente nel file con quanto segue e salvare le modifiche.
@Override
protected List<ServiceInstanceListener> createServiceInstanceListeners() {
EndpointResourceDescription endpoint = this.getServiceContext().getCodePackageActivationContext().getEndpoint(webEndpointName);
int port = endpoint.getPort();
List<ServiceInstanceListener> listeners = new ArrayList<ServiceInstanceListener>();
listeners.add(new ServiceInstanceListener((context) -> new HttpCommunicationListener(context, port)));
return listeners;
}
Aggiungere il file HTTPCommunicationListener.java
Il listener di comunicazione HTTP funge da controller che configura il server HTTP ed espone le API che definiscono le azioni di voto. Fare clic con il pulsante destro del mouse sul pacchetto statelessservice nella cartella VotingWeb/src/statelessservice , quindi scegliere Nuovo>file. Assegnare al file il nome HttpCommunicationListener.java e selezionare Fine.
Sostituire il contenuto del file con quanto segue, quindi salvare le modifiche. Nella sezione Aggiornare il file HttpCommunicationListener.java, disponibile più avanti, questo file viene modificato per eseguire il rendering, leggere e scrivere i dati di voto dal servizio back-end. Per il momento, il listener restituisce semplicemente il codice HTML statico per l'app Voting.
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
package statelessservice;
import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.Headers;
import java.io.File;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import microsoft.servicefabric.services.communication.runtime.CommunicationListener;
import microsoft.servicefabric.services.runtime.StatelessServiceContext;
import microsoft.servicefabric.services.client.ServicePartitionKey;
import microsoft.servicefabric.services.remoting.client.ServiceProxyBase;
import microsoft.servicefabric.services.communication.client.TargetReplicaSelector;
import system.fabric.CancellationToken;
public class HttpCommunicationListener implements CommunicationListener {
private static final Logger logger = Logger.getLogger(HttpCommunicationListener.class.getName());
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final int STATUS_OK = 200;
private static final int STATUS_NOT_FOUND = 404;
private static final int STATUS_ERROR = 500;
private static final String RESPONSE_NOT_FOUND = "404 (Not Found) \n";
private static final String MIME = "text/html";
private static final String ENCODING = "UTF-8";
private static final String ROOT = "wwwroot/";
private static final String FILE_NAME = "index.html";
private StatelessServiceContext context;
private com.sun.net.httpserver.HttpServer server;
private ServicePartitionKey partitionKey;
private final int port;
public HttpCommunicationListener(StatelessServiceContext context, int port) {
this.partitionKey = new ServicePartitionKey(0);
this.context = context;
this.port = port;
}
// Called by openAsync when the class is instantiated
public void start() {
try {
logger.log(Level.INFO, "Starting Server");
server = com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(this.port), 0);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
// Responsible for rendering the HTML layout described in the previous step
server.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange t) {
try {
File file = new File(ROOT + FILE_NAME).getCanonicalFile();
if (!file.isFile()) {
// Object does not exist or is not a file: reject with 404 error.
t.sendResponseHeaders(STATUS_NOT_FOUND, RESPONSE_NOT_FOUND.length());
OutputStream os = t.getResponseBody();
os.write(RESPONSE_NOT_FOUND.getBytes());
os.close();
} else {
Headers h = t.getResponseHeaders();
h.set(HEADER_CONTENT_TYPE, MIME);
t.sendResponseHeaders(STATUS_OK, 0);
OutputStream os = t.getResponseBody();
FileInputStream fs = new FileInputStream(file);
final byte[] buffer = new byte[0x10000];
int count = 0;
while ((count = fs.read(buffer)) >= 0) {
os.write(buffer,0,count);
}
fs.close();
os.close();
}
} catch (Exception e) {
logger.log(Level.WARNING, null, e);
}
}
});
/*
[Replace this entire comment block in the 'Connect the services' section]
*/
server.setExecutor(null);
server.start();
}
//Helper method to parse raw HTTP requests
private Map<String, String> queryToMap(String query){
Map<String, String> result = new HashMap<String, String>();
for (String param : query.split("&")) {
String pair[] = param.split("=");
if (pair.length>1) {
result.put(pair[0], pair[1]);
}else{
result.put(pair[0], "");
}
}
return result;
}
//Called closeAsync when the service is shut down
private void stop() {
if (null != server)
server.stop(0);
}
//Called by the Service Fabric runtime when this service is created on a node
@Override
public CompletableFuture<String> openAsync(CancellationToken cancellationToken) {
this.start();
logger.log(Level.INFO, "Opened Server");
String publishUri = String.format("http://%s:%d/", this.context.getNodeContext().getIpAddressOrFQDN(), port);
return CompletableFuture.completedFuture(publishUri);
}
//Called by the Service Fabric runtime when the service is shut down
@Override
public CompletableFuture<?> closeAsync(CancellationToken cancellationToken) {
this.stop();
return CompletableFuture.completedFuture(true);
}
//Called by the Service Fabric runtime to forcibly shut this listener down
@Override
public void abort() {
this.stop();
}
}
Configurare la porta di ascolto
Quando viene creato il servizio front-end del servizio VotingWeb, Service Fabric seleziona una porta per l'ascolto del servizio. Il servizio VotingWeb funge da front-end per questa applicazione e accetta il traffico esterno, quindi associa il servizio a una porta fissa e ben nota. Nell'Esplora Pacchetti, apri VotingApplication/VotingWebPkg/ServiceManifest.xml. Trovare la risorsa Endpoint nella sezione Risorse e modificare il valore porta in 8080 (questa porta continuerà a essere usata in tutta l'esercitazione). Per distribuire ed eseguire l'applicazione in locale, la porta di ascolto dell'applicazione deve essere aperta e disponibile nel computer. Incollare il frammento di codice seguente all'interno dell'elemento ServiceManifest ,ad esempio sotto l'elemento <DataPackage> .
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="WebEndpoint" Protocol="http" Port="8080" />
</Endpoints>
</Resources>
Aggiungere un servizio back-end con stato a un'applicazione
Ora che la struttura del servizio API Web Java è stata completata, completare il servizio back-end con stato.
Service Fabric consente di archiviare in modo coerente e affidabile i dati direttamente all'interno del servizio usando raccolte affidabili. Le raccolte Reliable sono un set di classi di raccolta affidabili e a disponibilità elevata. L'uso di queste classi è familiare a chiunque abbia usato raccolte Java.
In Esplora Pacchetti, fare clic con il pulsante destro del mouse su Voting all'interno del progetto dell'applicazione e selezionare Service Fabric>.
Nella finestra di dialogo Aggiungi servizio selezionare Servizio con stato e assegnare al servizio il nome VotingDataService e selezionare Aggiungi servizio.
Dopo aver creato il progetto di servizio, nell'applicazione sono disponibili due servizi. Man mano che si continua a compilare l'applicazione, è possibile aggiungere altri servizi nello stesso modo. Ogni elemento può essere versionato e aggiornato indipendentemente.
Eclipse crea un progetto di servizio e lo visualizza in Esplora pacchetti.
Aggiungere il file VotingDataService.java
Il file VotingDataService.java contiene i metodi che contengono la logica per recuperare, aggiungere e rimuovere voti dalle raccolte Reliable Collections. Aggiungere i metodi della classe VotingDataService seguenti al file VotingDataService/src/statefulservice/VotingDataService.java .
package statefulservice;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import microsoft.servicefabric.services.communication.runtime.ServiceReplicaListener;
import microsoft.servicefabric.services.runtime.StatefulService;
import microsoft.servicefabric.services.remoting.fabrictransport.runtime.FabricTransportServiceRemotingListener;
import microsoft.servicefabric.data.ReliableStateManager;
import microsoft.servicefabric.data.Transaction;
import microsoft.servicefabric.data.collections.ReliableHashMap;
import microsoft.servicefabric.data.utilities.AsyncEnumeration;
import microsoft.servicefabric.data.utilities.KeyValuePair;
import system.fabric.StatefulServiceContext;
import rpcmethods.VotingRPC;
class VotingDataService extends StatefulService implements VotingRPC {
private static final String MAP_NAME = "votesMap";
private ReliableStateManager stateManager;
protected VotingDataService (StatefulServiceContext statefulServiceContext) {
super (statefulServiceContext);
}
@Override
protected List<ServiceReplicaListener> createServiceReplicaListeners() {
this.stateManager = this.getReliableStateManager();
ArrayList<ServiceReplicaListener> listeners = new ArrayList<>();
listeners.add(new ServiceReplicaListener((context) -> {
return new FabricTransportServiceRemotingListener(context,this);
}));
return listeners;
}
// Method that will be invoked via RPC from the front end to retrieve the complete set of votes in the map
public CompletableFuture<HashMap<String,String>> getList() {
HashMap<String, String> tempMap = new HashMap<String, String>();
try {
ReliableHashMap<String, String> votesMap = stateManager
.<String, String> getOrAddReliableHashMapAsync(MAP_NAME).get();
Transaction tx = stateManager.createTransaction();
AsyncEnumeration<KeyValuePair<String, String>> kv = votesMap.keyValuesAsync(tx).get();
while (kv.hasMoreElementsAsync().get()) {
KeyValuePair<String, String> k = kv.nextElementAsync().get();
tempMap.put(k.getKey(), k.getValue());
}
tx.close();
} catch (Exception e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(tempMap);
}
// Method that will be invoked via RPC from the front end to add an item to the votes list or to increase the
// vote count for a particular item
public CompletableFuture<Integer> addItem(String itemToAdd) {
AtomicInteger status = new AtomicInteger(-1);
try {
ReliableHashMap<String, String> votesMap = stateManager
.<String, String> getOrAddReliableHashMapAsync(MAP_NAME).get();
Transaction tx = stateManager.createTransaction();
votesMap.computeAsync(tx, itemToAdd, (k, v) -> {
if (v == null) {
return "1";
}
else {
int numVotes = Integer.parseInt(v);
numVotes = numVotes + 1;
return Integer.toString(numVotes);
}
}).get();
tx.commitAsync().get();
tx.close();
status.set(1);
} catch (Exception e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(new Integer(status.get()));
}
// Method that will be invoked via RPC from the front end to remove an item
public CompletableFuture<Integer> removeItem(String itemToRemove) {
AtomicInteger status = new AtomicInteger(-1);
try {
ReliableHashMap<String, String> votesMap = stateManager
.<String, String> getOrAddReliableHashMapAsync(MAP_NAME).get();
Transaction tx = stateManager.createTransaction();
votesMap.removeAsync(tx, itemToRemove).get();
tx.commitAsync().get();
tx.close();
status.set(1);
} catch (Exception e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(new Integer(status.get()));
}
}
La struttura del servizio front-end senza stato e del servizio back-end è stata così creata.
Creare l'interfaccia di comunicazione con l'applicazione
Il passaggio successivo consiste nel connettere il servizio senza stato front-end e il servizio back-end. Entrambi i servizi utilizzano un'interfaccia denominata VotingRPC che definisce le operazioni dell'applicazione Voting. Questa interfaccia viene implementata dai servizi front-end e back-end per abilitare le chiamate di procedura remota (RPC) tra i due servizi. Purtroppo Eclipse non supporta l'aggiunta di sottoprogetti Gradle, quindi il pacchetto che contiene questa interfaccia deve essere aggiunto manualmente.
Fare clic con il pulsante destro del mouse sul progetto Voting in Package Explorer e scegliere Nuova Cartella>. Denominare la cartella VotingRPC/src/rpcmethods.
Creare un file in Voting/VotingRPC/src/rpcmethods denominato VotingRPC.java e incollare quanto segue all'interno del file VotingRPC.java .
package rpcmethods; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.List; import java.util.HashMap; import microsoft.servicefabric.services.remoting.Service; public interface VotingRPC extends Service { CompletableFuture<HashMap<String, String>> getList(); CompletableFuture<Integer> addItem(String itemToAdd); CompletableFuture<Integer> removeItem(String itemToRemove); }Creare un file vuoto denominato build.gradle nella directory Voting/VotingRPC e incollare quanto segue. Questo file gradle viene usato per compilare e creare il file JAR importato dagli altri servizi.
apply plugin: 'java' apply plugin: 'eclipse' sourceSets { main { java.srcDirs = ['src'] output.classesDir = 'out/classes' resources { srcDirs = ['src'] } } } clean.doFirst { delete "${projectDir}/out" delete "${projectDir}/VotingRPC.jar" } repositories { mavenCentral() } dependencies { compile ('com.microsoft.servicefabric:sf-actors:1.0.0') } jar { from configurations.compile.collect { (it.isDirectory() && !it.getName().contains("native")) ? it : zipTree(it)} manifest { attributes( 'Main-Class': 'rpcmethods.VotingRPC') baseName "VotingRPC" destinationDir = file('./') } exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' } defaultTasks 'clean', 'jar'Nel file Voting/settings.gradle aggiungere una riga per includere il progetto appena creato nella compilazione.
include ':VotingRPC'Nel file Voting/VotingWeb/src/statelessservice/HttpCommunicationListener.java sostituire il blocco di commenti con quanto segue.
server.createContext("/getStatelessList", new HttpHandler() { @Override public void handle(HttpExchange t) { try { t.sendResponseHeaders(STATUS_OK,0); OutputStream os = t.getResponseBody(); HashMap<String,String> list = ServiceProxyBase.create(VotingRPC.class, new URI("fabric:/VotingApplication/VotingDataService"), partitionKey, TargetReplicaSelector.DEFAULT, "").getList().get(); String json = new Gson().toJson(list); os.write(json.getBytes(ENCODING)); os.close(); } catch (Exception e) { logger.log(Level.WARNING, null, e); } } }); server.createContext("/removeItem", new HttpHandler() { @Override public void handle(HttpExchange t) { try { OutputStream os = t.getResponseBody(); URI r = t.getRequestURI(); Map<String, String> params = queryToMap(r.getQuery()); String itemToRemove = params.get("item"); Integer num = ServiceProxyBase.create(VotingRPC.class, new URI("fabric:/VotingApplication/VotingDataService"), partitionKey, TargetReplicaSelector.DEFAULT, "").removeItem(itemToRemove).get(); if (num != 1) { t.sendResponseHeaders(STATUS_ERROR, 0); } else { t.sendResponseHeaders(STATUS_OK,0); } String json = new Gson().toJson(num); os.write(json.getBytes(ENCODING)); os.close(); } catch (Exception e) { logger.log(Level.WARNING, null, e); } } }); server.createContext("/addItem", new HttpHandler() { @Override public void handle(HttpExchange t) { try { URI r = t.getRequestURI(); Map<String, String> params = queryToMap(r.getQuery()); String itemToAdd = params.get("item"); OutputStream os = t.getResponseBody(); Integer num = ServiceProxyBase.create(VotingRPC.class, new URI("fabric:/VotingApplication/VotingDataService"), partitionKey, TargetReplicaSelector.DEFAULT, "").addItem(itemToAdd).get(); if (num != 1) { t.sendResponseHeaders(STATUS_ERROR, 0); } else { t.sendResponseHeaders(STATUS_OK,0); } String json = new Gson().toJson(num); os.write(json.getBytes(ENCODING)); os.close(); } catch (Exception e) { logger.log(Level.WARNING, null, e); } } });Aggiungere l'istruzione di importazione appropriata nella parte superiore del file Voting/VotingWeb/src/statelessservice/HttpCommunicationListener.java .
import rpcmethods.VotingRPC;
In questa fase, le funzionalità per le interfacce front-end, back-end e RPC sono complete. La fase successiva consiste nel configurare gli script Gradle in modo appropriato prima della distribuzione in un cluster di Service Fabric.
Descrizione dettagliata dell'applicazione di voto di esempio
L'applicazione di voto è costituita da due servizi:
- Servizio front-end Web (VotingWeb): servizio front-end Web Java che serve la pagina Web ed espone le API per comunicare con il servizio back-end.
- Servizio back-end (VotingDataService): servizio Web Java, che definisce i metodi richiamati tramite chiamate rpc (Remote Procedure Call) per rendere persistenti i voti.
Quando si esegue un'azione nell'applicazione (aggiungere elemento, votare, rimuovere elemento) si verificano gli eventi seguenti:
JavaScript invia la richiesta appropriata all'API Web nel servizio front-end Web come richiesta HTTP.
Il servizio front-end Web usa la funzionalità di comunicazione remota dei servizi predefinita di Service Fabric per individuare e inoltrare la richiesta al servizio back-end.
Il servizio back-end definisce metodi che aggiornano il risultato in un dizionario affidabile. Il contenuto di questo dizionario affidabile viene replicato in più nodi all'interno del cluster e salvato in modo permanente su disco. Tutti i dati dell'applicazione vengono archiviati nel cluster.
Configurare gli script Gradle
In questa sezione vengono configurati gli script Gradle per il progetto.
Sostituire il contenuto del file Voting/build.gradle con quanto segue.
apply plugin: 'java' apply plugin: 'eclipse' subprojects { apply plugin: 'java' } defaultTasks 'clean', 'jar', 'copyDeps'Sostituire il contenuto del file Voting/VotingWeb/build.gradle con quanto segue.
apply plugin: 'java' apply plugin: 'eclipse' sourceSets { main { java.srcDirs = ['src'] output.classesDir = 'out/classes' resources { srcDirs = ['src'] } } } clean.doFirst { delete "${projectDir}/../lib" delete "${projectDir}/out" delete "${projectDir}/../VotingApplication/VotingWebPkg/Code/lib" delete "${projectDir}/../VotingApplication/VotingWebPkg/Code/VotingWeb.jar" } repositories { mavenCentral() } dependencies { compile ('com.microsoft.servicefabric:sf-actors:1.0.0-preview1') compile project(':VotingRPC') } task explodeDeps(type: Copy, dependsOn:configurations.compile) { task -> configurations.compile.filter {!it.toString().contains("native")}.each{ from it } configurations.compile.filter {it.toString().contains("native")}.each{ from zipTree(it) } into "../lib/" include "lib*.so", "*.jar" } task copyDeps<< { copy { from("../lib/") into("../VotingApplication/VotingWebPkg/Code/lib") include('lib*.so') } } compileJava.dependsOn(explodeDeps) jar { from configurations.compile.collect {(it.isDirectory() && !it.getName().contains("native")) ? it : zipTree(it)} manifest { attributes( 'Main-Class': 'statelessservice.VotingWebServiceHost') baseName "VotingWeb" destinationDir = file('../VotingApplication/VotingWebPkg/Code/') } exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' } defaultTasks 'clean', 'jar', 'copyDeps'Sostituire il contenuto del file Voting/VotingDataService/build.gradle .
apply plugin: 'java' apply plugin: 'eclipse' sourceSets { main { java.srcDirs = ['src'] output.classesDir = 'out/classes' resources { srcDirs = ['src'] } } } clean.doFirst { delete "${projectDir}/../lib" delete "${projectDir}/out" delete "${projectDir}/../VotingApplication/VotingDataServicePkg/Code/lib" delete "${projectDir}/../VotingApplication/VotingDataServicePkg/Code/VotingDataService.jar" } repositories { mavenCentral() } dependencies { compile ('com.microsoft.servicefabric:sf-actors:1.0.0-preview1') compile project(':VotingRPC') } task explodeDeps(type: Copy, dependsOn:configurations.compile) { task -> configurations.compile.filter {!it.toString().contains("native")}.each{ from it } configurations.compile.filter {it.toString().contains("native")}.each{ from zipTree(it) } into "../lib/" include "lib*.so", "*.jar" } compileJava.dependsOn(explodeDeps) task copyDeps<< { copy { from("../lib/") into("../VotingApplication/VotingDataServicePkg/Code/lib") include('lib*.so') } } jar { from configurations.compile.collect { (it.isDirectory() && !it.getName().contains("native")) ? it : zipTree(it)} manifest { attributes('Main-Class': 'statefulservice.VotingDataServiceHost') baseName "VotingDataService" destinationDir = file('../VotingApplication/VotingDataServicePkg/Code/') } exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' } defaultTasks 'clean', 'jar', 'copyDeps'
Distribuire l'applicazione nel cluster locale
A questo punto, l'applicazione è pronta per essere distribuita in un cluster di Service Fabric locale.
Fare clic con il pulsante destro del mouse sul progetto Voting in Esplora pacchetti e selezionare Service Fabric>Build Application per compilare l'applicazione.
Esegui il cluster locale di Service Fabric. Questo passaggio dipende dall'ambiente di sviluppo (Mac o Linux).
Se si usa un Mac, eseguire il cluster locale con il comando seguente: Sostituire il comando passato nel parametro -v con il percorso della propria area di lavoro.
docker run -itd -p 19080:19080 -p 8080:8080 -p --name sfonebox mcr.microsoft.com/service-fabric/onebox:latestVedere istruzioni più dettagliate nella guida alla configurazione di OS X.
Se si esegue in un computer Linux, avviare il cluster locale con il comando seguente:
sudo /opt/microsoft/sdk/servicefabric/common/clustersetup/devclustersetup.shVedere istruzioni più dettagliate nella guida alla configurazione di Linux.
In Esplora pacchetti per Eclipse, fare clic con il pulsante destro del mouse sul progetto Voting e selezionare Service Fabric>Pubblica applicazione
Nella finestra Pubblica applicazione selezionare Local.json dall'elenco a discesa e selezionare Pubblica.
Passare al Web browser e accedere http://localhost:8080 per visualizzare l'applicazione in esecuzione nel cluster locale di Service Fabric.
Passaggi successivi
In questa parte dell'esercitazione si è appreso come:
- Creare un servizio Java come servizio Reliable con stato
- Creare un servizio Java come servizio Web senza stato
- Aggiungere un'interfaccia Java per gestire le chiamate rpc (Remote Procedure Call) tra i servizi
- Configurare gli script Gradle
- Compilare e distribuire l'applicazione in un cluster di Service Fabric locale
Passare all'esercitazione successiva: