1)    gRPC 관련 지식

A.    gRPC

구글에서 처음 개발하여 공개한 원격 프로시저 호출(RPC) 시스템으로 HTTP/2 를 사용하고 인터페이스 설명 언어로 프로토콜 버퍼를 사용함.

 인터페이스 설명 언어가 하나이기 때문에 다양한 언어에서 데이터를 주고 받을 수 있는 장점이 있음

 

B.     HTTP/2

HTTP/1.1의 알려진 성능 제한이 개선된 프로토콜로 헤더 필드 압축과 동일한 연결에서 다중 동시 교환 허용 등을 통한 리소스 비용 절감 및 성능 개선이 진행됨

 

C.     프로토콜 버퍼

구글에서 공개한 직렬화 데이터 구조로 다양한 언어를 지원한다는 장점이 있다. 최대 64M 까지 데이터를 전달할 수 있다고 하며, JSON 과 유연한 데이터 전환이 가능하다.

관련 출처 : http://bcho.tistory.com/1182

 


2)    gRPC 사용 (python to nodejs)

I.      사용한 소프트웨어  (ubuntu 16.04 환경에서 설치함)

l  nodejs (v6.14.4)

n  nodejs grpc module (v1.11.0)


l  mongoDB(v2.6.10)


l  Python (v2.7.12)

n  grpcio, grpcio-tool (v1.16.0)


II.    protobuf 인터페이스 선언

gRPC 를 사용하려면 받는 쪽과 주는 쪽 모두 인터페이스 모델을 정의해주어야 한다.  message 는 각 언어에서 Object 에 해당하는 값이며 Service Function 에 해당하는 값이다.


syntax = "proto3";

package json;

service Json {

  rpc grpcSend (Request) returns (Response) {}

}

message Request {

  string jsonStr = 1;

}

message Response {

  string jsonStr = 1;

}

 

III.   서버 및 클라이언트 설치


l  nodejs 설치

cd ~

curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh

bash nodesource_setup.sh

apt-get install nodejs

apt-get install build-essential

 

 

l  npm 모듈 설치 (아래 package.json 파일 작성 후 npm install 실행)

{

  "name": "myapp",

  "version": "0.1.0",

  "description": "",

  "main": "app_grpc.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "author": "nanum",

  "license": "ISC",

  "bugs": {},

  "homepage": "",

  "dependencies": {

    "body-parser": "^1.18.3",

    "ejs": "^2.3.3",

    "express": "^4.16.3",

    "grpc-web": "^1.0.0",

    "method-override": "^3.0.0",

    "mongoose": "^5.3.0",

    "requests": "^0.2.2",

    "websocket": "^1.0.28",

    "@grpc/proto-loader": "^0.1.0",

    "async": "^1.5.2",

    "google-protobuf": "^3.0.0",

    "grpc": "^1.11.0",

    "lodash": "^4.6.1",

    "minimist": "^1.2.0"

  }

}

 

l  mongoDB 설치

vi /usr/sbin/policy-rc.d

# exit 101 대신 exit 0 으로 변경.

 

#실행까지 같이

apt-get install mongodb-clients mongodb-server

apt-get update

 

 

l  python 관련 grpc 라이브러리 설치

apt-get install python-pip

pip install --upgrade pip

python -m pip install grpcio #--ignore-installed

python -m pip install --user grpcio-tools

 

 

 

IV.  클라이언트 코드


grpc_sender.py (공통 코드)


from __future__ import print_function

import json

import time

import grpc

import json_pb2

import json_pb2_grpc

 

def make_request(_jsonStr):

    return json_pb2.Request(jsonStr=_jsonStr)

 

def grpcSend(_jsonStr):

    with grpc.insecure_channel('10.0.0.133:3002') as channel:

        request = make_request(_jsonStr)

        stub = json_pb2_grpc.JsonStub(channel)

        response = stub.grpcSend(request)

 

    print('Sent : grpc')

 

 



grpc_inference.py (main)


import sum

import base64

import time

import grpc_sender

 

#ws_sender.sendWs()

 

def toJson(message):

    jsonString = json.dumps(message, ensure_ascii=False).encode('utf8')

    return jsonString

 

def get_data(num):

 

    json_path = '/root/client_send/json/api/inference/current_tracking_'+str(num)+'.json'

    img_path = '/root/client_send/images/cam/cam ('+str(num)+').jpg'

    #json_path = '/home/star/generator/images/screenshots/conference.jpg'

    with open(json_path, 'rb') as single_json:

        json_text = single_json.read()

    with open(img_path, 'rb') as single_img:

        img_b64 = base64.b64encode(single_img.read())

 

    json_text = "{\"api-type\": \"inference\", \"current_tracking\":" + json_text + ", \"img\" : \"" + img_b64 + "\"}"

    print(json_text)

    return json_text

 

 

def main():

    type_num = 1

    while type_num <= 21:

 

        tmpString = get_data(type_num)

        print(tmpString)

        grpc_sender.grpcSend(tmpString)

        type_num = type_num+1

 

        time.sleep(2)

 

if __name__ == '__main__':

    print('start')

    main()

    print('end')

 

 

current_tracking_1.json (샘플 json 데이터)

{

    "time": "2018-10-19T14:45:04.080163+09:00",

    "state_name": "daily_ready",

    "status": null,

    "state_id": 140001861004480,

    "intent": "unknown",

    "slots": null,

    "state_group": "ready",

    "domain": "Daily",

    "state_tag": "Daily.ready.daily_ready.Ready",

    "stance": "neutral",

    "speech": "안녕",

    "subject": null

}

 

 

 

cam (1).jpg (샘플 이미지 데이터)

 


 

V.    서버 코드


l  app_grpc.js (주요 핵심 코드)


/*  proto파일에서 service descriptors 로드    */

var PROTO_PATH = __dirname + '/protobuf/json.proto';

var grpc = require('grpc');

var protoLoader = require('@grpc/proto-loader');

// Suggested options for similarity to existing grpc.load behavior

var packageDefinition = protoLoader.loadSync(

    PROTO_PATH,

    {keepCase: true,

     longs: String,

     enums: String,

     defaults: true,

     oneofs: true

    });

var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);

// The protoDescriptor object has the full package hierarchy

var json = protoDescriptor.json;

/*  proto파일에서 service descriptors 로드 end    */

 

 

 

 

function grpcSend(call, callback) {

  //sendWS(call.request.message);

  console.log(call.request.jsonStr);

  broadcast(call.request.jsonStr);

  callback(null, call.request);

}

function grpc_start() {

  var server = new grpc.Server();

  server.addService(json.Json.service, {grpcSend: grpcSend});

  server.bind('0.0.0.0:4996', grpc.ServerCredentials.createInsecure());

  server.start();

}

grpc_start();



 

VI.  통합 테스트

Receive서버 실행

node app_grpc.js

 

Send클라이언트 실행

python grpc_inference.py






 grpc 도 처음이지만, protobuf 라는 것을 처음 써보았다.


사용해본 바로는 Java의 SOAP 와 JNI 를 합친 느낌이었다.



 JNI 인터페이스 처럼 protobuf 인터페이스를 지정해서 언어에 맞는 전처리 작업을 해주고,


SOAP 처럼 gRPC도 원격으로 서버의 함수를 실행시키는 듯한 느낌을 주었다.


  

 웹 소켓으로 되어 있던 모듈을 grpc로 바꾸는 작업이었던 지라, 


웹 소켓과의 속도 비교도 할 수 있었는데, 약 1.2배정도 빠른 것을 확인할 수 있었다.  (63 kb 데이터)


'프로그래밍' 카테고리의 다른 글

이클립스 꿀팁  (0) 2016.02.12

+ Recent posts