Swift에서 async/await를 사용하여 gRPC 서버 구축

개시하다


MacOS에서 Swift를 사용하여 gRPC 서버를 실행하고 Swift5를 사용합니다.5에서 가져온 async/await에 기술합니다.
ASync/await를 사용하려면 iOS 15와 MacOS Monteerey가 필요하지만, iOS 시뮬레이터를 사용하면 Big Sur도 ASync/await의 Swift 코드를 이동할 수 있다.
이번에 맥OS에서 이동하기 때문에 맥OS를 몬테리로 끌어올려야 한다.어쨌든 향상시키고 싶지 않은 사람은 Dcoker에서 리눅스 이미지를 사용해서 행동하거나 뒤의 디버깅을 기다려야 한다.
gRPC의 설치 사용grpc-swift.
async/await의 지원이 현재 Experimental이기 때문에 주의해야 합니다.앞으로 인터페이스 등은 파괴적인 변경이 있을 수 있다.

컨디션

  • macOS Monterey 12.0.1
  • Xcode 13.1
  • Swift 5.5.1
  • protoc와grpc에 사용할 protoc 플러그인 준비


    프로토 파일에서 코드를 생성하기 위해서는 명령protoc이 필요합니다.
    또한 스위프트용 Protocol Buffers 모델의 코드와 gRPC의 서버 클라이언트 코드를 생성하기 위해서는 각각의 플러그인이 필요하다.
    이번protocbrew로 설치했어요.
    $ brew install protobuf
    
    Swift용 플러그인의 두 가지는 자체적으로 구축하고 준비한 것입니다.
    본고에서 처음에 말한 바와 같이grpc-swift의 async/await 지원은 현재도 Experimental이며main 지점은 지원되지 않습니다.
    따라서 전용 지점으로 전환한 후 구축합니다.
    $ git clone https://github.com/grpc/grpc-swift.git 
    $ cd grpc-swift
    $ git switch 1.4.1-async-await
    $ make plugins
    
    이로써 창고 경로에서 생성protoc-gen-swiftprotoc-gen-grpc-swift.
    (실제로 SwiftPM에서 작성된 파일만 복사.build/release합니다.)
    이 두 실행 가능한 파일의 경로를 통해

    SwiftPM 프로젝트 제작


    패키지 초기화


    $ mkdir GrpcEcho
    $ cd GrpcEcho
    $ swift package init --type executable
    

    Package.swift의 기술


    이전과 마찬가지로 async/await 지원은 Experimental이기 때문에 전용 지점을 지정합니다.
    또한 async/await를 사용하기 위해서plaforms에 지정해야 합니다.macOS(.v12)Package.swift
    // swift-tools-version:5.5
    
    import PackageDescription
    
    let package = Package(
        name: "GrpcEcho",
        platforms: [.macOS(.v12)],
        dependencies: [
            .package(url: "https://github.com/grpc/grpc-swift.git", .branch("1.4.1-async-await")),
        ],
        targets: [
            .executableTarget(
                name: "GrpcEcho",
                dependencies: [.product(name: "GRPC", package: "grpc-swift")]
            ),
        ]
    )
    

    .proto 파일 작성


    연습 서버에 대한 .proto 파일을 설명합니다.
    이번에는 포장된 루트 목록에 적당히 놓을 것이다.
    echo.proto
    syntax = "proto3";
    
    package echo;
    
    service Echo {
        rpc Hello(HelloRequest) returns (HelloResponse) {}
    }
    
    message HelloRequest {
        string name = 1;
    }
    
    message HelloResponse {
        string message = 1;
    }
    

    .proto 파일 구성

    protoc를 사용하여 코드를 생성합니다.
    async/await를 지원하는 서버 코드를 생성하려면 ExperimentalAsyncServer 옵션을 사용해야 합니다.
    EventLoop의 일반 서버용 코드를 직접 사용할 필요가 없기 때문에 그쪽은 무효입니다.고객 코드가 이번에도 실현되지 않아서 무효입니다.각자의 목적에 따라 적절하게 설정하다.
    옵션plugin.md에 기재되어 있습니다.
    $ mkdir -p Sources/GrpcEcho/Gen
    $ protoc echo.proto --swift_out=Sources/GrpcEcho/Gen --grpc-swift_out=Sources/GrpcEcho/Gen --grpc-swift_opt=Server=false,Client=false,ExperimentalAsyncServer=true
    
    를 생성물echo.grpc.swiftecho.pb.swift로 출력Sources/GrpcEcho/Gen.생성물은 일반적인 Swift 코드입니다.

    서버 코드 설명


    각 Service는 Provider 클래스를 정의하고 서버에 클래스를 등록합니다.
    이번에 Echo 서비스와 Hello 방법을 정의했고 이 인터페이스를 바탕으로 자동으로 생성된 프로토콜Echo_EchoAsyncProvider에 대해 구체적인 실시 방안을 정의했다.
    main.swift
    import GRPC
    import NIO
    
    class EchoProvider: Echo_EchoAsyncProvider {
        func hello(request: Echo_HelloRequest, context: GRPCAsyncServerCallContext) async throws -> Echo_HelloResponse {
            return Echo_HelloResponse.with {
                $0.message = "Hello, \(request.name)"
            }
        }
    }
    
    let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
    defer {
        try! group.syncShutdownGracefully()
    }
    
    let server = try Server.insecure(group: group)
        .withServiceProviders([
            EchoProvider(),
        ])
        .bind(host: "localhost", port: 8080)
        .wait()
    
    print("started server: \(server.channel.localAddress!)")
    
    try server.onClose.wait()
    
    실행되면 서버에서 요청을 시작하고 기다립니다.

    동작 확인


    방금 만든 서버에 적절한 gRPC 클라이언트 요청을 보냅니다.
    이번에grpcurl을 사용했습니다.
    $ brew install grpcurl
    
    실제로 요구를 제기하려고 시도한다.
    $ grpcurl -plaintext -proto echo.proto -d '{"name": "Iceman"}' localhost:8080 echo.Echo.Hello
    {
      "message": "Hello, Iceman"
    }
    
    동작이 순조롭다.-plaintext가 없으면 Unable to determine http version, closing 오류 로그를 뱉으면서 응답이 없음Failed to dial target host "localhost:8080": EOF으로 답장하는 것을 주의하십시오.
    기본 설정에서 서버 로그는 허무하게 버려지기 때문에 적절한 설정이 필요합니다.

    끝말


    Swift를 사용하여 gRPC 서버를 이동할 수 있습니다.또한 Swift의 최신 기능인 async/await도 사용할 수 있다(이번 샘플 코드는 특별한 장점이 없다).
    주의점은 스위프트에 대응하는 structured conceurncy라고 하지만 실제 상황은 스위프트 NIO를 기반으로 한 이벤트롭으로 움직인다는 점이다.
    현재 EventLoop은 actor의 custom executor를 지원하지 않으며 이에 대해 특별한 협력이 없습니다.예를 들어, await의 위치에 따라 EventLoop의 바깥쪽도 처리됩니다.Async 컨텍스트에서 EventLoop에서 보호되는 리소스를 액세스할 때는 주의해야 합니다.
    또 await 등 퓨처가 완성되는 타이밍에 본질적으로 필요 없는 스레드 스위치가 발생할 수 있다.기본적으로 문제가 없지만 연기에 불리하다.퍼포먼스가 엄격한 API에서는 사용하지 않는 것이 현명하다고 생각합니다.
    이번 작업 코드는 여기 있습니다https://github.com/sidepelican/GrpcEcho

    좋은 웹페이지 즐겨찾기