6/19(日)「JJUG CCC Spring 2022」がオンラインにて開催され、GMOインターネットグループはセッションスポンサーとして協賛・登壇しました!イベントでは2セッション登壇したうち、今回はGMOペイメントゲートウェイ株式会社が登壇したスポンサーセッション「クレジットカード決済システムをJavaで構築して10年間運用した話」について、登壇レポートをお届けいたします。イベント告知:https://developers.gmo.jp/19185/本記事はこちらのスポンサーセッションの書き起こしとなりますので、ぜひご覧ください。
登壇者
GMOペイメントゲートウェイ株式会社システム本部 決済サービス統括部 エグゼクティブ・リーダー米原 孝太
はじめに
クレジットカード決済システムをJavaで構築して10年間運用した話をしたいと思います。GMOペイメントゲートウェイの米原と申します。よろしくお願いいたします。
我々GMOペイメントゲートウェイでは、ECサイトなどのお客様に対して様々な決済手段のサービスを提供しております。本日はその様々な決済手段サービスの中でも一番主力となる、クレジットカード決済のシステムについてお話しします。
まず、クレジットカード決済システムについて概要と、システムを設計した時に考慮した点、それから今回は移行のシステムとなるので、その際のパフォーマンステストについてお話しします。そして、そのシステムを10年間運用してきてその中でどのような対応を行ってきたのかというところを最後に説明します。
1.クレジットカード決済システムについて
クレジットカード決済システムは、クレジットカードの会社へ決済の要求電文を送信するシステムとなります。送信先は、CAFISやCARDNETなどの決済用のネットワーク。これらに接続して要求電文を送ります。CAFISはNTTデータさん、CARDNETはJCBさんが提供しているサービスです。
このCAFISやCARDNETの決済ネットワークは、インターネットが始まるよりも前のサービスなので、RFCなどの規格とは関係のない、独自の通信プロトコルを使った独自仕様です。よって、専用のパッケージソフトを利用して接続するのが一般的です。
決済処理なので、トランザクションの一貫性(整合性)を確保することが重要です。また、ECサイトに提供するシステムなので、当然ながら24時間365日無停止のサービスを提供することになります。
今回の対象のシステムは、もともとベンダーへ委託して構築運用していたシステムがあり、それを新しくシステム更改という形で(内製で)新しく作り直したシステムとなります。
サービスの全体概要としては、まず購入者からECサイトを通じてGMOペイメントゲートウェイへのサービスの方に接続があります。そのあと、バックエンドサービスとしてCAFIS、CARDNETなどを通じてカード会社(三井住友カードさん、楽天カードさんなど)に決済要求の電文を上げる仕組みとなっています。
GMOペイメントゲートウェイの中のサービスとしては各種フロントサービスがあり、その後ろのサービスとしてクレジットカード決済システムが存在します。フロントサービス自体は目的によって無数のサービスが存在していて、お客様に対して管理画面を提供したいというようなサービスも提供しています。それらのフロントサービスの中でもクレジットカード決済の処理に関しては、今回発表するクレジットカード決済システムですべて処理する仕組みになっています。
2.システム構築時に考慮した点
このクレジットカード決済システムを構築する時にどういった点を考慮したかということについてお話しします。まず、セキュリティ監査。クレジットカードの世界ではPCI-DSSという監査仕様があるので、これに準拠するところ、また、24時間サービスなので耐障害性のための分散構成を採ったというところ、さらにトランザクションの一貫性を保つところもあり、厳密なトランザクションの処理制御を行うようにしています。では、障害調査の容易性、サービス移行後のパフォーマンスを確保するための仕組みというところを説明します。
■セキュリティ監査(PCI-DSS)対応
まずセキュリティ監査(PCI-DSS)への対応ですが、PCI-DSSとはクレジットカードの国際ブランドが策定したセキュリティの仕様となります。2008年頃に実際に適用され始めたのですが、ちょうどこのシステムを作る時(2011年)、実際に日本国内でも徐々に対応するという形が広まってきたので、我々が社内で最初にPCI-DSSに準拠しようということで対応することになりました。
要点としては、まずソフトウェア脆弱性の即時対応があります。具体的には、セキュリティーホール等が見つかった時に、それらのパッチが発表されるとそれをすぐに適用するというのが、セキュリティの対応要件としてあります。これに対応するための方法は、CI/CDといったような即時にリリースするための仕組みもまだ用意していませんでした。そのため、採った方法としては変わっていますが、フレームワークとかOSSといったライブラリを極力使用しないことで、対応するというよりもそもそも対応の頻度を下げるという形で対応することにしました。
次にセキュリティ要件として、クレジットカード番号や3Dセキュアの認証情報といったセンシティブデータについては、非保持または暗号化が必要となっています。したがって、センシティブデータをDBに保存する際には暗号化を施しました。また、ログデータとして出力する時は伏せ字、TRUNCATEと呼びますが、アスタリスクで置き換える、意味を持たないデータにするようにしています。
次の要件として、機能単位にサーバを用意するというのが必要になります。具体的には、Webサーバ・DBサーバ・APサーバを1つの筐体の中で1つのサーバOS上に同居させるのではなく、それぞれ個別のサーバを用意してその上に稼働させるというのが要件としてあります。そのために複数サーバを実際に購入して運用するのは非常に費用もかかるので、OS仮想化機能を利用して、1筐体の中で論理的にサーバを細分化することでこれらを実現しました。
■耐障害性のための分散構成
耐障害性のための分散構成ということで、主要なオンライン機能、具体的にはAPサーバについては多重化構成をとるようにしました。万が一ハードウェア障害などによってサーバがダウンしたとしても、シングル構成にはしない、最低でも2台以上は稼働している状態を保つようにしました。例えばプロセスのフリーズなどが発生した際に、シングル構成だと、早急に復旧させるということでエンジニアによる調査がなかなかできないという問題が出てきます。多重化構成にしておくことで、問題になったサーバについて時間をかけて調査ができるという利点が発生します。また、バッチ処理用のサーバや社内の管理画面に使うサーバについては、それほどクリティカルでもないために、単純な正/副構成とすることで効率を上げています。
■厳密なトランザクション処理制御
今回、通信処理の部分はTCPソケットライブラリを利用して作成しています。複数のトランザクションを並列処理する部分についてはConcurrency Utilitiesを活用して作成しています。具体的には、処理の役割ごとにスレッドプールを用意して処理を行っています。受信処理と業務処理スレッドを分離することで、受信処理から業務処理スレッドを呼ぶことで制御を行っています。業務処理スレッドが不足している場合にはエラーを即時応答することができます。業務処理が規定時間内に完了しない場合には、規定の時間が経ったところでエラー応答するという仕組みを実装しています。また、規定時間内に完了しなかった取引については、障害取消の処理を行うようにしています。
業務処理スレッドプールについては、処理種別ごとにグループを用意しています。具体的には、業務処理スレッドとして決済要求を処理するグループ、それから取消処理をするグループ、照会などの参照をするためのグループというようにグループを分けることで、例えば決済処理をするグループの方でカード会社が遅延していて、スレッドがすべて埋まってしまったというような場合にでも、取消や照会の要求に対しては正常に処理をすることができるという利点が生まれます。
■受信処理スレッドから業務処理スレッドへ処理依頼
具体的なコードとしては、ThreadPoolExecutorを利用してスレッドプールを事前に生成しておきます。処理スレッドから業務処理スレッドへ処理依頼する際には、対象のスレッドプールに対して業務処理のクラスを生成した上で渡します。submitの部分になります。submitした際にfutureパターンに沿ってfutureオブジェクトで受け取っておいて、タイムアウト時間を設定した上でget処理を行います。こうすることで、業務処理スレッドプールに空きがない場合にはリジェクトのexceptionが返却されるので、クライアントに対しては現在過負荷状態であるという旨を即時にエラー応答することができます。
この過負荷状態のエラー応答については、実際に大量の処理をする場合のコツがあります。例えば加盟店様がバッチ処理的に連続で処理を呼び出すような場合、即座に応答を返すと連続して処理要求が来て即座にエラー応答を返すということが連続で発生するために、ひたすら処理要求をエラーで返すということでCPUリソースなどを食いつぶしてしまう危険性があります。なので、意図的にエラーを返す前に少しスリープを入れるなどの工夫をして、あえてお客様へ対して処理を待たせるといった検討もしています。
このfuture.getによって規定時間内に処理が返って来なかった場合はタイムアウトエラーとしてクライアントに対してエラーを返します。この場合、業務処理スレッドの方は正常に処理ができている可能性もあるため、後処理として障害取消を行うことでトランザクションの一貫性を保つというふうにしています。
■障害取消処理を非同期実行
この障害取消の処理を実行する部分の扱いは非同期実行で行うようにしています。障害取消処理の実行を依頼する部分については、DB上にキューに相当するテーブルを用意しておいて、そちらに障害取引の要求電文を書き込みます。処理を行っているスレッド自体は速やかにエラー応答をクライアントへ返すというふうにしています。また、CAFIS等の決済ネットワークの障害取消電文は、大元である決済要求電文とほぼ同じ形でデータ電文を生成することが多いため、通常の処理電文生成時点で障害取消電文も同時に生成しておくと非常に処理が楽になります。書き込まれたDB上の障害取消要求に対して別スレッドでキューから定期的にフェッチして処理を実行することになります。こちらについては別プロセスまたはバッチ処理での実行をするようにしています。
障害取消については、障害取消自体が失敗するということも考慮して設計する必要があります。失敗した場合リトライを行いますが、その際もリトライ間隔やリトライの回数、こちらは決済ネットワークの方の仕様によって時間等が決まっていることがあるので、それを加味した状態で調整するようになります。また、実際に運用しているとそれらの決済ネットワークの事業者からこういう間隔に変えて欲しいとか、間隔をもっと延ばして欲しいといったリクエストを受ける場合もあるので、それらはプロパティ等に設定するようにして、後から調整可能にしておくことが重要です。リトライを繰り返しても障害取消がどうしても完結しないというケースがどうしても出てきてしまいます。そういった場合については、完了しなかった障害取消は日次のレポート等に出力して、その後は人間による運用で対応するという形にしています。
■障害調査を容易にする
障害調査を容易にする仕組みについて説明します。まず、ログについては1トランザクションにつき1行というのを原則にしています。こちらは、実際に秒何十件何百件というトランザクションが発生するので、1件ごとに複数のログを出してしまうと滝のように流れるログになってしまうため、1トランザクションにつき1行くらいがちょうどよいと思います。ログの内容としては、調査用のキーとなる項目を端的に出力する必要があります。具体的には、トランザクションIDや処理結果(OK/NG、エラーであればエラーコード)、実際にはどの処理がどうなっているかというのを確認する意味も含めまして処理時間、できればそのトランザクションに全体で何ミリ秒かかったのか、またそのうち自身のシステムの中で経過した時間は何ミリ秒だったのか、このあたりを出力しておくと、例えば処理遅延が発生した場合に原因が自身にあるのかそれとも外部にあるのか、こちらを素早く判断することができるので非常に有効になります。
また、調査専用のDBテーブルを用意しておくと非常に楽になります。実際運用していると、月間件数の集計など統計的なものを見ることが多いので、そういった場合に大元の実際の取引のデータ、こちらはレコードサイズも大きいし実際センシティブデータを含んでいることが多いので、集計等をするためだけに要点となる項目のみ絞り込んでさらに集計しやすいようにあらかじめコードで表現するとか、そういった加工したデータを保存するようにしておくと非常に便利になります。
運用していく中でリソースの処理状況を見る必要がありますので、できるだけ詳細な統計情報を取得するようにします。実際にスレッドの利用状況とかDBコネクションの利用数など、こちらを秒単位とかの短い間隔で統計情報として出力することで、実際にはどういう状態になっているのかというのを外部から判断することができるようになります。
3.移行を意識したパフォーマンステスト
今回は既存のシステムを移行する形でサービスシステムを構築しました。したがって、稼働してすぐに大量のトランザクションが実際に発生するというのが分かっていましたので、既存システムの負荷状況を分析しています。具体的には、時間帯ごとの負荷状況や、送信元(お客様)の偏り具合、取引が少ないお客様もいればすごく大量に発生するお客様もいますので、そのあたりの偏り具合も統計をとっています。また、送信先のカード会社、こちらもカードを発行する会社によって発行枚数が違い、平均的とはなっていないので偏り具合も分析しました。あと処理種別、先ほどあったような決済要求や取消など、そういった業務の処理の偏り具合も分析しています。さらに、正常処理だけではなく実際にはエラー処理も発生しますので、そのエラー処理も混ぜるようにしています。
これらの偏り具合を実際に再現するテストをするわけですが、この負荷状況をシミュレートするのにちょうどいいツールがなかったために、今回はJavaで専用のテストドライバを用意して対応しています。
データについては、実際に1ヵ月分のデータを採るのはちょっと難しいので、負荷がピークとなる日、経験上毎月1日に取引量が多いというのが分かっていたので、そちらの1日分のデータを統計としてとっています。
システム移行では当然ながら目標値は現行よりも数倍の性能を目指すので、テストドライバとしても倍率を指定して負荷テストを実施できるような仕組みを用意しています。
実際に統計を取った時の情報をご紹介します。詳細に公開できないところもあるので一部ぼかしていますが、こういった形で実際はかなり偏りがあるというところを再現するようにしています。円グラフになっているのはカード会社の偏り具合です。右下のグラフは、時間帯による取引の偏り具合を統計として取得したものになります。実際、0時~24時の統計ですので、真夜中2時・3時は取引が減っていて、昼休みにあたる12時頃とか、夜の23時頃にどうしても取引が大量に発生するというのが見えています。
今回、テストのために用意した仕組みですが、テストドライバ自体は単純な仕組みとしておいて、それらに渡すデータ(CSVデータ)を事前に生成しておき、そちらで様々なパターンをテストできるという仕組みにしました。実際、先ほど挙げたような統計情報を元にデータ生成ツール(スクリプト言語やExcelなど)を使ってあらかじめCSVのデータを生成しておき、テストドライバとしてはそれらを読み込んでテストデータをシステムの方に投げるという仕組みにしています。その取引のデータを投げた結果をまたデータとして出力して、それらをExcel等でグラフ化することで、処理遅延が発生していないかとか、エラーが大量に発生しないかというのを視覚化した上で、どこに問題があるかというのを推測することに充てています。
■パフォーマンステスト時のポイント(1)
パフォーマンス時のポイントをいくつか挙げたいと思います。
まず非機能要件を確認する。一般的にピーク時に秒何件発生するのかといった要件を入れていますが、同じように処理件数のピーク時の件数を設定します。こちらは瞬間ではなくて1時間それが継続するという想定を挙げています。結構重要なのが、最大の日次処理件数や月間の最大処理件数で、これらも明確にしておく必要があります。日次バッチの処理時間やDBバックアップの時の処理時間、これらを測定する必要があるので、日次・月間の処理件数を明確にしておくことは非常に重要になります。
また、取引の応答時間についても最大処理時間を決めておいて目標値としてその時間内に収まるようにします。ただ、どうしても突発的に遅延というのは発生するので、全体の99.9%が収まるようにといったような少し緩和する条件を付け加えるのもよいと思います。
あとは、前提条件として耐用年数を迎えた状態を用意する必要があります。具体的には、例えば5年後に月間5,000万件を処理するという想定であれば、仮にデータを3か月間保持するシステムで、1.5億件のデータをあらかじめデータベースのレコードとして用意しておきます。できれば意図的にデータも汚す、例えば正常の取引だけが1.5億件入っているだけよりも、そのうちの何割かは取消処理されたといったような少し汚れた状態を作っておくことが重要になります。
■パフォーマンステスト時のポイント(2)
次に、特殊なテストパターンも用意しておく必要があります。我々はバースト処理と呼んでいますが、瞬間的に大量の取引が発生するということを想定しておきます。実際に、チケット販売のお客様の場合だと、チケットの販売直後にものすごく大量の取引が発生するようなこともあるので、そういった状況も考慮して、例えばある瞬間だけ5秒程度、秒1,000件~2,000件といった大量取引が発生するというような状態を試しておきます。この時、当然ながら目標値を超えるようなものはエラーになってしまいますが、重要なのは、その波を乗り切った後に正常状態に戻るかどうかというところになります。経験的にTomcatのServletとかでシステム構築していますと、そこでエラーが発生した後、さらになかなか波を乗り切っても元の正常状態に戻らないというようなことがよく発生するので、そういった状態が起きないかというのをあらかじめ検証しておくことは非常に重要になります。
それから、ロングランテスト。取引を流した状態で、できれば1週間程度連続稼働させています。こちらは、メモリリークが発生していないか、GC遅延等が発生しないかという様子を見るために非常に重要になります。
プロセス起動直後での高負荷が発生する状況というのもテストしておいた方がよいです。システムのプロセスを起動した直後は、スレッド数とかDBコネクションとか動的に増えるような要素がある部分についてはその高負荷状態に追いつかないケースがありますので、こちらはそれをなんとかするというよりは、そういう状態が起きるというのを事前に知っておくというのが非常に重要になります。なので、例えばリリースをする場合でも、もし負荷が高い時間帯にどうしてもやる場合は、こういったことが起きるかもしれないというのを考慮することができます。
■パフォーマンステスト時のポイント(3)
当然ながら、設定した要件をクリアできることというのがパフォーマンステストのポイントですが、設定した要件を超えた場合の挙動を確認するという意味もあります。この場合、特にボトルネックになる箇所を見極めておくのが非常に重要になります。CPUのリソースがなくなるのか、メモリが不足するのか、それともDBコネクションが枯渇するのか、そういったボトルネックの箇所を見極めておくことで、将来サービスが拡張していく際にスケールアウトなどのシステムの拡張を行うにあたり、どこを拡張すべきかというのを事前に把握しておくことができます。
また、過負荷状態になった時、その後の平常状態に戻った時に問題が出ないことを確認する、過負荷状態が発生してエラーが発生しますが、その際に不整合の取引が発生していないかどうかを確認することが重要です。
これらのテストを行っていくわけですが、実際にどこまでテストすればいいのかというのが非常に悩ましいところ。私の経験としては、そのテストを指導する立場として、「ここまでテストしても実際の稼働した範囲に問題が出るならば仕方ない」という、自分の中でもう諦めがつくところまでテストするようにしています。
4.10年の間に行ったこと
これまでがクレジットカード決済のシステムをJavaで構築した際の話ですが、ここからは、それを実際に運用していてどうなったかというところについて説明します。
■システム変更履歴
実際に今回のシステムは、2012年10月に移行を開始しました。今回はお客様がたくさんいましたので、一度に全部ではなくて段階的に移行するという手法をとっています。その段階移行が完了したのが2013年1月になります。この後に旧システムを撤去しています。それからあまり時間が空いていませんが、2013年3月にサーバを増強しています。こちらはパフォーマンステストと実際のサービス移行をしている中で、すでにAPサーバが少しリソース不足であるというのがもう統計的には分かっていましたので、早いうちにサーバ増強の対応を行っています。続いて、2015年11月の時点で、お客様の要件としてもあったためにDR環境を新しく追加しています。こちらはシステム増強というか、DR用に別拠点にサブセットの環境も一式用意して、データ同期等を行うというような対応を行っています。2017年10月、ちょうど5年ほど経過したところで、DBサーバの方のリソースが不足気味であるというのが分かっていたのと、他のサービスでのDB移行を行うという目論みがあったために、この時点でDBサーバの更改を行っています。こちらのDBサーバの更改を行ったことで、システムとしてはさらに処理件数も増やせるような状況になっていました。それからさらに少し年数が経ちまして、去年(2021年12月)の時点で、それまで稼働していたDBサーバ以外の部分、WEB/AP/バッチサーバといった部分について、全面的に更改を行っています。
■2021年更改時内容
その2021年の更改時に変えたところを説明します。
OS/Javaのバージョンアップ実際にサーバも変えていますので、当然ながらOSもバージョンアップし、このタイミングでJavaもほぼ最新のものにバージョンアップしています。
制御系処理の改善制御系処理を改善しています。具体的には、処理経路アルゴリズムを見直すことで処理の効率化を図っています。
制御系テーブルをインメモリDBへ変更分散処理をする上でどうしても制御系のテーブルをデータベースに持っていたのですが、この部分がどうしても処理パフォーマンス上のネックになるというのが分かっていました。よって、それらの制御系テーブルについてはインメモリDBを新しく用意してそちらに移し替えるという形にしています。これによってかなり処理のパフォーマンスが良くなっています。
バッチ処理モジュールにフレームワーク導入それまではバッチ処理も含めて基本的に各モジュールにはフレームワークを入れていなかったのですが、このタイミングでバッチ処理についてはフレームワークを入れるようにしました。具体的には、Spring Batchのフレームワークを導入しています。
バッチ処理実行をcron起動からジョブ管理製品へ変更それまではバッチ処理の実行をcron起動によって行っていましたが、このタイミングで一般的に売っているジョブネット等が組めるジョブ管理製品に変更しています。
ビルドツール変更それまでは使っているライブラリ等も少なかったので、実際にEcripse上でただコンパイルするだけというようなビルドの仕方だったのですが、今回の更改のタイミングから、Maven&Jenkinsを使ったビルド方式に変更しています。
不要テーブルの削除バッチ処理を変更したということと、監視系の見直し等も行った結果、使っていない、なくても特に問題ないといったテーブルがあったので、それらの不要テーブルをこのタイミングで削除しています。
暗号化用秘密鍵の管理方式変更セキュリティのために暗号化処理を行っていますが、そのための秘密鍵の管理方法を変更しています。こちらは、PCI-DSSの要件が2~3年おきに更新されていくので、PCI-DSSのバージョンアップに対応するための変更を行いました。
■統括
今回のシステムのように業務要件の変更があまりないシステムについては、Javaで構築した息の長いプログラムを稼働するケースがあります。この場合、計画時に性能要件を明確にするとともに、定期的な性能測定を行いつつ、早めの拡張計画を立てることが必要です。また、ハードウェアの保守期間やOS/ミドルウェアのサポート期間の期限が切れることがあるので、これらを考慮した設計導入を行うことが非常に重要だと思っています。
むすび
最後になりますが、我々GMOペイメントゲートウェイではエンジニアの仲間を募集しています。我々GMOペイメントゲートウェイでは、お客様にサービスを提供することを主目的としています。そのサービス自体は我々自身で構築して運用しています。実際に、先ほども説明したように、10年近く前に構築したシステムを運用し続けるといったようなことを行っています。その中で実際に私自身もここについてはちょっと失敗したなというような思う点もありますが、逆にここについては狙い通りにできたというふうに思える部分もあります。我々はシステムを構築することが目的ではなくて、実際にそのシステムを運用してサービスを提供することが目的です。そういった自分の実績を確認できることは、エンジニアとして非常に幸せなことだと思っています。皆さんも、もし興味がありましたらぜひ採用サイトを訪れてみてください。
以上、ありがとうございました。
Q&A
質問1:金融システム観点で、設計段階から注意するべきことなどありますか?
主催設定した要件を超えた場合の挙動、ここはシステム設計及び運用をされている上でも、やはり一番重要視されてることの1つだという風に考えてるんですが、いかがでしょうか。
そうですね、決済・お金を扱っていますので、これがあとで不整合みたいなことが起きると、非常に後処理が大変ですし信用も失っていきますので、その辺を十分に考慮して処理をすると。ウェブサービスよりも1段上のシステムを作っているという意識を持って設計テストをするようにしています。米原
質問2:システム更改の際、スプリングバッチ及びスプリングフレームワークの選定理由について教えてください。
主催PCI-DSSの観点で、当初のパッチの適用が必要になることからプレイバックをさけるみたいな話もちょっとあったかと思うんですけれども、そこはあえてスプリングバッチを入れた判断というのと、そのスプリングバッヂを選択した理由とかですね、その辺ちょっと差し支えない範囲でお話しいただけますか。
それまでがバッチ処理はクーロン起動っていう形でかなり作り込んで実施していたんですが、今回更改のタイミングでJP1を使ってジョブスケジュールでやることになりました。社内的に全部そちらで統一しましたので、そちらに移行した方が運用しやすい、となりました。その上でいろいろ作り変えていくとこがあったので、これを機にフレームワークを入れようという形で、特にこの機能が使いたいとか、そういうわけではなかったんですけれど、徐々にフレームワークを入れていこうという方針に変わってますので、それで対応したという感じですね。米原
主催そうなんですね。じゃあそこでスプリングバッジを選択したっていうのは、たまたまそこで何らかの基準があって、スプリングバッチを選択されたという感じですかね。
社内のシステムが基本的にはスプリング、フレームワーク系を使うように今なってますので、そうすると他のチームから移ってきた人とかも、同じフレームワークを使っている方がやりやすいというところもあって、社内的にある程度共通化していく、技術の共通化を図るというところを目指してます。米原
質問3:「安定稼働するシステムの更改タイミングについて」アーキテクト・プロジェクトリードの側面からコツ(勘所)を教えてください。
主催10年間(実際にはもう移行前からいうともっと長いかとは思うんですけれども)例えば今回の場合だと移行のタイムとアプリケーションのロジックをちょっと変えてみたりとかあったかと思うんですが、そのタイミングというのはどのように判断したのかっていうあたりで、アーキテクトの立場なのか、そのプロジェクトリードの立場なのかっていうのもいろいろあるかと思うんですけども、「このタイミングだ」という風に判断された理由とそのタイミングがいつだったかというところを、もしよければお話しいただければと思います。
そうですね。これタイミングが非常に難しくて、やっぱり安定して動いているシステムほどなかなか変えるっていうのが難しくて、正直に言えば一番はハードウェアの保守が切れて、もう変えなきゃいけないというタイミングでハードウェアを変えるのであれば、この時にアプリケーションも見直しをかけましょう、という、そういったタイミングでもないとなかなか動いているものに手を入れるっていうのは非常に勇気がいるというか難しいのが実情ですね。あとは長年運用していますと、だんだんサポート切れという問題も増えてきますので、そこをそういったサポート切れが一番タイミングとしては分かりやすいといいますか、機会としてはちょうどいいかと思います。ただ、そのために色々準備は必要ですので、早めにここから公開するぞという計画を立てるのが重要なところになります。米原
主催どっちかというと、外部要因が一番大きなインパクトや影響を受けたところではあるけれども、事前にもちろんそういうことがあってもいいように準備だけはしておかれたということですかね。
そうですね。まあ、普段から運用していますと、この辺がちょっとネックになっているとか問題があるとかだんだん上がってきますので、でもその場ですぐに対応するしかないというのは、どうしてもたまってくるんですよね。そういうのを事前に調べて、こういう風に変えていこうとかっていう調査とかは進めておいて、それをまとめて一気に対応するというような感じで運用することが多いですね。米原
質問4:フレームワークを使うメリット等あれば、教えてください。
主催PCI-DSSの観点でフレームワーク極力排しますっていうお話は、これが多分視聴者の皆さんもかなりインパクトがあったようでして。例えばこの10年間の中で、フレームワークあれば良かったなと思った時というとどんな時がありましたか?というご質問をいただいております。
一番は、やはりだんだんメンバーが入れ替わったり増えたりしていく中で、フレームワークを使ってないと、そもそもこれ何?みたいな感じでやっぱり入ってきた人にはちょっと意味が分からないみたいで。設計書はあるんですが、そんなに詳細な説明書を用意したわけでもないのでやはり少し見てもらって対応していく、という流れになります。最初の入り口で時間かかるので、そういう意味ではやっぱりフレームワークを入れていると、特にスプリングなんてもうメジャーですから、皆さんだいたい触ったことあります、分かりますと。プロジェクトに参加した時、最初の慣れてくるまでの時間という意味では、フレームはあった方がいいなというのは思いました。米原
主催なるほど、ありがとうございます。やはり最初に作った人と後から入ってきたというところにギャップがあって、そのキャッチアップに時間がかかるというのは、運用する上では結構厳しかったりもしますもんね。
そうですね、はい。米原
質問5:言語やライブラリのバージョンアップ問題による、影響などあれば教えてください。
主催2021年のタイミングでJAVAのバージョンアップもされましたよっていうお話をされていらっしゃいましたけど、今までいわゆるフレームワークを使わずに、元々のライブラリをずっとメンテナンスされてってことだったと思います。今まで御社で作られたライブラリもバージョンアップによってやはり何らかの形で影響があったかと思うのですが、どのあたりが一番影響が大きかったですか?ここも言える範囲でお願いいたします。
そうですね。まあ正直そんなに影響はなかったですね。ただやっぱりJAVAはバージョン変わるとそれまでとちょっと微妙に動きが変わるっていうところが出てきますので、そこを事前に動作を調べてどういう風に変わるか、どういう風に影響出るかっていう、これには結構時間がかかっています。具体的にはテスト担当が1人2人専属でやってもらって、やっぱり1~2ヶ月ぐらい。工数でいうと2人月3人月ぐらいはやっぱりかけて調べて、どういう風に対応すればいいかというところを調べた上で、結構やっぱり時間はかかります。米原
主催小さいけれど、やっぱりそれを潰していかないと突然予想できない動きをされると困るっていうところもありますよね。
そうですね。米原
質問6:パフォーマンス改善に伴う、ボトルネックなどあれば教えてください。
主催パフォーマンスが問題になる場合に、どの辺がボトルネックになることが多いですか?ピンポイントに絞っていただいて結構なので、例えばこういう例どうですかっていうところでお話しいただけますか。
そうですね、一番ネックになるのはやっぱりデータベースのコネクションですね。DB(データベース)コネクションはやっぱり無限に使えるわけではないので、今回例えば決済システムですと、この後ろに具体的にはクレジットカード会社をまたいで最終的な与信枠まで使って大丈夫かっていうチェックまでして返ってくるので、1秒から数秒かかるんですよね。単純に作ると、その間はずっとスレッドが動きっぱなしというか。そうするとコネクションも握りっぱなしで実際何もしてないのに、ただリソースだけを使う状態っていうのが発生するので、その辺りはちょっと工夫して一旦コネクション離すとかそういう工夫しないとすぐにコネクションツールを使い切ってしまうので、結構苦労してますね。米原
主催なかなかそこはそうですよね。やはりブロッキングになってしまうと、なかなか難しいというころですかね。やっぱりその辺りが興味深いと思ってる方々がやはり多いですね。
質問7:JAVAバージョンアップによるパフォーマンス改善の恩恵などあれば教えてください。
主催先程のJAVAのバージョンアップによって、挙動が変わったところをつぶして行かれた、というところに関連してなのですが、バージョンが上がると、例えばGC(ガベージコレクション)のパフォーマンスが上がるとか色々良いこと、もちろんこの機能無くなって困ったねっていうパターンもあるかとは思うんですけど、パフォーマンス的な違いっていうのって何か体感されたこと、例えばパフォーマンス指標自体収集されて、これぐらい変わったんだっていうのをもし御社の今回の移行の中でこの辺はすごいアップデートすることによって良い影響をもらったなっていうのは何かありましたらお話しいただけますか。
JAVAのバージョンアップ自体でパフォーマンスがこう劇的に上がったっていうのは実はそこまでないですね。どちらかというと、その仕組みを色々見直して結果2倍3倍ぐらいの処理能力に上げていますけど、やっぱ一番効いたのはデータベースで排他制御していたところをインメモリーDBに変えたとか、そういうところも効いてますし、でもそのためにやっぱりいろいろJAVAの新機能を使ったりしてますので、なかなか単純にJAVAのバージョンを上げたから、ポンと上がるっていうのはちょっと体感的にはないですね。米原
主催なるほど、そうですよね。GCE単体のベンチマークを取ってるとかっていうんだったら別でしょうけども、システムとしてはトータルで見ないといけないというところもあるんで、トータルで見ると、例えばインメモリーでDBを入れましたとか、その他あと工夫というかアーキテクチャー上の改善とかをされて、パフォーマンスが当初よりももちろん上がったというような感じですかね。
おまけ
主催米原さんよりお話をしたいことがあるということで、イベントの告知ですかね。では、ちょっとお願いできますか。
はい、我々GMOインターネットグループが開催してますオンラインイベントとして、Developers Nightというのがあります。これが7月14日(木)に開催されるのですが、その中で我々の新しい決済サービスであるfincode byGMOっていうのがあり、こちらの話をします。実はこちらのサービスの裏側として、今回説明した決済システムを使ってクレジットカードの処理をしています。こちらのサービス開始する時に実は私も一部関与していまして、実際に一緒に開発したメンバーがこちらのイベントで登壇しますので、ぜひ参加して話を聞いていただければと思います。よろしくお願いいたします。米原
主催はい、ありがとうございます。(イベントでは)なかなか濃い話をしていただける、と思っていいですか?
そうですね。具体的にどう作ったとかっていう話があるはずですので。米原
主催なるほど、ありがとうございます。どう作ったかというのもあるだろうし、ここ少し頑張ったよ、みたいなしみじみ感あるようなお話もあればいいかなと思います。視聴者の皆さん、ぜひ楽しみにしていただいて参加登録いただければと思います。
参加申し込みはこちら
https://developers.gmo.jp/20150/
さいごに
ご視聴・ご参加いただき、ありがとうございました。たくさんのご質問もいただき、重ねて御礼申し上げます。
映像はアーカイブ公開しておりますので、以下より是非ご視聴ください。
https://youtu.be/2tGMbGfJkW4
クレジットカード決済システムをJavaで構築して10年間運用した話
連載記事はこちら
https://developers.gmo.jp/21413/