こんにちは。はると申します。
WordやExcelとの格闘に四苦八苦。
あれ?私コード書く人間じゃなかったっけ?とか時々遠い目をすることが多くなってきたぺーぺーエンジニアです。
そんな中最近起きたトラブルとその対処について、つらつらと書いていきたいと思います。
状況説明
私どもが開発をしているアプリの一つは、Curlという言語でフロントが組み立てられています。
Curlとは、リッチクライアントアプリ(RIA)を構築できる言語の一つ。
httpを通じてブラウザ内や、独立したアプリケーションと同じような形で起動できるアプリを作ることができる言語です。
作り込めばかなり細かい挙動を指定したアプリを作ることは出来るのですが、いかんせん主流になれなかった言語の悲しさ。Curl使いなエンジニアは数少ないと思われます。
問題発生「画面が突然固まるんだけど!!」
そんな中、とある問題が発生しました。
アプリの中で、画面から外部のAPIに接続して情報を取得する処理があるのですが、稀にデータを取得中に動作が固まるという報告を受けたのです。
どうやら、外部のサーバーが接続は受け入れるものの、そのままサーバーがBusyになり、データリターンがない状態のままになってしまうようです。
この部分の通信処理は同期処理で書かれていたこともあり、相手先からの返答を待って次の処理を実施します。
それが、外部サーバーが何も戻さないものだから、反応できなくなってしまったんでしょうね。困ったものです。
原因調査「あれ?Curlの通信って。。。」
って言うわけで、対処に当たったわけですが。。
「返答がなかったら接続切っちゃえばいいだろ(Java脳)」とか思っていたわけです。
とりあえず、通信実行APIをヘルプで確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public {HttpFile.http-read-open character-encoding:CharEncoding = CharEncoding.none-specified, buffer-size:int = {calculate-instances-per-memory-size default-buffer-memory-size, char }, unread-size:int = {min 4, buffer-size div 4}, request-data:#HttpRequestData = null, request-headers:#HttpRequestHeaders = null, request-method:HttpRequestMethod = HttpRequestMethod.get, auto-redirect?:bool = true, always-return-response-headers?:bool = false }:HttpTextInputStream クラス: HttpFile |
。。。。あれ?タイムアウトなんてないじゃん??この時点で軽く狼狽です。
その後いろいろ調べてみたのですが、通信系のAPIでTimeoutの設定ができるのは、TCP/IPレベルの通信ができるAPIのみでした。流石に独自でHTTPをしゃべるクラスを作る気にはなれません。
対策
と言う訳で、苦肉の策としてある方法により対処を行いました。
ここの部分の通信処理だけ非同期通信にして、外部タイマーにより自動キャンセルを行うように変更し、とりあえず擬似的なTimeoutをするようにしたのです。
Curlのヘルプページに非同期通信のサンプルがありますので、それを変更してみましょう。
リンクはこちらから → 非同期ストリーム入出力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
{let buffer:TextFlowBox = {TextFlowBox width = 5in, border-width = 1pt, border-color = "black", margin = 2pt } } {let rdr:#AsyncStreamReader} {let tm:#Timer = null} {let cancel-button:CommandButton = {CommandButton label = "Cancel reading", {on Action do {if-non-null rdr then {rdr.cancel} } } } } {set cancel-button.enabled? = false} {VBox {CommandButton label = "Select File to Read", {on Action do {buffer.clear} let file-url:#Url = {choose-location} {if-non-null file-url then set tm = {Timer delay = 10s, repeat = 1, interval = 0.1s, {on TimerEvent do {rdr.cancel} } } set rdr = {async-read-from file-url, || partial? = true means that this will be called || for each chunk of data that is downloaded. partial? = true, character-encoding = "utf8", {on asre:AsyncStreamReadEvent do {if-non-null e = asre.exception then {buffer.add "<!!!Open or read failed: " & e & "!!!>" } else let buf:#StringBuf = (asre.data asa #StringBuf) {if-non-null buf then {buffer.add buf} } {if asre.canceled? then {buffer.add "<Open or read was canceled.>"} } } {if asre.exception != null or asre.canceled? or asre.done? then set cancel-button.enabled? = false } } } set cancel-button.enabled? = true } } }, cancel-button, buffer } |
Timerクラスの中の delay というプロパティに対して、10s(10秒)の設定を行いました。
ボタン押下した際のイベントの中に、tmというタイマーインスタンスを生成。
これに時間管理をさせて、時間が来たら自動でキャンセルをしてもらうようにしました。
非常に簡易的な対処ではありましたが、これの方法でどうにか現象を回避することができました。
ただ、元々が同期処理で書かれていたものを非同期にしたため、接続からデータ構造体へのパースを行う処理を別立てにしてイベントの中に放り込むなどの調整が必要でした。
最近Node.jsを触ったときにも思ったのですが、非同期処理はとにかくインデントが横に伸びがちです。上手いことメソッドを分けて、見づらいコードにならないように気をつけましたね
(まぁこれは他の言語にも共通して言えることですけど。。)
では今回はこれにて!
- 【React】フロントエンドのテストコードを書いてみよう【Vitest】 - 2024-04-30
- Simple AWS DeepRacer Reward Function Using Waypoints - 2023-12-19
- Restrict S3 Bucket Access from Specified Resource - 2023-12-16
- Expand Amazon EBS Volume on EC2 Instance without Downtime - 2023-09-28
- Monitor OpenSearch Status On EC2 with CloudWatch Alarm - 2023-07-02