-
[Golang] go에서 RPC 다루기개발언어/Go 2020. 10. 11. 18:47
간단한 RPC
1. HelloWorldHandler라는 구조체와 구조체에서 사용하는 메서드
type HelloWorldHandler struct{} func (h *HelloWorldHandler) HelloWorld(args *contract.HelloWorldRequest, reply *contract.HelloWorldResponse) error { reply.Message = "Hello " + args.Name return nil }
=> 빈 구조체를 하나 만들고, 해당 구조체는 HelloWorld라는 메서드를 가지고 있다.
2. 서버 시작
func StartServer() { helloWorld := &HelloWorldHandler{} rpc.Register(helloWorld) // ① l, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) // ② if err != nil { log.Fatal(fmt.Sprintf("Unable to listen on given port: %s", err)) } defer l.Close() for { // ③ conn, _ := l.Accept() // ④ go rpc.ServeConn(conn) // ⑤ } }
① helloworld 구조체를 기본 RPC서버에 등록(Register)한다.
② net.Listen을 통해 주어진 프로토콜과 port에 Listen한다. (프로토콜은 tpc뿐만 아니라, tcp4, unix 등과 같이 서버에서
사용할 프로토콜을 구체적으로 선택할 수 있다.)
참고로, Listen 함수는 Listener 인터페이스를 구현하는 인스턴스를 리턴한다.
type Listener interface { Accept() (Conn, error) Close() error Addr() addr }
③ RPC 서버는 각 연결을 개별적으로 처리하며, 처음 연결을 처리하고 나서 Accept를 호출해 후속 연결을 처리하거나
애플리케이션을 종료하기 때문에 무한루프를 돌림.
④ 연결을 수신하기 위해 Accept() 메서드를 호출
⑤ 주어진 conn을 가지고 DefaultServer 메서드를 실행하고, 클라이언트가 완료될 때 까지 대기한다.
참고로, 통신 프로토콜 측면에서 ServeConn은 Gob wire 타입을 사용하며,
3. 클라이언트 연결
func CreateClient() *rpc.Client { client, err := rpc.Dial("tcp", fmt.Sprintf("localhost:%v", port)) if err != nil { log.Fatal("dialing:", err) } return client }
=> Dial() 함수를 사용해서 클라이언트 자체를 생성한다.
func PerformRequest(client *rpc.Client) contract.HelloWorldResponse { args := &contract.HelloWorldRequest{Name: "World"} var reply contract.HelloWorldResponse err := client.Call("HelloWorldHandler.HelloWorld", args, &reply) if err != nil { log.Fatal("error:", err) } return reply }
=> client.Call() 메서드를 사용해서 서버에 붙여진 함수, 인자값, 해당 결과를 싣어서 보낼 응답객체(위에서는 reply)를 각각 입력해준다.
HTTP를 통한 RPC
func StartServer() { helloWorld := &HelloWorldHandler{} rpc.Register(helloWorld) rpc.HandleHTTP() l, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) if err != nil { log.Fatal(fmt.Sprintf("Unable to listen on given port: %s", err)) } log.Printf("Server starting on port %v\n", port) http.Serve(l, nil) }
기본 rpc와 다른 점은,
첫째, 전송 프로토콜로 HTTP를 사용해야 하는 경우, rpc.HandleHTTP() 메서드를 호출한다는 점이고,
둘째로는, http.Serve를 통해 listener를 매개변수로 전달한다는 점이 다르다.
HTTP를 통한 JSON-RPC
func StartServer() { helloWorld := new(HelloWorldHandler) rpc.Register(helloWorld) l, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) if err != nil { log.Fatal(fmt.Sprintf("Unable to listen on given port: %s", err)) } http.Serve(l, http.HandlerFunc(httpHandler)) } func httpHandler(w http.ResponseWriter, r *http.Request) { serverCodec := jsonrpc.NewServerCodec(&HttpConn{in: r.Body, out: w}) err := rpc.ServeRequest(serverCodec) if err != nil { log.Printf("Error while serving JSON request: %v", err) http.Error(w, "Error while serving JSON request, details have been logged.", 500) return } }
=> 위의 예제들과의 주요 차이점은 RPC 서버를 시작하는 대신 http 서버를 시작하고, 핸들러와 함께 리스너를 매개변수로 전달한다는 점이다.
'개발언어 > Go' 카테고리의 다른 글
[Golang] 마이크로서비스 공통패턴 (0) 2020.10.14 [Golang] RPC API (0) 2020.10.12 [Golang] net/http 패키지 (0) 2020.09.30 [golang] 마샬링(Marshaling) (0) 2020.09.26 [golang] 인터페이스(Interface) (0) 2020.09.24