RPC 프로토콜 사용하기

이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07

RPC(Remote Procedure Call, 원격 프로시저 호출)는 원격에서 함수를 실행하는 기술입니다. 서버에 있는 함수를 로컬에 있는 함수처럼 사용할 수 있기 때문에 매우 편리합니다. RPC는 Go 언어에서 기본 패키지로 지원하므로 간단하게 구현할 수 있습니다.

서버 작성하기

이번에는 원격에서 덧셈 함수를 호출하여 두 수를 더해보겠습니다.

다음은 net/rpc 패키지에서 제공하는 RPC 함수입니다.

  • func Register(rcvr interface{}) error: RPC로 사용할 함수 등록

다음 내용을 GOPATH/src/rpcserver/rpcserver.go 파일로 저장합니다. GOPATH를 설정하는 방법은 ‘Unit 3 기본 디렉터리 설정하기’를 참조하기 바랍니다.

GOPATH/src/rpcserver/rpcserver.go

package main

import (
	"fmt"
	"net"
	"net/rpc"
)

type Calc int // RPC 서버에 등록하기 위해 임의의 타입으로 정의

type Args struct { // 매개변수
	A, B int
}

type Reply struct { // 리턴값
	C int
}

func (c *Calc) Sum(args Args, reply *Reply) error {
	reply.C = args.A + args.B // 두 값을 더하여 리턴값 구조체에 넣어줌
	return nil
}

func main() {
	rpc.Register(new(Calc)) // Calc 타입의 인스턴스를 생성하여 RPC 서버에 등록
	ln, err := net.Listen("tcp", ":6000") // TCP 프로토콜에 6000번 포트로 연결을 받음
	if err != nil {
		fmt.Println(err)
		return
	}
	defer ln.Close() // main 함수가 종료되기 직전에 연결 대기를 닫음

	for {
		conn, err := ln.Accept() // 클라이언트가 연결되면 TCP 연결을 리턴
		if err != nil {
			continue
		}
		defer conn.Close() // main 함수가 끝나기 직전에 TCP 연결을 닫음

		go rpc.ServeConn(conn) // RPC를 처리하는 함수를 고루틴으로 실행
	}
}

RPC 서버에 함수를 등록하려면 함수만으로는 안되고, 구조체나 일반 자료형과 같은 타입에 메서드 형태로 구성되어 있어야 합니다. 여기서는 Calc 타입을 int 형으로 정의했는데, 다른 자료형이나 빈 구조체로 정의해도 됩니다.

type Calc int // RPC 서버에 등록하기 위해 임의의 타입으로 정의

덧셈 함수의 매개변수 및 리턴값을 정의합니다.

type Args struct { // 매개변수
	A, B int
}

type Reply struct { // 리턴값
	C int
}

func (c *Calc) Sum(args Args, reply *Reply) error {
	reply.C = args.A + args.B // 두 값을 더하여 리턴값 구조체에 넣어줌
	return nil
}

RPC로 호출되는 함수는 매개변수 두 개와 error 형 리턴값 형식입니다. 따라서 매개변수를 위한 구조체 Args와 리턴값을 위한 구조체(Reply)를 정의해줍니다. 여기서 구조체 필드의 이름에서 첫글자는 항상 대문자로 시작해야 합니다.

덧셈 함수 SumCalc 타입의 메서드로 정의해줍니다. 그리고 reply.C = args.A + args.B처럼 매개변수 구조체에서 받은 값을 더해서 리턴값 구조체에 넣어줍니다. 매개변수와 리턴값이 구조체 형식이므로 다양한 자료형을 사용할 수 있고, 리턴값의 개수도 제한이 없습니다.

RPC 서버에서 함수가 처리될 수 있도록 등록을 해주고, TCP 연결을 받을 준비를 합니다.

rpc.Register(new(Calc)) // Calc 타입의 인스턴스를 생성하여 RPC 서버에 등록
ln, err := net.Listen("tcp", ":6000") // TCP 프로토콜에 6000번 포트로 연결을 받음
if err != nil {
	fmt.Println(err)
	return
}
defer ln.Close() // main 함수가 종료되기 직전에 연결 대기를 닫음

우리는 Calc 타입의 메서드를 사용할 것이므로 Calc 타입의 메모리를 할당하여 rpc.Register 함수에 넣어줍니다. 그리고 net.Listen 함수에 tcp와 포트 번호를 설정합니다. “:6000”처럼 포트 번호만 설정하면 모든 네트워크 인터페이스(NIC)의 IP 주소에서 연결을 받고, “192.168.0.100:6000”처럼 IP 주소와 함께 설정하면 특정 NIC에서만 TCP 연결을 받습니다. 그리고 TCP 연결 대기 ln은 지연 호출을 사용하여 서버가 끝나면 닫아줍니다.

for 반복문으로 무한 루프를 돌면서 클라이언트의 연결을 기다립니다. 클라이언트와 연결되면 Accept 함수에서 TCP 연결(커넥션) conn이 리턴됩니다. 그 뒤 RPC를 처리할 rpc.ServeConn 함수를 고루틴으로 실행해줍니다. 마찬가지로 conn도 지연 호출을 사용하여 서버가 끝나면 닫아줍니다.

for {
	conn, err := ln.Accept() // 클라이언트가 연결되면 TCP 연결을 리턴
	if err != nil {
		continue
	}
	defer conn.Close() // main 함수가 끝나기 직전에 TCP 연결을 닫음

	go rpc.ServeConn(conn) // RPC를 처리하는 함수를 고루틴으로 실행
}

이제 RPC 요청이 오면 알아서 적절한 함수를 실행해줍니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015