go 원생 rpc 패키지 사용: 데 이 터 를 전달 하 는 캐리어: golang 패키지 http 프로 토 콜 / tcp / jsonrpc

13904 단어 MQ/미들웨어/RPC
rpc 는 통신 프로 토 콜, 즉 컴퓨터 간 의 의사 소통 방식, 유사 http, my sql 등 입 니 다. 컴퓨터 간 통신 은 네트워크 연결 입 니 다. tcp / ip 프로 토 콜 의 한 네트워크 연결 은 5 원 그룹 정보 - 프로 토 콜 - 포트 - ip 를 포함 하고 rpc 는 한 네트워크 연결 에 stub 를 설정 하여 이 연결 을 제어 하 는 rpc 요청 입 니 다.일반적인 상황 rpc 의 server 엔 드 에 연 결 된 stub 의 캐리어 는 1. http 프로 토 콜 에서 모든 http 연결 (server 엔 드 ip, 프로 토 콜, 포트 지정) 을 감청 하고 http 프로 토 콜 을 통 해 온 모든 연결 이 rpc 요청 이 있 으 면 rpc 의 server 엔 드 에 포 착 됩 니 다 (현재 전 제 는 이 요청 이 rpc - sever 에 등록 되 어 있 음).2. tcp 연결
rpc 는 2 가지 내용 에 주목 합 니 다. 1. 전송 방식 - 서버 의 캐리어 - 데 이 터 를 전달 하 는 프로 토 콜 - rpc 는 세 션 계층 의 의사 소통 방식 이기 때문에 전송 하 는 하층 의 어느 층 에 연결 하 는 지 고려 해 야 합 니 다. 프로 토 콜 - http 프로 토 콜 을 바탕 으로 하 는 지, tcp 연결 에 연결 되 어 있 는 지, 2. 서버 와 클 라 이언 트 가 공동으로 사용 하 는 - 데이터 전송 형식: json / xml / protobuf   데이터 전송 형식 과 캐리어 를 통일 하면 클 라 이언 트 로 서버 의 rpc 서 비 스 를 호출 할 수 있 고 클 라 이언 트 는 언어 를 제한 하지 않 습 니 다.
JSON 과 protobuf 는 다 중 언어 를 지원 합 니 다. - 즉, 이 프로 토 콜 들 은 자바 와 같은 다른 언어 로 번역 할 수 있 습 니 다.golang 공식 net/rpc 라 이브 러 리 사용 encoding/gob 을 디 코딩 하여 http 와 tcp 프로 토 콜 의 전송 방식 을 지원 합 니 다.
RPC 가 뭐야?
원 격 프로 세 스 호출 (Remote Procedure Call, RPC 로 약칭) 은 컴퓨터 통신 프로 토 콜 입 니 다.이 프로 토 콜 은 한 컴퓨터 에서 실행 되 는 프로그램 에서 다른 컴퓨터 의 서브루틴 을 호출 할 수 있 으 며, 프로그래머 는 이 상호작용 을 위해 프로그램 을 작성 할 필요 가 없다.관련 소프트웨어 가 대상 을 대상 으로 프로 그래 밍 을 한다 면 원 격 프로 세 스 호출 도 원 격 호출 또는 원 격 방법 호출 이 라 고 할 수 있다.위 키 백과: 원 격 프로 세 스 호출
통속 적 이 고 알 기 쉬 운 언어 로 설명 하면 RPC 는 기 계 를 뛰 어 넘 고 언어 를 뛰 어 넘 는 컴퓨터 프로그램 을 사용 하 는 방법 을 허용 하 는 것 이다.예 를 들 어 저 는 go 언어 로 사용자 정 보 를 얻 는 방법 getUser Info 를 썼 고 go 프로그램 을 아 리 클 라 우 드 서버 에 배 치 했 습 니 다. 지금 저 는 텐 센트 클 라 우 드 에 배 치 된 phop 프로젝트 가 있 습 니 다. golang 의 getUser Info 방법 으로 사용자 정 보 를 얻 어야 합 니 다. phop 크로스 기기 가 go 방법 을 호출 하 는 과정 은 바로 RPC 호출 입 니 다.
golang 에서 RPC 를 어떻게 실현 합 니까?
골 랑 에서 RPC 를 실현 하 는 것 은 매우 간단 하 며, 포 장 된 공식 라 이브 러 리 와 일부 제3자 라 이브 러 리 가 지원 합 니 다.Go RPC 는 tcp 나 http 를 이용 해 데 이 터 를 전달 할 수 있 으 며, 전달 할 데이터 에 대해 서 는 다양한 종류의 코딩 방식 을 사용 할 수 있다.golang 공식 net/rpc 라 이브 러 리 사용 encoding/gob 을 디 코딩 하여 지원 tcp 또는 http 데이터 전송 방식 을 지원 합 니 다. 다른 언어 는 지원 되 지 않 기 때문에 gob 라 이브 러 리 에서 실 현 된 RPC 방법 을 사용 하여 언어 간 디 코딩 을 할 수 없습니다.
골 랑 은 공식 적 으로 net/rpc 라 이브 러 리 를 제공 하여 RPC 를 실현 하 는 방법 도 제 공 했 고 JSON RPC 는 JSON 으로 데이터 디 코딩 을 하기 때문에 크로스 언어 호출 을 지원 합 니 다.그러나 현재 jsonrpc 라 이브 러 리 는 tcp 프로 토 콜 을 기반 으로 이 루어 졌 으 며 http 로 데이터 전송 을 하 는 것 은 잠시 지원 되 지 않 습 니 다.
골 랑 이 공식 적 으로 제공 하 는 rpc 라 이브 러 리 를 제외 하고 많은 제3자 라 이브 러 리 가 골 랑 에서 RPC 를 실현 하 는 데 지원 을 제공 합 니 다. 대부분의 제3자 rpc 라 이브 러 리 의 실현 은 net/rpc/jsonrpc 데이터 디 코딩 을 사용 합 니 다. protobuf 성명 파일 에 따라 rpc 방법 정의 와 서비스 등록 코드 를 자동 으로 생 성하 여 골 랑 에서 rpc 서비스 호출 을 편리 하 게 할 수 있 습 니 다.
     rpc
    
rpc 패 키 지 는 네트워크 나 다른 I / O 를 통 해 대상 에 연결 하 는 외부 방법 을 제공 합 니 다.
          jsonrpc
    
jsonrpc 패 키 지 는 rpc 패 키 지 를 사용 하여 JSON - RPC 의 클 라 이언 트 디코더 와 서버 의 디코더 를 실현 합 니 다.
다음은 golang 공식 protobuf 라 이브 러 리 를 사용 하여 RPC 를 실현 하 는 방법 을 보 여 줍 니 다. net/rpc 을 RPC 의 캐리어 로 사용 하고 http 패키지 로 클 라 이언 트 연결 요청 을 감청 합 니 다.정확히 말 하면, 나 는 golang 봉 인 된 http 프로 토 콜 을 사용 하여 통신 을 하 는 연결 이 라 고 생각 하 는데, 모두 rpc 의 server 엔 드 stub 에 등록 되 었 다.
//     
package main

import (
	"errors"
	"fmt"
	"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
}

//test http rpc
func main() {

	arith := new(Arith)
	rpc.Register(arith)//   rpc   server-called
	rpc.HandleHTTP()//   http    rpc     server-stub

	go func() {
		err := http.ListenAndServe(":1234", nil)
		if err != nil {
			fmt.Println(err.Error())
		}
	}()

	go func() {
		err1 := http.ListenAndServe(":12341", nil)
		if err1 != nil {
			fmt.Println(err1.Error())
		}
	}()
	select{}
}
==============================     ===================
package main

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

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {

	serverAddress := "127.0.0.1"	
	client, err := rpc.DialHTTP("tcp", serverAddress+":1234") //!!!!!!!! client-stub
	if err != nil {
		log.Fatal("dialing:", err)
	}
	// Synchronous call
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)//!!!!!!!!client-call
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d*%d=%d
", args.A, args.B, reply) var quot Quotient err = client.Call("Arith.Divide", args, &quot) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d/%d=%d remainder %d
", args.A, args.B, quot.Quo, quot.Rem) test(serverAddress) } func test(serverAddress string){ fmt.Println("===question2--from-port-12341") client, err := rpc.DialHTTP("tcp", serverAddress+":12341") if err != nil { log.Fatal("dialing:", err) } // Synchronous call args := Args{17, 8} var reply int err = client.Call("Arith.Multiply", args, &reply) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d*%d=%d
", args.A, args.B, reply) var quot Quotient err = client.Call("Arith.Divide", args, &quot) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d/%d=%d remainder %d
", args.A, args.B, quot.Quo, quot.Rem) }

net / rpc / jsonrpc 라 이브 러 리
위의 예 에서 우 리 는 net/http 을 사용 하여 RPC 를 실현 하 는 과정 을 보 여 주 었 으 나 다른 언어 에서 위의 예 에서 실 현 된 RPC 방법 을 호출 할 수 없 었 다.그래서 다음 예 는 net/rpc 라 이브 러 리 를 사용 하여 RPC 를 실현 하 는 방법 을 보 여 줍 니 다. 이 방식 으로 실 현 된 RPC 방법 은 크로스 언어 호출 을 지원 합 니 다.
$GOPATH/src/test/rpc/jsonrpc_server.go
package main

import (
    "errors"
    "fmt"
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)

//        
type Arith struct {
}

//          
type ArithRequest struct {
    A int
    B int
}

//          
type ArithResponse struct {
    Pro int //   
    Quo int //  
    Rem int //   
}

//       
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
    res.Pro = req.A * req.B
    return nil
}

//       
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
    if req.B == 0 {
        return errors.New("divide by zero")
    }
    res.Quo = req.A / req.B
    res.Rem = req.A % req.B
    return nil
}

func main() {
    rpc.Register(new(Arith)) //   rpc  

    lis, err := net.Listen("tcp", "127.0.0.1:8096")
    if err != nil {
        log.Fatalln("fatal error: ", err)
    }

    fmt.Fprintf(os.Stdout, "%s", "start connection")

    for {
        conn, err := lis.Accept() //          
        if err != nil {
            continue
        }

        go func(conn net.Conn) { //          
            fmt.Fprintf(os.Stdout, "%s", "new client in coming
") jsonrpc.ServeConn(conn) }(conn) } }

상기 서버 프로그램 이 시작 되면 로 컬 8096 포트 를 감청 하고 클 라 이언 트 의 tcp 연결 요청 을 처리 합 니 다.우 리 는 golang 으로 클 라 이언 트 프로그램 이 상기 서버 에 연결 되 고 RPC 호출 을 할 수 있 습 니 다.
$GOPATH/src/test/rpc/jsonrpc_client.go
package main

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

//          
type ArithRequest struct {
    A int
    B int
}

//          
type ArithResponse struct {
    Pro int //   
    Quo int //  
    Rem int //   
}

func main() {
    conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
    if err != nil {
        log.Fatalln("dailing error: ", err)
    }

    req := ArithRequest{9, 2}
    var res ArithResponse

    err = conn.Call("Arith.Multiply", req, &res) //     
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d * %d = %d
", req.A, req.B, res.Pro) err = conn.Call("Arith.Divide", req, &res) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d
", req.A, req.B, res.Quo, res.Rem) }

protorpc 라 이브 러 리
크로스 언어 호출 을 실현 하기 위해 golang 에서 RPC 방법 을 실현 할 때 우 리 는 크로스 언어의 데이터 코딩 방식 을 선택해 야 한다. 예 를 들 어 JSON, 상기 net/rpc/jsonrpc 는 이 요 구 를 만족 시 킬 수 있 지만 http 전송 을 지원 하지 않 고 데이터 코딩 성능 이 높 지 않다 는 단점 도 있다.그래서 일부 제3자 rpc 라 이브 러 리 는 jsonrpc 데이터 디 코딩 을 선택 하고 서비스 등록 코드 자동 생 성 기능 을 제공 합 니 다.아래 의 예 는 RPC 방법 과 요청 응답 파 라 메 터 를 정의 하고 제3자 protobuf 라 이브 러 리 를 사용 하여 RPC 서비스 등록 코드 를 생 성 합 니 다.
우선, 설치 protobufprotorpc 명령 을 실행 할 수 있 습 니 다.
그리고 실행 할 RPC 방법 과 관련 된 인 자 를 정의 하 는 proto 파일 을 만 듭 니 다.
$GOPATH/src/test/rpc/pb/arith.proto
syntax = "proto3";
package pb;

//         
message ArithRequest {
    int32 a = 1;
    int32 b = 2;
}

//         
message ArithResponse {
    int32 pro = 1;  //   
    int32 quo = 2;  //  
    int32 rem = 3;  //   
}

// rpc  
service ArithService {
    rpc multiply (ArithRequest) returns (ArithResponse);    //       
    rpc divide (ArithRequest) returns (ArithResponse);      //       
}

다음은 위 에서 정의 한 protobuf 파일 에 따라 RPC 서비스 코드 를 생 성 해 야 합 니 다.먼저 protoc 라 이브 러 리 를 설치 한 다음 arith.proto 도 구 를 사용 하여 코드 를 생 성 합 니 다. protorpc 명령 을 실행 한 후 go get github.com/chai2010/protorpc 파일 과 같은 등급 의 디 렉 터 리 에 protoc 파일 을 생 성 했 습 니 다. 그 안에 RPC 방법 정의 와 서비스 등록 코드 가 포함 되 어 있 습 니 다.
생 성 된 protoc --go_out=plugin=protorpc=. arith.proto 코드 를 기반 으로 rpc 서버 를 실현 합 니 다.
$GOPATH/src/test/rpc/protorpc_server.go
package main

import (
    "errors"
    "test/rpc/pb"
)

//        
type Arith struct {
}

//       
func (this *Arith) Multiply(req *pb.ArithRequest, res *pb.ArithResponse) error {
    res.Pro = req.GetA() * req.GetB()
    return nil
}

//       
func (this *Arith) Divide(req *pb.ArithRequest, res *pb.ArithResponse) error {
    if req.GetB() == 0 {
        return errors.New("divide by zero")
    }
    res.Quo = req.GetA() / req.GetB()
    res.Rem = req.GetA() % req.GetB()
    return nil
}

func main() {
    pb.ListenAndServeArithService("tcp", "127.0.0.1:8097", new(Arith))
}

이 프로그램 을 실행 하면 로 컬 8097 포트 를 감청 하고 클 라 이언 트 의 tcp 연결 을 받 습 니 다.protoc 을 바탕 으로 클 라 이언 트 프로그램 을 다시 실현 합 니 다.
$GOPATH/src/test/protorpc_client.go
package main

import (
    "fmt"
    "log"
    "test/rpc/pb"
)

func main() {
    conn, err := pb.DialArithService("tcp", "127.0.0.1:8097")
    if err != nil {
        log.Fatalln("dailing error: ", err)
    }
    defer conn.Close()

    req := &pb.ArithRequest{9, 2}

    res, err := conn.Multiply(req)
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d * %d = %d
", req.GetA(), req.GetB(), res.GetPro()) res, err = conn.Divide(req) if err != nil { log.Fatalln("arith error ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d
", req.A, req.B, res.Quo, res.Rem) }

어떻게 언어 간 에 golang 의 RPC 방법 을 호출 합 니까?
위의 세 가지 예 를 들 어 우 리 는 각각 arith.proto, arith.pb.go, arith.pb.go 을 사용 하여 golang 중의 RPC 서버 를 실현 하고 해당 하 는 golang 클 라 이언 트 RPC 호출 예 시 를 제시 했다. JSON 과 protobuf 는 다 중 언어 를 지원 하기 때문에 ariti.pb.gonet/rpc 실 현 된 RPC 방법 을 사용 하면 우 리 는 다른 언어 에서 호출 할 수 있다.다음은 php 클 라 이언 트 프로그램 을 제공 합 니 다. socket 연결 을 통 해 jsonrpc 를 호출 하 는 서버 RPC 방법 입 니 다.
$PHPROOT/jsonrpc.php
conn = fsockopen($host, $port, $errno, $errstr, 3);
        if (!$this->conn) {
            return false;
        }
    }

    public function Call($method, $params) {
        if (!$this->conn) {
            return false;
        }
        $err = fwrite($this->conn, json_encode(array(
                'method' => $method,
                'params' => array($params),
                'id'     => 0,
            ))."
"); if ($err === false) { return false; } stream_set_timeout($this->conn, 0, 3000); $line = fgets($this->conn); if ($line === false) { return NULL; } return json_decode($line,true); } } $client = new JsonRPC("127.0.0.1", 8096); $args = array('A'=>9, 'B'=>2); $r = $client->Call("Arith.Multiply", $args); printf("%d * %d = %d
", $args['A'], $args['B'], $r['result']['Pro']); $r = $client->Call("Arith.Divide", array('A'=>9, 'B'=>2)); printf("%d / %d, Quo is %d, Rem is %d
", $args['A'], $args['B'], $r['result']['Quo'], $r['result']['Rem']);

기타 RPC 라 이브 러 리
위 에서 언급 한 세 가지 golang 에서 RPC 를 실현 하 는 방식 을 제외 하고 다른 rpc 라 이브 러 리 는 비슷 한 기능 을 제공 합 니 다. 비교적 유명한 구 글 오픈 소스 가 있 는 grpc 이지 만 grpc 의 첫 설치 가 비교적 번 거 롭 습 니 다. 여 기 는 더 이상 소개 하지 않 고 관심 이 있 는 것 은 스스로 알 수 있 습 니 다.
참고 자료
  • Go 공식 라 이브 러 리 RPC 개발 안내
  • go 웹 개발 튜 토리 얼 - RPC
  • Go RPC 개발 지침
  •  
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    좋은 웹페이지 즐겨찾기