Tutorial: Criar um aplicativo com um serviço front-end de API Java e um serviço back-end com monitoração de estado no Azure Service Fabric
Este tutorial é a primeira parte de uma série. Quando terminar, você terá um aplicativo de votação com um front-end da Web Java que salva os resultados da votação em um serviço back-end com monitoração de estado no Azure Service Fabric. Esta série de tutoriais requer que tenha um computador de programador Mac OSX ou Linux. Se não quiser criar manualmente a aplicação de voto, pode transferir o código de origem da aplicação concluída e avançar diretamente para o Guia do exemplo de aplicação de voto. Além disso, considere seguir o Guia de início rápido para serviços confiáveis Java ..
Nesta série de tutoriais, ficará a saber como:
- Criar uma aplicação Java Service Fabric Reliable Services
- Implementar e depurar a aplicação num cluster local
- Implementar a aplicação num cluster do Azure
- Configurar a monitorização e os diagnósticos da aplicação
- Configurar CI/CD
Na primeira parte da série, saiba como:
- Criar um serviço Java fiável e com estado
- Criar um serviço de aplicações Web em Java sem estado
- Utilizar a comunicação remota do serviço para comunicar com o serviço com estado
- Implementar a aplicação num cluster do Service Fabric local
Pré-requisitos
Antes de começar este tutorial:
- Se não tiver uma subscrição do Azure, crie uma conta gratuita.
- Configure o ambiente de desenvolvimento para Mac ou Linux. Siga as instruções para instalar o plug-in do Eclipse, Gradle, o SDK do Service Fabric e a CLI do Service Fabric (sfctl).
Criar o serviço front-end em Java sem estado
Em primeiro lugar, crie o front-end para a Web da aplicação de Voto. Uma interface do usuário da Web alimentada por AngularJS envia solicitações para o serviço sem estado Java, que executa um servidor HTTP leve. Este serviço processa cada solicitação e envia uma chamada de procedimento remoto para o serviço stateful para armazenar os votos.
Abra o Eclipse.
Crie um projeto com File (Ficheiro) >New (Novo) >Other (Outro) >Service Fabric>Service Fabric Project (Projeto do Service Fabric).
Na caixa de diálogo Assistente de Projeto do ServiceFabric, nomeie a Votação do Projeto e selecione Avançar.
Na página Adicionar Serviço, selecione Serviço sem Estado e nomeie seu serviço VotingWeb. Selecione Concluir para criar o projeto.
O Eclipse cria uma aplicação e um projeto de serviço e apresenta-os no Package Explorer (Explorador de Pacotes).
A tabela mostra uma descrição breve de cada item no Package Explorer da captura de ecrã anterior.
Item do Explorador de Pacotes | Descrição |
---|---|
PublishProfiles | Contém os ficheiros JSON que descrevem os detalhes do perfil de clusters locais e do Azure Service Fabric. O plug-in utiliza o conteúdo destes ficheiros durnte a implementação da aplicação. |
Scripts | Contém scripts de programa auxiliar que podem ser utilizados na linha de comandos para gerir rapidamente a sua aplicação com um cluster. |
VotingApplication | Contém a aplicação do Service Fabric que é enviada por push para o cluster do Service Fabric. |
VotingWeb | Contém os ficheiros de origem do serviço sem estado do front-end, juntamente com o ficheiro de compilação do gradle relacionados. |
build.gradle | Ficheiro do Gradle utilizado para gerir o projeto. |
settings.gradle | Contém os nomes de projetos do Gradle nesta pasta. |
Adicionar HTML e JavaScript ao serviço VotingWeb
Para adicionar uma interface do usuário que possa ser processada pelo serviço sem monitoração de estado, adicione um arquivo HTML. Este ficheiro HTML é, então, processado pelo servidor HTTP simples que está incorporado no serviço de Java sem estado.
Expanda o diretório VotingApplication para ver o diretório VotingApplication/VotingWebPkg/Code.
Clique com o botão direito do mouse no diretório Código e selecione Nova>pasta.
Nomeie a pasta wwwroot e selecione Concluir.
Adicione um ficheiro ao wwwroot chamado index.html e cole os seguintes conteúdos neste ficheiro.
<!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>
Atualizar o ficheiro VotingWeb.java
No sub-projeto VotingWeb, abra o ficheiro VotingWeb/src/statelessservice/VotingWeb.java. O serviço VotingWeb é o gateway para o serviço sem estado e é responsável por configurar o serviço de escuta de comunicação para a API de front-end.
Substitua o método createServiceInstanceListeners existente no arquivo pelo seguinte e salve as alterações.
@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;
}
Adicionar o ficheiro HTTPCommunicationListener.java
O serviço de escuta de comunicação HTTP atua como um controlador que configura o servidor HTTP e expõe as APIs que definem as ações de voto. Clique com o botão direito do mouse no pacote statelessservice na pasta VotingWeb/src/statelessservice e selecione Novo>arquivo. Nomeie o arquivo HttpCommunicationListener.java e selecione Concluir.
Substitua o conteúdo do ficheiro pelo seguinte e, em seguida, guarde as alterações. Mais adiante, em Atualizar o ficheiro HttpCommunicationListener.java, este ficheiro é modificado para compor, ler e escrever dados de voto do serviço back-end. Por agora, o serviço de escuta devolve simplesmente o HTML estático da aplicação de Voto.
// ------------------------------------------------------------
// 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();
}
}
Configurar a porta de escuta
Quando o serviço front-end VotingWeb é criado, o Service Fabric seleciona uma porta na qual o serviço escuta. O serviço VotingWeb atua como front-end desta aplicação e aceita tráfego externo, pelo que vamos vincular este serviço a uma porta fixa e conhecida. No Package Explorer, abra VotingApplication/VotingWebPkg/ServiceManifest.xml. Encontre o recurso Endpoint na seção Recursos e altere o valor da porta para 8080 (continuaremos usando essa porta ao longo do tutorial). Para implementar e executar a aplicação localmente, a porta de escuta da aplicação tem de estar aberta e disponível no seu computador. Cole o seguinte fragmento de código no elemento ServiceManifest (imediatamente abaixo do 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>
Adicionar um serviço de back-end com monitorização de estado à sua aplicação
Agora que a estrutura do serviço de API Web em Java está concluída, vamos avançar e concluir o serviço back-end com estado.
O Service Fabric permite-lhe armazenar de forma consistente e fiável os seus dados diretamente dentro do seu serviço através das Reliable Collections. As coleções fiáveis são um conjunto de classes de coleção de elevada disponibilidade e fiáveis. A utilização destas classes é familiar para qualquer pessoa que tenha utilizado coleções de Java.
No Explorador de Pacotes, clique com o botão direito do mouse em Votação dentro do projeto de aplicativo e selecione Adicionar Serviço do Service Fabric do Service Fabric>.
Na caixa de diálogo Adicionar Serviço, selecione Serviço com Estado, nomeie o serviço VotingDataService e selecione Adicionar Serviço.
Uma vez criado o projeto de serviço, terá dois serviços na sua aplicação. À medida que continua a criar a sua aplicação, pode adicionar mais serviços com o mesmo método. Pode dar uma versão e atualizar cada serviço de forma independente.
O Eclipse cria um projeto de serviço e apresenta-o no Package Explorer.
Adicione o ficheiro VotingDataService.java
O ficheiro VotingDataService.java inclui os métodos que contêm a lógica para obter, adicionar e remover votos de coleções fiáveis. Adicione os seguintes métodos de classe VotingDataService ao ficheiro 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()));
}
}
A estrutura do serviço front-end sem estado e do serviço back-end está agora criada.
Criar a interface de comunicação para a aplicação
A próxima etapa é conectar o serviço sem estado front-end e o serviço back-end. Ambos os serviços utilizam uma interface chamada VotingRPC que define as operações do aplicativo de votação. Esta interface é implementada por ambos os serviços front-end e back-end para ativar as chamadas de procedimento remoto (RPC) entre os dois serviços. Infelizmente, o Eclipse não suporta a adição de subprojetos Gradle, portanto, o pacote que contém essa interface deve ser adicionado manualmente.
Clique com o botão direito do mouse no projeto de votação no Explorador de Pacotes e selecione Nova>pasta. Dê à pasta o nome VotingRPC/src/rpcmethods.
Crie um ficheiro em Voto/VotingRPC/src/rpcmethods com o nome VotingRPC.java e cole o seguinte no interior do ficheiro 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); }
Crie um arquivo vazio chamado build.gradle no diretório Voting/VotingRPC e cole o seguinte dentro dele. Este ficheiro gradle é utilizado para criar o ficheiro jar que é importado pelos outros serviços.
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'
No ficheiro Voto/settings.gradle, adicione uma linha para incluir o projeto recentemente criado na compilação.
include ':VotingRPC'
No ficheiro Voting/VotingWeb/src/statelessservice/HttpCommunicationListener.java, substitua o bloco de comentário pelo seguinte.
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); } } });
Adicione a declaração de importação adequada à parte superior do ficheiro Voto/VotingWeb/src/statelessservice/HttpCommunicationListener.java.
import rpcmethods.VotingRPC;
Nesta fase, as funções das interfaces de front-end, back-end e RPC estão concluídas. A fase seguinte é configurar os scripts de Gradle corretamente antes de implementar nm cluster do Service Fabric.
Percorrer a aplicação de votação de exemplo
A aplicação de votação é composta por dois serviços:
- Serviço front-end para a Web (VotingWeb) - um serviço front-end para a Web em Java, que serve a página Web e expõe as APIs para comunicar com o serviço back-end.
- Serviço back-end (VotingDataService) - um serviço Web em Java, que define os métodos que são invocados através de Chamadas de Procedimento Remoto (RPC) para manter os votos.
Quando efetua uma ação na aplicação (adicionar item, votar, remover item) ocorrem os seguintes eventos:
Um JavaScript envia o pedido adequado para a API Web no serviço front-end para a Web como um pedido HTTP.
O serviço front-end para a Web utiliza a funcionalidade incorporada de Comunicação Remota do Serviço do Service Fabric para localizar e reencaminhar o pedido para o serviço back-end.
O serviço back-end define os métodos que atualizam o resultado num dicionário fiável. O conteúdo deste dicionário fiável é replicado para vários nós dentro do cluster e é mantido no disco. Todos os dados da aplicação são armazenados no cluster.
Configurar scripts de Gradle
Nesta secção, vai configurar os scripts de Gradle para o projeto.
Substitua os conteúdos do ficheiroVoto/build.gradle pelo seguinte.
apply plugin: 'java' apply plugin: 'eclipse' subprojects { apply plugin: 'java' } defaultTasks 'clean', 'jar', 'copyDeps'
Substitua os conteúdos do ficheiro Voting/VotingWeb/build.gradle pelo seguinte.
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'
Substitua os conteúdos do ficheiro 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'
Implementar aplicação no cluster local
Neste momento, a aplicação está pronta para ser implementada num cluster do Service Fabric local.
Clique com o botão direito do mouse no projeto de votação no Explorador de Pacotes e selecione Aplicativo de compilação do Service Fabric>para criar seu aplicativo.
Execute o seu cluster do Service Fabric local. Este passo depende do seu ambiente de desenvolvimento (Mac ou Linux).
Se estiver a utilizar um Mac, execute o cluster local com o seguinte comando: substitua o comando transmitido no parâmetro -v pelo caminho para a sua área de trabalho.
docker run -itd -p 19080:19080 -p 8080:8080 -p --name sfonebox mcr.microsoft.com/service-fabric/onebox:latest
Consulte instruções mais detalhadas no guia de configuração do OS X.
Se estiver a executar num computador Linux, inicie o cluster local com o seguinte comando:
sudo /opt/microsoft/sdk/servicefabric/common/clustersetup/devclustersetup.sh
Veja instruções mais detalhadas no guia de configuração do Linux.
No Explorador de Pacotes para Eclipse, clique com o botão direito do mouse no projeto de votação e selecione Aplicativo de publicação do Service Fabric>
Na janela Publicar aplicativo, selecione Local.json na lista suspensa e selecione Publicar.
Vá para o seu browser e aceda a http://localhost:8080 para ver a sua aplicação em execução no cluster do Service Fabric local.
Próximos passos
Nesta parte do tutorial, ficou a saber como:
- Criar o serviço Java como um serviço fiável com estado
- Criar o serviço Java como um serviço Web sem estado
- Adicionar uma interface de Java para processar as Chamadas de Procedimento Remoto (RPC) entre os serviços
- Configurar os scripts de Gradle
- Criar e implementar a aplicação num cluster do Service Fabric local
Avance para o tutorial seguinte: