server.py 31.1 KB
Newer Older
Z
update  
zhangjun 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
# Copyright (c) 2021 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.
M
MRXLT 已提交
14 15 16

import os
import tarfile
M
MRXLT 已提交
17
import socket
Z
zhangjun 已提交
18
import paddle_serving_server as paddle_serving_server
Z
zhangjun 已提交
19
from paddle_serving_server.rpc_service import MultiLangServerServiceServicer
Z
update  
zhangjun 已提交
20 21
from .proto import server_configure_pb2 as server_sdk
from .proto import general_model_config_pb2 as m_config
Z
zhangjun 已提交
22
from .proto import multi_lang_general_model_service_pb2_grpc
Z
update  
zhangjun 已提交
23
import google.protobuf.text_format
24
import time
25
from .version import version_tag, version_suffix, device_type
M
MRXLT 已提交
26
from contextlib import closing
G
guru4elephant 已提交
27
import argparse
Z
zhangjun 已提交
28

J
Jiawei Wang 已提交
29
import sys
W
wangjiawei04 已提交
30 31
if sys.platform.startswith('win') is False:
    import fcntl
M
MRXLT 已提交
32
import shutil
Z
update  
zhangjun 已提交
33
import platform
B
barrierye 已提交
34 35
import numpy as np
import grpc
B
barrierye 已提交
36
import sys
37
import collections
Z
update  
zhangjun 已提交
38
import subprocess
Z
zhangjun 已提交
39

B
barrierye 已提交
40 41 42
from multiprocessing import Pool, Process
from concurrent import futures

Z
update  
zhangjun 已提交
43

M
MRXLT 已提交
44 45
class Server(object):
    def __init__(self):
H
HexToString 已提交
46 47 48 49 50 51 52 53 54 55 56
        """
        self.model_toolkit_conf:'list'=[] # The quantity of self.model_toolkit_conf is equal to the InferOp quantity/Engine--OP
        self.model_conf:'collections.OrderedDict()' # Save the serving_server_conf.prototxt content (feed and fetch information) this is a map for multi-model in a workflow
        self.workflow_fn:'str'="workflow.prototxt" # Only one for one Service/Workflow
        self.resource_fn:'str'="resource.prototxt" # Only one for one Service,model_toolkit_fn and general_model_config_fn is recorded in this file
        self.infer_service_fn:'str'="infer_service.prototxt" # Only one for one Service,Service--Workflow
        self.model_toolkit_fn:'list'=[] # ["general_infer_0/model_toolkit.prototxt"]The quantity is equal to the InferOp quantity,Engine--OP
        self.general_model_config_fn:'list'=[] # ["general_infer_0/general_model.prototxt"]The quantity is equal to the InferOp quantity,Feed and Fetch --OP
        self.subdirectory:'list'=[] # The quantity is equal to the InferOp quantity, and name = node.name = engine.name
        self.model_config_paths:'collections.OrderedDict()' # Save the serving_server_conf.prototxt path (feed and fetch information) this is a map for multi-model in a workflow
        """
M
MRXLT 已提交
57 58
        self.server_handle_ = None
        self.infer_service_conf = None
H
HexToString 已提交
59
        self.model_toolkit_conf = []
M
MRXLT 已提交
60 61
        self.resource_conf = None
        self.memory_optimization = False
M
MRXLT 已提交
62
        self.ir_optimization = False
Z
zhangjun 已提交
63
        self.model_conf = collections.OrderedDict()
H
HexToString 已提交
64 65 66
        self.workflow_fn = "workflow.prototxt"
        self.resource_fn = "resource.prototxt"
        self.infer_service_fn = "infer_service.prototxt"
Z
zhangjun 已提交
67 68 69
        self.model_toolkit_fn = []
        self.general_model_config_fn = []
        self.subdirectory = []
W
wangjiawei04 已提交
70
        self.cube_config_fn = "cube.conf"
M
MRXLT 已提交
71 72
        self.workdir = ""
        self.max_concurrency = 0
M
MRXLT 已提交
73
        self.num_threads = 2
M
MRXLT 已提交
74
        self.port = 8080
75 76
        self.precision = "fp32"
        self.use_calib = False
M
MRXLT 已提交
77
        self.reload_interval_s = 10
M
MRXLT 已提交
78
        self.max_body_size = 64 * 1024 * 1024
M
MRXLT 已提交
79 80
        self.module_path = os.path.dirname(paddle_serving_server.__file__)
        self.cur_path = os.getcwd()
M
MRXLT 已提交
81
        self.use_local_bin = False
Z
zhangjun 已提交
82
        self.mkl_flag = False
Z
zhangjun 已提交
83
        self.device = "cpu"
84 85 86
        self.gpuid = []
        self.op_num = [0]
        self.op_max_batch = [32]
M
add trt  
MRXLT 已提交
87
        self.use_trt = False
88
        self.gpu_multi_stream = False
Z
zhangjun 已提交
89 90
        self.use_lite = False
        self.use_xpu = False
Z
zhangjun 已提交
91
        self.model_config_paths = collections.OrderedDict()
92 93
        self.product_name = None
        self.container_id = None
M
MRXLT 已提交
94

Z
zhangjun 已提交
95 96 97 98 99
    def get_fetch_list(self, infer_node_idx=-1):
        fetch_names = [
            var.alias_name
            for var in list(self.model_conf.values())[infer_node_idx].fetch_var
        ]
B
fix cpu  
barriery 已提交
100 101
        return fetch_names

M
MRXLT 已提交
102 103 104 105 106 107
    def set_max_concurrency(self, concurrency):
        self.max_concurrency = concurrency

    def set_num_threads(self, threads):
        self.num_threads = threads

M
MRXLT 已提交
108 109 110 111 112 113 114 115
    def set_max_body_size(self, body_size):
        if body_size >= self.max_body_size:
            self.max_body_size = body_size
        else:
            print(
                "max_body_size is less than default value, will use default value in service."
            )

116 117 118
    def use_encryption_model(self, flag=False):
        self.encryption_model = flag

M
MRXLT 已提交
119 120 121
    def set_port(self, port):
        self.port = port

122 123 124 125 126 127
    def set_precision(self, precision="fp32"):
        self.precision = precision

    def set_use_calib(self, use_calib=False):
        self.use_calib = use_calib

M
MRXLT 已提交
128 129 130 131 132 133
    def set_reload_interval(self, interval):
        self.reload_interval_s = interval

    def set_op_sequence(self, op_seq):
        self.workflow_conf = op_seq

B
barrierye 已提交
134 135 136
    def set_op_graph(self, op_graph):
        self.workflow_conf = op_graph

M
MRXLT 已提交
137 138 139
    def set_memory_optimize(self, flag=False):
        self.memory_optimization = flag

M
MRXLT 已提交
140 141 142
    def set_ir_optimize(self, flag=False):
        self.ir_optimization = flag

143
    # Multi-Server does not have this Function. 
144 145 146 147 148
    def set_product_name(self, product_name=None):
        if product_name == None:
            raise ValueError("product_name can't be None.")
        self.product_name = product_name

149
    # Multi-Server does not have this Function.
150 151 152 153 154
    def set_container_id(self, container_id):
        if container_id == None:
            raise ValueError("container_id can't be None.")
        self.container_id = container_id

M
MRXLT 已提交
155 156 157 158
    def check_local_bin(self):
        if "SERVING_BIN" in os.environ:
            self.use_local_bin = True
            self.bin_path = os.environ["SERVING_BIN"]
M
MRXLT 已提交
159

M
MRXLT 已提交
160
    def check_cuda(self):
M
MRXLT 已提交
161 162 163
        if os.system("ls /dev/ | grep nvidia > /dev/null") == 0:
            pass
        else:
M
MRXLT 已提交
164
            raise SystemExit(
M
MRXLT 已提交
165
                "GPU not found, please check your environment or use cpu version by \"pip install paddle_serving_server\""
M
MRXLT 已提交
166 167
            )

Z
zhangjun 已提交
168 169 170
    def set_device(self, device="cpu"):
        self.device = device

171
    def set_gpuid(self, gpuid):
H
HexToString 已提交
172 173 174 175 176 177 178
        if isinstance(gpuid, int):
            self.gpuid = str(gpuid)
        elif isinstance(gpuid, list):
            gpu_list = [str(x) for x in gpuid]
            self.gpuid = ",".join(gpu_list)
        else:
            self.gpuid = gpuid
M
MRXLT 已提交
179

180 181 182 183 184 185
    def set_op_num(self, op_num):
        self.op_num = op_num

    def set_op_max_batch(self, op_max_batch):
        self.op_max_batch = op_max_batch

M
bug fix  
MRXLT 已提交
186
    def set_trt(self):
M
add trt  
MRXLT 已提交
187 188
        self.use_trt = True

189 190 191
    def set_gpu_multi_stream(self):
        self.gpu_multi_stream = True

Z
zhangjun 已提交
192 193 194 195 196 197
    def set_lite(self):
        self.use_lite = True

    def set_xpu(self):
        self.use_xpu = True

H
HexToString 已提交
198
    def _prepare_engine(self, model_config_paths, device, use_encryption_model):
M
MRXLT 已提交
199
        if self.model_toolkit_conf == None:
H
HexToString 已提交
200
            self.model_toolkit_conf = []
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
        self.device = device

        if isinstance(self.gpuid, str):
            self.gpuid = [self.gpuid]
        if len(self.gpuid) == 0:
            if self.device == "gpu" or self.use_trt:
                self.gpuid.append("0")
            else:
                self.gpuid.append("-1")

        if isinstance(self.op_num, int):
            self.op_num = [self.op_num]
        if len(self.op_num) == 0:
            self.op_num.append(0)

        if isinstance(self.op_max_batch, int):
            self.op_max_batch = [self.op_max_batch]
        if len(self.op_max_batch) == 0:
            self.op_max_batch.append(32)

        index = 0
M
MRXLT 已提交
222

B
barrierye 已提交
223 224 225 226
        for engine_name, model_config_path in model_config_paths.items():
            engine = server_sdk.EngineDesc()
            engine.name = engine_name
            # engine.reloadable_meta = model_config_path + "/fluid_time_file"
227
            engine.reloadable_meta = model_config_path + "/fluid_time_file"
B
barrierye 已提交
228 229
            os.system("touch {}".format(engine.reloadable_meta))
            engine.reloadable_type = "timestamp_ne"
230 231 232 233 234
            engine.runtime_thread_num = self.op_num[index % len(self.op_num)]
            engine.batch_infer_size = self.op_max_batch[index %
                                                        len(self.op_max_batch)]

            engine.enable_batch_align = 1
Z
update  
zhangjun 已提交
235
            engine.model_dir = model_config_path
B
barrierye 已提交
236
            engine.enable_memory_optimization = self.memory_optimization
M
MRXLT 已提交
237
            engine.enable_ir_optimization = self.ir_optimization
M
add trt  
MRXLT 已提交
238
            engine.use_trt = self.use_trt
239
            engine.gpu_multi_stream = self.gpu_multi_stream
Z
update  
zhangjun 已提交
240 241
            engine.use_lite = self.use_lite
            engine.use_xpu = self.use_xpu
Z
zhangjun 已提交
242
            engine.use_gpu = False
243
            if self.device == "gpu" or self.use_trt:
Z
zhangjun 已提交
244 245
                engine.use_gpu = True

246 247 248 249 250 251
            if len(self.gpuid) == 0:
                raise ValueError("CPU: self.gpuid = -1, GPU: must set it ")
            op_gpu_list = self.gpuid[index % len(self.gpuid)].split(",")
            for ids in op_gpu_list:
                engine.gpu_ids.extend([int(ids)])

Z
fix  
zhangjun 已提交
252
            if os.path.exists('{}/__params__'.format(model_config_path)):
Z
update  
zhangjun 已提交
253
                engine.combined_model = True
Z
fix  
zhangjun 已提交
254 255
            else:
                engine.combined_model = False
Z
update  
zhangjun 已提交
256 257
            if use_encryption_model:
                engine.encrypted_model = True
Z
fix  
zhangjun 已提交
258
            engine.type = "PADDLE_INFER"
H
HexToString 已提交
259 260
            self.model_toolkit_conf.append(server_sdk.ModelToolkitConf())
            self.model_toolkit_conf[-1].engines.extend([engine])
261
            index = index + 1
M
MRXLT 已提交
262 263 264 265 266 267 268 269 270 271

    def _prepare_infer_service(self, port):
        if self.infer_service_conf == None:
            self.infer_service_conf = server_sdk.InferServiceConf()
            self.infer_service_conf.port = port
            infer_service = server_sdk.InferService()
            infer_service.name = "GeneralModelService"
            infer_service.workflows.extend(["workflow1"])
            self.infer_service_conf.services.extend([infer_service])

M
MRXLT 已提交
272
    def _prepare_resource(self, workdir, cube_conf):
273
        self.workdir = workdir
M
MRXLT 已提交
274 275
        if self.resource_conf == None:
            self.resource_conf = server_sdk.ResourceConf()
Z
zhangjun 已提交
276 277
            for idx, op_general_model_config_fn in enumerate(
                    self.general_model_config_fn):
H
HexToString 已提交
278
                with open("{}/{}".format(workdir, op_general_model_config_fn),
Z
zhangjun 已提交
279
                          "w") as fout:
H
HexToString 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293
                    fout.write(str(list(self.model_conf.values())[idx]))
                for workflow in self.workflow_conf.workflows:
                    for node in workflow.nodes:
                        if "dist_kv" in node.name:
                            self.resource_conf.cube_config_path = workdir
                            self.resource_conf.cube_config_file = self.cube_config_fn
                            if cube_conf == None:
                                raise ValueError(
                                    "Please set the path of cube.conf while use dist_kv op."
                                )
                            shutil.copy(cube_conf, workdir)
                            if "quant" in node.name:
                                self.resource_conf.cube_quant_bits = 8
                self.resource_conf.model_toolkit_path.extend([workdir])
Z
zhangjun 已提交
294 295
                self.resource_conf.model_toolkit_file.extend(
                    [self.model_toolkit_fn[idx]])
H
HexToString 已提交
296
                self.resource_conf.general_model_path.extend([workdir])
Z
zhangjun 已提交
297 298
                self.resource_conf.general_model_file.extend(
                    [op_general_model_config_fn])
H
HexToString 已提交
299 300 301 302 303
                #TODO:figure out the meaning of product_name and container_id.
                if self.product_name != None:
                    self.resource_conf.auth_product_name = self.product_name
                if self.container_id != None:
                    self.resource_conf.auth_container_id = self.container_id
M
MRXLT 已提交
304 305 306 307 308

    def _write_pb_str(self, filepath, pb_obj):
        with open(filepath, "w") as fout:
            fout.write(str(pb_obj))

H
HexToString 已提交
309
    def load_model_config(self, model_config_paths_args):
B
barrierye 已提交
310 311 312
        # At present, Serving needs to configure the model path in
        # the resource.prototxt file to determine the input and output
        # format of the workflow. To ensure that the input and output
B
barrierye 已提交
313
        # of multiple models are the same.
H
HexToString 已提交
314 315 316 317 318 319 320
        if isinstance(model_config_paths_args, str):
            model_config_paths_args = [model_config_paths_args]

        for single_model_config in model_config_paths_args:
            if os.path.isdir(single_model_config):
                pass
            elif os.path.isfile(single_model_config):
Z
zhangjun 已提交
321 322 323
                raise ValueError(
                    "The input of --model should be a dir not file.")

H
HexToString 已提交
324
        if isinstance(model_config_paths_args, list):
B
barrierye 已提交
325
            # If there is only one model path, use the default infer_op.
M
MRXLT 已提交
326
            # Because there are several infer_op type, we need to find
B
barrierye 已提交
327
            # it from workflow_conf.
H
HexToString 已提交
328
            default_engine_types = [
Z
zhangjun 已提交
329 330 331 332
                'GeneralInferOp',
                'GeneralDistKVInferOp',
                'GeneralDistKVQuantInferOp',
                'GeneralDetectionOp',
B
barrierye 已提交
333
            ]
H
HexToString 已提交
334 335 336
            # now only support single-workflow.
            # TODO:support multi-workflow
            model_config_paths_list_idx = 0
B
barrierye 已提交
337
            for node in self.workflow_conf.workflows[0].nodes:
H
HexToString 已提交
338 339 340 341 342
                if node.type in default_engine_types:
                    if node.name is None:
                        raise Exception(
                            "You have set the engine_name of Op. Please use the form {op: model_path} to configure model path"
                        )
Z
zhangjun 已提交
343

H
HexToString 已提交
344
                    f = open("{}/serving_server_conf.prototxt".format(
Z
zhangjun 已提交
345 346 347 348 349 350 351 352 353 354 355 356
                        model_config_paths_args[model_config_paths_list_idx]),
                             'r')
                    self.model_conf[
                        node.name] = google.protobuf.text_format.Merge(
                            str(f.read()), m_config.GeneralModelConfig())
                    self.model_config_paths[
                        node.name] = model_config_paths_args[
                            model_config_paths_list_idx]
                    self.general_model_config_fn.append(
                        node.name + "/general_model.prototxt")
                    self.model_toolkit_fn.append(node.name +
                                                 "/model_toolkit.prototxt")
H
HexToString 已提交
357 358
                    self.subdirectory.append(node.name)
                    model_config_paths_list_idx += 1
Z
zhangjun 已提交
359 360
                    if model_config_paths_list_idx == len(
                            model_config_paths_args):
H
HexToString 已提交
361 362 363 364 365
                        break
        #Right now, this is not useful.
        elif isinstance(model_config_paths_args, dict):
            self.model_config_paths = collections.OrderedDict()
            for node_str, path in model_config_paths_args.items():
B
barrierye 已提交
366 367 368 369 370
                node = server_sdk.DAGNode()
                google.protobuf.text_format.Parse(node_str, node)
                self.model_config_paths[node.name] = path
            print("You have specified multiple model paths, please ensure "
                  "that the input and output of multiple models are the same.")
H
HexToString 已提交
371 372
            f = open("{}/serving_server_conf.prototxt".format(path), 'r')
            self.model_conf[node.name] = google.protobuf.text_format.Merge(
Z
zhangjun 已提交
373
                str(f.read()), m_config.GeneralModelConfig())
B
barrierye 已提交
374
        else:
Z
zhangjun 已提交
375 376 377 378
            raise Exception(
                "The type of model_config_paths must be str or list or "
                "dict({op: model_path}), not {}.".format(
                    type(model_config_paths_args)))
M
MRXLT 已提交
379 380
        # check config here
        # print config here
Z
update  
zhangjun 已提交
381

Z
zhangjun 已提交
382 383 384
    def use_mkl(self, flag):
        self.mkl_flag = flag

Z
zhangjun 已提交
385
    def check_avx(self):
386 387 388 389 390
        p = subprocess.Popen(
            ['cat /proc/cpuinfo | grep avx 2>/dev/null'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            shell=True)
Z
zhangjun 已提交
391
        out, err = p.communicate()
Z
zhangjun 已提交
392
        if err == b'' and len(out) > 0:
Z
zhangjun 已提交
393 394 395 396
            return True
        else:
            return False

Z
zhangjun 已提交
397 398
    def get_device_version(self):
        avx_flag = False
Z
zhangjun 已提交
399
        avx_support = self.check_avx()
Z
update  
zhangjun 已提交
400
        if avx_support:
Z
zhangjun 已提交
401
            avx_flag = True
Z
zhangjun 已提交
402 403
            self.use_mkl(True)
        mkl_flag = self.mkl_flag
Z
zhangjun 已提交
404 405
        if avx_flag:
            if mkl_flag:
Z
update  
zhangjun 已提交
406
                device_version = "cpu-avx-mkl"
Z
zhangjun 已提交
407
            else:
Z
update  
zhangjun 已提交
408
                device_version = "cpu-avx-openblas"
Z
zhangjun 已提交
409 410 411 412 413
        else:
            if mkl_flag:
                print(
                    "Your CPU does not support AVX, server will running with noavx-openblas mode."
                )
Z
update  
zhangjun 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426
            device_version = "cpu-noavx-openblas"
        return device_version

    def get_serving_bin_name(self):
        if device_type == "0":
            device_version = self.get_device_version()
        elif device_type == "1":
            if version_suffix == "101" or version_suffix == "102":
                device_version = "gpu-" + version_suffix
            else:
                device_version = "gpu-cuda" + version_suffix
        elif device_type == "2":
            device_version = "xpu-" + platform.machine()
Z
zhangjun 已提交
427
        return device_version
M
MRXLT 已提交
428 429 430 431

    def download_bin(self):
        os.chdir(self.module_path)
        need_download = False
M
MRXLT 已提交
432 433 434

        #acquire lock
        version_file = open("{}/version.py".format(self.module_path), "r")
Z
update  
zhangjun 已提交
435

Z
fix  
zhangjun 已提交
436
        folder_name = "serving-%s-%s" % (self.get_serving_bin_name(),
437
                                         version_tag)
Z
fix  
zhangjun 已提交
438 439 440
        tar_name = "%s.tar.gz" % folder_name
        bin_url = "https://paddle-serving.bj.bcebos.com/bin/%s" % tar_name

441 442 443 444
        self.server_path = os.path.join(self.module_path, folder_name)

        download_flag = "{}/{}.is_download".format(self.module_path,
                                                   folder_name)
M
MRXLT 已提交
445 446 447

        fcntl.flock(version_file, fcntl.LOCK_EX)

448 449 450 451 452
        if os.path.exists(download_flag):
            os.chdir(self.cur_path)
            self.bin_path = self.server_path + "/serving"
            return

M
MRXLT 已提交
453 454
        if not os.path.exists(self.server_path):
            print('Frist time run, downloading PaddleServing components ...')
M
MRXLT 已提交
455

M
MRXLT 已提交
456 457 458 459
            r = os.system('wget ' + bin_url + ' --no-check-certificate')
            if r != 0:
                if os.path.exists(tar_name):
                    os.remove(tar_name)
M
MRXLT 已提交
460
                raise SystemExit(
T
TeslaZhao 已提交
461 462
                    'Download failed, please check your network or permission of {}.'
                    .format(self.module_path))
M
MRXLT 已提交
463 464 465 466 467 468
            else:
                try:
                    print('Decompressing files ..')
                    tar = tarfile.open(tar_name)
                    tar.extractall()
                    tar.close()
Z
zhangjun 已提交
469
                    open(download_flag, "a").close()
M
MRXLT 已提交
470
                except:
Z
zhangjun 已提交
471 472
                    if os.path.exists(self.server_path):
                        os.remove(self.server_path)
M
MRXLT 已提交
473
                    raise SystemExit(
T
TeslaZhao 已提交
474 475
                        'Decompressing failed, please check your permission of {} or disk space left.'
                        .format(self.module_path))
M
MRXLT 已提交
476 477
                finally:
                    os.remove(tar_name)
M
MRXLT 已提交
478
        #release lock
B
barrierye 已提交
479
        version_file.close()
M
MRXLT 已提交
480 481
        os.chdir(self.cur_path)
        self.bin_path = self.server_path + "/serving"
Z
update  
zhangjun 已提交
482

M
MRXLT 已提交
483 484 485 486
    def prepare_server(self,
                       workdir=None,
                       port=9292,
                       device="cpu",
W
wangjiawei04 已提交
487
                       use_encryption_model=False,
M
MRXLT 已提交
488
                       cube_conf=None):
489
        self.device = device
M
MRXLT 已提交
490 491
        if workdir == None:
            workdir = "./tmp"
Z
zhangjun 已提交
492
            os.system("mkdir -p {}".format(workdir))
M
MRXLT 已提交
493
        else:
Z
zhangjun 已提交
494
            os.system("mkdir -p {}".format(workdir))
H
HexToString 已提交
495
        for subdir in self.subdirectory:
496
            os.system("mkdir -p {}/{}".format(workdir, subdir))
H
HexToString 已提交
497 498
            os.system("touch {}/{}/fluid_time_file".format(workdir, subdir))

M
MRXLT 已提交
499
        if not self.port_is_available(port):
G
gongweibao 已提交
500
            raise SystemExit("Port {} is already used".format(port))
M
MRXLT 已提交
501

G
guru4elephant 已提交
502
        self.set_port(port)
M
MRXLT 已提交
503
        self._prepare_resource(workdir, cube_conf)
H
HexToString 已提交
504 505
        self._prepare_engine(self.model_config_paths, device,
                             use_encryption_model)
M
MRXLT 已提交
506 507 508 509 510
        self._prepare_infer_service(port)
        self.workdir = workdir

        infer_service_fn = "{}/{}".format(workdir, self.infer_service_fn)
        self._write_pb_str(infer_service_fn, self.infer_service_conf)
H
HexToString 已提交
511 512

        workflow_fn = "{}/{}".format(workdir, self.workflow_fn)
M
MRXLT 已提交
513
        self._write_pb_str(workflow_fn, self.workflow_conf)
H
HexToString 已提交
514 515

        resource_fn = "{}/{}".format(workdir, self.resource_fn)
M
MRXLT 已提交
516
        self._write_pb_str(resource_fn, self.resource_conf)
H
HexToString 已提交
517

Z
zhangjun 已提交
518
        for idx, single_model_toolkit_fn in enumerate(self.model_toolkit_fn):
H
HexToString 已提交
519 520
            model_toolkit_fn = "{}/{}".format(workdir, single_model_toolkit_fn)
            self._write_pb_str(model_toolkit_fn, self.model_toolkit_conf[idx])
M
MRXLT 已提交
521

M
MRXLT 已提交
522
    def port_is_available(self, port):
M
MRXLT 已提交
523 524
        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
            sock.settimeout(2)
525
            result = sock.connect_ex(('0.0.0.0', port))
M
MRXLT 已提交
526 527 528 529 530
        if result != 0:
            return True
        else:
            return False

M
MRXLT 已提交
531 532 533
    def run_server(self):
        # just run server with system command
        # currently we do not load cube
M
MRXLT 已提交
534
        self.check_local_bin()
M
MRXLT 已提交
535 536
        if not self.use_local_bin:
            self.download_bin()
B
fix bug  
barrierye 已提交
537 538 539
            # wait for other process to download server bin
            while not os.path.exists(self.server_path):
                time.sleep(1)
M
MRXLT 已提交
540 541
        else:
            print("Use local bin : {}".format(self.bin_path))
Z
zhangjun 已提交
542
        #self.check_cuda()
Z
zhangjun 已提交
543 544
        # Todo: merge CPU and GPU code, remove device to model_toolkit
        if self.device == "cpu" or self.device == "arm":
Z
zhangjun 已提交
545 546 547 548 549 550 551
            command = "{} " \
                      "-enable_model_toolkit " \
                      "-inferservice_path {} " \
                      "-inferservice_file {} " \
                      "-max_concurrency {} " \
                      "-num_threads {} " \
                      "-port {} " \
552 553
                      "-precision {} " \
                      "-use_calib {} " \
Z
zhangjun 已提交
554 555 556 557 558 559 560 561 562 563 564 565 566
                      "-reload_interval_s {} " \
                      "-resource_path {} " \
                      "-resource_file {} " \
                      "-workflow_path {} " \
                      "-workflow_file {} " \
                      "-bthread_concurrency {} " \
                      "-max_body_size {} ".format(
                          self.bin_path,
                          self.workdir,
                          self.infer_service_fn,
                          self.max_concurrency,
                          self.num_threads,
                          self.port,
567 568
                          self.precision,
                          self.use_calib,
Z
zhangjun 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
                          self.reload_interval_s,
                          self.workdir,
                          self.resource_fn,
                          self.workdir,
                          self.workflow_fn,
                          self.num_threads,
                          self.max_body_size)
        else:
            command = "{} " \
                      "-enable_model_toolkit " \
                      "-inferservice_path {} " \
                      "-inferservice_file {} " \
                      "-max_concurrency {} " \
                      "-num_threads {} " \
                      "-port {} " \
584 585
                      "-precision {} " \
                      "-use_calib {} " \
Z
zhangjun 已提交
586 587 588 589 590 591 592 593 594 595 596 597 598
                      "-reload_interval_s {} " \
                      "-resource_path {} " \
                      "-resource_file {} " \
                      "-workflow_path {} " \
                      "-workflow_file {} " \
                      "-bthread_concurrency {} " \
                      "-max_body_size {} ".format(
                          self.bin_path,
                          self.workdir,
                          self.infer_service_fn,
                          self.max_concurrency,
                          self.num_threads,
                          self.port,
599 600
                          self.precision,
                          self.use_calib,
Z
zhangjun 已提交
601 602 603 604 605 606 607
                          self.reload_interval_s,
                          self.workdir,
                          self.resource_fn,
                          self.workdir,
                          self.workflow_fn,
                          self.num_threads,
                          self.max_body_size)
M
MRXLT 已提交
608 609
        print("Going to Run Comand")
        print(command)
610

M
MRXLT 已提交
611
        os.system(command)
B
barrierye 已提交
612

Z
zhangjun 已提交
613

B
barrierye 已提交
614
class MultiLangServer(object):
B
barrierye 已提交
615
    def __init__(self):
B
barrierye 已提交
616
        self.bserver_ = Server()
B
barrierye 已提交
617 618 619
        self.worker_num_ = 4
        self.body_size_ = 64 * 1024 * 1024
        self.concurrency_ = 100000
620
        self.is_multi_model_ = False  # for model ensemble, which is not useful right now.
B
barrierye 已提交
621

B
barrierye 已提交
622
    def set_max_concurrency(self, concurrency):
B
barrierye 已提交
623
        self.concurrency_ = concurrency
B
barrierye 已提交
624 625
        self.bserver_.set_max_concurrency(concurrency)

626 627 628
    def set_device(self, device="cpu"):
        self.device = device

B
barrierye 已提交
629
    def set_num_threads(self, threads):
B
barrierye 已提交
630
        self.worker_num_ = threads
B
barrierye 已提交
631 632 633 634
        self.bserver_.set_num_threads(threads)

    def set_max_body_size(self, body_size):
        self.bserver_.set_max_body_size(body_size)
B
barrierye 已提交
635 636 637 638 639 640
        if body_size >= self.body_size_:
            self.body_size_ = body_size
        else:
            print(
                "max_body_size is less than default value, will use default value in service."
            )
B
barrierye 已提交
641

642 643 644
    def use_encryption_model(self, flag=False):
        self.encryption_model = flag

B
barrierye 已提交
645 646 647
    def set_port(self, port):
        self.gport_ = port

648 649 650 651 652 653
    def set_precision(self, precision="fp32"):
        self.precision = precision

    def set_use_calib(self, use_calib=False):
        self.use_calib = use_calib

B
barrierye 已提交
654 655
    def set_reload_interval(self, interval):
        self.bserver_.set_reload_interval(interval)
B
barrierye 已提交
656 657 658 659

    def set_op_sequence(self, op_seq):
        self.bserver_.set_op_sequence(op_seq)

B
barrierye 已提交
660 661
    def set_op_graph(self, op_graph):
        self.bserver_.set_op_graph(op_graph)
Z
update  
zhangjun 已提交
662

Z
zhangjun 已提交
663 664
    def use_mkl(self, flag):
        self.bserver_.use_mkl(flag)
Z
update  
zhangjun 已提交
665

B
barrierye 已提交
666 667 668 669 670
    def set_memory_optimize(self, flag=False):
        self.bserver_.set_memory_optimize(flag)

    def set_ir_optimize(self, flag=False):
        self.bserver_.set_ir_optimize(flag)
B
barrierye 已提交
671

672
    def set_gpuid(self, gpuid):
B
barrierye 已提交
673 674
        self.bserver_.set_gpuid(gpuid)

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    def set_op_num(self, op_num):
        self.bserver_.set_op_num(op_num)

    def set_op_max_batch(self, op_max_batch):
        self.bserver_.set_op_max_batch(op_max_batch)

    def set_trt(self):
        self.bserver_.set_trt()

    def set_gpu_multi_stream(self):
        self.bserver_.set_gpu_multi_stream()

    def set_lite(self):
        self.bserver_.set_lite()

    def set_xpu(self):
        self.bserver_.set_xpu()

Z
zhangjun 已提交
693 694 695
    def load_model_config(self,
                          server_config_dir_paths,
                          client_config_path=None):
H
HexToString 已提交
696 697 698 699 700 701
        if isinstance(server_config_dir_paths, str):
            server_config_dir_paths = [server_config_dir_paths]
        elif isinstance(server_config_dir_paths, list):
            pass
        else:
            raise Exception("The type of model_config_paths must be str or list"
Z
zhangjun 已提交
702
                            ", not {}.".format(type(server_config_dir_paths)))
H
HexToString 已提交
703 704 705 706 707

        for single_model_config in server_config_dir_paths:
            if os.path.isdir(single_model_config):
                pass
            elif os.path.isfile(single_model_config):
Z
zhangjun 已提交
708 709
                raise ValueError(
                    "The input of --model should be a dir not file.")
H
HexToString 已提交
710 711

        self.bserver_.load_model_config(server_config_dir_paths)
B
barrierye 已提交
712
        if client_config_path is None:
H
HexToString 已提交
713 714
            #now dict is not useful.
            if isinstance(server_config_dir_paths, dict):
B
barrierye 已提交
715
                self.is_multi_model_ = True
H
HexToString 已提交
716
                client_config_path = []
Z
zhangjun 已提交
717 718 719
                for server_config_path_items in list(
                        server_config_dir_paths.items()):
                    client_config_path.append(server_config_path_items[1])
H
HexToString 已提交
720 721 722
            elif isinstance(server_config_dir_paths, list):
                self.is_multi_model_ = False
                client_config_path = server_config_dir_paths
B
barrierye 已提交
723
            else:
Z
zhangjun 已提交
724 725 726 727
                raise Exception(
                    "The type of model_config_paths must be str or list or "
                    "dict({op: model_path}), not {}.".format(
                        type(server_config_dir_paths)))
H
HexToString 已提交
728 729 730 731
        if isinstance(client_config_path, str):
            client_config_path = [client_config_path]
        elif isinstance(client_config_path, list):
            pass
Z
zhangjun 已提交
732 733 734 735 736
        else:  # dict is not support right now.
            raise Exception(
                "The type of client_config_path must be str or list or "
                "dict({op: model_path}), not {}.".format(
                    type(client_config_path)))
H
HexToString 已提交
737
        if len(client_config_path) != len(server_config_dir_paths):
Z
zhangjun 已提交
738 739 740
            raise Warning(
                "The len(client_config_path) is {}, != len(server_config_dir_paths) {}."
                .format(len(client_config_path), len(server_config_dir_paths)))
H
HexToString 已提交
741
        self.bclient_config_path_list = client_config_path
B
barrierye 已提交
742

M
MRXLT 已提交
743 744 745 746
    def prepare_server(self,
                       workdir=None,
                       port=9292,
                       device="cpu",
H
HexToString 已提交
747
                       use_encryption_model=False,
M
MRXLT 已提交
748
                       cube_conf=None):
749
        self.device = device
B
barrierye 已提交
750
        if not self._port_is_available(port):
B
bjjwwang 已提交
751
            raise SystemExit("Port {} is already used".format(port))
B
barrierye 已提交
752 753 754 755 756 757 758 759
        default_port = 12000
        self.port_list_ = []
        for i in range(1000):
            if default_port + i != port and self._port_is_available(default_port
                                                                    + i):
                self.port_list_.append(default_port + i)
                break
        self.bserver_.prepare_server(
M
MRXLT 已提交
760 761 762
            workdir=workdir,
            port=self.port_list_[0],
            device=device,
H
HexToString 已提交
763
            use_encryption_model=use_encryption_model,
M
MRXLT 已提交
764
            cube_conf=cube_conf)
B
barrierye 已提交
765
        self.set_port(port)
B
barrierye 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779

    def _launch_brpc_service(self, bserver):
        bserver.run_server()

    def _port_is_available(self, port):
        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
            sock.settimeout(2)
            result = sock.connect_ex(('0.0.0.0', port))
        return result != 0

    def run_server(self):
        p_bserver = Process(
            target=self._launch_brpc_service, args=(self.bserver_, ))
        p_bserver.start()
B
barrierye 已提交
780 781
        options = [('grpc.max_send_message_length', self.body_size_),
                   ('grpc.max_receive_message_length', self.body_size_)]
B
barrierye 已提交
782
        server = grpc.server(
B
barrierye 已提交
783 784 785
            futures.ThreadPoolExecutor(max_workers=self.worker_num_),
            options=options,
            maximum_concurrent_rpcs=self.concurrency_)
B
barrierye 已提交
786
        multi_lang_general_model_service_pb2_grpc.add_MultiLangGeneralModelServiceServicer_to_server(
B
barrierye 已提交
787
            MultiLangServerServiceServicer(
H
HexToString 已提交
788
                self.bclient_config_path_list, self.is_multi_model_,
B
barrierye 已提交
789
                ["0.0.0.0:{}".format(self.port_list_[0])]), server)
B
barrierye 已提交
790 791 792
        server.add_insecure_port('[::]:{}'.format(self.gport_))
        server.start()
        p_bserver.join()
Z
update  
zhangjun 已提交
793
        server.wait_for_termination()