数据流:使用BigQueryIO编写时出现SocketTimeoutException

时间:2016-08-30 13:16:14

标签: google-bigquery google-cloud-dataflow

我使用Dataflow使用BigQueryIO.Write.to()将数据写入BigQuery。

有时,我从Dataflow收到此警告:

{
 metadata: {
  severity: "WARNING"    
  projectId: "[...]"    
  serviceName: "dataflow.googleapis.com"    
  region: "us-east1-d"    
  labels: {
   compute.googleapis.com/resource_type: "instance"     
   compute.googleapis.com/resource_name: "dataflow-[...]-08240401-e41e-harness-7dkd"     
   dataflow.googleapis.com/region: "us-east1-d"     
   dataflow.googleapis.com/job_name: "[...]"     
   compute.googleapis.com/resource_id: "[...]"     
   dataflow.googleapis.com/step_id: ""     
   dataflow.googleapis.com/job_id: "[...]"     
  }
  timestamp: "2016-08-30T11:32:00.591Z"    
  projectNumber: "[...]"    
 }
 insertId: "[...]"   
 log: "dataflow.googleapis.com/worker"   
 structPayload: {
  message: "exception thrown while executing request"    
  work: "[...]"    
  thread: "117"    
  worker: "dataflow-[...]-08240401-e41e-harness-7dkd"    
  exception: "java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
    at sun.security.ssl.InputRecord.read(InputRecord.java:503)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:961)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:918)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
    at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1535)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at com.google.api.client.http.javanet.NetHttpResponse.<init>(NetHttpResponse.java:37)
    at com.google.api.client.http.javanet.NetHttpRequest.execute(NetHttpRequest.java:94)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:981)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
    at com.google.cloud.dataflow.sdk.util.BigQueryTableInserter$1.call(BigQueryTableInserter.java:229)
    at com.google.cloud.dataflow.sdk.util.BigQueryTableInserter$1.call(BigQueryTableInserter.java:222)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)"    
  logger: "com.google.api.client.http.HttpTransport"    
  stage: "F5"    
  job: "[...]"    
 }
}

我没有看到任何&#34;重试&#34;记录下这个。

我的问题是:

  • 我丢失了数据吗?我不知道写操作是否正确完成。如果我正确理解了代码,整个写批处理都处于不确定状态。
  • 如果是这样,我是否有办法确保将数据写入BigQuery一次?
  • 如果是,那么严重性不应该是ERROR而不是WARNING吗?

以下是我使用的一些背景信息:

  • 我在流模式下使用Dataflow,使用KafkaIO.java从Kafka读取
  • &#34;有时&#34;可以是每小时0到3次
  • 根据工作情况,我使用2到36名n1-standard-4型工人
  • 根据工作情况,我将3k到10k的消息写入BigQuery
  • 平均邮件大小为3kB
  • Dataflow worker位于us-east1-d区域,BigQuery数据集位置为US

1 个答案:

答案 0 :(得分:1)

您将看到与来自BigQuery流服务的瞬态问题相关的这些错误。我的经验是,你可能会看到这些在工作生涯中分散。如果您看到这些日志的大量突破,通常意味着BigQuery流服务正在出现故障。

Cloud Dataflow将重试请求的行(请参阅此处的代码BigQuery... line 290)。如果您在警告后的某个时刻没有在表格中看到这些日志项目或您的记录 - 还有其他错误。

在流媒体模式下,该服务将无限次重试。这意味着该作业不会因此问题而失败。因为我们永远尝试 - 它确实提出了这个是错误还是警告的问题。我们将在内部对此进行辩论,您也可以在Apache Beam user group发布一条说明以推动辩论:-)

您可以在Cloud Logging中为该警告消息创建一个指标,并对其执行操作。我们正在进行更深入的Stackdriver集成,这是一个很好的用例。

您没有丢失数据,而是您的数据到达BigQuery将被延迟。我已经构建了一些简单的固定窗口并计算1分钟的窗口 - 使用事件处理时间。然后我把计数随着时间的推移看作是新鲜度的指标。如果我的固定窗口滞后于水印,则插入物有问题。

  • 根据评论
  • 编辑进一步澄清

如果是IOException(此异常继承自该异常),则路径会调用ApiErrorExtractor()来测试是否由于速率限制问题。

在这种情况下,SocketTimeout不是由于速率限制,因此向调用者抛出异常。调用者是finishBundle中的BigQuery.IO行2308。它调用flushRows()来捕获IOException并抛出RuntimeException。

在蒸汽模式下,任何以这种方式失败的捆绑都会无限期地重试。注意:在批处理模式下,跑步者将尝试4次然后失败。

在这种情况下(非速率限制情况),您不会重试行日志。

您的数据不会丢失,而是会在重试捆绑包时延迟。

最糟糕的情况是所有员工都遇到了这个问题,因此管道无法取得进展。如果BigQuery流服务已关闭或丢弃所有连接,则可能会发生这种情况。现在 - 一旦BiqQuery摄取服务稳定并且捆绑包通过,您可能会看到速率限制案件启动,但后退代码将有助于抑制这些错误。

最糟糕的情况是,您的传入管道数据速率一直徘徊在接近最大写入速率(速率限制率)的范围内,受BigQuery流接收服务的控制。因此,如果您遇到重试(暂时或其他)的积压 - 您的管道可能永远不会赶上。

流数据流中有一个Drain功能,它将停止处理传入的数据,然后推进管道以优雅地排出所有未完成的窗口。但是,Drain要求finishBundle()成功。因此,在这种情况下(SocketTimeout)Drain将被卡住。如果您终止了管道与排水 - 您将遇到未完成的捆绑包的数据丢失。

如果您愿意,可以覆盖BigQuery.IO逻辑并管理其他地方错误的数据。你可以做到这一点,但我依靠BigQuery流媒体服务永远不会有终端中断。有了这个说法,如果你一直以接近速率限制率运行并且对不可恢复的积压处理敏感,你可能想要实现不同的减少或分片机制以避免速率限制问题。

关于积压恢复的另一个建议是,您可以阻止事件流进入您的流媒体源。例如,停止在Pub / Sub中写入主题。您将开始使用订阅写入另一个主题。您现有的Dataflow管道将耗尽现有主题。您仍然需要处理如何处理新订阅中的新积压,但至少您保证不会丢失现有管道中的任何数据。

如果您没有使用事件时间处理,这种方法可能非常有效;但是,您正在使用事件时间处理,您的窗口将具有重叠的输出,这些输出都标记为ONTIME,即使情况并非如此。

我在这里就你的用例做了很多假设,但我想分享,因为你的问题在考虑数据丢失时提出了其他架构概念。

希望这有帮助。