thrift clientレポートEOFのエラー
2450 ワード
最近、同僚がオンラインに変更したことがあります.しばらくはサービスBと呼ばれ、その上流はサービスAです.サービスBはthrift serverで、オンラインになった後、サービスAは C/Sエンドプロトコルが一致しません.CRコードは、Sエンドプロトコルに新しいrequiredフィールドが追加されていることを発見しました.optionalに変更して再オンラインにしたが、EOFエラーがあった.プロトコルが間違っている場合は、各リクエストが失敗するはずですが、一部の失敗にすぎず、他の問題があるはずです.(注:プロトコルが一致しないとEOFが現れるという記事がありますが、goでテストしました.S端reqにrequiredフィールドを追加すると、s端は S端子が接続を閉じると、C端子がreadを続け、EOFが読み出されます.サービスBはどのような場合に自発的に接続をオフにしますか?タイムアウトをチェックして大丈夫です.プロセスの開始時間を確認したが、再起動は中止されなかった.thriftソースコードと組み合わせて、Bがリクエストを処理するときにpanicしたが、server全体がpanicしていないため、一部の失敗が発生する可能性があります.Bのhandlerをチェックすると、最初からdefer funcがあり、主に要求と応答を印刷し、このコモンスタックの下層のpanicを受け取るためです.しかし、書き方に問題があります.大体 です.
ログを見ると、panicの記録は見つかりませんでした.ここではポインタがオブジェクトを取る操作が見られますが、この操作にはリスクがあります.ポインタが空の場合panicになります.requestは空でないことを保証できますが、responseは後の操作で生成され、空であるかどうかは保証できません.だから疑問点はここに落ちた.ログを追加してオンラインにすると、確かに空の場合があります.実はgoの中のMarshalパラメータはポインタでいいので、オブジェクトを取る必要はありません.オブジェクトを取る操作を外してからオンラインにして、問題が修復されました.
ここのpanicは、どのようにして接続が閉じてもserverが生存しているのでしょうか.これらのコードを見てみましょう.
ServerのAcceptLoopでは、新規接続のための個別のコプロセッサが実行されます
processRequests関数では、handler関数が最終的に呼び出されます.後者はpanicで、ここのProcess()まで上に投げます.ここでpanicに遭遇するとreturnされ、その前にdeferスタック関数を順次終了して実行します.ここでは実際にtransportつまりsocketを閉じていることがわかります.スタックの上部にrecoverがpanicを接続しているため、serverは削除されず、現在の接続にのみ影響します.サービスAはEOFを受け取ります.
またgoでのtcpプログラミングも検証されており,serverが要求を受信すると直接
EOF
エラーを報告した.これにより、以下の点が疑われます.error processing request: Not enought frame size
、s端の新しいフィールドはランダム値で、c端はすべて正常です.C端reqにrequiredフィールドを追加すると、S端は要求を受け取れず、C端はread io timeoutを提示します)defer func() {
breq, _ := json.Marshal(*request)
bresp, _ := json.Marshal(*response)
logger.Info("inout||req=%+v||resp=%+v||trace_id=%v", string(req), string(resp), traceId)
if err := recover(); err != nil {
// log
}
}()
ログを見ると、panicの記録は見つかりませんでした.ここではポインタがオブジェクトを取る操作が見られますが、この操作にはリスクがあります.ポインタが空の場合panicになります.requestは空でないことを保証できますが、responseは後の操作で生成され、空であるかどうかは保証できません.だから疑問点はここに落ちた.ログを追加してオンラインにすると、確かに空の場合があります.実はgoの中のMarshalパラメータはポインタでいいので、オブジェクトを取る必要はありません.オブジェクトを取る操作を外してからオンラインにして、問題が修復されました.
ここのpanicは、どのようにして接続が閉じてもserverが生存しているのでしょうか.これらのコードを見てみましょう.
ServerのAcceptLoopでは、新規接続のための個別のコプロセッサが実行されます
go func() {
if err := p.processRequests(client); err != nil {
log.Println("error processing request:", err)
}
}()
processRequests関数では、handler関数が最終的に呼び出されます.後者はpanicで、ここのProcess()まで上に投げます.ここでpanicに遭遇するとreturnされ、その前にdeferスタック関数を順次終了して実行します.ここでは実際にtransportつまりsocketを閉じていることがわかります.スタックの上部にrecoverがpanicを接続しているため、serverは削除されず、現在の接続にのみ影響します.サービスAはEOFを受け取ります.
defer func() {
if e := recover(); e != nil {
log.Printf("panic in processor: %s: %s", e, debug.Stack())
}
}()
if inputTransport != nil {
defer inputTransport.Close()
}
if outputTransport != nil {
defer outputTransport.Close()
}
for {
ok, err := processor.Process(inputProtocol, outputProtocol)
//...
}
またgoでのtcpプログラミングも検証されており,serverが要求を受信すると直接
conn.Close()
,clientはEOF
と読み,ここでは後述しない.