2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
Listing Service モノリスからマイクロサービスへ(全1記事)
リンクをコピー
記事をブックマーク
森國泰平氏(以下、森國):こんにちは。私はメルカリでマイクロサービスの開発をしている、森國と申します。今日は私が担当しているListing Serviceというサービスについて、どのようにマイクロサービス化を進めているかをお話いたします。
まず最初に、Listing Serviceについて簡単にご紹介します。
Listing Serviceは、メルカリのなかでも出品機能を担当するマイクロサービスとなっています。現在メルカリが進めているPHPのモノリスからGoのマイクロサービスへ移行する、最初のプロジェクトとなっています。
現在、Listing Serviceでは、1つのHTTPエンドポイントを対象として、マイクロサービス化を進めています。Listing Serviceが提供するのはHTTPのエンドポイントなんですが、Listing Serviceが他のサービスと通信する際には、gRPCを使用しています。
次に、メルカリのマイクロサービス化の進め方の戦略について、2つのポイントをお話します。
1つ目が、APIの互換性を保ちながらサービスを分割していくということです。APIの互換性を保つことによってカナリアリリース、つまりは段階的にPHPからGoのコードに移行していくことができます。
カナリアリリースをすることによって、万が一バグなどが最初に見つかった場合にも、PHPのコードに戻すこともできますし、そもそも影響範囲を少なくすることができるので、お客さまに対する負担というものも低下することができます。
2つ目の点として、チームとして独立した開発を可能にするということがあります。マイクロサービス化の目的は、ただコードを分割するということではなくて、長期的にスケール可能な組織を作り上げるということです。
そのため、Listing Serviceの開発を進めるなかでも、ただただ目の前の問題にとらわれすぎるのではなく、長期的な組織の在り方などを意識しながら、開発を進めていく必要があります。
これに対して、Listing Serviceがどのように実際に開発を進めているのかを紹介します。
まず、APIの互換性を保つということについては、3つのテストによって、互換性が保たれていることを保証しようとしています。
1つ目が、既存のAPIに対するアプリ、要するにiOSやAndroidの自動テストがあります。これをListing Serviceにもそのまま適用することによって、クライアントからの動作が変わっていないことを確認します。
2つ目のテストとして、QAチームによる手動の確認を行います。1つ目のアプリの自動テストだけではカバーしきれない範囲や細かいチェックなどを、QAチームの人の手によって行います。
3つ目は、AQAチームによるインテグレーションテストツールの開発と、それを使ったAPIの自動テストの構築を計画しています。
2つ目のチームとして独立した開発を可能にするという点については、そもそもListing Serviceで扱いづらいものは、出品に関するロジックだけなので、現在のエンドポイントに含まれているすべてのコードをListing Serviceに持ってくるというわけではなく、別のサービスに配置しなおしたりしています。
あるいは、そのままPHPのコードに残しておいて、Listing ServiceからWebhook経由でコードを実行するようなことにしています。そして、このPHPに残ったコードについては、適切なマイクロサービスが誕生した時に、コードの移行を考えています。
次に、Listing Serviceの開発を進めるにあたって直面した、4つの点について紹介します。
1つがGCPからさくらのMySQLへの接続、2つ目がトランザクションの分割、3つ目が機能追加・修正への追従、4つ目がPHPの例外との互換性です。それぞれについて、少し詳しく話していきます。
1つ目のGCPからさくらのMySQLへの接続についてです。
まず前提として、マイクロサービスというのは、ぞれぞれのサービスが独立したデータベースを持つべきだとは思います。ただし、今回のメルカリのようにモノリスからマイクロサービスに移行するという過程においては、どうしても一時的にデータベースの共有をしなければならない場合もあると思います。
メルカリのマイクロサービスは、基本的にはGCPの上で動いているんですが、既存のメルカリのPHPのコードであったりMySQLというのは、さくらのクラウド上で動いています。このGCPとさくらのMySQL間の接続をどうするかということが、課題になりました。
もちろんMySQLのポートを外部のインターネットに公開してしまえば、GCPから接続することは可能なんですが、それはセキュリティなどの要件的にあまりやりたくはないということで、課題となりました。
そこで我々がどのように進めているかというと、MySQLをgRPC経由で触るためのSQL on gRPCのようなサービスと、それを使うためのGoのクライアントライブラリを開発しています。gRPCを使ってMySQLを触ることによって、既存のgRPC上の認証・認可の仕組みに乗ることができるので、セキュアに通信を行うことができます。
この作成しているサービスでは、基本的にはCRUDの基本的な操作のみを提供し、JoinやTransactionといった複雑な処理は対応していません。実際にコードなどを見ていきます。
まず、こちらにはSELECT文に対応するProtocol Buffersのリクエストとレスポンスを載せています。
リクエストにはSQLで指定するカラム名だったりテーブル名、あるいは、WHERE句などが指定できます。
これをサービスに投げることによって、レスポンスにRowの配列が返ってきます。
このRowというのは、データベースの1行に相当する、カラム名とその値の配列のようなものとなっています。そして、このメッセージをうまく扱うために、Goのライブラリを開発しました。
Goのライブラリは、メソッドチェーンを使って、SQLのようにプロトバフのリクエストを組み立てることができます。
そして、このクライアントのもう1つの機能として、レスポンスのProtocol BuffersをGoのstructにマッピングするための、O/Rマッパーのような機能も備えています。これを使うことによって、Protocol Buffersというものをあまり意識せずに、SQLに近いかたちでMySQLを扱うことができるようになっています。
次に、トランザクションの分割についてお話します。Listing Serviceでは、基本的には出品に関する商品のデータの更新をメインとしたいんですけども、既存のAPIのコードには商品だけでなく、例えば配送情報だったり、分析用のデータなどが1つのトランザクションで更新されていました。
最終的にマイクロサービス化が進んでいくと、それぞれのサービスが独立したデータベースを持つことになるので、もちろんトランザクションは使えなくなります。そのため、この段階からトランザクションの分割っていうものを意識しながら、開発を進めていく必要があります。
そこで我々がどうしたかというと、まずどうしても商品に関連するようなデータについては、トランザクションが必要だと判断しました。そのため、さくら上にItem Serviceというサービスを作って、このItem Serviceはトランザクションを扱うようにしました。
それ以外についても、お客さまの目に直接触れるようなデータについては、できるだけリクエスト内で同期的に処理をするようにしています。それ以外の内部データなどについては、Cloud PubSubを使った非同期の結果整合性を受け入れるようにしています。
具体的に見ていくと、例えば、出品に関する機能があった時に、そのなかでトランザクションを貼って、例えば商品や画像、配送情報、分析用データなどが更新されていたとします。
その時に、商品についてはトランザクションが必要なので、Item Serviceが担保する。画像についてはお客さまの目に直接触れるものなので、トランザクションからは出すものの、できるだけ同期的に更新をする。
配送情報などについては、商品が売れるまでは配送されることもないので、例えば非同期の結果整合性を受け入れるようなかたちにする。そして、内部データについても、お客さまには意識してもらう必要はないので、非同期のデータ整合性を受け入れる、というふうに進めていきました。
次に、機能追加・修正への追従についてです。
Listing Serviceは出品を扱うのですが、出品はメルカリのなかでもコアな機能の1つとなっているので、このマイクロサービス化を進める過程においても、機能の追加だったり修正が行われる場合があります。新しいマイクロサービスではAPIの互換性を保とうとしているため、この機能の追加や修正に対しても追従していく必要があります。
そこで我々がどうしたかというと、もとのPHPのリポジトリ上に変更追跡用のブランチというものを作成しました。そして、Goのコードに移行したコードについては、そのブランチ上から削除しています。
これによって、例えばPHPのmasterブランチに新しい変更が加えられた場合に、その変更追跡用のブランチにマージした際にコンフリクトが発生するので、新しい変更が行われたことに気付くことができます。そして、この変更追跡用ブランチからすべてのコードを削除することができれば、マイクロサービス化のコードの移行が完了したと判断することができます。具体的に見ていきます。
例えば、この左側にあるPHPのコードを、右にあるGoのコードに移行していくようなことを考えます。例えば、最初にif文のところをGoのコードに移したとします。
すると、左のPHPのブランチからは、対応するコードを削除します。
同様にreturn文についても、例えばGoに移したとすると、PHPからは削除します。
そして、この左の状態のPHPのように、すべてのコードが削除された状態になれば、Goへの移行が完了したと判断することができます。
例えば、機能の追加や修正が行われたことを考えてみます。
例えば、新しくPHPでif文に変更があった場合を考えると、これはmasterブランチをこのブランチに対してマージしてくる時に、コンフリクトが発生するので新しい変更に気付くことができて、それをGoのコードに反映することができます。
あるいは、完全に新しい機能が追加された場合を考えると、これはもしかしたらコンフリクトしないかもしれないんですけども、最後に移行が終わったかどうかを確認する際に、対応する箇所だけがPHPのコードに現れることになるので、その部分を新しくGoに移しなおせば、互換性を保つことができます。
最後に、PHPの例外との互換性についてお話します。
これはたぶんメルカリ特有の課題にあたる部分だとは思うんですけども、既存のメルカリのAPIでは、レスポンスの一部にエラーコードとしてPHPの例外名が含まれている場合がありました。
さらに、この例外名というのをクライアントがハンドリングしている部分があったため、互換性を保つGoのAPIからもPHPの例外名を返さなくてはならない、という状況にありました。
さらには、この例外情報というのは、例外の種類によってレスポンスのJSONのかたちが一部異なっていたり、あるいはマイクロサービス化によって、この例外が発生する位置というもの自体が複数のサービスに分割されていくようなことになったため、各サービスがこの例外情報っていうのを意識する必要が出てきました。
そこで我々がどうしたかというと、このPHPの例外情報を扱うためのGoの共有ライブラリを開発しました。
このライブラリを使うことによって、例えばサービス内でエラーが発生した際に、それに対応するPHPの例外情報をgRPCのStatus.detailsっていうところに付与して、レスポンスを返すことができます。
そして、PHP互換のHTTPエンドポイントでは、このgRPCのレスポンスに含まれる例外情報から、直接メルカリのPHP互換のエラー情報のレスポンスを生成することができます。具体的にコードで見ていきます。
これがgRPCサーバーのコードの例です。
例えばエラーが発生した際に、通常のgRPCのエラーコードであるNotFoundに加えて、ここではPHPのNotFoundExceptionのようなものを一緒にメッセージにくっ付けて、エラーのレスポンスを返すことができます。
そして、PHPと互換性を保つHTTPエンドポイントでは、この後ろのサービスから返されてきたエラーからExceptionの情報を取り出して、レスポンスのJSONを直接生成することができます。
これによって、後ろのサービスで発生した例外情報というのを、等価的にクライアントに返すことができるので、各サービスは自分のサービスのなかで発生についてだけ意識すればよくなりました。
以上、マイクロサービス化を進めるにあたって出てきた4つの課題について、ご紹介いたしました。最後にまとめです。
Listing Serviceでは、長期的な組織の課題を解決できるように、かつ、短期的な負債を生んでしまわないようにすることを意識しながら、マイクロサービス化を進めています。
Listing Serviceはメルカリのマイクロサービス化の最初のプロジェクトであるため、資産だったり知識、経験などがそろっていなくて、なかなか大変な部分が多いところはあります。
ただし、正解がない中で、「自分が信じる設計を信じろ!」という精神で、ひたすらただしい姿を目指して全員で開発を進めていくのは、なかなか楽しいものがあるなと思いながら、開発を進めています。以上で、Listing Serviceについての発表を終わります。ありがとうございました。
(会場拍手)
関連タグ:
2024.10.29
5〜10万円の低単価案件の受注をやめたら労働生産性が劇的に向上 相見積もり案件には提案書を出さないことで見えた“意外な効果”
2024.10.24
パワポ資料の「手戻り」が多すぎる問題の解消法 資料作成のプロが語る、修正の無限ループから抜け出す4つのコツ
2024.10.28
スキル重視の採用を続けた結果、早期離職が増え社員が1人に… 下半期の退職者ゼロを達成した「関係の質」向上の取り組み
2024.10.22
気づかぬうちに評価を下げる「ダメな口癖」3選 デキる人はやっている、上司の指摘に対する上手な返し方
2024.10.24
リスクを取らない人が多い日本は、むしろ稼ぐチャンス? 日本のGDP4位転落の今、個人に必要なマインドとは
2024.10.23
「初任給40万円時代」が、比較的早いうちにやってくる? これから淘汰される会社・生き残る会社の分かれ目
2024.10.23
「どうしてもあなたから買いたい」と言われる営業になるには 『無敗営業』著者が教える、納得感を高める商談の進め方
2024.10.28
“力を抜くこと”がリーダーにとって重要な理由 「人間の達人」タモリさんから学んだ自然体の大切さ
2024.10.29
「テスラの何がすごいのか」がわからない学生たち 起業率2年連続日本一の大学で「Appleのフレームワーク」を教えるわけ
2024.10.30
職場にいる「困った部下」への対処法 上司・部下間で生まれる“常識のズレ”を解消するには
2024.10.29
5〜10万円の低単価案件の受注をやめたら労働生産性が劇的に向上 相見積もり案件には提案書を出さないことで見えた“意外な効果”
2024.10.24
パワポ資料の「手戻り」が多すぎる問題の解消法 資料作成のプロが語る、修正の無限ループから抜け出す4つのコツ
2024.10.28
スキル重視の採用を続けた結果、早期離職が増え社員が1人に… 下半期の退職者ゼロを達成した「関係の質」向上の取り組み
2024.10.22
気づかぬうちに評価を下げる「ダメな口癖」3選 デキる人はやっている、上司の指摘に対する上手な返し方
2024.10.24
リスクを取らない人が多い日本は、むしろ稼ぐチャンス? 日本のGDP4位転落の今、個人に必要なマインドとは
2024.10.23
「初任給40万円時代」が、比較的早いうちにやってくる? これから淘汰される会社・生き残る会社の分かれ目
2024.10.23
「どうしてもあなたから買いたい」と言われる営業になるには 『無敗営業』著者が教える、納得感を高める商談の進め方
2024.10.28
“力を抜くこと”がリーダーにとって重要な理由 「人間の達人」タモリさんから学んだ自然体の大切さ
2024.10.29
「テスラの何がすごいのか」がわからない学生たち 起業率2年連続日本一の大学で「Appleのフレームワーク」を教えるわけ
2024.10.30
職場にいる「困った部下」への対処法 上司・部下間で生まれる“常識のズレ”を解消するには