KR_gRPC - somaz94/python-study GitHub Wiki
gRPCλ ꡬκΈμ΄ κ°λ°ν κ³ μ±λ₯ RPC(Remote Procedure Call) νλ μμν¬μ΄λ€.
# νλ‘ν λ²ν μ μ (user.proto)
syntax = "proto3";
package user;
// μλΉμ€ μ μ
service UserService {
// λ¨μΌ μμ²/μλ΅ λ©μλ
rpc GetUser (UserRequest) returns (UserResponse) {}
// μ¬μ©μ μμ± λ©μλ
rpc CreateUser (CreateUserRequest) returns (UserResponse) {}
// μ¬μ©μ λͺ©λ‘ μ‘°ν λ©μλ
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse) {}
// μ¬μ©μ μ
λ°μ΄νΈ λ©μλ
rpc UpdateUser (UpdateUserRequest) returns (UserResponse) {}
// μ¬μ©μ μμ λ©μλ
rpc DeleteUser (UserRequest) returns (DeleteUserResponse) {}
}
// μμ²/μλ΅ λ©μμ§ μ μ
message UserRequest {
string user_id = 1;
}
message CreateUserRequest {
string name = 1;
string email = 2;
string phone = 3;
int32 age = 4;
}
message UpdateUserRequest {
string user_id = 1;
string name = 2;
string email = 3;
string phone = 4;
int32 age = 5;
}
message ListUsersRequest {
int32 page_size = 1; // νμ΄μ§λΉ νλͺ© μ
int32 page_number = 2; // νμ΄μ§ λ²νΈ
string sort_by = 3; // μ λ ¬ κΈ°μ€ νλ
}
message UserResponse {
string user_id = 1;
string name = 2;
string email = 3;
string phone = 4;
int32 age = 5;
string created_at = 6;
string updated_at = 7;
}
message ListUsersResponse {
repeated UserResponse users = 1;
int32 total_count = 2;
int32 total_pages = 3;
}
message DeleteUserResponse {
bool success = 1;
string message = 2;
}
β
νΉμ§:
- νλ‘ν μ½ λ²νΌ
- κ°λ ₯ν νμ μμ€ν
- λ€μ€ μΈμ΄ μ§μ
- λμ μ±λ₯ λ° ν¨μ¨μ±
- μ½λ μμ± μλν
- μλ°©ν₯ μ€νΈλ¦¬λ° μ§μ
gRPCμ RESTλ API μ€κ³μ μμ΄ κ°κ° λ€λ₯Έ μ₯λ¨μ μ κ°μ§κ³ μλ€.
κΈ°λ₯ | gRPC | REST |
---|---|---|
νλ‘ν μ½ | HTTP/2 | HTTP/1.1 λλ HTTP/2 |
λ°μ΄ν° νμ | νλ‘ν μ½ λ²νΌ (μ΄μ§) | JSON, XML (ν μ€νΈ) |
μ½λ μμ± | μλ μμ± | μλ λλ λꡬ μ¬μ© |
μ€νΈλ¦¬λ° | μλ°©ν₯ μ€νΈλ¦¬λ° | μ νμ |
νμ μμ μ± | κ°λ ₯ν νμ κ²μ¬ | μ€ν€λ§ κΈ°λ° κ²μ¦ νμ |
λΈλΌμ°μ μ§μ | μ νμ | λ€μ΄ν°λΈ μ§μ |
μ±λ₯ | ν¨μ¨μ μΈ λ°μ΄ν° μ μ‘ | μλμ μΌλ‘ μ€λ²ν€λ νΌ |
νμ΅ κ³‘μ | κ°νλ¦ | μλ§ν¨ |
Pythonμμ gRPC μλ²λ₯Ό ꡬννλ λ°©λ²κ³Ό μλΉμ€ λ‘μ§ μμ± λ°©λ²μ΄λ€.
import grpc
from concurrent import futures
import time
import logging
import user_pb2
import user_pb2_grpc
from database import UserDatabase
# λ‘κΉ
μ€μ
logging.basicConfig(level=logging.INFO)
# μλΉμ€ ꡬν ν΄λμ€
class UserServicer(user_pb2_grpc.UserServiceServicer):
def __init__(self):
# λ°μ΄ν°λ² μ΄μ€ μ°κ²° (μμ)
self.db = UserDatabase()
def GetUser(self, request, context):
logging.info(f"GetUser μμ² μμ : {request.user_id}")
try:
# μ¬μ©μ μ‘°ν (κ°μ ν¨μ)
user = self.db.find_user(request.user_id)
if not user:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details(f"μ¬μ©μ ID {request.user_id}λ₯Ό μ°Ύμ μ μμ΅λλ€")
return user_pb2.UserResponse()
# μλ΅ μμ±
return user_pb2.UserResponse(
user_id=user.id,
name=user.name,
email=user.email,
phone=user.phone,
age=user.age,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat()
)
except Exception as e:
logging.error(f"GetUser μ€λ₯: {str(e)}")
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"λ΄λΆ μλ² μ€λ₯: {str(e)}")
return user_pb2.UserResponse()
def CreateUser(self, request, context):
logging.info(f"CreateUser μμ² μμ : {request.name}, {request.email}")
try:
# μ
λ ₯ κ²μ¦
if not request.name or not request.email:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
context.set_details("μ΄λ¦κ³Ό μ΄λ©μΌμ νμ νλͺ©μ
λλ€")
return user_pb2.UserResponse()
# μ΄λ©μΌ μ€λ³΅ νμΈ
if self.db.email_exists(request.email):
context.set_code(grpc.StatusCode.ALREADY_EXISTS)
context.set_details(f"μ΄λ©μΌ {request.email}λ μ΄λ―Έ μ¬μ© μ€μ
λλ€")
return user_pb2.UserResponse()
# μ¬μ©μ μμ± (κ°μ ν¨μ)
user = self.db.create_user(
name=request.name,
email=request.email,
phone=request.phone,
age=request.age
)
# μλ΅ μμ±
return user_pb2.UserResponse(
user_id=user.id,
name=user.name,
email=user.email,
phone=user.phone,
age=user.age,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat()
)
except Exception as e:
logging.error(f"CreateUser μ€λ₯: {str(e)}")
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"λ΄λΆ μλ² μ€λ₯: {str(e)}")
return user_pb2.UserResponse()
def ListUsers(self, request, context):
logging.info(f"ListUsers μμ² μμ : νμ΄μ§ {request.page_number}, ν¬κΈ° {request.page_size}")
try:
# νμ΄μ§λ€μ΄μ
μ²λ¦¬ (κ°μ ν¨μ)
users, total = self.db.list_users(
page_size=request.page_size,
page_number=request.page_number,
sort_by=request.sort_by
)
# μ¬μ©μ λͺ©λ‘ ꡬμ±
user_responses = []
for user in users:
user_responses.append(user_pb2.UserResponse(
user_id=user.id,
name=user.name,
email=user.email,
phone=user.phone,
age=user.age,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat()
))
# μ΄ νμ΄μ§ μ κ³μ°
total_pages = (total + request.page_size - 1) // request.page_size
# μλ΅ μμ±
return user_pb2.ListUsersResponse(
users=user_responses,
total_count=total,
total_pages=total_pages
)
except Exception as e:
logging.error(f"ListUsers μ€λ₯: {str(e)}")
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"λ΄λΆ μλ² μ€λ₯: {str(e)}")
return user_pb2.ListUsersResponse()
# μλ² μμ ν¨μ
def serve():
# μλ² μμ±
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# μλΉμ€ λ±λ‘
user_pb2_grpc.add_UserServiceServicer_to_server(
UserServicer(), server
)
# ν¬νΈ λ°μΈλ©
server.add_insecure_port('[::]:50051')
# μλ² μμ
server.start()
logging.info("μλ²κ° ν¬νΈ 50051μμ μμλμμ΅λλ€")
try:
# μλ² μ€ν μ μ§
while True:
time.sleep(86400) # 1μΌ
except KeyboardInterrupt:
# μλ² μ’
λ£
server.stop(0)
logging.info("μλ²κ° μ’
λ£λμμ΅λλ€")
if __name__ == '__main__':
serve()
β
νΉμ§:
- μλΉμ€ ꡬν
- μμ² μ²λ¦¬
- μλ΅ μμ±
- μ€λ₯ μ²λ¦¬
- λΉλκΈ° μ§μ
- μ€λ λ ν κ΄λ¦¬
- λ©νλ°μ΄ν° μ²λ¦¬
Pythonμμ gRPC ν΄λΌμ΄μΈνΈλ₯Ό ꡬννκ³ μλ²μ ν΅μ νλ λ°©λ²μ΄λ€.
import grpc
import logging
import time
from datetime import datetime
import user_pb2
import user_pb2_grpc
# λ‘κΉ
μ€μ
logging.basicConfig(level=logging.INFO)
class UserClient:
def __init__(self, host='localhost', port=50051):
# μ±λ μ€μ λ° μ€ν
μμ±
self.channel = grpc.insecure_channel(f'{host}:{port}')
self.stub = user_pb2_grpc.UserServiceStub(self.channel)
logging.info(f"ν΄λΌμ΄μΈνΈκ° {host}:{port}μ μ°κ²°λμμ΅λλ€")
def __del__(self):
# μ±λ μ’
λ£
self.channel.close()
def get_user(self, user_id):
"""μ¬μ©μ μ‘°ν"""
logging.info(f"μ¬μ©μ μ‘°ν: {user_id}")
request = user_pb2.UserRequest(user_id=user_id)
try:
# νμμμ μ€μ (5μ΄)
response = self.stub.GetUser(request, timeout=5)
# μλ΅ μ²λ¦¬
return {
'user_id': response.user_id,
'name': response.name,
'email': response.email,
'phone': response.phone,
'age': response.age,
'created_at': response.created_at,
'updated_at': response.updated_at
}
except grpc.RpcError as e:
# gRPC μ€λ₯ μ²λ¦¬
status_code = e.code()
details = e.details()
if status_code == grpc.StatusCode.NOT_FOUND:
logging.warning(f"μ¬μ©μλ₯Ό μ°Ύμ μ μμ΅λλ€: {details}")
return None
elif status_code == grpc.StatusCode.DEADLINE_EXCEEDED:
logging.error("μμ² μκ° μ΄κ³Ό")
return None
else:
logging.error(f"RPC μ€λ₯: {status_code} - {details}")
return None
except Exception as e:
logging.error(f"μΌλ° μ€λ₯: {str(e)}")
return None
def create_user(self, name, email, phone=None, age=None):
"""μ¬μ©μ μμ±"""
logging.info(f"μ¬μ©μ μμ±: {name}, {email}")
request = user_pb2.CreateUserRequest(
name=name,
email=email,
phone=phone or "",
age=age or 0
)
try:
# λ©νλ°μ΄ν° μ€μ (μ: API ν€)
metadata = [
('api-key', 'your-api-key'),
('client-id', 'python-client')
]
# νμμμκ³Ό λ©νλ°μ΄ν°λ‘ μμ²
response = self.stub.CreateUser(
request,
timeout=5,
metadata=metadata
)
# μλ΅ μ²λ¦¬
return {
'user_id': response.user_id,
'name': response.name,
'email': response.email,
'phone': response.phone,
'age': response.age,
'created_at': response.created_at,
'updated_at': response.updated_at
}
except grpc.RpcError as e:
status_code = e.code()
details = e.details()
if status_code == grpc.StatusCode.ALREADY_EXISTS:
logging.warning(f"μ΄λ―Έ μ‘΄μ¬νλ μ¬μ©μ: {details}")
elif status_code == grpc.StatusCode.INVALID_ARGUMENT:
logging.warning(f"μλͺ»λ μΈμ: {details}")
else:
logging.error(f"RPC μ€λ₯: {status_code} - {details}")
return None
except Exception as e:
logging.error(f"μΌλ° μ€λ₯: {str(e)}")
return None
def list_users(self, page_size=10, page_number=1, sort_by='name'):
"""μ¬μ©μ λͺ©λ‘ μ‘°ν"""
logging.info(f"μ¬μ©μ λͺ©λ‘ μ‘°ν: νμ΄μ§ {page_number}, ν¬κΈ° {page_size}")
request = user_pb2.ListUsersRequest(
page_size=page_size,
page_number=page_number,
sort_by=sort_by
)
try:
# gRPC μμ²
response = self.stub.ListUsers(request, timeout=10)
# μλ΅ μ²λ¦¬
users = []
for user in response.users:
users.append({
'user_id': user.user_id,
'name': user.name,
'email': user.email,
'phone': user.phone,
'age': user.age,
'created_at': user.created_at,
'updated_at': user.updated_at
})
# νμ΄μ§λ€μ΄μ
λ©νλ°μ΄ν° ν¬ν¨
return {
'users': users,
'total_count': response.total_count,
'total_pages': response.total_pages,
'current_page': page_number,
'page_size': page_size
}
except grpc.RpcError as e:
logging.error(f"RPC μ€λ₯: {e.code()} - {e.details()}")
return None
except Exception as e:
logging.error(f"μΌλ° μ€λ₯: {str(e)}")
return None
# ν΄λΌμ΄μΈνΈ μ¬μ© μμ
def main():
# ν΄λΌμ΄μΈνΈ μΈμ€ν΄μ€ μμ±
client = UserClient()
# μ μ¬μ©μ μμ±
new_user = client.create_user(
name="νκΈΈλ",
email="[email protected]",
phone="010-1234-5678",
age=30
)
if new_user:
# μμ±λ μ¬μ©μ IDλ‘ μ‘°ν
user_id = new_user['user_id']
user = client.get_user(user_id)
if user:
print(f"μ‘°νλ μ¬μ©μ: {user['name']}, {user['email']}")
# μ¬μ©μ λͺ©λ‘ μ‘°ν
users_page = client.list_users(page_size=5, page_number=1)
if users_page:
print(f"μ΄ μ¬μ©μ μ: {users_page['total_count']}")
for user in users_page['users']:
print(f"- {user['name']} ({user['email']})")
if __name__ == "__main__":
main()
β
νΉμ§:
- μ±λ μ€μ
- μ€ν μμ±
- μλ¬ μ²λ¦¬
- λ©νλ°μ΄ν° μ μ‘
- νμμμ κ΄λ¦¬
- μλ΅ νμ±
- 리μμ€ μ 리
service UserService {
rpc GetUserStream (UserRequest) returns (stream UserResponse) {}
rpc CreateUsers (stream CreateUserRequest) returns (UsersResponse) {}
rpc ChatStream (stream ChatMessage) returns (stream ChatMessage) {}
}
class UserServicer(user_pb2_grpc.UserServiceServicer):
async def GetUserStream(self, request, context):
users = get_users_batch(request.user_id)
for user in users:
yield user_pb2.UserResponse(
user_id=user.id,
name=user.name,
email=user.email
)
β νΉμ§:
- μλ² μ€νΈλ¦¬λ°
- ν΄λΌμ΄μΈνΈ μ€νΈλ¦¬λ°
- μλ°©ν₯ μ€νΈλ¦¬λ°
class AuthInterceptor(grpc.ServerInterceptor):
def intercept_service(self, continuation, handler_call_details):
metadata = dict(handler_call_details.invocation_metadata)
token = metadata.get('authorization')
if not token:
return self._unauthenticated()
try:
user = verify_token(token)
context = set_user_context(user)
return continuation(handler_call_details, context)
except Exception:
return self._unauthenticated()
β νΉμ§:
- μΈμ¦/μΈκ°
- λ‘κΉ
- λ©νλ°μ΄ν° μ²λ¦¬
β λͺ¨λ² μ¬λ‘:
- νλ‘ν λ²ν μ€κ³ μ μ€ν
- μ€νΈλ¦¬λ° μ μ ν νμ©
- μλ¬ μ²λ¦¬ νμ€ν
- μΈν°μ ν° νμ©
- μ±λ₯ μ΅μ ν
- 보μ κ³ λ €
- λͺ¨λν°λ§ ꡬν
- ν μ€νΈ μμ±