Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Это руководство представляет первую часть цикла. По завершении у вас есть приложение voting с веб-интерфейсом Java, которое сохраняет результаты голосования в серверной службе с отслеживанием состояния в Azure Service Fabric. В этой серии учебников требуется, чтобы у вас был рабочий компьютер разработчика Mac OSX или Linux. Если вы не хотите вручную создать приложение для голосования, можно скачать исходный код для завершенного приложения и перейти к пошаговому просмотру примера приложения для голосования. Кроме того, рассмотрите возможность следовать краткому руководству по надежным службам Java.
Из этого цикла руководств вы узнаете, как выполнять следующие задачи:
- Создание приложения Java Service Fabric «Надежные службы»
- Развертывание и отладка приложения в локальном кластере.
- Развертывание приложения в кластере Azure.
- Настройка мониторинга и диагностики приложения
- Настройка CI/CD
В первой части цикла вы узнаете, как выполнять такие задачи:
- Создание надежной Java-службы с сохранением состояния
- Создание бездейственного веб-сервиса для приложений на Java
- Использование удаленного взаимодействия службы для взаимодействия со службой с отслеживанием состояния
- Развертывание приложения в локальном кластере Service Fabric
Предпосылки
Перед началом работы с этим руководством выполните следующие действия:
- Если у вас еще нет подписки Azure, создайте бесплатную учетную запись.
- Настройте среду разработки для Mac или Linux. Следуйте инструкциям по установке подключаемого модуля Eclipse, Gradle, пакета SDK Service Fabric и интерфейса командной строки Service Fabric (sfctl).
Создать клиентский бесcостояниевый Java-сервис
Сначала создайте веб-интерфейс приложения Voting. Веб-интерфейс на основе AngularJS отправляет запросы в стейтлесс-сервис Java, который работает на легковесном HTTP-сервере. Эта служба обрабатывает каждый запрос и отправляет удаленный вызов процедуры службе с отслеживанием состояния для хранения голосов.
Откройте Eclipse.
Создайте проект с Файл>Создать>Другой>Service Fabric>Проект Service Fabric.
В диалоговом окне мастера проектов ServiceFabric назовите голосование проекта и нажмите кнопку "Далее".
На странице "Добавить службу" выберите "Служба без отслеживания состояния" и назовите службу VotingWeb. Нажмите кнопку "Готово ", чтобы создать проект.
Eclipse создает приложение и проект службы и отображает их в обозревателе пакетов.
В таблице приводится краткое описание каждого элемента в обозревателе пакетов на предыдущем снимке экрана.
| Элемент обозревателя пакетов | Описание |
|---|---|
| PublishProfiles | Содержит JSON-файлы, описывающие сведения о профиле локальных и кластеров Azure Service Fabric. Содержимое этих файлов используется подключаемым модулем при развертывании приложения. |
| Сценарии | Содержит вспомогательные скрипты, которые можно использовать из командной строки для быстрого управления приложением с помощью кластера. |
| VotingApplication | Содержит приложение Service Fabric, которое отправляется в кластер Service Fabric. |
| VotingWeb | Содержит исходные файлы фронтенд-бесстатусной службы вместе с соответствующим файлом сборки Gradle. |
| build.gradle | Файл Gradle, используемый для управления проектом. |
| settings.gradle | Содержит имена проектов Gradle в этой папке. |
Добавление HTML и JavaScript в службу VotingWeb
Чтобы добавить пользовательский интерфейс, который может быть отображён бестатусной службой, добавьте HTML-файл. Затем этот HTML-файл отображается упрощенным HTTP-сервером, внедренным в службу Java без отслеживания состояния.
Разверните каталог VotingApplication, чтобы добраться до каталога VotingApplication/VotingWebPkg/Code.
Щелкните правой кнопкой мыши на папке Code и выберите Создать>папку.
Назовите папку wwwroot и нажмите кнопку "Готово".
Добавьте файл в wwwroot с именем index.html и вставьте следующее содержимое в этот файл.
<!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>
Обновление файла VotingWeb.java
В подпроекте VotingWeb откройте файл VotingWeb/src/statelessservice/VotingWeb.java . Служба VotingWeb — это шлюз в бесcостояниевую службу и отвечает за настройку прослушивателя для связи с API интерфейса.
Замените существующий метод createServiceInstanceListeners в файле следующим и сохраните изменения.
@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;
}
Добавление файла HTTPCommunicationListener.java
Прослушиватель связи HTTP выступает в качестве контроллера, который настраивает HTTP-сервер и предоставляет API-интерфейсы, определяющие действия голосования. Щелкните правой кнопкой мыши на пакете statelessservice в папке VotingWeb/src/statelessservice, а затем выберите новый>файл. Назовите файл HttpCommunicationListener.java и нажмите кнопку "Готово".
Замените содержимое файла следующим образом, а затем сохраните изменения. Позже в обновлении файла HttpCommunicationListener.java этот файл изменяется для отрисовки, чтения и записи данных голосования из внутренней службы. Теперь прослушиватель просто возвращает статический HTML-код для приложения 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();
}
}
Настройка порта прослушивания
Когда создается фронтенд-сервис VotingWeb, Service Fabric выбирает порт для прослушивания. Служба VotingWeb выступает в качестве внешнего интерфейса для этого приложения и принимает внешний трафик, поэтому давайте привязываем эту службу к фиксированному и хорошо известному порту. В обозревателе пакетов откройте VotingApplication/VotingWebPkg/ServiceManifest.xml. Найдите ресурс конечной точки в разделе "Ресурсы" и измените значение порта на 8080 (мы продолжаем использовать этот порт во всем руководстве). Чтобы развернуть и запустить приложение локально, порт прослушивания приложения должен быть открыт и доступен на компьютере. Вставьте следующий фрагмент кода в элемент ServiceManifest (т. е. чуть ниже <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>
Добавление серверной службы с отслеживанием состояния в приложение
Теперь, когда скелет веб-API Java завершен, давайте пойдем вперед и завершите серверную службу с отслеживанием состояния.
Service Fabric позволяет постоянно и надежно хранить данные прямо в службе с помощью надежных коллекций. Надежные коллекции — это набор высокодоступных и надежных классов коллекций. Использование этих классов знакомо всем, кто использовал коллекции Java.
В обозревателе пакетов щелкните правой кнопкой мыши Voting в проекте приложения и выберите Service Fabric>Добавить службу Service Fabric.
В диалоговом окне "Добавить службу" выберите службу с отслеживанием состояния и назовите службу VotingDataService и выберите "Добавить службу".
После создания проекта службы в вашем приложении есть две службы. При продолжении сборки приложения можно добавлять дополнительные службы таким же образом. Каждая из них может быть независимо версионирована и обновлена.
Eclipse создает проект службы и отображает его в обозревателе пакетов.
Добавление файла VotingDataService.java
Файл VotingDataService.java содержит методы, содержащие логику для извлечения, добавления и удаления голосов из надежных коллекций. Добавьте следующие методы класса VotingDataService в файл 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()));
}
}
Теперь создается скелет для интерфейсной службы без отслеживания состояния и серверной службы.
Создание интерфейса связи для приложения
Следующий шаг — подключение бесстатусной фронтенд-службы и бэкенд-сервиса. Обе службы используют интерфейс с именем VotingRPC, который определяет операции приложения Voting. Этот интерфейс реализуется как клиентскими, так и серверными службами для обеспечения удаленных вызовов процедур (RPC) между двумя службами. К сожалению, Eclipse не поддерживает добавление вложенных проектов Gradle, поэтому пакет, содержащий этот интерфейс, необходимо добавить вручную.
Щелкните правой кнопкой мыши проект голосования в обозревателе пакетов и выберите "Создать>папку". Назовите папку VotingRPC/src/rpcmethods.
Создайте файл в разделе Voting/VotingRPC/src/rpcmethods с именем VotingRPC.java и вставьте следующий файл в файл 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); }Создайте пустой файл с именем build.gradle в каталоге Voting/VotingRPC и вставьте в него следующий файл. Этот файл gradle используется для сборки и создания jar-файла, импортируемого другими службами.
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'В файле Voting/settings.gradle добавьте строку, чтобы включить созданный проект в сборку.
include ':VotingRPC'В файле Voting/VotingWeb/src/statelessservice/HttpCommunicationListener.java замените блок комментариев следующим образом.
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); } } });Добавьте соответствующую инструкцию импорта в верхней части файла Voting/VotingWeb/src/statelessservice/HttpCommunicationListener.java .
import rpcmethods.VotingRPC;
На этом этапе функциональность для фронтенда, серверной части и RPC-интерфейсов завершена. Следующий этап — настроить скрипты Gradle соответствующим образом перед развертыванием в кластере Service Fabric.
Обзор примера приложения для голосования
Приложение для голосования состоит из двух служб:
- Веб-интерфейсная служба (VotingWeb) — веб-интерфейсная служба Java, которая служит веб-страницей и предоставляет API для взаимодействия с серверной службой.
- Серверная служба (VotingDataService) — веб-служба Java, которая определяет методы, которые вызываются с помощью удаленных вызовов процедур (RPC) для сохранения голосов.
При выполнении действия в приложении (добавление элемента, голосование, удаление элемента) происходят следующие события:
JavaScript отправляет соответствующий запрос в веб-API в веб-интерфейсной службе в виде HTTP-запроса.
Веб-интерфейсная служба использует встроенные функции удаленного взаимодействия служб Service Fabric для поиска и пересылки запроса в серверную службу.
Задний сервис определяет методы, которые обновляют результат в этом надежном словаре. Содержимое этого надежного словаря реплицируется на несколько узлов в кластере и сохраняется на диске. Все данные приложения хранятся в кластере.
Настройка скриптов Gradle
В этом разделе настраиваются скрипты Gradle для проекта.
Замените содержимое файла Voting/build.gradle следующим образом.
apply plugin: 'java' apply plugin: 'eclipse' subprojects { apply plugin: 'java' } defaultTasks 'clean', 'jar', 'copyDeps'Замените содержимое файла Voting/VotingWeb/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/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'Замените содержимое файла 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'
Развертывание приложения в локальном кластере
На этом этапе приложение готово к развертыванию в локальном кластере Service Fabric.
Щелкните правой кнопкой мыши проект голосования в обозревателе пакетов и выберите Service Fabric>Build Application для создания приложения.
Запустите локальный кластер Service Fabric. Этот шаг зависит от среды разработки (Mac или Linux).
Если вы используете Mac, запустите локальный кластер с помощью следующей команды: замените команду, переданную в параметр -v , путем к собственной рабочей области.
docker run -itd -p 19080:19080 -p 8080:8080 -p --name sfonebox mcr.microsoft.com/service-fabric/onebox:latestДополнительные инструкции см. в руководстве по настройке OS X.
Если вы работаете на компьютере Linux, запустите локальный кластер с помощью следующей команды:
sudo /opt/microsoft/sdk/servicefabric/common/clustersetup/devclustersetup.shДополнительные инструкции см. в руководстве по настройке Linux.
В обозревателе пакетов для Eclipse щелкните правой кнопкой мыши проект голосования и выберите"Публикация приложения>"
В окне "Опубликовать приложение " выберите Local.jsonв раскрывающемся списке и выберите "Опубликовать".
Откройте ваш веб-браузер и перейдите по адресу http://localhost:8080, чтобы посмотреть ваше запущенное приложение в локальном кластере Service Fabric.
Дальнейшие действия
В этой части руководства вы узнали, как:
- Создайте надежную сервисную службу Java с отслеживанием состояния.
- Создать службу Java как веб-службу без сохранения состояния
- Добавление интерфейса Java для обработки удаленных вызовов процедур (RPC) между службами
- Настройка скриптов Gradle
- Создание и развертывание приложения в локальном кластере Service Fabric
Перейдите к следующему уроку: