Java SDK提供TensorFlow輸入輸出封裝、佇列服務非同步呼叫等能力,支援字串和Byte數組等多種請求格式。本文介紹各介面詳情並提供完整程式樣本。
關於SDK的適用情境、調用原理,請參見服務調用SDK。
前置準備
在Maven工程中使用EAS Java SDK,需在pom.xml檔案<dependencies>中添加eas-sdk的依賴,樣本如下,最新版本以Maven倉庫中顯示的為準。
<dependency>
<groupId>com.aliyun.openservices.eas</groupId>
<artifactId>eas-sdk</artifactId>
<version>2.0.20</version>
</dependency>EAS2.0.5及以上版本增加了QueueService用戶端功能,支援多優先順序非同步佇列服務。如果需要使用該功能,為避免依賴版本衝突,您還需自行添加如下兩個依賴,並修改這兩個依賴至合適版本:
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>快速上手
使用Java SDK完成一次服務調用,端到端涉及三步:
擷取調用資訊:在EAS控制台的服務詳情頁 > 調用資訊中,擷取目標服務的Endpoint、服務名稱和Token。
選擇請求類型並編寫代碼:根據模型輸入資料格式,選擇對應的Request/Response類,參考下文最小樣本完成代碼編寫。
說明使用預置Processor部署的,SDK提供了對應的輸入輸出類(如預置Tensorflow Processor對應TFRequest),請參見預置Processor目錄下各Processor詳情介紹。
運行驗證:執行用戶端程式,檢查返回結果是否符合預期;如有異常,參考下文常見問題排查。
以下是最簡單的字串請求端到端調用樣本,更多請參見程式樣本:
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
public class TestString {
public static void main(String[] args) throws Exception {
PredictClient client = new PredictClient(new HttpConfig());
// VPC高速直連需使用setDirectEndpoint方法。格式通常為 {uid}.vpc.{region-id}.pai-eas.aliyuncs.com
client.setDirectEndpoint("182848887922****.vpc.cn-shanghai.aliyuncs.com");
// EAS服務公網終端地址,格式通常為 {uid}.{region-id}.pai-eas.aliyuncs.com
// client.setEndpoint("182848887922****.cn-shanghai.pai-eas.aliyuncs.com");
// EAS服務名稱
client.setModelName("your_service_name");
client.setToken("YOUR_SERVICE_TOKEN");
// 請求路徑為 http://<endpoint>/api/direct/<modelName>/<requestPath>
client.setRequestPath("your_custom_path");
// 構造請求body,支援的輸入類以sdk為準,這裡以 String 為例
String request = "[{}]";
String response = client.predict(request);
System.out.println(response);
client.shutdown();
}
}介面列表
Java SDK 提供以下介面類:
分組 | 類說明 |
用戶端主類 | PredictClient:主類。佈建服務資訊(Endpoint / ModelName / Token)、發送請求、接收響應。 |
串連配置 | HttpConfig:HTTP 串連參數配置(逾時、最大串連數等)。 |
輸入輸出 |
|
佇列服務 |
|
類PredictClient
用戶端主類,用於佈建服務資訊、發送請求和接收預測結果。
介面 | 描述 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 功能:自訂請求 URL。 |
|
|
|
|
|
|
|
|
|
|
|
|
類HttpConfig
用於配置底層的HTTP串連參數,如逾時時間、線程數和串連池。
介面 | 描述 |
|
|
|
重要 這是建立串連後的等待逾時時間,需要和setRequestTimeout方法做區分。 |
|
重要 這是請求發起到響應完成的完整鏈路逾時時間(包括串連建立、資料轉送、伺服器處理等所有階段),需要和setReadTimeout方法做區分。 |
|
|
|
|
|
|
|
|
| 返回最近一次調用的狀態代碼。 |
| 返回最近一次調用的狀態資訊。 |
類TFRequest
用於構建TensorFlow模型的輸入資料。
介面 | 描述 |
|
|
|
|
|
|
類TFResponse
用於解析TensorFlow模型的輸出資料。
介面 | 描述 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
類QueueClient
用於與EAS佇列服務進行互動,實現資料的生產、消費和管理。
介面 | 描述 |
|
|
|
|
|
|
|
|
|
|
|
|
|
重要 在使用QueueClient用戶端調用search介面時,必須將group id設定為對應的服務名(如下所示)。否則,search返回的結果中IsPending將始終為false。
|
|
|
|
|
| 功能:關閉佇列服務。 |
類DataFrame
佇列服務中資料項目的封裝對象。
介面 | 描述 |
|
|
|
|
|
|
程式樣本
同步推理樣本(按輸入輸出格式)
根據服務的輸入輸出類型,選擇對應的範例程式碼。
字串
對於使用自訂Processor部署服務的使用者而言,通常採用字串進行服務調用(例如,PMML模型服務的調用),具體的Demo程式如下。
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
public class TestString {
public static void main(String[] args) throws Exception {
// 啟動並初始化用戶端, client對象需要共用,千萬不可每個請求都建立一個client對象。
PredictClient client = new PredictClient(new HttpConfig());
client.setToken("YWFlMDYyZDNmNTc3M2I3MzMwYmY0MmYwM2Y2MTYxMTY4NzBkNzdj****");
// 如果要使用網路直連功能,需使用setDirectEndpoint方法
// 如 client.setDirectEndpoint("182848887922****.vpc.cn-shanghai.aliyuncs.com");
// 網路直連需打通在EAS控制台開通,提供用於訪問EAS服務的源vswitch,打通後可繞過網關以軟負載的方式直接存取服務的執行個體,以實現更好的穩定性和效能。
// 註:普通網關訪問時請使用以使用者uid為開頭的endpoint,在eas控制台服務的調用資訊中可查到。直連訪問時請使用如上的182848887922****.vpc.{region_id}.aliyuncs.com的網域名稱進行訪問。
client.setEndpoint("182848887922****.vpc.cn-shanghai.pai-eas.aliyuncs.com");
client.setModelName("scorecard_pmml_example");
// 輸入字串定義
String request = "[{\"money_credit\": 3000000}, {\"money_credit\": 10000}]";
System.out.println(request);
// 通過eas返回字串
try {
String response = client.predict(request);
System.out.println(response);
} catch (Exception e) {
e.printStackTrace();
}
// 關閉用戶端
client.shutdown();
return;
}
}TensorFlow
使用TensorFlow的使用者,需要將TFRequest和TFResponse分別作為輸入和輸出資料格式,具體Demo樣本如下。
import java.util.List;
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
import com.aliyun.openservices.eas.predict.request.TFDataType;
import com.aliyun.openservices.eas.predict.request.TFRequest;
import com.aliyun.openservices.eas.predict.response.TFResponse;
public class TestTF {
public static TFRequest buildPredictRequest() {
TFRequest request = new TFRequest();
request.setSignatureName("predict_images");
float[] content = new float[784];
for (int i = 0; i < content.length; i++) {
content[i] = (float) 0.0;
}
request.addFeed("images", TFDataType.DT_FLOAT, new long[]{1, 784}, content);
request.addFetch("scores");
return request;
}
public static void main(String[] args) throws Exception {
PredictClient client = new PredictClient(new HttpConfig());
// 如果要使用網路直連功能,需使用setDirectEndpoint方法,endpoint格式為:{uid}.vpc.{region_id}.aliyuncs.com
// client.setDirectEndpoint("182848887922****.vpc.cn-shanghai.aliyuncs.com");
// 普通網關訪問時請使用以使用者uid為開頭的endpoint,在eas控制台服務的調用資訊中可查到。
client.setEndpoint("182848887922****.vpc.cn-shanghai.pai-eas.aliyuncs.com");
client.setModelName("mnist_saved_model_example");
client.setToken("YTg2ZjE0ZjM4ZmE3OTc0NzYxZDMyNmYzMTJjZTQ1YmU0N2FjMTAy****");
long startTime = System.currentTimeMillis();
int count = 1000;
for (int i = 0; i < count; i++) {
try {
TFResponse response = client.predict(buildPredictRequest());
List<Float> result = response.getFloatVals("scores");
System.out.print("Predict Result: [");
for (int j = 0; j < result.size(); j++) {
System.out.print(result.get(j).floatValue());
if (j != result.size() - 1) {
System.out.print(", ");
}
}
System.out.print("]\n");
} catch (Exception e) {
e.printStackTrace();
}
}
long endTime = System.currentTimeMillis();
System.out.println("Spend Time: " + (endTime - startTime) + "ms");
client.shutdown();
}
}佇列服務樣本
通過QueueClient介面使用佇列服務,樣本如下:
import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
import com.aliyun.openservices.eas.predict.http.QueueClient;
import com.aliyun.openservices.eas.predict.queue_client.QueueUser;
import com.aliyun.openservices.eas.predict.queue_client.WebSocketWatcher;
public class DemoWatch {
public static void main(String[] args) throws Exception {
/** 建立佇列服務用戶端 */
String queueEndpoint = "18*******.cn-hangzhou.pai-eas.aliyuncs.com";
String inputQueueName = "test_queue_service";
String sinkQueueName = "test_queue_service/sink";
String queueToken = "test-token";
/** 輸入隊列,往輸入隊列添加資料,推理服務會自動從輸入隊列中讀取請求資料 */
QueueClient inputQueue =
new QueueClient(queueEndpoint, inputQueueName, queueToken, new HttpConfig(), new QueueUser());
/** 輸出隊列,推理服務處理輸入資料後會將結果寫入輸出隊列 */
QueueClient sinkQueue =
new QueueClient(queueEndpoint, sinkQueueName, queueToken, new HttpConfig(), new QueueUser());
/** 清除隊列資料!!!請謹慎使用 */
inputQueue.clear();
sinkQueue.clear();
/** 往輸入隊列添加資料 */
int count = 10;
for (int i = 0; i < count; ++i) {
String data = Integer.toString(i);
inputQueue.put(data.getBytes(), null);
/** 佇列服務支援多優先順序隊列,可通過put函數設定資料優先順序,預設優先順序為0 */
// inputQueue.put(data.getBytes(), 0L, null);
}
/** 通過watch函數訂閱輸出隊列的資料,視窗大小為5 */
WebSocketWatcher watcher = sinkQueue.watch(0L, 5L, false, true, null);
/** WatchConfig參數可自訂重試次數、稍候再試(單位為秒)、是否無限重試;未配置WatchConfig則預設重試次數:3,稍候再試:5 */
// WebSocketWatcher watcher = sink_queue.watch(0L, 5L, false, true, null, new WatchConfig(3, 1));
// WebSocketWatcher watcher = sink_queue.watch(0L, 5L, false, true, null, new WatchConfig(true, 10));
/** 擷取輸出資料 */
for (int i = 0; i < count; ++i) {
try {
/** getDataFrame 函數用於擷取DataFrame資料類,沒有資料時會被阻塞 */
byte[] data = watcher.getDataFrame().getData();
System.out.println("[watch] data = " + new String(data));
} catch (RuntimeException ex) {
System.out.println("[watch] error = " + ex.getMessage());
break;
}
}
/** 關閉已經開啟的watcher對象,每個用戶端執行個體只允許存在一個watcher對象,若watcher對象不關閉,再運行時會報錯 */
watcher.close();
Thread.sleep(2000);
JSONObject attrs = sinkQueue.attributes();
System.out.println(attrs.toString());
/** 關閉用戶端 */
inputQueue.shutdown();
sinkQueue.shutdown();
}
}使用Java SDK調用佇列服務的流程如下:
通過
QueueClient介面建立佇列服務用戶端對象。如果建立了推理服務,需同時建立輸入隊列和輸出隊列對象。使用
put()函數向輸入隊列中發送資料;使用watch()函數從輸出隊列中訂閱資料。說明現實情境中,發送資料和訂閱資料可以由不同的線程處理,本樣本中為了示範方便,在同一線程中完成,先Put資料,後Watch結果。
請求資料壓縮樣本
對於請求資料量較大的情況,EAS支援將資料壓縮之後再發送至服務端,目前支援Zlib和Gzip兩種壓縮格式。該功能需要在服務配置中指定相應的rpc.decompressor才會生效。
服務配置如下所示:
"metadata": {
"rpc": {
"decompressor": "zlib"
}
}SDK代碼調用樣本如下:
package com.aliyun.openservices.eas.predict;
import com.aliyun.openservices.eas.predict.http.Compressor;
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
public class TestString {
public static void main(String[] args) throws Exception{
// 啟動並初始化用戶端。
PredictClient client = new PredictClient(new HttpConfig());
client.setEndpoint("18*******.cn-hangzhou.pai-eas.aliyuncs.com");
client.setModelName("echo_compress");
client.setToken("YzZjZjQwN2E4NGRkMDMxNDk5NzhhZDcwZDBjOTZjOGYwZDYxZGM2****");
// 或者使用Compressor.Gzip。
client.setCompressor(Compressor.Zlib);
// 輸入字串定義。
String request = "[{\"money_credit\": 3000000}, {\"money_credit\": 10000}]";
System.out.println(request);
// 通過eas返回字串。
String response = client.predict(request);
System.out.println(response);
// 關閉用戶端。
client.shutdown();
return;
}
}常見問題排查
Java SDK調用異常的現象、原因與排查方向(鑒權、路由、串連、服務端類等共性問題)請參見服務調用SDK的"調用異常排查"章節。
完整的服務狀態代碼、錯誤資訊含義與處理建議請參見附錄:服務狀態代碼與常見報錯。