إشعار
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تسجيل الدخول أو تغيير الدلائل.
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تغيير الدلائل.
يُعد هذا البرنامج التعليمي جزءًا أول ضمن سلسلة. عند الانتهاء، يكون لديك تطبيق تصويت مع واجهة أمامية على ويب Java تحفظ نتائج التصويت في خدمة خلفية ذات حالة على Azure Service Fabric. تتطلب هذه السلسلة التعليمية أن يكون لديك جهاز مطور Mac OSX أو Linux يعمل. إذا كنت لا ترغب في إنشاء تطبيق التصويت يدويا، فيمكنك تنزيل التعليمات البرمجية المصدر للتطبيق المكتمل والتخطي إلى الاطلاع على نموذج تطبيق التصويت. ضع في اعتبارك أيضا اتباع خدمات التشغيل السريع لخدمات Java الموثوقة..
في هذه السلسلة التعليمية ، تتعلم كيفية:
- إنشاء تطبيق Java Service Fabric Reliable Services
- نشر التطبيق وتصحيحه على نظام مجموعة محلي
- توزيع التطبيق إلى نظام مجموعة Azure
- إعداد المراقبة والتشخيص للتطبيق
- إعداد CI/CD
في الجزء الأول من السلسلة، ستتعلم كيفية:
- إنشاء خدمة Java موثوقة بحالة
- إنشاء خدمة تطبيق ويب Java عديمة الحالة
- استخدام الخدمة التتبع عن بعد للتواصل مع الخدمة ذات الحالة
- نشر التطبيق على مجموعة Service Fabric محلية
المتطلبات الأساسية
قبل أن تبدأ هذا البرنامج التعليمي:
- إذا لم يكن لديك اشتراك Azure، فبادر بإنشاء حساب مجاني.
- قم بإعداد بيئة التطوير الخاصة بك لنظام التشغيل Mac أو Linux. اتبع الإرشادات لتثبيت المكون الإضافي Eclipse و Gradle و Service Fabric SDK و Service Fabric CLI (sfctl).
إنشاء خدمة Java عديمة الحالة للواجهة الأمامية
أولا، قم بإنشاء الواجهة الأمامية للويب لتطبيق التصويت. ترسل واجهة مستخدم الويب المدعومة من AngularJS طلبات إلى خدمة Java عديمة الحالة ، والتي تدير خادم HTTP خفيف الوزن. تعالج هذه الخدمة كل طلب وترسل استدعاء إجراء عن بعد إلى الخدمة ذات الحالة لتخزين الأصوات.
افتح Eclipse.
قم بإنشاء مشروع باستخدام ملف>مشروع>>جديد لبنية خدمة>.
في مربع الحوار معالج مشروع ServiceFabric ، قم بتسمية Project Voting وحدد التالي.
في صفحة إضافة خدمة ، حدد خدمة عديمة الحالة، وقم بتسمية شبكة التصويت الخاصة بخدمتك. حدد إنهاء لإنشاء المشروع.
يقوم Eclipse بإنشاء تطبيق ومشروع خدمة ويعرضهما في مستكشف الحزم.
يقدم الجدول وصفا موجزا لكل عنصر في مستكشف الحزم من لقطة الشاشة السابقة.
| عنصر مستكشف العبوات | وصف |
|---|---|
| PublishProfiles | يحتوي على ملفات JSON تصف تفاصيل ملف التعريف لمجموعات Azure Service Fabric المحلية. يتم استخدام محتويات هذه الملفات بواسطة المكون الإضافي عند نشر التطبيق. |
| البرامج النصيه | يحتوي على برامج نصية مساعدة يمكن استخدامها من سطر الأوامر لإدارة التطبيق بسرعة باستخدام نظام مجموعة. |
| التصويتتطبيق | يحتوي على تطبيق Service Fabric الذي يتم دفعه إلى مجموعة Service Fabric. |
| شبكة التصويت | يحتوي على ملفات مصدر الخدمة عديمة الحالة للواجهة الأمامية جنبا إلى جنب مع ملف إنشاء gradle ذي الصلة. |
| build.gradle | يستخدم ملف gradle لإدارة المشروع. |
| settings.gradle | يحتوي على أسماء مشاريع Gradle في هذا المجلد. |
إضافة HTML وJavaScript إلى خدمة VotingWeb
لإضافة واجهة مستخدم يمكن عرضها بواسطة الخدمة عديمة الحالة، أضف ملف HTML. ثم يتم عرض ملف HTML هذا بواسطة خادم HTTP خفيف الوزن المضمن في خدمة Java عديمة الحالة.
قم بتوسيع دليل VotingApplication للوصول إلى دليل VotingApplication/VotingWebPkg/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 هي البوابة إلى الخدمة عديمة الحالة وهي مسؤولة عن إعداد مستمع الاتصال لواجهة برمجة تطبيقات الواجهة الأمامية.
استبدل طريقة 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 وتعرض واجهات برمجة التطبيقات التي تحدد إجراءات التصويت. انقر بزر الماوس الأيمن على حزمة الخدمة عديمة الحالة في مجلد VotingWeb/src/statelessservice، ثم حدد ملف جديد>. قم بتسمية HttpCommunicationListener.java الملف وحدد إنهاء.
استبدل محتويات الملف بما يلي، ثم احفظ التغييرات. لاحقا، في تحديث ملف HttpCommunicationListener.java، يتم تعديل هذا الملف لعرض بيانات التصويت وقراءتها وكتابتها من الخدمة الخلفية. في الوقت الحالي ، يقوم المستمع ببساطة بإرجاع HTML الثابت لتطبيق التصويت.
// ------------------------------------------------------------
// 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>
إضافة خدمة خلفية ذات حالة إلى تطبيقك
الآن بعد اكتمال الهيكل العظمي لخدمة Java Web API ، دعنا نمضي قدما ونكمل خدمة الواجهة الخلفية ذات الحالة.
يتيح لك Service Fabric تخزين بياناتك بشكل متسق وموثوق داخل خدمتك مباشرة باستخدام مجموعات موثوقة. المجموعات الموثوقة هي مجموعة من فئات التجميع الموثوقة والمتوفرة للغاية. استخدام هذه الفئات مألوف لأي شخص استخدم مجموعات Java.
في مستكشف الحزم، انقر بزر الماوس الأيمن فوق التصويت داخل مشروع التطبيق وحدد Service Fabric>Add 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 تحدد عمليات تطبيق التصويت. يتم تنفيذ هذه الواجهة بواسطة كل من خدمات الواجهة الأمامية والخلفية لتمكين استدعاءات الإجراءات البعيدة (RPC) بين الخدمتين. لسوء الحظ ، لا يدعم Eclipse إضافة مشاريع فرعية Gradle ، لذلك يجب إضافة الحزمة التي تحتوي على هذه الواجهة يدويا.
انقر بزر الماوس الأيمن فوق مشروع التصويت في مستكشف الحزم وحدد مجلد جديد>. قم بتسمية المجلد VotingRPC/src/rpcmethods.
قم بإنشاء ملف ضمن Voting/VotingRPC/src/rpcNamesVotingRPC.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 تخدم صفحة الويب وتعرض واجهات برمجة التطبيقات للتواصل مع خدمة الواجهة الخلفية.
- خدمة الواجهة الخلفية (VotingDataService) - خدمة ويب Java، والتي تحدد الأساليب التي يتم استدعاؤها عبر استدعاءات الإجراءات البعيدة (RPC) لاستمرار التصويت.
عند تنفيذ إجراء في التطبيق (إضافة عنصر، تصويت، إزالة عنصر) تحدث الأحداث التالية:
يرسل JavaScript الطلب المناسب إلى واجهة برمجة تطبيقات الويب في خدمة الواجهة الأمامية للويب كطلب HTTP.
تستخدم خدمة الواجهة الأمامية على الويب وظيفة Service Reremote المضمنة في 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 المحلية. تعتمد هذه الخطوة على بيئة التطوير الخاصة بك (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 محلية
تقدم إلى البرنامج التعليمي الآتي: