1

我正在尝试在 iOS 上的 React Native 中构建一个 gRPC 客户端。

对于上下文:不直接支持 gRPC 的 React Native 必须从 Swift 调用自定义的 Native Module,它会调用 gRPC 并返回值。

gRPC 服务器是一个本地编译的 goLang 模块,它使用该http2server模块。我没有编写 gRPC 服务器,所以我无法更改它的代码。

看来 React Native 方法正在循环执行本机 gRPC 客户端调用,导致 Golang 的 http2Server 模块崩溃。

这个 gRPC 客户端调用是从按钮onPress()事件调用的,而不是从循环调用的。我尝试将 gRPC 调用包装在超时测试中,以防止调用过快。

我的 Native 模块有一个如下所示的导出函数:

@objc(SwiftNativeGrpcClient) class SwiftNativeGrpcClient: NSObject {
  // ...
  @objc func swiftGetGrpcTest(
    _ resolve: RCTPromiseResolveBlock,
    rejecter reject: RCTPromiseRejectBlock
  ) {
    print("SwiftNativeGrpcClient.swiftGetGrpcTest()")
    // connect to gRPC channel if necessary
    if (self.secureGrpcChannel == nil) {
      self.createSecureChannel()
    }
    // out of paranoia, don't let this call be used less than
    // once per second
    if (getMilliSecondsSinceLastCall() < 1000) {
      print("Method called to soon.")
      reject("0", "Method called too soon", nil)
      return
    }
    let grpcServiceClient = Service_ServiceName(channel: self.secureGrpcChannel!)
    let testRequest: Service_TestRequest = Service_TestRequest()
    // Service_TestResponse should contain a String:
    // "gRPC response success"
    let testResponse: Service_TestResponse
    let testCall = grpcServiceClient.getTest(testRequest)
    do {
        try testResponse = testCall.response.wait()
        print(testResponse)
    } catch {        ​
      ​print("RPC method 'getInfo' failed \(error)")
      ​return
    ​}
    // update the last call time to ensure this isn't being called
    // more than once per second
    self.lastCallTime = DispatchTime.now()
    resolve(getInfoResponse)
  }
  // ...
}

​</p>

我的 React Native 像这样调用原生模块:

const { SwiftNativeGrpcClient } = NativeModules;

export default function App() {
  const nativeGrpcClient = useRef(SwiftNativeGrpcClient)
  const [lastCallTime, setLastCallTime] = useState(new Date())

  const rnGetGrpcTest = async () => {
    try {
      const currentTime = new Date()
      console.log(`lastCallTime: ${lastCallTime}`)
      console.log(`currentTime: ${currentTime}`)
      const timeDiff = currentTime - lastCallTime
      console.log(`timeDiff: ${timeDiff} ms`)
      // Just checking... don't let this method
      // be executed more than once per second
      if (timeDiff > 1000) {
        await nativeGrpcClient.current.swiftGetGrpcTest()
      }
    } catch (error) {
      console.error(error.message)
    }
    setLastCallTime(currentTime)
  }
  // ...
}

Xcode 输出如下所示 ​</p>

  1. 看起来 gRPC 客户端正在对 gRPC 服务器进行多次调用。你会看到 React Native 模块发射器在崩溃之前发出了大约 20 次相同的响应
2021-12-01 15:23:56.400068+0200 testApp[13091:123303] [javascript] { output: 'SwiftNativeGrpcClient.swiftGetGrpcTest()\n' }

2021-12-01 15:23:58.698908+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.699576+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.700075+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.700606+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.701067+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.701596+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.702036+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.702726+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.704172+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.704766+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.705121+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.705497+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.705833+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.715472+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
panic: 2021-12-01 15:23:58.715856+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.716342+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.716751+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.717020+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.717247+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.717510+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.718216+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
close of closed channel

goroutine 24507 [2021-12-01 15:23:58.718544+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
running]:
2021-12-01 15:23:58.718827+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.719167+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
  1. Golang 的 http2Server 在handlePing()通过原生 Swift 模块向 React Native 返回响应后,在方法过程中崩溃。似乎 gRPC 连接已关闭,然后再次尝试关闭,http2server 无法正常处理

这是 Xcode 控制台日志:

2021-12-01 15:23:58.717247+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.717510+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
2021-12-01 15:23:58.718216+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
close of closed channel
goroutine 24507 [2021-12-01 15:23:58.718544+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
running]:
goroutine 24507 [2021-12-01 15:23:58.718544+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
running]:
2021-12-01 15:23:58.718827+0200 testApp[13091:123303] [javascript] Got output from Naive Module Emitter:
2021-12-01 15:23:58.719167+0200 testApp[13091:123303] [javascript] { output: '1638365038 [INF] test.go:3294 gRPC response success\n' }
google.golang.org/grpc/internal/transport.(*http2Server).handlePing(0xc00007a1c0, 0xc003c08090)
    google.golang.org/grpc@v1.34.0-dev.0.20201021230544-4e8458e5c638/internal/transport/http2_server.go:680 +0x6d
google.golang.org/grpc/internal/transport.(*http2Server).HandleStreams(0xc00015d800, 0xc0029d0f68, 0x10a742005)
    google.golang.org/grpc@v1.34.0-dev.0.20201021230544-4e8458e5c638/internal/transport/http2_server.go:494 +0x31f
google.golang.org/grpc.(*Server).serveStreams(0xc000499860, {0x10b916390, 0xc00015d800})
    google.golang.org/grpc@v1.34.0-dev.0.20201021230544-4e8458e5c638/server.go:742 +0x114
google.golang.org/grpc.(*Server).handleRawConn.func1()
    google.golang.org/grpc@v1.34.0-dev.0.20201021230544-4e8458e5c638/server.go:703 +0x34
created by google.golang.org/grpc.(*Server).handleRawConn
    google.golang.org/grpc@v1.34.0-dev.0.20201021230544-4e8458e5c638/server.go:702 +0x405
CoreSimulator 757.5 - Device: iPhone SE (2nd generation) (ECBD797A-E2B4-49F2-9DD5-BC8FB95EFACC) - Runtime: iOS 14.5 (18E182) - DeviceType: iPhone SE (2nd generation)

当我使用完全相同的 Swift 代码创建一个测试项目,但没有 React Native 前端时,我没有遇到这种崩溃。React Native 以某种方式参与了崩溃行为,可能是由于 Native Module 功能的功能?

有谁知道如何防止这种循环发生?

4

1 回答 1

0

万一其他人有类似的问题,

我的问题与循环的出现无关。

我需要在 Swift 函数结束时关闭 gRPC 通道。这可以使用以下defer语句来完成:

@objc(SwiftNativeGrpcClient) class SwiftNativeGrpcClient: NSObject {
  // ...
  @objc func swiftGetGrpcTest(
    _ resolve: RCTPromiseResolveBlock,
    rejecter reject: RCTPromiseRejectBlock
  ) {
    if (self.secureGrpcChannel == nil) {
      self.createSecureChannel()
    }
    let grpcServiceClient = Service_ServiceName(channel: self.secureGrpcChannel!)
    defer {
        // close the channel when the method exits
        try? grpcServiceClient.channel.close().wait()
    }
    let testRequest: Service_TestRequest = Service_TestRequest()
    // Service_TestResponse should contain a String:
    // "gRPC response success"
    let testResponse: Service_TestResponse
    let testCall = grpcServiceClient.getTest(testRequest)
    do {
        try testResponse = testCall.response.wait()
        print(testResponse)
        resolve(getInfoResponse)
    } catch {        ​
      ​print("RPC method failed \(error)")
      reject("0","RPC method failed: \(error)", nil)
      ​return
    ​}
    // update the last call time to ensure this isn't being called
    // more than once per second
  }
  // ...
}

循环控制台输出是由两件事的交互引起的:

  1. 我正在捕获标准输出并通过RCTEventEmitter
  2. 在事件之间没有清除缓冲区。

所以看起来好像 RCTEventEmitter 多次收到 gRPC 成功消息。

于 2021-12-03T19:49:35.663 回答