Golang - gRPC + grpc - web + nginx 서비스 구축

11019 단어 grpc
Golang - gRPC 서비스 구축
본 고 는 Golang - gRPC, grpc - web + nginx 구축 과정 과 중간 에 발생 한 문제 프로젝트 코드 를 기록 하고 자 한다.
1. gRPC 가 무엇 인지 이해 하기
gRPC 의 묘사 인터넷 은 이미 매우 많 고 gRPC 대체적으로 두 가지 지식 과 관련된다.
(1)、RPC Remote Procedure Call
RPC 의 핵심 은 로 컬 에서 원 격 (메모리 에 접근 할 수 있 는) 방법 을 호출 하 는 것 이다.
  • RPC 프레임 워 크: 상 자 를 열 면 RPC 호출 프레임 워 크 가 실 현 됩 니 다. 그 중에서 아 리 Dubbo, Google gRPC, Facebook Thrift
  • 와 같은 오픈 소스 프레임 워 크 입 니 다.
  • 원 격 통신 프로 토 콜: REST (HTTP JSON), SOAP (HTTP XML), gRPC (HTTP 2 protobuf)
  • 직렬 화 / 역 직렬 화: 텍스트 (XML, JSON) 와 이 진 (Java 원생, Hessian, protobuf, Thrift, Avro, Kryo, Message Pack
  • (2) protobuf
  • 충분히 간단 하 다
  • 직렬 화 후 부피 가 작 습 니 다. 메시지 크기 는 XML 의 1 / 10 ~ 1 / 3
  • 만 필요 합 니 다.
  • 해석 속도 가 빠르다: 해석 속도 가 XML 보다 20 ~ 100 배 빠르다
  • 다 국어 지원
  • 더 좋 은 호환성, Protobuf 디자인 의 원칙 은 아래로 또는 위로 호 환
  • 을 잘 지원 하 는 것 이다.
    2. hello 세계 구축
    실현 절차
  • protobuf 를 통 해 인터페이스 와 데이터 형식 을 정의 합 니 다
  • 인터페이스 코드 생 성
  • gRPC 서버 엔 드 코드 작성
  • gRPC 클 라 이언 트 엔 드 코드 작성
  • 디 렉 터 리 구 조 는 다음 과 같다 gihub 의 golang 프로젝트 표준 프레임 워 크.
    1. 인터페이스 와 데이터 형식 정의
    syntax = "proto3";
    
    package api;
    
    //          
    service HelloWorldService {
        //          
        rpc SayHello (HelloRequest) returns (HelloResponse) {}
    }
    
    //            
    message HelloRequest {
        //          
        string name = 1;
    }
    
    //            
    message HelloResponse {
        //          
        string message = 1;
    }

    2. 인터페이스 코드 생 성
  • protobuf 생 성 도구 protoc 를 다운로드 하여 실행 가능 한 파일 을 PATH 에 편리 하 게 사용 mv protoc-3.11.4-osx-x86_64/bin/protoc /usr/local/bin
  • go 언어 인터페이스 코드 를 만 드 는 플러그 인 다운로드 protoc-gen-go
  • go get -u github.com/golang/protobuf/protoc-gen-go
  • api / proto / v1 / init. go 를 작성 하여 스 크 립 트 생 성 (명령 행 에서 직접 실행 할 수도 있 음)
  • package api
    
    //go:generate protoc -I. --go_out=plugins=grpc:. ./hello.proto
    
    func init() {}
    
  • 실행 후 파일 hello. pb. go 에 hello 서비스 에 대한 설명 과 인터페이스 설명
  • 을 받 을 수 있 습 니 다.
    3. gRPC 서버 엔 드 코드 작성
  • api/service/hello/hello_service.go
  • package hello
    
    type Service struct {}
  • api/service/hello/say_hello.go
  • package hello
    
    import (
        "context"
        api "grpc-demo/api/proto/v1"
    )
    
    func (hello Service) SayHello (_ context.Context, params *api.HelloRequest) (res *api.HelloResponse, err error)  {
        res = &api.HelloResponse{
            Message: "server response: hello " + params.Name,
        }
    
        return res, nil
    }
    
  • api/server.go
  • package api
    
    import (
        "google.golang.org/grpc"
        api "grpc-demo/api/proto/v1"
        "grpc-demo/api/service/hello"
        "log"
        "net"
        "strconv"
    )
    
    func RungGRPCServer (grpcPort int16)  {
        //     grpc server
        grpcServer := grpc.NewServer()
        //        RegisterHelloWorldServiceServer
    
        api.RegisterHelloWorldServiceServer(grpcServer, &hello.Service{})
    
        //     
        listen, e := net.Listen("tcp", ":"+strconv.Itoa(int(grpcPort)))
    
        if e != nil {
            log.Fatal(e)
        }
    
        //       
        log.Printf("serve gRPC server: 127.0.0.1:%d", grpcPort)
        if err := grpcServer.Serve(listen); err != nil {
            log.Printf("failed to serve: %v", err)
            return
        }
    }
    
  • main.go
  • package main
    
    import "grpc-demo/api"
    
    func main ()  {
        c := make(chan bool, 1)
    
        go api.RungGRPCServer(9999)
    
        
  • 시작 프로그램
  • 4. gRPC 클 라 이언 트 엔 드 코드 작성
  • examples/client/go/mian.go
  • package main
    
    import (
        "context"
        "google.golang.org/grpc"
        api "grpc-demo/api/proto/v1"
        "log"
        "os"
    )
    
    const (
        address = "localhost:9999"
    )
    
    func main() {
        conn, err := grpc.Dial(address, grpc.WithInsecure())
    
        if err != nil {
            log.Fatalf("did not connect: %v", err)
        }
    
        defer conn.Close()
    
        c := api.NewHelloWorldServiceClient(conn)
    
        name := "world"
        if len(os.Args) > 1 {
            name = os.Args[1]
        }
    
        r, err := c.SayHello(context.Background(), &api.HelloRequest{Name: name})
    
        if err != nil {
            log.Fatalf("call say hello fail: %v", err)
        }
    
        log.Println(r.Message)
    }
    

    이제 간단 한 gRPC 프로그램 이 완성 되 었 습 니 다.
    3. 협조 grpc-web
    grpc - web 는 웹 엔 드 의 grpcClient 프로젝트 입 니 다. 현재 브 라 우 저 에서 grpc 프로 토 콜 을 직접 지원 하지 못 하 는 방안 을 해결 하려 면 프 록 시 서비스 와 함께 사용 해 야 합 니 다. grpc - web 구축 은 다음 과 같은 몇 단계 로 나 눌 수 있 습 니 다.
  • grpc - web client 코드 생 성
  • 프 록 시 서비스 설정
  • 참고 문장 grpc-web grpc-web-nginx
    1. grpc - web client 코드 생 성
  • 1. go 언어 와 마찬가지 로 플러그 인 을 다운로드 하고 js 언어 인터페이스 코드 를 만 드 는 플러그 인 protoc-gen-grpc-web
  • 을 다운로드 해 야 합 니 다.
    #           bin       
    mv ~/Downloads/protoc-gen-grpc-web-1.0.7-darwin-x86_64 /usr/local/bin/protoc-gen-grpc-web
    #        
    chmod +x /usr/local/bin/protoc-gen-grpc-web
  • 2 examples / client / js 디 렉 터 리 를 만 들 고 설명 파일 proto 를 js 프로젝트 로 이동 합 니 다. 구 조 는 다음 과 같 습 니 다
  • 3. js 스 크 립 트 examples / client / ts / protogen. sh 를 작성 하여 실행 하기 편리 합 니 다 (명령 을 직접 실행 할 수도 있 습 니 다)
  • #!/bin/bash
    
    PROJ_ROOT="$(dirname "$(dirname "$(readlink "$0")")")"
    
    protoc \
      -I ${PROJ_ROOT}/src/api/v1 \
      --js_out=import_style=commonjs:${PROJ_ROOT}/src/api/v1 \
      --grpc-web_out=import_style=typescript,mode=grpcweb:${PROJ_ROOT}/src/api/v1 \
      ${PROJ_ROOT}/src/api/v1/hello.proto
  • 실행 후 2 개의 파일 이 생 성 됩 니 다. 다음 그림
  • 전단 항목 초기 화, package. json 설정
  • {
      "name": "js",
      "version": "1.0.0",
      "dependencies": {},
      "main": "src/main.js",
      "devDependencies": {
        "google-protobuf": "^3.11.4",
        "grpc-web": "^1.0.7",
        "webpack": "^4.16.5",
        "webpack-cli": "^3.1.0"
      }
    }
  • examples/client/js/src/main.js
  • const { HelloRequest } = require('./api/v1/hello_pb');
    const { HelloWorldServiceClient } = require('./api/v1/hello_grpc_web_pb');
    
    //                ,  grpc   
    var client = new HelloWorldServiceClient('http://localhost:8199',
        null, null);
    
    // simple unary call
    var request = new HelloRequest();
    request.setName('World');
    
    client.sayHello(request, {}, (err, response) => {
        document.getElementById("response").innerHTML = response.getMessage();
    });
  • examples/client/js/index.html
  • 
    
    
        
        gRPC-Web Demode
        
    
    
    

    error get message

  • 전단 구축 프로젝트
  • yarn install
    npx webpack src/main.js
  • 실행 하기 전에 프 록 시 서버 설정
  • 2. 프 록 시 설정
  • 대리 서비스 선택 envoy 또는 nginx, envoy 설정 공식 예제 에는 , 가 있 고 본 고 는 nginx 설정 (로 컬 구축) 을 사용 합 니 다.
  • grpc.proxy.conf
  • server {
      listen 8199;
      server_name _;
    
      access_log /tmp/grpc.log;
      error_log /tmp/grpc.log debug;
    
      location ~ \.(html|js)$ {
        root /var/www/html;
      }
      location / {
        #   !!   Content-Type    application/grpc
        # grpc-web    application/grpc-web+proto || application/grpc-web+text (     js   grpc-web_out  mode  ,   grpcweb   application/grpc-web+proto)
        grpc_set_header Content-Type application/grpc;
        grpc_pass localhost:9999;
        #          ,     nginx    
        if ($request_method = 'OPTIONS') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
          add_header 'Access-Control-Max-Age' 1728000;
          add_header 'Content-Type' 'text/plain charset=UTF-8';
          add_header 'Content-Length' 0;
          return 204;
        }
    
        if ($request_method = 'POST') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
          add_header 'Access-Control-Expose-Headers' 'Content-Transfer-Encoding, grpc-message,grpc-status';
        }
      }
    }

    3. 전단 항목 실행
  • 브 라 우 저 에서 index. html 파일 을 열 면 됩 니 다. 다음 내용 을 보면 정상적으로 작 동 합 니 다
  • 닥 친 문제
    1. [grpc 응답 헤드 중 grpc - message] grpc: received message larger than max (1094795585 vs. 4194304) (nginx 로그 나 curl - vvv 모드 를 통 해 볼 수 있 습 니 다)
    mode = grpcwebtext 를 사용 할 때 표시 되 는 메시지 크기 문제 (단, 크게 평가 하 더 라 도 안 됩 니 다. 이 값 은 1094795585 입 니 다. 약 1094 M 입 니 다. 분명히 grpc 에서 받 은 값 이 잘못 되 었 습 니 다. nginx 쪽 에서 어떤 설정 이나 확장 을 해 야 할 지 추측 하고 grpc - web - text 형식 데 이 터 를 변환 합 니 다) 방안: mode = grpcweb 을 사용 합 니 다.
    2. [nginx] upstream rejected request with error 2 while reading response header from upstream
    구 글 은 원인 이 무엇 인지 아무 도 말 하지 않 았 지만, 아래 의 요청 머리 를 늘 려 문 제 를 해결 했다.
    프로젝트: grpc set header Content - Type application / grpc;
    3. grpc - web 현재 서버 error 에 있 을 때 두 번 의 리 셋 함수 가 실 행 됩 니 다. issue 현재 master 에 통합 되 었 습 니 다. 발표 날 짜 는 알 수 없습니다.

    좋은 웹페이지 즐겨찾기