Golangrpc 패키지 사용 안내
6254 단어 RPC
rpc
패키지는 네트워크나 기타I/0
를 통해 연결된 대상에 대한 내보내기 방법에 대한 접근을 제공합니다.서버는 개체를 개체 유형 이름을 가진 가시적인 서비스로 등록합니다.등록 후 객체의 내보내기 방법에 원격으로 액세스할 수 있습니다.서버는 서로 다른 유형의 여러 대상 서비스를 등록할 수 있지만 같은 유형의 여러 대상을 등록하는 것은 잘못된 것이다.그리고 대상의 방법은 아래의 조건을 충족시켜야만 원격 호출을 사용할 수 있으며, 그렇지 않으면 방법은 무시될 수 있다.func (t *T) MethodName(argType T1, replyType *T2) error
T1
와 T2
는 encoding/gob
를 통해 인코딩할 수 있다.방법의 첫 번째 파라미터는 호출자가 제공한 파라미터를 대표하고, 두 번째 파라미터는 호출자에게 되돌아오는 결과 파라미터를 대표한다.방법이 되돌아오는 값이 비어 있지 않으면 되돌아오는 reply
파라미터는 클라이언트에게 되돌아오지 않습니다.예제
서버 프로세싱 프로세스
Register
또는 RegisterName
를 사용하여 rpc
서비스를 등록한다.Listen
방법으로 로컬 네트워크의 주소를 직접 감청하고 정부에서 준 예에서 감청하기 전에 rpc.HandleHTTP
방법을 사용했다.// rpc.HandleHTTP()
func HandleHTTP() {
DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}
const (
DefaultRPCPath = "/_goRPC_"
DefaultDebug = "/debug/rpc"
)
func (server *Server) HandleHTTP(rpcPath, debugPath string) {
http.Handle(rpcPath, server)
http.Handle(debugPath, debugHTTP{server})
}
두 개
handler
를 등록하여 서비스 호출 상황을 볼 수 있습니다.예를 들어 Listen
감청localhost:1234
네트워크를 사용하면 루트localhost:1234/debug/rpc
를 통해 다음과 같은 내용을 볼 수 있다.http.Serve
방법을 사용하여 다가올 HTTP
연결을 수신하고 각 연결에 새로운 서비스를 만들 것이다goroutine
.모든 서비스는 요청을 읽고 handler
호출해서 그들에게 회답할 것입니다.handler
는 보통nil
이며, 이때DefaultServeMux
는 사용된다.func Serve(l net.Listener, handler Handler) error {
srv := &Server{Handler: handler}
return srv.Serve(l)
}
func (srv *Server) Serve(l net.Listener) error {
// ...
for {
rw, e := l.Accept()
if e != nil {
// ...
}
c := srv.newConn(rw)
c.setState(c.rwc, StateNew)
go c.serve(ctx)
}
}
위의
http.Serve
방법도 다른 방법으로 처리할 수 있다. 예를 들어 Accept
인터페이스와 ServeConn
방법, 그리고 for
문장을 사용하여 지속적인 처리 요청을 실현할 수 있다.두 번째 단계에서 Listen
방법은 listener
감청 대상을 되돌려주고 그 위에 Accept
방법을 호출한다.호출이 성공하면 사용 가능한 연결이 되돌아옵니다.그리고 rpc.ServeConn
방법을 사용해서 이 연결에서 기본 서비스를 실행합니다.ServeConn
클라이언트가 중단될 때까지 차단됩니다.보통 go
를 사용하여 병발ServeConn
하고, ServeConn
방법은 gob
형식의 codec
를 사용하며, 교체가 필요하면 ServeCodec
방법을 사용합니다.rpc
가방은 두 가지codec
를 제공한다. 하나는 gob
, 하나는 json
이다.// ...
listener, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println("failed to listen tcp, error:", err)
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("failed to accept connection, error:", err)
}
go rpc.ServeConn(conn)
}
서버 전체 예
package main
import (
"errors"
"fmt"
"net"
"net/http"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
arith := new(Arith)
err := rpc.Register(arith)
if err != nil {
fmt.Println("failed to register rpc service. error: ", err)
return
}
rpc.HandleHTTP()
l, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println("listen error:", err)
}
if err = http.Serve(l, nil); err != nil {
fmt.Println("failed to accept incoming request. error: ", err)
return
}
}
클라이언트 프로세스
Dial
서비스를 연결합니다.연결이 완료되면 새 클라이언트가 생성됩니다.RPC
방법도 사용할 수 있다. DialHTTP
는 특정 네트워크 주소의 DialHTTP
서비스에 연결하고 기본값HTTP RPC
경로를 감청한다.func DialHTTP(network, address string) (*Client, error) {
return DialHTTPPath(network, address, DefaultRPCPath)
}
func DialHTTPPath(network, address, path string) (*Client, error) {
var err error
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
// ...
}
HTTP RPC
인터페이스를 호출하고 Call
인터페이스는 지정한 Call
서비스를 호출하여 완료를 기다린 후 오류 상태를 되돌려줍니다.RPC
인터페이스는 동기화 호출Call
서비스로 비동기화 호출이 필요하면 클라이언트에서 호출RPC
인터페이스를 사용할 수 있습니다.func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
// ...
}
호출이 잘못되지 않았다면, 위에서 되돌아온
Go
중 Call
채널은 오류가 없는 Done
로 전송될 것입니다.잘못 여부에 따라 호출 성공 여부를 판단할 수 있다.예를 들면 다음과 같습니다.// ...
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall :=
클라이언트 전체 예
package main
import (
"fmt"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
if err != nil {
fmt.Println("failed to dial http rpc server, error:", err)
}
// Synchronous call
args := &Args{7, 8}
var reply int
if err = client.Call("Arith.Multiply", args, &reply); err != nil {
fmt.Println("failed to call function synchronously, error:", err)
}
fmt.Printf("Arith: %d*%d=%d
", args.A, args.B, reply)
// Asynchronous call
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall :=