├── python ├── .gitignore └── resnet_client.py ├── protos ├── .gitignore └── example.proto ├── .style.yapf ├── src ├── CMakeLists.txt ├── resnet.h ├── resnet.cc └── resnet_server.cc ├── get_resnet_libtorch_save.py ├── .editorconfig ├── CMakeLists.txt ├── .gitignore ├── docker └── libtorch_cpu_Dockerfile └── README.md /python/.gitignore: -------------------------------------------------------------------------------- 1 | *pb*.py 2 | -------------------------------------------------------------------------------- /protos/.gitignore: -------------------------------------------------------------------------------- 1 | *.h 2 | *.cc 3 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | # YAPF uses the google style 3 | based_on_style = google 4 | indent_width = 2 5 | column_limit = 80 6 | -------------------------------------------------------------------------------- /protos/example.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package example; 4 | 5 | service ResNet { 6 | rpc ClassifyImage (ImageMatrix) returns (ClassifyResult) {} 7 | } 8 | 9 | message ImageMatrix { 10 | repeated int32 image_matrix = 1; 11 | } 12 | 13 | message ClassifyResult { 14 | float category = 1; 15 | } 16 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(resnet resnet.h resnet.cc) 2 | target_link_libraries(resnet 3 | ${TORCH_LIBRARIES}) 4 | 5 | add_executable(resnet_server resnet_server.cc ${PROTO_GENERATED_FILES_DIR}/example.pb.cc ${PROTO_GENERATED_FILES_DIR}/example.grpc.pb.cc) 6 | target_link_libraries(resnet_server 7 | ${_GRPC_GRPCPP_UNSECURE} 8 | ${_PROTOBUF_LIBPROTOBUF} 9 | resnet) 10 | -------------------------------------------------------------------------------- /get_resnet_libtorch_save.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torchvision 3 | 4 | # An instance of your model. 5 | model = torchvision.models.resnet18() 6 | 7 | # An example input you would normally provide to your model's forward() method. 8 | example = torch.rand(1, 3, 224, 224) 9 | 10 | # Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing. 11 | traced_script_module = torch.jit.trace(model, example) 12 | 13 | traced_script_module.save("traced_resnet_model.pt") 14 | -------------------------------------------------------------------------------- /python/resnet_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import grpc 4 | import example_pb2 5 | import example_pb2_grpc 6 | 7 | def run(): 8 | with grpc.insecure_channel('localhost:50051') as channel: 9 | stub = example_pb2_grpc.ResNetStub(channel) 10 | request = example_pb2.ImageMatrix() 11 | request.image_matrix.extend([100]*1*3*224*224) 12 | response = stub.ClassifyImage(request) 13 | print("image category: " + str(int(response.category))) 14 | 15 | if __name__ == '__main__': 16 | run() 17 | 18 | -------------------------------------------------------------------------------- /src/resnet.h: -------------------------------------------------------------------------------- 1 | #ifndef RESNET_H_ 2 | #define RESNET_H_ 3 | 4 | #include // One-stop header. 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class ResNetModule : public torch::jit::script::Module { 11 | public: 12 | ResNetModule(const std::string& model_path) { 13 | _module = torch::jit::load(model_path); 14 | } 15 | float classify(std::vector& vec); 16 | private: 17 | torch::jit::script::Module _module; 18 | }; 19 | 20 | #endif // RESNET_H_ 21 | -------------------------------------------------------------------------------- /src/resnet.cc: -------------------------------------------------------------------------------- 1 | #include "resnet.h" 2 | 3 | float ResNetModule::classify(std::vector& vec) { 4 | torch::Tensor tensor_image = torch::from_blob(vec.data(), {1, 3, 224, 224}, torch::kInt32); 5 | tensor_image = tensor_image.toType(torch::kFloat); 6 | tensor_image = tensor_image.div(255); 7 | // Create a vector of inputs. 8 | std::vector inputs; 9 | inputs.push_back(tensor_image); 10 | 11 | auto output = _module.forward(inputs).toTensor(); 12 | 13 | auto max_result = output.max(1, true); 14 | auto max_index = std::get<1>(max_result).item(); 15 | return max_index; 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | # EditorConfig is awesome: https://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # general 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | # c/c++, python 15 | [*.{h,hh,hpp,c,cc,cpp,py}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | # python 20 | [*.py] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.java] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | # javascript 29 | [*.js] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.{json,y{a,}ml}] 34 | indent_style = space 35 | indent_size = 4 36 | 37 | # markdown, txt 38 | [*.{md,txt}] 39 | indent_style = space 40 | indent_size = 4 41 | trim_trailing_whitespace = false 42 | 43 | # Makefile build 44 | [Makefile] 45 | indent_style = tab 46 | 47 | # proto buffer 48 | [*.proto] 49 | indent_style = space 50 | indent_size = 2 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(custom_ops 4 | VERSION 1.0 5 | LANGUAGES C CXX) 6 | 7 | # Let's ensure -std=c++xx instead of -std=g++xx 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | set(PROTO_GENERATED_FILES_DIR "${PROJECT_SOURCE_DIR}/protos") 10 | 11 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 12 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 13 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 14 | 15 | add_definitions(-DRESNETSAVEPATH="${PROJECT_SOURCE_DIR}/traced_resnet_model.pt") 16 | 17 | # set(protobuf_MODULE_COMPATIBLE TRUE) 18 | find_package(Protobuf CONFIG REQUIRED) 19 | message(STATUS "Using protobuf ${protobuf_VERSION}") 20 | 21 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 22 | set(_PROTOBUF_PROTOC $) 23 | 24 | find_package(gRPC CONFIG REQUIRED) 25 | message(STATUS "Using gRPC ${gRPC_VERSION}") 26 | 27 | find_package(Torch REQUIRED) 28 | message(STATUS "Torch libraries: ${TORCH_LIBRARIES}") 29 | 30 | set(_GRPC_GRPCPP_UNSECURE gRPC::grpc++_unsecure) 31 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 32 | 33 | include_directories("${PROTO_GENERATED_FILES_DIR}") 34 | 35 | add_subdirectory(src) 36 | -------------------------------------------------------------------------------- /src/resnet_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include "example.grpc.pb.h" 8 | #include "resnet.h" 9 | 10 | using grpc::Server; 11 | using grpc::ServerBuilder; 12 | using grpc::ServerContext; 13 | using grpc::Status; 14 | 15 | using example::ImageMatrix; 16 | using example::ClassifyResult; 17 | using example::ResNet; 18 | 19 | ResNetModule resnet(RESNETSAVEPATH); 20 | 21 | class ResNetServiceImpl : public ResNet::Service { 22 | Status ClassifyImage (ServerContext* context, const ImageMatrix* image, 23 | ClassifyResult* res) override { 24 | std::vector temp; 25 | for (int i = 0; i < image->image_matrix_size(); ++i) { 26 | temp.push_back(image->image_matrix(i)); 27 | } 28 | auto classify_result = resnet.classify(temp); 29 | res->set_category(classify_result); 30 | return Status::OK; 31 | } 32 | }; 33 | 34 | void RunServer() { 35 | std::string server_address("0.0.0.0:50051"); 36 | ResNetServiceImpl service; 37 | 38 | ServerBuilder builder; 39 | // Listen on the given address without any authentication mechanism. 40 | builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); 41 | // Register "service" as the instance through which we'll communicate with 42 | // clients. In this case it corresponds to an *synchronous* service. 43 | builder.RegisterService(&service); 44 | // Finally assemble the server. 45 | std::unique_ptr server(builder.BuildAndStart()); 46 | std::cout << "Server listening on " << server_address << std::endl; 47 | 48 | // Wait for the server to shutdown. Note that some other thread must be 49 | // responsible for shutting down the server for this call to ever return. 50 | server->Wait(); 51 | } 52 | 53 | 54 | int main(int argc, const char* argv[]) { 55 | RunServer(); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### C++ ignore 2 | # Prerequisites 3 | *.d 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | *.smod 23 | 24 | # Compiled Static libraries 25 | *.lai 26 | *.la 27 | *.a 28 | *.lib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | 35 | ### cmake ignore 36 | 37 | CMakeLists.txt.user 38 | CMakeCache.txt 39 | CMakeFiles 40 | CMakeScripts 41 | Testing 42 | Makefile 43 | cmake_install.cmake 44 | install_manifest.txt 45 | compile_commands.json 46 | CTestTestfile.cmake 47 | _deps 48 | 49 | ### python ignore 50 | # Byte-compiled / optimized / DLL files 51 | __pycache__/ 52 | *.py[cod] 53 | *$py.class 54 | 55 | # C extensions 56 | *.so 57 | 58 | # Distribution / packaging 59 | .Python 60 | build/ 61 | develop-eggs/ 62 | dist/ 63 | downloads/ 64 | eggs/ 65 | .eggs/ 66 | lib/ 67 | lib64/ 68 | parts/ 69 | sdist/ 70 | var/ 71 | wheels/ 72 | pip-wheel-metadata/ 73 | share/python-wheels/ 74 | *.egg-info/ 75 | .installed.cfg 76 | *.egg 77 | MANIFEST 78 | 79 | # PyInstaller 80 | # Usually these files are written by a python script from a template 81 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 82 | *.manifest 83 | *.spec 84 | 85 | # Installer logs 86 | pip-log.txt 87 | pip-delete-this-directory.txt 88 | 89 | # Unit test / coverage reports 90 | htmlcov/ 91 | .tox/ 92 | .nox/ 93 | .coverage 94 | .coverage.* 95 | .cache 96 | nosetests.xml 97 | coverage.xml 98 | *.cover 99 | .hypothesis/ 100 | .pytest_cache/ 101 | 102 | # Translations 103 | *.mo 104 | *.pot 105 | 106 | # Django stuff: 107 | *.log 108 | local_settings.py 109 | db.sqlite3 110 | db.sqlite3-journal 111 | 112 | # Flask stuff: 113 | instance/ 114 | .webassets-cache 115 | 116 | # Scrapy stuff: 117 | .scrapy 118 | 119 | # Sphinx documentation 120 | docs/_build/ 121 | 122 | # PyBuilder 123 | target/ 124 | 125 | # Jupyter Notebook 126 | .ipynb_checkpoints 127 | 128 | # IPython 129 | profile_default/ 130 | ipython_config.py 131 | 132 | # pyenv 133 | .python-version 134 | 135 | # pipenv 136 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 137 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 138 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 139 | # install all needed dependencies. 140 | #Pipfile.lock 141 | 142 | # celery beat schedule file 143 | celerybeat-schedule 144 | 145 | # SageMath parsed files 146 | *.sage.py 147 | 148 | # Environments 149 | .env 150 | .venv 151 | env/ 152 | venv/ 153 | ENV/ 154 | env.bak/ 155 | venv.bak/ 156 | 157 | # Spyder project settings 158 | .spyderproject 159 | .spyproject 160 | 161 | # Rope project settings 162 | .ropeproject 163 | 164 | # mkdocs documentation 165 | /site 166 | 167 | # mypy 168 | .mypy_cache/ 169 | .dmypy.json 170 | dmypy.json 171 | 172 | # Pyre type checker 173 | .pyre/ 174 | 175 | ### custom ignore 176 | *.pt 177 | build/ 178 | -------------------------------------------------------------------------------- /docker/libtorch_cpu_Dockerfile: -------------------------------------------------------------------------------- 1 | # docker build --pull -t resnet-libtorch-serving -f libtorch_cpu_Dockerfile . 2 | FROM ubuntu:18.04 3 | 4 | MAINTAINER Peter-Chou <2747244153@qq.com> 5 | 6 | # use aliyun mirror 7 | RUN sed -i 's/http:\/\/archive\.ubuntu\.com\/ubuntu\//http:\/\/mirrors\.aliyun\.com\/ubuntu\//g' /etc/apt/sources.list 8 | 9 | RUN apt-get update && apt-get install -y \ 10 | build-essential \ 11 | cmake \ 12 | git \ 13 | curl \ 14 | wget \ 15 | zip \ 16 | unzip \ 17 | vim \ 18 | tree \ 19 | autoconf \ 20 | libtool \ 21 | pkg-config \ 22 | libgflags-dev \ 23 | libgtest-dev \ 24 | clang \ 25 | libc++-dev \ 26 | libssl-dev \ 27 | python3-distutils 28 | 29 | RUN ln -sf /usr/bin/python3.6 /usr/bin/python 30 | 31 | RUN curl -O https://bootstrap.pypa.io/get-pip.py && \ 32 | python get-pip.py && \ 33 | rm get-pip.py 34 | 35 | # use tencent pypi mirror 36 | RUN pip config set global.index-url https://mirrors.cloud.tencent.com/pypi/simple 37 | 38 | RUN pip install grpcio-tools==1.23.0 39 | RUN pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html 40 | 41 | # Install golang 42 | RUN cd /tmp && \ 43 | wget https://storage.googleapis.com/golang/go1.12.9.linux-amd64.tar.gz && \ 44 | tar -C /usr/local -xzf go*linux-amd64.tar.gz && \ 45 | rm -rf go1.12.9.linux-amd64.tar.gz 46 | 47 | ENV PATH="/usr/local/go/bin:${PATH}" 48 | ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib" 49 | 50 | # Install gRPC 51 | RUN cd /tmp && git clone -b v1.23.0 https://github.com/grpc/grpc.git 52 | RUN cd /tmp/grpc && git submodule update --init 53 | RUN cd /tmp/grpc/third_party/zlib && \ 54 | mkdir build && cd build && cmake .. && make -j $(nproc) && make install && \ 55 | cd /tmp/grpc/third_party/cares/cares && \ 56 | mkdir build && cd build && cmake .. && make -j $(nproc) && make install && \ 57 | cd /tmp/grpc/third_party/protobuf/cmake && \ 58 | mkdir build && cd build && cmake -Dprotobuf_BUILD_TESTS=OFF .. && make -j $(nproc) && make install && \ 59 | cd /tmp/grpc && mkdir build && cd build && \ 60 | cmake -DgRPC_INSTALL=ON -DgRPC_ZLIB_PROVIDER=package -DgRPC_CARES_PROVIDER=package -DgRPC_PROTOBUF_PROVIDER=package -DgRPC_SSL_PROVIDER=package .. && \ 61 | make -j $(nproc) && make install && ldconfig && \ 62 | rm -rf /tmp/grpc 63 | 64 | # install libtorch library 65 | RUN cd /opt && \ 66 | wget https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.2.0.zip && \ 67 | unzip libtorch*.zip && \ 68 | rm -rf libtorch-cxx11-abi-shared-with-deps-1.2.0.zip 69 | 70 | # build resnet_server 71 | RUN mkdir -p /pytorch_models/resnet 72 | WORKDIR /pytorch_models/resnet 73 | RUN git clone https://github.com/Peter-Chou/libtorch_grpc_serving.git . && \ 74 | python get_resnet_libtorch_save.py && \ 75 | protoc -I ./protos --grpc_out=./protos --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ./protos/example.proto && \ 76 | protoc -I ./protos --cpp_out=./protos ./protos/example.proto && \ 77 | python -m grpc_tools.protoc -I./protos --python_out=./python --grpc_python_out=./python ./protos/example.proto && \ 78 | mkdir build && cd build && cmake -DCMAKE_PREFIX_PATH=/opt/libtorch .. && make -j $(nproc) 79 | WORKDIR /pytorch_models/resnet/build/bin 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploy libtorch model with gRPC (Ubuntu 18.04) 2 | 3 | this project shows how to deploy a resnet libtorch model using gRPC. 4 | 5 | ## 1. Serving with docker 6 | 7 | The easiest and most reliable way to deploy the resnet libtorch model is to use docker, make sure you have already install docker and run the following commands. 8 | 9 | ```sh 10 | # in project root dir 11 | cd docker 12 | # build the image from Dockerfile 13 | [sudo] docker build --pull -t resnet-libtorch-serving -f libtorch_cpu_Dockerfile . 14 | # create docker container and provide service 15 | [sudo] sudo docker run -p 50051:50051 --name=resnet_service -d -it resnet-libtorch-serving /bin/bash -c './resnet_server' 16 | ``` 17 | 18 | Now you can create a client and send a request to the resnet server, here I use the python client to do the demonstration. 19 | 20 | ```sh 21 | # in project root dir 22 | cd python 23 | # make sure grpcio-tools has installed in your python environment 24 | python -m grpc_tools.protoc -I../protos --python_out=. --grpc_python_out=. ../protos/example.proto 25 | python resnet_client.py # image category: image_category_num 26 | ``` 27 | 28 | ## 2. Serving without docker 29 | 30 | If you want to get all the things without docker, You need to follow the instructions below. 31 | 32 | Since there is no prebuilt gRPC package in C++, it must be compiled and installed from the source code. 33 | 34 | ### Compile and install gRPC 35 | 36 | #### Install prerequisites 37 | 38 | - necessary packages 39 | ```sh 40 | sudo apt-get update && sudo apt-get upgrade 41 | sudo apt-get install build-essential autoconf libtool pkg-config libgflags-dev libgtest-dev clang libc++-dev libssl-dev cmake python3-distutils vim tree git curl 42 | 43 | ``` 44 | 45 | - go language 46 | 47 | ```sh 48 | wget https://storage.googleapis.com/golang/go1.12.9.linux-amd64.tar.gz 49 | sudo tar -C /usr/local -xzf go*linux-amd64.tar.gz 50 | echo 'export PATH=$PATH:/usr/local/go/bin' >> $HOME/.bashrc 51 | echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib' >> $HOME/.bashrc 52 | source $HOME/.bashrc 53 | ``` 54 | 55 | - download gRPC project 56 | 57 | ```sh 58 | # in $HOME folder 59 | git clone -b v1.23.0 https://github.com/grpc/grpc.git 60 | cd grpc 61 | git submodule update --init 62 | ``` 63 | 64 | The build process is a bit tricky, If you follow the build instructions given by the grpc's readme, it won't automatically generate *targets.cmake files which is useful when you use cmake to link grpc into your project and you will receive the error: 65 | 66 | ``` 67 | include could not find load file: 68 | 69 | /usr/local/lib/cmake/grpc/gRPCTargets.cmake 70 | ``` 71 | 72 | The current workaround is to build zlib, cares, protobuf at first, then tell cmake to use these installed packages when building grpc, then grpc will generate all the files as expected. 73 | 74 | #### Install zlib 75 | 76 | ```sh 77 | GPRC_COMPILE_ROOT_PATH=$(pwd) 78 | cd $GPRC_COMPILE_ROOT_PATH/third_party/zlib 79 | mkdir build && cd build 80 | cmake .. 81 | make -j $(nproc) 82 | sudo make install 83 | ``` 84 | 85 | #### Install cares 86 | ```sh 87 | cd $GPRC_COMPILE_ROOT_PATH/third_party/cares/cares 88 | mkdir build && cd build 89 | cmake .. 90 | make -j $(nproc) 91 | sudo make install 92 | ``` 93 | 94 | #### Install protobuf 95 | ```sh 96 | cd $GPRC_COMPILE_ROOT_PATH/third_party/protobuf/cmake 97 | mkdir build && cd build 98 | cmake -Dprotobuf_BUILD_TESTS=OFF .. 99 | make -j $(nproc) 100 | sudo make install 101 | ``` 102 | 103 | #### Install gRPC 104 | ```sh 105 | cd $GPRC_COMPILE_ROOT_PATH 106 | mkdir build && cd build 107 | cmake -DgRPC_INSTALL=ON -DgRPC_ZLIB_PROVIDER=package -DgRPC_CARES_PROVIDER=package -DgRPC_PROTOBUF_PROVIDER=package -DgRPC_SSL_PROVIDER=package .. 108 | make -j $(nproc) 109 | sudo make install 110 | sudo ldconfig 111 | ``` 112 | 113 | ### Install libtorch library 114 | 115 | ```sh 116 | cd $HOME 117 | wget https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.2.0.zip 118 | unzip libtorch*.zip 119 | ``` 120 | 121 | ### Build gRPC server of resnet libtorch model 122 | 123 | #### Generate resnet libtorch saved file from pytorch 124 | 125 | ```sh 126 | curl -O https://bootstrap.pypa.io/get-pip.py 127 | # ubuntu 18.04 python3.6.8 128 | sudo python3 get-pip.py 129 | pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 130 | # install pytorch package 131 | sudo pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html 132 | ``` 133 | 134 | ```sh 135 | git clone https://github.com/Peter-Chou/libtorch_grpc_serving.git 136 | cd libtorch_grpc_serving 137 | python3 get_resnet_libtorch_save.py 138 | ``` 139 | 140 | #### Generate server structure code by protoc 141 | 142 | - generate C++ server side code 143 | 144 | ```sh 145 | # in libtorch_grpc_serving root dir 146 | PROTO_SRC_DIR=./protos 147 | ## generate C++ server side code 148 | protoc -I $PROTO_SRC_DIR --grpc_out=./protos --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` $PROTO_SRC_DIR/example.proto 149 | ## generate C++ message classes needed by server 150 | protoc -I $PROTO_SRC_DIR --cpp_out=./protos $PROTO_SRC_DIR/example.proto 151 | 152 | ``` 153 | 154 | - generate python client side code 155 | 156 | ```sh 157 | # in libtorch_grpc_serving root dir 158 | sudo pip install grpcio-tools 159 | python3 -m grpc_tools.protoc -I$PROTO_SRC_DIR --python_out=./python --grpc_python_out=./python $PROTO_SRC_DIR/example.proto 160 | ``` 161 | 162 | #### Build the project 163 | 164 | ```sh 165 | # in libtorch_grpc_serving root dir 166 | mkdir build && cd build 167 | cmake -DCMAKE_PREFIX_PATH=$HOME/libtorch .. 168 | make -j $(nproc) 169 | ``` 170 | 171 | #### Start resnet server 172 | 173 | ```sh 174 | # in libtorch_grpc_serving root dir 175 | ./build/bin/resnet_server & # Server listening on 0.0.0.0:50051 176 | ``` 177 | 178 | #### Client request from server 179 | 180 | ```sh 181 | # in libtorch_grpc_serving root dir 182 | python3 python/resnet_client.py # image category: image_category_num 183 | ``` 184 | --------------------------------------------------------------------------------