提交 e1d7a0f1 编写于 作者: X xulongteng

Merge branch 'develop' of https://github.com/PaddlePaddle/Serving into develop

......@@ -75,7 +75,6 @@ include(generic)
include(flags)
if (NOT CLIENT_ONLY)
include(external/mklml)
include(paddlepaddle)
include(external/opencv)
endif()
......
# Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
IF(NOT ${WITH_MKLML})
return()
ENDIF(NOT ${WITH_MKLML})
IF(APPLE)
MESSAGE(WARNING "Mac is not supported with MKLML in Paddle yet. Force WITH_MKLML=OFF.")
SET(WITH_MKLML OFF CACHE STRING "Disable MKLML package in MacOS" FORCE)
return()
ENDIF()
INCLUDE(ExternalProject)
SET(MKLML_DST_DIR "mklml")
SET(MKLML_INSTALL_ROOT "${THIRD_PARTY_PATH}/install")
SET(MKLML_INSTALL_DIR ${MKLML_INSTALL_ROOT}/${MKLML_DST_DIR})
SET(MKLML_ROOT ${MKLML_INSTALL_DIR})
SET(MKLML_INC_DIR ${MKLML_ROOT}/include)
SET(MKLML_LIB_DIR ${MKLML_ROOT}/lib)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}" "${MKLML_ROOT}/lib")
SET(TIME_VERSION "2019.0.1.20181227")
IF(WIN32)
SET(MKLML_VER "mklml_win_${TIME_VERSION}" CACHE STRING "" FORCE)
SET(MKLML_URL "https://paddlepaddledeps.cdn.bcebos.com/${MKLML_VER}.zip" CACHE STRING "" FORCE)
SET(MKLML_LIB ${MKLML_LIB_DIR}/mklml.lib)
SET(MKLML_IOMP_LIB ${MKLML_LIB_DIR}/libiomp5md.lib)
SET(MKLML_SHARED_LIB ${MKLML_LIB_DIR}/mklml.dll)
SET(MKLML_SHARED_IOMP_LIB ${MKLML_LIB_DIR}/libiomp5md.dll)
ELSE()
SET(MKLML_VER "mklml_lnx_${TIME_VERSION}" CACHE STRING "" FORCE)
SET(MKLML_URL "http://paddlepaddledeps.cdn.bcebos.com/${MKLML_VER}.tgz" CACHE STRING "" FORCE)
SET(MKLML_LIB ${MKLML_LIB_DIR}/libmklml_intel.so)
SET(MKLML_IOMP_LIB ${MKLML_LIB_DIR}/libiomp5.so)
SET(MKLML_SHARED_LIB ${MKLML_LIB_DIR}/libmklml_intel.so)
SET(MKLML_SHARED_IOMP_LIB ${MKLML_LIB_DIR}/libiomp5.so)
ENDIF()
SET(MKLML_PROJECT "extern_mklml")
MESSAGE(STATUS "MKLML_VER: ${MKLML_VER}, MKLML_URL: ${MKLML_URL}")
SET(MKLML_SOURCE_DIR "${THIRD_PARTY_PATH}/mklml")
SET(MKLML_DOWNLOAD_DIR "${MKLML_SOURCE_DIR}/src/${MKLML_PROJECT}")
ExternalProject_Add(
${MKLML_PROJECT}
${EXTERNAL_PROJECT_LOG_ARGS}
PREFIX ${MKLML_SOURCE_DIR}
URL ${MKLML_URL}
DOWNLOAD_DIR ${MKLML_DOWNLOAD_DIR}
DOWNLOAD_NO_PROGRESS 1
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
UPDATE_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND} -E copy_directory ${MKLML_DOWNLOAD_DIR}/include ${MKLML_INC_DIR} &&
${CMAKE_COMMAND} -E copy_directory ${MKLML_DOWNLOAD_DIR}/lib ${MKLML_LIB_DIR}
)
INCLUDE_DIRECTORIES(${MKLML_INC_DIR})
ADD_LIBRARY(mklml SHARED IMPORTED GLOBAL)
SET_PROPERTY(TARGET mklml PROPERTY IMPORTED_LOCATION ${MKLML_LIB})
ADD_DEPENDENCIES(mklml ${MKLML_PROJECT})
LIST(APPEND external_project_dependencies mklml)
......@@ -16,8 +16,8 @@ package main
import (
"fmt"
"github.com/docopt/docopt-go"
"github.com/Badangel/logex"
"github.com/docopt/docopt-go"
"os"
"path/filepath"
"runtime"
......@@ -64,6 +64,7 @@ Log options:
// settings:
if opts["-p"] == nil {
logex.Fatal("ERROR: -p PORT must be set!")
fmt.Fprintln(os.Stderr, "ERROR: -p PORT must be set!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -72,6 +73,7 @@ Log options:
logex.Notice(">>> port:", transfer.Port)
if opts["--config"] == nil {
logex.Fatal("ERROR: --config config_file must be set!")
fmt.Fprintln(os.Stderr, "ERROR: --config config_file must be set!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -81,6 +83,7 @@ Log options:
configMgr.Init(opts["--config"].(string))
transfer.Dict.DictName = configMgr.Read("default", "dict_name")
if transfer.Dict.DictName == "" {
logex.Fatal("ERROR: nead [default] DictName in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] DictName in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -89,6 +92,7 @@ Log options:
transfer.Dict.DictMode = configMgr.Read("default", "mode")
if transfer.Dict.DictMode == "" {
logex.Fatal("ERROR: nead [default] DictMode in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] DictMode in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -97,28 +101,31 @@ Log options:
transfer.Dict.DownloadMode = configMgr.Read("default", "download_mode")
if transfer.Dict.DownloadMode != "http" && transfer.Dict.DownloadMode != "ftp" {
logex.Fatal("ERROR: nead [default] download_mode in config_file! only support ftp or http")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] download_mode in config_file! only support ftp or http")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
}
logex.Notice(">>> DownloadMode:", transfer.Dict.DownloadMode)
transfer.Dict.WgetPort = configMgr.Read("default", "wget_port")
if transfer.Dict.WgetPort == "" {
fmt.Fprintln(os.Stderr, "ERROR: nead [default] wget_port in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
}
var wget_port int
wget_port, err = strconv.Atoi(transfer.Dict.WgetPort)
if err != nil {
logex.Fatal("wget_port form is not right need int")
os.Exit(1)
}
logex.Notice(">>> WgetPort:", wget_port)
transfer.Dict.WgetPort = configMgr.Read("default", "wget_port")
if transfer.Dict.WgetPort == "" {
logex.Fatal("ERROR: nead [default] wget_port in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] wget_port in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
}
var wget_port int
wget_port, err = strconv.Atoi(transfer.Dict.WgetPort)
if err != nil {
logex.Fatal("wget_port form is not right need int")
os.Exit(1)
}
logex.Notice(">>> WgetPort:", wget_port)
transfer.BuildToolLocal = configMgr.Read("default", "buildtool_local")
if transfer.BuildToolLocal == "" {
logex.Fatal("ERROR: nead [default] BuildToolLocal in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] BuildToolLocal in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -127,6 +134,7 @@ Log options:
transfer.Dict.DonefileAddress = configMgr.Read("default", "donefile_address")
if transfer.Dict.DonefileAddress == "" {
logex.Fatal("ERROR: nead [default] DonefileAddress in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] DonefileAddress in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -135,6 +143,7 @@ Log options:
transfer.Dict.OutputAddress = configMgr.Read("default", "output_address")
if transfer.Dict.OutputAddress == "" {
logex.Fatal("ERROR: nead [default] OutputAddress in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] OutputAddress in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -143,6 +152,7 @@ Log options:
transfer.Dict.TmpAddress = configMgr.Read("default", "tmp_address")
if transfer.Dict.TmpAddress == "" {
logex.Fatal("ERROR: nead [default] TmpAddress in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] TmpAddress in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -151,6 +161,7 @@ Log options:
ShardNumStr := configMgr.Read("default", "shard_num")
if ShardNumStr == "" {
logex.Fatal("ERROR: nead [default] ShardNum in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] ShardNum in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -164,6 +175,7 @@ Log options:
CopyNumStr := configMgr.Read("default", "copy_num")
if CopyNumStr == "" {
logex.Fatal("ERROR: nead [default] CopyNum in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] CopyNum in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -179,6 +191,7 @@ Log options:
transfer.Dict.DeployPath = configMgr.Read("default", "deploy_path")
if transfer.Dict.DeployPath == "" {
logex.Fatal("ERROR: nead [default] DeployPath in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] DeployPath in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -187,6 +200,7 @@ Log options:
transfer.TransferAddr = configMgr.Read("default", "transfer_address")
if transfer.TransferAddr == "" {
logex.Fatal("ERROR: nead [default] TransferAddr in config_file!")
fmt.Fprintln(os.Stderr, "ERROR: nead [default] TransferAddr in config_file!")
fmt.Fprintln(os.Stderr, usage)
os.Exit(1)
......@@ -199,9 +213,17 @@ Log options:
agentName := fmt.Sprintf("agent%d_%d", i, j)
agentInfo := configMgr.Read("cube_agent", agentName)
agentInfoSlice := strings.Split(agentInfo, ":")
if len(agentInfoSlice) != 2 {
logex.Fatal("agent conf format not right! sample: ip:port")
os.Exit(1)
}
cubeName := fmt.Sprintf("cube%d_%d", i, j)
cubeInfo := configMgr.Read("cube_agent", cubeName)
cubeInfoSlice := strings.Split(cubeInfo, ":")
if len(cubeInfoSlice) != 3 {
logex.Fatal("cube conf format not right! sample: ip:port:deploy_path")
os.Exit(1)
}
instance.DictName = transfer.Dict.DictName
instance.AgentIp = agentInfoSlice[0]
instance.AgentPort, _ = strconv.Atoi(agentInfoSlice[1])
......
......@@ -52,7 +52,16 @@ func GetDoneFileInfo(addr string) (version dict.DictVersionInfo, err error) {
} else {
contentss := string(contents)
lines := strings.Split(contentss, "\n")
index := len(lines) - 2
index := len(lines) - 1
//one line length smaller than 3 maybe blank or return
for len(lines[index]) < 3 {
index--
}
if index < 0 {
logex.Noticef("[trigrer]get base donfile info error")
err = fmt.Errorf("[trigrer]get base donfile info error")
return
}
var donefileInfo dict.DonefileInfo
fmt.Printf("line %v: %v\n", index, lines[index])
if err = json.Unmarshal([]byte(lines[index]), &donefileInfo); err != nil {
......@@ -83,7 +92,13 @@ func GetDoneFileInfo(addr string) (version dict.DictVersionInfo, err error) {
} else {
contentss := string(contents)
lines := strings.Split(contentss, "\n")
for index := 0; index < len(lines)-1; index++ {
if len(lines[index]) < 3 {
logex.Noticef("[trigrer]get patch donfile info error")
err = fmt.Errorf("[trigrer]get patch donfile info error")
return
}
var donefileInfo dict.DonefileInfo
if err = json.Unmarshal([]byte(lines[index]), &donefileInfo); err != nil {
return
......
......@@ -77,6 +77,10 @@ install(TARGETS echo
RUNTIME DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/demo/client/echo/bin)
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/conf DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/client/echo/)
install(FILES ${CMAKE_CURRENT_LIST_DIR}/python/echo.py
DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/demo/client/echo/python)
install(FILES ${CMAKE_CURRENT_LIST_DIR}/php/echo.php
DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/demo/client/echo/php)
install(TARGETS echo_kvdb
RUNTIME DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/demo/client/echo_kvdb/bin)
......@@ -108,6 +112,14 @@ install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/conf DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/client/text_classification/)
install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/data/text_classification DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/client/text_classification/data)
install(FILES ${CMAKE_CURRENT_LIST_DIR}/python/text_classification.py
DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/client/text_classification/python)
install(FILES ${CMAKE_CURRENT_LIST_DIR}/php/text_classification.php
DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/client/text_classification/php)
install(TARGETS ctr_prediction
RUNTIME DESTINATION
......
<?
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
function http_post($url, $data) {
// array to json string
$data_string = json_encode($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
// post data 封装
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
// true是获取文本,不直接输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 强制curl不使用100-continue
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
// set header
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
// 执行
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
//key value 数组,如果多,后面用逗号分开key =>value ,key1 => value1 ,....
echo http_post('http://127.0.0.1:8010/BuiltinTestEchoService/inference', array("a" => 1, "b" => 0.5));
?>
<?
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
define(BATCH_SIZE, "10");
function read_data($data_file, &$samples, &$labels) {
$handle = fopen($data_file, "r");
$search = array("(", ")", "[", "]");
$count = 0;
while (($buffer = fgets($handle)) !== false) {
$count++;
$buffer = str_ireplace($search, "", $buffer);
$x = explode(",", $buffer);
$ids = array();
for ($i = 0; $i < count($x); ++$i) {
$ids[] = (int)($x[$i]);
}
$label = array_slice($ids, count($ids) - 1);
$sample = array_slice($ids, 0, count($ids) - 1);
$samples[] = array("ids" => $sample);
$labels[] = $label;
unset($x);
unset($buffer);
unset($ids);
unset($sample);
unset($label);
}
if (!feof($handle)) {
echo "Unexpected fgets() fail";
return -1;
}
fclose($handle);
}
function &http_connect($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
// true是获取文本,不直接输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 强制curl不使用100-continue
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
// set header
curl_setopt($ch,
CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json'
)
);
return $ch;
}
function http_post(&$ch, $data) {
// array to json string
$data_string = json_encode($data);
// post data 封装
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
// set header
curl_setopt($ch,
CURLOPT_HTTPHEADER,
array(
'Content-Length: ' . strlen($data_string)
)
);
// 执行
$result = curl_exec($ch);
return $result;
}
if ($argc != 2) {
echo "Usage: php text_classification.php DATA_SET_FILE\n";
return -1;
}
ini_set('memory_limit', '-1');
$samples = array();
$labels = array();
read_data($argv[1], $samples, $labels);
echo count($samples) . "\n";
$ch = &http_connect('http://127.0.0.1:8010/TextClassificationService/inference');
$count = 0;
for ($i = 0; $i < count($samples) - BATCH_SIZE; $i += BATCH_SIZE) {
$instances = array_slice($samples, $i, BATCH_SIZE);
echo http_post($ch, array("instances" => $instances)) . "\n";
}
curl_close($ch);
?>
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import urllib2
data = {"a": 1, "b": 0.5}
request_json = json.dumps(data)
req = urllib2.Request("http://127.0.0.1:8010/BuiltinTestEchoService/inference")
try:
response = urllib2.urlopen(req, request_json, 1)
print response.read()
except urllib2.HTTPError as e:
print e.reason
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import httplib
import sys
import os
BATCH_SIZE = 10
def data_reader(data_file, samples, labels):
if not os.path.exists(data_file):
print "Path %s not exist" % data_file
return -1
with open(data_file, "r") as f:
for line in f:
line = line.replace('(', ' ')
line = line.replace(')', ' ')
line = line.replace('[', ' ')
line = line.replace(']', ' ')
ids = line.split(',')
ids = [int(x) for x in ids]
label = ids[-1]
ids = ids[0:-1]
samples.append(ids)
labels.append(label)
if __name__ == "__main__":
""" main
"""
if len(sys.argv) != 2:
print "Usage: python text_classification.py DATA_FILE"
sys.exit(-1)
samples = []
labels = []
ret = data_reader(sys.argv[1], samples, labels)
conn = httplib.HTTPConnection("127.0.0.1", 8010)
for i in range(0, len(samples) - BATCH_SIZE, BATCH_SIZE):
batch = samples[i:i + BATCH_SIZE]
ids = []
for x in batch:
ids.append({"ids": x})
ids = {"instances": ids}
request_json = json.dumps(ids)
try:
conn.request('POST', "/TextClassificationService/inference",
request_json, {"Content-Type": "application/json"})
response = conn.getresponse()
print response.read()
except httplib.HTTPException as e:
print e.reason
......@@ -25,7 +25,12 @@ if (NOT EXISTS
endif()
include_directories(SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../kvdb/include)
find_library(MKLML_LIBS NAMES libmklml_intel.so libiomp5.so)
find_library(MKLML_LIB NAMES libmklml_intel.so PATHS
${CMAKE_BINARY_DIR}/Paddle/third_party/install/mklml/lib/)
find_library(MKLML_IOMP_LIB NAMES libiomp5.so PATHS
${CMAKE_BINARY_DIR}/Paddle/third_party/install/mklml/lib)
include(op/CMakeLists.txt)
include(proto/CMakeLists.txt)
add_executable(serving ${serving_srcs})
......@@ -75,8 +80,7 @@ install(FILES ${inc}
DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/include/serving)
if (${WITH_MKL})
install(FILES ${THIRD_PARTY_PATH}/install/mklml/lib/libmklml_intel.so
${THIRD_PARTY_PATH}/install/mklml/lib/libmklml_gnu.so
${THIRD_PARTY_PATH}/install/mklml/lib/libiomp5.so DESTINATION
install(FILES ${CMAKE_BINARY_DIR}/Paddle/third_party/install/mklml/lib/libmklml_intel.so
${CMAKE_BINARY_DIR}/Paddle/third_party/install/mklml/lib/libiomp5.so DESTINATION
${PADDLE_SERVING_INSTALL_DIR}/demo/serving/bin)
endif()
# HTTP Inferface
Paddle Serving服务均可以通过HTTP接口访问,客户端只需按照Service定义的Request消息格式构造json字符串即可。客户端构造HTTP请求,将json格式数据以POST请求发给serving端,serving端**自动**按Service定义的Protobuf消息格式,将json数据转换成protobuf消息。
本文档介绍以python和PHP语言访问Serving的HTTP服务接口的用法。
## 1. 访问地址
访问Serving节点的HTTP服务与C++服务使用同一个端口(例如8010),访问URL规则为:
```
http://127.0.0.1:8010/ServiceName/inference
http://127.0.0.1:8010/ServiceName/debug
```
其中ServiceName应该与Serving的配置文件`conf/services.prototxt`中配置的一致,假如有如下2个service:
```protobuf
services {
name: "BuiltinTestEchoService"
workflows: "workflow3"
}
services {
name: "TextClassificationService"
workflows: "workflow6"
}
```
则访问上述2个Serving服务的HTTP URL分别为:
```
http://127.0.0.1:8010/BuiltinTestEchoService/inference
http://127.0.0.1:8010/BuiltinTestEchoService/debug
http://127.0.0.1:8010/TextClassificationService/inference
http://127.0.0.1:8010/TextClassificationService/debug
```
## 2. Python访问HTTP Serving
Python语言访问HTTP Serving,关键在于构造json格式的请求数据,可以通过以下步骤完成:
1) 按照Service定义的Request消息格式构造python object
2) `json.dump()` / `json.dumps()` 等函数将python object转换成json格式字符串
以TextClassificationService为例,关键代码如下:
```python
# Connect to server
conn = httplib.HTTPConnection("127.0.0.1", 8010)
# samples是一个list,其中每个元素是一个ids字典:
# samples[0] = [190, 1, 70, 382, 914, 5146, 190...]
for i in range(0, len(samples) - BATCH_SIZE, BATCH_SIZE):
# 构建批量预测数据
batch = samples[i: i + BATCH_SIZE]
ids = []
for x in batch:
ids.append({"ids" : x})
ids = {"instances": ids}
# python object转成json
request_json = json.dumps(ids)
# 请求HTTP服务,打印response
try:
conn.request('POST', "/TextClassificationService/inference", request_json, {"Content-Type": "application/json"})
response = conn.getresponse()
print response.read()
except httplib.HTTPException as e:
print e.reason
```
完整示例请参考[text_classification.py](../demo-client/python/text_classification.py)
## 3. PHP访问HTTP Serving
PHP语言构造json格式字符串的步骤如下:
1) 按照Service定义的Request消息格式,构造PHP array
2) `json_encode()`函数将PHP array转换成json字符串
以TextCLassificationService为例,关键代码如下:
```PHP
function http_post(&$ch, $data) {
// array to json string
$data_string = json_encode($data);
// post data 封装
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
// set header
curl_setopt($ch,
CURLOPT_HTTPHEADER,
array(
'Content-Length: ' . strlen($data_string)
)
);
// 执行
$result = curl_exec($ch);
return $result;
}
$ch = &http_connect('http://127.0.0.1:8010/TextClassificationService/inference');
$count = 0;
# $samples是一个2层array,其中每个元素是一个如下array:
# $samples[0] = array(
# "ids" => array(
# [0] => int(190),
# [1] => int(1),
# [2] => int(70),
# [3] => int(382),
# [4] => int(914),
# [5] => int(5146),
# [6] => int(190)...)
# )
for ($i = 0; $i < count($samples) - BATCH_SIZE; $i += BATCH_SIZE) {
$instances = array_slice($samples, $i, BATCH_SIZE);
echo http_post($ch, array("instances" => $instances)) . "\n";
}
curl_close($ch);
```
完整代码请参考[text_classification.php](../demo-client/php/text_classification.php)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册