-
파이썬 gRPC Tutorial
-
Buf - protobuf 파일을 빌드시켜주는 툴
-
BloomRPC
- gRPC를 GUI에서 할 수 있도록 하는 툴
- https://github.com/uw-labs/bloomrpc
TODO 애플리케이션 작성
- 할일 등록 (Create) => CreateTodo
- 할일 삭제 (Update) => Clo seTodo
- 할일 완료 (Update) => SuccessTodo
- 할일 리스트 가져오기 (Read) => ListTodo
0. prac_.proto proto_file 생성
- 각 service instance에 맞춰, message 생성
prac_.proto
syntax = "proto3";
package practice;
service TODO_APP {
// Create TODO_
rpc Create_TD (TD_create) returns (complete_C);
// Remove TODO_
rpc Remove_TD (TD_remove) returns (complete_D);
// finish TODO_
rpc Update_TD (TD_update) returns (complete_U);
// Read TODO_
rpc Read_TD (TD_read) returns (complete_R);
}
// the Create ToDo
### create service
message TD_create {
string Todo = 1;
}
message complete_C {
string message = 1;
}
---
### remove service
message TD_remove {
string Todo = 1;
}
message complete_D {
string message = 1;
}
---
### update service
message TD_update {
string Todo = 1;
}
message complete_U {
string message = 1;
}
---
### read service
message TD_read {
string Todo = 1;
}
message complete_R {
string list = 1;
}
1. 위 생성된 prac_.proto proto_file 실행
python -m grpc_tools.protoc -I. --python_out=./python_ --grpc_python_out=./python_ prac_.proto
output_file위치 입력prac_.proto파일 실행prac_pb2__grpc.py,prac__pb2.py이라는output_file생성- 이후에
server.py을 작성시 request 형식을 prac_pb2.py 아래에 있는 객체 양식에 맞춰줘야 함 client.py작성시prac__pb2_grpc.py의 stub 과prac__pb2.py의 객체 양식 또한 맞춰줘야 함
사실상 여기서 생성된 파일들은 내부 구조만 파악하고 건드릴 것은 없음
2. TOdo_server.py 와 Todo_client.py file 작성
-
객체내 메소드 작성시 request 타입을 정할 수 있음
-
dataset은 따로 생성해둔 csv 파일 이용
-
딕셔너리 형식을 이용해 csv 파일을 key:value 쌍으로 묶는다.
-
argparse 라이브러리를 이용해 cli 명령어 입력시
원하는 모드 : <c,r,u,d>와원하는리스트 : key받아옴 -
Create_TD -> 이미 리스트에 존재하는 경우 존재 메시지 출력 -> 없는 경우 0 으로 할당하여 key:value 값 할당 -> 실행했을 경우 1 안했을 경우 0
-
Remove_TD -> 존재하는 경우 제거 -> 존재하지 않는 경우 메시지 출력
-
Update_TD -> 존재하는 경우 0->1 로 변환 -> 존재하지 않는 경우 메시지 출력
-
Read_TD -> key 값으로 검색 -> value 값이 1인 경우 : Suceess_
key-> value 값이 0인 경우 : Not_yet_'key`
Todo_server.py
import pandas as pd
from concurrent import futures
import logging
import grpc
import prac__pb2
import prac__pb2_grpc
## 객체내 메소드 작성시 request 의 타입을 미리 정할 수 있음
class TODO_APP(prac__pb2_grpc.TODO_APPServicer):
def Create_TD(self, request : prac__pb2.TD_create, context):
df = pd.read_csv('./datas/todo_lst.csv', index_col='name')
dict_ = df.to_dict(orient='dict')
dict_ = dict_['value']
## todo_lst 에 이미 존재할 경우
if request.Todo in dict_.keys():
return prac__pb2.complete_C(message=f'It already have {request.Todo}')
## 그렇지 않을 경우 생성해주고 진행되지 않았으므로 '0' 으로 할당
else:
dict_[request.Todo] = 0
df = pd.DataFrame.from_dict(dict_, orient='index', columns=['value'])
df.to_csv('./datas/todo_lst.csv', index_label='name')
return prac__pb2.complete_C(message=f'append_{request.Todo}')
def Remove_TD(self, request : prac__pb2.TD_remove, context):
df = pd.read_csv('./datas/todo_lst.csv', index_col='name')
dict_ = df.to_dict(orient='dict')
dict_ = dict_['value']
if request.Todo in dict_.keys():
del dict_[request.Todo]
df = pd.DataFrame.from_dict(dict_, orient='index', columns=['value'])
df.to_csv('./datas/todo_lst.csv', index_label='name')
return prac__pb2.complete_D(message=f'deleted_{request.Todo}')
else:
return prac__pb2.complete_D(message=f'TODO_list does not have {request.Todo}')
def Update_TD(self, request : prac__pb2.TD_update, context):
df = pd.read_csv('./datas/todo_lst.csv', index_col='name')
dict_ = df.to_dict(orient='dict')
dict_ = dict_['value']
if request.Todo in dict_.keys():
dict_[request.Todo] = 1
df = pd.DataFrame.from_dict(dict_, orient='index', columns=['value'])
df.to_csv('./datas/todo_lst.csv', index_label='name')
return prac__pb2.complete_U(message=f'Success_{request.Todo}')
else:
return prac__pb2.complete_U(message=f'there is no {request.Todo}')
def Read_TD(self, request : prac__pb2.TD_read, context) :
df = pd.read_csv('./datas/todo_lst.csv', index_col='name')
dict_ = df.to_dict(orient='dict')
dict_ = dict_['value']
Succeed_lst = [key for key, value in dict_.items() if value ==1]
not_yet_lst = [key for key, value in dict_.items() if value ==0]
return prac__pb2.complete_R(list=f'Succeed_{",".join(Succeed_lst)},\n Not_yet_{",".join(not_yet_lst)}')
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
prac__pb2_grpc.add_TODO_APPServicer_to_server(TODO_APP(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig()
serve()
Todo_client.py
from __future__ import print_function
import logging
import argparse
import grpc
import prac__pb2
import prac__pb2_grpc
parser = argparse.ArgumentParser(description='grpc Training')
parser.add_argument('-m', '--mode', default='r', type=str,
help='mode : c : create, r : read, u : update , d : delete')
parser.add_argument('-k', '--key', default=None, type=str,
help='key : the list to add, delete or be succeed')
def run():
args = parser.parse_args()
if args.mode == 'c':
with grpc.insecure_channel('localhost:50051') as channel:
stub = prac__pb2_grpc.TODO_APPStub(channel)
response = stub.Create_TD(prac__pb2.TD_create(Todo=args.key))
print("received: " + response.message)
elif args.mode == 'r':
with grpc.insecure_channel('localhost:50051') as channel:
stub = prac__pb2_grpc.TODO_APPStub(channel)
response = stub.Read_TD(prac__pb2.TD_read(Todo=args.key))
print("received: " + response.list)
elif args.mode == 'u':
with grpc.insecure_channel('localhost:50051') as channel:
stub = prac__pb2_grpc.TODO_APPStub(channel)
response = stub.Update_TD(prac__pb2.TD_update(Todo=args.key))
print("received: " + response.message)
elif args.mode == 'd':
with grpc.insecure_channel('localhost:50051') as channel:
stub = prac__pb2_grpc.TODO_APPStub(channel)
response = stub.Remove_TD(prac__pb2.TD_remove(Todo=args.key))
print("received: " + response.message)
if __name__ == "__main__":
logging.basicConfig()
run()
3. cli 환경에서 server 띄우기
python Todo_server.py
4. 다른 cli 환경을 열어 요청보내기
python Todo_client.py -m `<c : 원하는 모드>` -k `<추가할 리스트>`