TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

927

はじめに こんにちは、 @rs_tukki です。 最近外に出られない日々が続いているので、自宅で完結できる趣味が増えた気がします。 さて、今は Android のWebViewがアツいみたいなので、それに関連してるようでしてない気がする話を少し。 はじめに 実装したかった仕様 実際の仕様 原因 どう修正したか まとめ 参考 実装したかった仕様 今回私が開発していた Android アプリは全体的にWebViewに依存しており、 まず API で認証処理を行ってから、取得したアクセス トーク ンを使ってWebサイトにログインするという流れを取っています。 アプリ( API )「君のサイトにログインしたいんだけど。まずIDとPASS投げるから認証して」 サーバ「オッケー、認証は問題ないね。君用のアクセス トーク ンあげるからこれくっつけてアクセスしてね」 API での通信を行う場合は、OkHttp3のCookiejarクラスを実装した独自の Cookie ストアを使用し、 レスポンスで受け取った Cookie を明示的にCookieManagerに保存する処理を行っていました。 class SharedCookieStore() : CookieJar { // CookieManagerはアプリ全体で共通のインスタンスとして定義する private val cookieManager = CookieManager.getInstance() // レスポンスの内容からCookieを永続化するメソッド override fun saveFromResponse(url: HttpUrl, cookies: MutableList <Cookie>) { val urlString = url.toString() cookies.map(Cookie :: toString) .forEach { cookieManager.setCookie(urlString, it) } sync() } private fun sync() { cookieManager.flush() } } val apiModule = module { single { SharedCookieStore() } bind CookieJar :: class // SharedCookieStoreクラスをインスタンス化 factory { buildHttpClient( get ()) } // 上記のSharedCookieStoreクラスを注入 private fun buildHttpClient(cookieJar: CookieJar): OkHttpClient { val dispatcher = Dispatcher() val builder = OkHttpClient.Builder() .dispatcher(dispatcher) .cookieJar(cookieJar) // SharedCookieStoreをOkHttpClientのcookieJarに指定(以下省略) これでCookieManagerに必要な Cookie が保存されましたので、あとはこれを使ってWebViewを開いてあげましょう。 アプリ(WebView)「今から君のサイトにアクセスするね。アクセス トーク ンはこれで っ Cookie 」 サーバ「はいはい、 トーク ン問題ないからアクセスしていいよー」 後は、このアクセス トーク ンの有効期限が切れるまでサイトへのアクセスが可能という仕様にするつもりでした。 実際の仕様 さて、このアクセスしたサイトですが、実は アクセス トーク ンの有効期限とKeyを保持したまま、定期的に Value だけ更新される という仕様が組み込まれています。 サーバ「ごめん、今君が使ってる Cookie 、値が変わったんだわ。次のアクセスからこっち使ってね」 アプリ(WebView)「えっ」 そして、この後再度アクセスしようとすると アプリ(WebView)「次このページにアクセスするね…えーっとアクセス トーク ンはこれかな? っ Cookie 」 サーバ「んー、その Cookie 知らない値だわ。申し訳ないけどログインからやり直して」 アプリ(WebView)「えっ」 というわけで、なぜかWebViewは新しい Cookie を使うことなく認証に失敗してしまいました。 原因 (サーバ側で既にある Cookie の Value を更新しただけなら、CookieManagerに自動的に反映されてそうなもんだけどな…) と思いながらWebViewの実装やらCookieManagerの仕様やらを見返していたのですが、ここに勘違いがありました。 spitfire-tree.hatenadiary.org 曰く、 Android アプリケーションで cookie を管理する際、デフォルトの動作はアプリとは非同期で揮発性のメモリに展開された値を最大数分(?)程度の遅れで不揮発な領域に書き込みに行くようです。 (中略) 実際には cookie に {user_id: 1} のような値を書き込むわけですが、それが不揮発性メモリと同期される前にアプリケーションを即時に終了させると、次回実行時に( cookie の expire が実行時より後に設定されていようと)不揮発性メモリに残った値を参照してアプリの状態を復元するため、今行ったログイン処理がなかったことにされてしまう可能性が存在する、ということです。 つまり、今貰った新しい Value を裏で永続化する前に次のアクセスを行ってしまったことで、 キャッシュとして残っていた古い Value を使ってしまい不整合が発生した…ということのようです。   どう修正したか Cookie を永続化できていなかったのが原因なわけですから、通信ごとに永続化してやればいいわけです。 WebViewのFragmentに内部クラスとしてWebViewClientを継承したクラスを作成し、 onPageFinishedのメソッドをオーバーライドして現在のアクセス トーク ンを明示的に永続化します。 class WebViewFragment() { private inner class MyWebViewClient( val isOtherWindow: Boolean ) : WebViewClient() { override fun onPageFinished(view: WebView?, url: String ?) { // Webviewでの遷移時に現在のアクセストークンを永続化する CookieManager.getInstance().flush() super .onPageFinished(view, url) } } 最初に書いた通り、 API 通信の時にはきっちり永続化しているんですけどね… WebViewでも同じ対応が必要という認識がすっぽり抜け落ちていました。反省。 class SharedCookieStore() : CookieJar { // CookieManagerはアプリ全体で共通のインスタンスとして定義する private val cookieManager = CookieManager.getInstance() // レスポンスの内容からCookieを永続化するメソッド override fun saveFromResponse(url: HttpUrl, cookies: MutableList <Cookie>) { val urlString = url.toString() cookies.map(Cookie :: toString) .forEach { cookieManager.setCookie(urlString, it) } sync() } // Cookieを永続化する private fun sync() { cookieManager.flush() } } まとめ さて、今回は Android における Cookie の仕様についてお話ししました。 CookieManager.flush() 、忘れるなよ!絶対だぞ! 参考 CookieJar - OkHttp - OkHttp AndroidのWebViewにクッキーを設定したい Android の cookie 管理と CookieManager と flush() - happy lie, happy life エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは。インフラエンジニアの gumamon です! 〇aaS、コンテナ、 オーケストレーション 。インフラ界隈でも便利なツールが普及しつつある昨今ですが、根強く使われ続けているのが シェルスクリプト です。 シェルスクリプト の用途は 主に「シェルコマンドの自動実行(とエラーハンドリング等)」であり、インフラエンジニアとしては業務効率化のツールとして重宝する一方、事故(バグ)が発生してしまうと重大な障害につながる危険なものでもあります。 今回は、これを(初心者でも)比較的安全に書けるようになる。 ひいては シェルスクリプト の品質管理が楽になる「2つの VSCode のアドオン」を紹介します。 VSCode = Visual Studio Code 、 Microsoft 製の ソースコード エディタです。 インフラエンジニアの場合「 vim で書くし」という方も多いと思うのですが、慣れてしまうと超便利です。 インテリジェントなコード補完や校正機能は、コードレビューの 工数 も削減出来てGood。 はじめに アドオンの紹介 さっそく使ってみる( その1: ShellCheck ) さっそく使ってみる( その2: ShellFormat ) まとめ チーム/組織 でlintを導入するメリット アドオンの紹介 順次2つ、ご紹介します。 インストール方法/使い方についてはリンク先に動画がありますのでそちらをご参照ください。 ShellCheck (検証ver = v0.14.0) (エディタで表示された)シェルの記述に対し、問題が無いかをチェックします。 問題のある記述について、なぜダメなのか、どう直せば良いのかを事細かに説明してくれます。 チェック内容はリアルタイムに更新されるので、問題の有無を確認しながらコーディングが出来るのも良いポイントです。 ShellFormat (検証ver = v7.0.1) インデントや改行など、構文のフォーマットを修正してくれます 。 例:if構文の;doをどこで改行するか。 これにより ソースコード のフォーマットが一意になり、複数名で書いている場合は非常に読みやすくなります。 リンク先の動画についての補足 VSCode を初めて使われる方は、コマンドパレット(動画でコマンドを実行している部分)の呼出しで 少し迷われるかもしれません。コマンドパレットはショートカット(Ctrl+Shift+P)を使うか、メニューから[表示]→[コマンドパレット]で呼び出せます) さっそく使ってみる( その1: ShellCheck ) まず、ShellCheck をOFFにした状態がこちら。 ShellCheck=OFF ファイルの一覧を読み込み、日付付きのファイル名としてコピーするだけの スクリプト です。 何か気になるところはありますか? 気になるところを探しつつ次へ。 ↓ ↓ ↓ ↓ ↓ ShellCheck=ON わんさか指摘点が出てきました。 波線の色は深刻度を表しています(黄色 > 青色)。今回は発生していませんが、Syntax Errorレベルだと赤の波線で表示されます。 各波線部分は、マウスオーバーすると QuickFix というリンクが出て来て、なぜダメなのかを説明してくれます。 それによると・・ SC2006 ` `囲みはいくつかの問題があるレガシーコード。$( ) を推奨 SC2086 変数は "" で囲むこと。split防止 SC2153 スペルミスの可能性。Variableが割り当てられていない。 SC2162 while read は -r オプションを付けるべき ⇒全部直してみます。 ShellCheck=ON(fixed) 綺麗になりました! 今回指摘された部分は何れも Bash の記法上、実行してもエラー扱いになりません。問題無く動いて exit 0 してしまいます。 つまり 「ShellCheckを通していないと 検証/レビュー時に見落とすリスクがある」 と言うことです。 こういう呪われたシェルは、 「いざ本番というタイミングで発火する」 ものなので、ShellCheckを入れておいて損は無い(というか積極的に使った方が良い)と個人的には思います。 さっそく使ってみる( その2: ShellFormat ) まず、ShellFormat を実行する前のコードをご覧ください。 (ShellFormatは コードを開いて Shift+Alt+F を実行することで瞬時に校正を行います。) ShellFormat (before) style1~3 は別々の人が書いたと思ってください。 何れも問題のない記述ですが、読みづらい。インデントもそろっていないので、レビュー時にロジックを誤認してしまうかもしれません。 ShellFormat を実行してみます。 ↓ ↓ ↓ ↓ ↓ ShellFormat (after) 全て同じ書式に統一されました! 見やすい! ShellFormatは既に書き終わったコードにも適用することができます。 つまり、いつ誰が書いたのかわからないグチャグチャのコードに対してShellFormatを実行すると、 論理を変えることなく見やすい状態にすることが出来る訳です。 こちらのアドオンも使わない理由は特に見あたりません。 まとめ 今回紹介したアドオンは、いわゆる「lint」の一種です。 lint= ソースコード を読み込んで内容を分析し、問題点を指摘してくれる静的解析ツール。 ※ IT用語辞典 e-words より lintを導入するメリットは個人単位でも享受出来ますが、チーム/組織の運用に組み込んで導入するとより大きなメリットを享受することができます。 チーム/組織 でlintを導入するメリット 品質向上 ソースコード の品質を一定レベル保証出来る 潜在的 なバグを事前に潰すことが出来る コスト削減 教育をする側もされる側も楽になる レビューをする側もされる側も楽になる 組織単位で統一されたコードの書式であれば、ジョブローテ時の立ち上げコストが削減できる 納品速度の向上 レビューがサッと終わる ソースコード の品質管理は常に頭を悩ませる課題の一つです。 その中でも、構文のチェックは 本来注力すべき開発の本質ではないので、自動化するに越したことはありません。 やってみようかな?と思われた方、10分で導入出来ますのでお試し頂ければ幸いです! 以上、最後までお読み頂きありがとうございました。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは、株式会社 ラク スで先行技術検証を行っている技術推進課の @t_okkan です。 技術推進課では、新サービス立ち上げ時の開発速度アップを目的に、現在 ラク スでは採用されていない新しい技術の検証を行う、技術推進プロジェクトがあります。 今回はその技術推進プロジェクトで、GraphQLについて検証を行いましたので、その結果の報告を行います。 なお、別テーマの取り組みや、過去の取り組みに関しては、こちらからご覧ください。 tech-blog.rakus.co.jp GraphQL GraphQLの言語仕様 クエリ言語 スキーマ言語 GraphQLのアーキテクチャ スキーマファーストな開発 検証してわかったこと フロントエンドとバックエンドの実装を切り離せ、お互いを疎結合にできる 共通の定義ファイルを元に実装を行うため、APIの仕様が明確になり実装の手戻りの防止や開発速度の向上が見込める その他わかったこと REST APIとGraphQLのAPIを1つのアプリケーションで運用できる DDDとGraphQLの相性が良い マイクロサービスアーキテクチャとの相性が良い GraphQLが向いているケース GraphQLが向いていないケース GraphQLのエコシステム GraphQLサーバー GraphQLクライアント その他エコシステム GraphiQL・GraphQL Playground GraphQL Code Generator GraphQL Multipart Request dataloader GraphQL Depth Limit Apollo Studio まとめ GraphQL GraphQLとは、 Facebook が2015年の React.js Conf 2015 で発表した、クライアントとサーバーの通信のための言語仕様になります。 グラフ理論 を用いて複雑なデータを階層構造にしてデータの取得を行います。元々は2012年に Facebook がネイティブモバイルアプリで使用するために開発しましたが、現在は OSS となっており GraphQL Foundation によってエコシステムの開発などが進められています。 graphql.org GraphQLを導入している企業としては、 GitHub 、 Twitter 、Yelp、Shopify、 Netflix などがあげられます。 GraphQLの仕様は2017年まで頻繁にリリースされたいましたが、2018年以降はリリースがなく安定していると言えます。 spec.graphql.org 実装言語や通信方式には縛りはありませんが、通信方式に関しては プロトコル はHTTP、HTTPメソッドはPOST、 データ形式 は JSON が推奨されています。 対応言語は公式ドキュメントで公開されています。 graphql.org GraphQLの言語仕様 クエリ言語 データの取得や更新の操作を指定する言語です。基本的にはクライアント側でクエリ言語を構築し、GraphQLサーバーにリク エス トとして送信します。 基本的なオペレーションとして、Query、Mutation、Subscriptionが存在します。その他にもFragementやVariables、Directivesなどの重要な言語仕様がありますが、詳細は 公式ドキュメント もしくは GraphQL Spec で確認してください。 Query データの取得、読み込み系の処理を担当します。 REST API のGETに相当します。 Mutation データの書き込み、更新系の処理を担当します。 REST API のPOST、PUT、DELETEに相当します。 Subscription WebSocketを利用した双方向のリアルタイムデータ通信を行います。サーバー側からクライアント側にリアルタイムで通知を送信できます。 スキーマ言語 GraphQLサーバーの API の仕様を定義します。サーバー側に スキーマ ファイルを配置し、GraphQLの型システムと API の操作を定義します。 ここでは主な仕様のみ紹介しますので、その他の仕様に関しては 公式ドキュメント と GraphQL Spec で確認してください。 Schema GraphQLサーバーで実行可能な操作(Query, Mutation, Subscription)を定義します。Schemaで宣言した操作がクライアントで実行できる操作となります。 Scalars GraphQL型システムのプリミティブ型を宣言します。GraphQL型システムには組み込み型として、String( UTF-8 の文字列)、Int(符号付き32ビットの整数)、Float(符号付き不動小数点)、Boolean(true, falseの論理値)、ID(一意の識別子でStringと同じ文字列)が存在します。開発者が独自にCustom Scalar型を定義できます。 Types GraphQL型システムの型で開発者が独自で定義できます。型は固有のオブジェクトで対応する複数のフィールドを持つことができます。アプリケーションの特性を反映できます。 GraphQLの アーキテクチャ GraphQLクライアント クエリ言語を構築し、データの取得やデータの更新操作を指定し、GraphQLサーバーにリク エス トを送信します。 GraphQLサーバー GraphQLクライアントから送信されたクエリを解析し、クエリで指定されたオペレーションを実行しデータを返却します。Query Parser、Query Validation、Resolverで構築されています。 Query Parser GraphQLサーバーでクライアントからリク エス トされたクエリ言語がGraphQLの仕様を満たしているか 構文解析 を行います。 構文解析 後にクエリ言語を 機械語 に変換します。基本的にはGraphQLサーバーのライブラリによって実装されています。 Query Validation Query ParserでGraphQLの 構文解析 をした後に、 スキーマ ファイルで定義されたGraphQLサーバーの仕様とクエリ言語の検証を行います。クエリ言語で宣言されている各オペレーションとResolverのナビゲーションを行います。基本的にはGraphQLサーバーのライブラリによって実装されています。 Resolver スキーマ ファイルで定義した型と操作に基づいて、サーバー側にて各 プログラミング言語 で実装を行います。GraphQLサーバーの ビジネスロジック やデータベースなどの永続化データ、またはバックエンドの API にアクセスし必要なデータを取得しレスポンスとして返却します。開発者が各 プログラミング言語 で実装します。 GraphQLの アーキテクチャ スキーマ ファーストな開発 GraphQLを導入することで スキーマ ファーストな開発が可能になります。 開発初期に API の仕様からGraphQLの スキーマ ファイルを定義し、その スキーマ ファイルを元にフロントエンドとバックエンドがそれぞれ実装します。 API の仕様が実装前に明確となるため、フロントエンドとバックエンドの実装を並行に行え、かつ結合した際にエラーが起きづらくなることを期待できます。 検証してわかったこと GraphQLの導入を検証し スキーマ ファーストな開発を行うことにより以下のようなことがわかりました。 フロントエンドとバックエンドの実装を切り離せ、お互いを 疎結合 にできる 共通の定義ファイルを元に実装を行うため、 API の仕様が明確になり実装の手戻りの防止や開発速度の向上が見込める フロントエンドとバックエンドの実装を切り離せ、お互いを 疎結合 にできる 従来の REST API ではSPAなどでフロントエンドとバックエンドを分離をしたい場合、開発の担当を分離することはできてもフロントエンドがバックエンドの API と直接通信していたため、実装を分離できずお互いのI/Fを確認しながら開発を行っていました。そのため、開発速度の低下やでき上がった API が想定とは異なる場合は手戻りが発生していました。 そこでGraphQLを導入することで スキーマ ファイルの存在により、バックエンドとフロントエンドの実装が隠蔽され、 疎結合 に分離できることがわかりました。 実装の分離 バックエンドは スキーマ ファイルで定義されているクエリと型に合わせたデータを提供し、フロントエンドは スキーマ ファイルにて定義されているクエリを実行し定義されているデータを取得することになります。お互い確認するのが スキーマ ファイルになり、それ以降の実装について関心がなくなるため実装を分離できます。実装を分離できることにより、バックエンドの実装や アーキテクチャ の変更による影響を受けづらくなります。 また、お互いの実装が分離されることにより各開発チームの最適な開発手法を取り入れることができます。 バックエンドは画面を意識せずビジネス ドメイン 知識に基づいた API を構築でき、フロントエンドは API の仕様を意識せず画面で必要なデータのみを取得できます。 共通の定義ファイルを元に実装を行うため、 API の仕様が明確になり実装の手戻りの防止や開発速度の向上が見込める GraphQLを導入することで、バックエンドは スキーマ ファイルに定義されているクエリと型を実装し、フロントエンドは スキーマ ファイルに定義されているクエリを実行して型の値を取得します。お互い スキーマ ファイルで定義されていない実装をすると、Query Validationでエラーが発生します。そのため、フロントエンド・バックエンドのどちらかが主導で仕様を変更できなくなります。仕様を変更する場合は、 スキーマ ファイルを変更してからお互いの実装を変更する必要があります。 スキーマ ファイルが API の仕様を明確にするので、仕様の齟齬による手戻りを防ぎ、それぞれの実装に集中できるので開発速度の向上が期待できるかと思います。 その他わかったこと 上述のこと以外で検証を通じてわかったことは以下になります。 REST API とGraphQLの API を1つのアプリケーションで運用できる GraphQLの仕様を実装しているライブラリには、Spring BootやLaravel、Expressとと言ったFrameworkと統合するためのライブラリが存在します。そのため、既存の REST API を一気にGraphQLに移行するのではなく、新機能や一部の機能のみ先行してGraphQLに移行できます。 DDDとGraphQLの相性が良い 先述したビジネス ドメイン をGraphQLの型システムに変換しやすいことから、DDDとGraphQLの相性が良いと考えられます。 AWS のreInvent:2019のBuilding modern APIs with GraphQLのセッションでもDDDとGraphQLとの親和性について紹介されています。(35:39あたりからです) www.youtube.com 登壇者の方のブログでもDDDとGraphQLの相性がよいと紹介されています。 GraphQL and DDD: the Missing Link | HackerNoon マイクロサービス アーキテクチャ との相性が良い Netflix では モノリス な API からマイクロサービスへ移行する際にGraphQLを導入しました。GraphQLを導入後にさらにサービスが増加しGraphQLのResolverの実装が複雑化し ボトルネック になっていたため、 Apollo 社が提唱している Apollo Federationを導入しました。 Apollo Federationは複数のGraphQLサービスを1つのGraphQL Gateway で集約しGraphQLサーバーとして提供する仕組みです。またその際に自社で開発したGraphQLをSpring Bootに統合するための実装を、Domain GraphQL Spring Boot Frameworkという フレームワーク として OSS で公開しています。 Home - DGS Framework GraphQLが向いているケース GraphQLの導入が向いているケースとして、適切に ドメイン を分割できるアプリケーションが考えられます。 GraphQLは導入すれば複雑な仕様を解消するツールではありません。同じクエリを扱う SQL ではデータベースの正規化で関係を整理するのと同様に、GraphQLにおいてもまずはアプリケーションの仕様を適切に分割できるかを検討してください。仕様を分割できる場合は、その分割の粒度に合わせてGraphQLの型を設計します。 例えば、ビジネス ドメイン 単位でGraphQLの型を設計することでバックエンドの実装をシンプルにできるのではないかと思います。 画面駆動から ドメイン 駆動の API 開発になり、画面に合わせた実装がなくなるためController層やView層といったフロントエンドと会話する層の実装が楽になるかと思います。 ドメイン とGraphQLの型 ビジネス ドメイン をGraphQLの型に変換することで、「検証でわかったこと」欄に上げたようなGraphQLの特徴を活かしながら導入できると思います。 この条件を満たしていれば、後述する向いていないケースに該当しない限り、基本的にはどのWeb API にも導入できるかと思います。 GraphQLが向いていないケース GraphQLの導入が向いていないケースとしては、 メディアファイルを扱う API データベースのテーブルを直接操作する API が考えられます。 GraphQLの データ形式 は JSON 形式が推奨されています。そのため周辺のエコシステムも JSON 形式を前提として実装されています。ファイルアップロードの機能を提供しているライブラリもありますが、メディアファイルを扱う場合は、GraphQLとは別の API を提供すべきかと思います。 また管理型のシステムなど、データベースのテーブルを直接操作するようなアプリケーションにも向いていないのではと考えています。GraphQLを導入するためのオーバーヘッドが高いため、 モノリス な構成か REST API で構築すべきかと思います。ただ Prisma や Hasura などテーブルとGraphQLの型が1対1になるようなツールを導入する場合は、検討の余地があるかと思います。 GraphQLのエコシステム GraphQLのエコシステムやその他のツールは公式のドキュメントでまとめられています。今回は検証で利用したツールを中心に紹介します。 GraphQL Landscape GraphQLサーバー 今回は Java とNodeでGraphQLサーバーを構築しました。それぞれで有効なライブラリを紹介します。 Java graphql- java GraphQLの仕様を Java で実装しているライブラリです。クライアントからのクエリのパーサーや、 スキーマ ファイルとの検証を実装しています。 Java でWebフレームワークを利用しない場合はもっとも有効な選択肢となります。 Hello from GraphQL Java | GraphQL Java graphql- java -spring Spring BootとGraphQLを統合するためのライブラリです。内部でgraphql- java を参照しているため、GraphQLの仕様はカバーできています。 @SpringBootTest に相当する @GraphQLTest を提供しています。 About GraphQL Spring Boot - GraphQL Java Kickstart Domain GraphQL Spring Boot Framework( DGS ) 先述した Netflix がGraphQLを導入し、 Apollo Federationを実現させた際に利用した フレームワーク です。Spring BootとGraphQLを統合し、テストライブラリの提供やSpring Securityとの統合をサポートしています。 Apollo Federationで必要な機能をサポートしているため、マイクロサービスとGraphQLの組み合わせを検討している場合は有効なライブラリであると考えられます。 Home - DGS Framework Node GraphQL.js GraphQL Foundationが実装しているライブラリです。GraphQLの仕様を準拠しており信頼性は高いが Apollo Serverに比べると低機能なため検証などで使用できると考えられます。 Getting Started With GraphQL.js | GraphQL Apollo Server Apollo 社によって開発されている フレームワーク です。NodeでGraphQLサーバーを構築する際の デファクト 的存在となっています。dataloaderやサーバー側のキャッシュなど機能が豊富で、 Apollo Federationを利用することでマイクロサービス アーキテクチャ との相性もよいです。 Introduction to Apollo Server - Apollo GraphQL Docs Express GraphQL Expressで構築されているアプリケーションに ミドルウェア としてGraphQL API を追加できます。 GitHub - graphql/express-graphql: Create a GraphQL HTTP server with Express. Apollo Server Plugin Expressで構築されているアプリケーションに ミドルウェア として Apollo Serverの機能を統合できます。 Choosing an Apollo Server package - Apollo GraphQL Docs GraphQLクライアント Apollo Client Apollo 社が開発しているライブラリです。React, Vue.js, Angular, iOS , Android に対応しています。機能が充実しており、ドキュメントや2次情報も充実しています。基本的には Apollo Clientを利用するべきであると思います。 Introduction to Apollo Client - Apollo GraphQL Docs Relay Facebook が開発している フレームワーク です。ReactとReact Nativeに対応しており、事前にクエリを コンパイル できるなどより安全にGraphQLのクライアントを構築できます。しかし、 フレームワーク であり学習コストが高いことや、React Hooksに未対応であることが懸念点として上げられます。 Relay その他 その他の選択肢として GraphQL Request や urql 、 graphqurl などがあります。 JavaScript フレームワーク との統合はサポートされていません。使用範囲が限られる場合は選択してもよいと思います。 また プロトコル にHTTP、HTTPメソッドにPOST、 データ形式 に JSON を指定すれば、クライアントツールを利用せずにクエリをリク エス トできます。ただしその場合はクエリの解析や、レスポンスの マッピング などの機能を独自で実装する必要があります。 その他エコシステム GraphiQL・GraphQL Playground GraphQL API のクライアントツールです。クエリの実行や、 スキーマ ファイルからドキュメントを自動生成することもできます。基本的にGraphQLサーバーのライブラリには開発環境用として内包されています。 github.com github.com GraphQL Code Generator スキーマ ファイルから自動で型定義ファイルを生成するツールです。TypeScriptや Java などの静的型付け言語に対応しています。 CLI も提供しており、ローカル環境で自動生成することもできます。GraphQLサーバーとクライアントのどちらの型定義ファイルを生成できます。 Home – GraphQL Code Generator GraphQL Multipart Request multipart/form-dataを利用したGraphQLでファイルアップロードを行う場合の仕様を定めています。この仕様を各GraphQLサーバーとクライアントのライブラリが実装をしています。GraphQLでファイルアップロードを行う場合はまずこの方法を選択すべきと思います。またライブラリを選定する場合もこの仕様を実装しているかを判断軸にすべきだと思います。GraphQLでファイルアップロードを行う他の手段としては、別 API を用意して登録先の識別子をGraphQLで登録するか、ファイルデータを Base64 で エンコード してGraphQLで登録する方法があります。 github.com dataloader GraphQLでは型同士が接続している場合にN+1問題が発生しやすいです。その回避策としてdataloaderを活用する方法があります。dataloaderは Facebook が提供している JavaScript のライブラリで、このライブラリを基に各 プログラミング言語 で移植されています。一意のIDを集合型でdataloaderに集約し、その集合体を利用して登録されている処理によって一括でデータを取得する仕組みになっています。そのため バッチ処理 とも言われています。N+1問題以外でも利用されています。 github.com また、dataloaderを利用することでバックエンドの負荷が高くなることもあるので、N+1問題を受け入れる選択もあるそうです。 Apollo Serverではサーバー側のキャッシュを利用する方法を推奨しています。 Data sources - Apollo GraphQL Docs GraphQL Depth Limit GraphQLの アンチパターン に循環参照があります。GraphQLの方がお互いに結合している場合に発生し、多段にデータが紐づいてしまいバックエンドの負荷が高くなります。対策としてクエリの深さを制限する方法があります。 graphql-depth-limit というライブラリや、各GraphQLサーバーのライブラリでも提供されています。 Apollo Studio GraphQLは単一のエンドポイントで API を提供するため、エンドポイント単位でのメトリクスの計測ができません。 Apollo Studioを利用すると、クエリごとに実行数や処理時間を計測でき、バックエンドの ボトルネック を発見できます。しかし、 Apollo Serverか 対応しているライブラリ でGraphQLサーバーを構築する必要があります。 Introduction to Apollo Studio - Apollo GraphQL Docs Apollo Studio以外でメトリクスを計測する方法として、マイクロサービスで利用されている Open Tracing を利用する方法もあります。 まとめ GraphQLの検証で得た知見のまとめは以下のようになります。 GraphQLを導入することで、フロントエンドとバックエンドでお互いの実装が切り離せ 疎結合 にできる 共通の定義ファイルを元に実装を行うため、 API の仕様が明確になり開発速度の向上が期待できる GraphQLを導入する前にアプリケーションの仕様を分割できるかを検討する必要がある DDDやマイクロサービス アーキテクチャ との相性が良い GraphQLと REST API は同じアプリケーションで共存できるため、段階的に移行することができる 検証のご紹介は以上になります。GraphQLの導入事例が徐々に増えてきており、エコシステムの強化も進んでいる印象です。 既存の REST API をGraphQLに移行する場合や、新規の API をGraphQLで開発することを検討されている方は是非参考にしてください。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは。 株式会社 ラク スで先行技術検証を行っている「技術推進課」の堀内( id:yhoriuchi )です。 ラク スの スマホ アプリ開発 ではCI/CD環境にBitriseを採用しているのですが、 iOS アプリ開発 の一部で使っているだけで十分に活用できているという状況ではありませんでした。 今回、技術推進プロジェクトの一貫として スマホ のCI/CDに取り組み、その中でBitriseを熟知し、最大限活用できるようにしました。 情報量が多くてその全てをお伝えすることはできませんが、調べても情報があまり出てこなかった"Bitrise CLI "をご紹介してみようと思います。 目次 目次 Bitriseとラクスが採用した理由 Bitrise CLIとは インストール方法 ローカルでの実行方法について Bitrise CLI実行時の環境変数 スゴイと叫ばずにはいられなかった機能 最後に Bitriseと ラク スが採用した理由 Bitriseについては多くの記事でも紹介されていますが、概要を理解してもらうには 公式サイトの説明 が分かりやすいので引用しておきます。 Bitriseはモバイルアプリ開発(iOS, Android, Xamarin, …)における 継続的インテグレーション・デリバリー(CI/CD) プラットフォームをサービス(PaaS)として提供しています。 ソフトウェアプロジェクトの開発・自動化を手助けするためのツール・サービスの集合体です。 数回のクリックであなたのアプリのテストやデプロイ作業を自動化しましょう! ラク スでBitriseを採用した理由は、 スマホ 対応に特化していることと、ワークフローと呼ばれる可視化されたフロー図でビルドプロセスが分かりやすくなっていることが大きいです。 また、 スマホ に特化していることもあって iOS 、 Android だけでなく、FlutterやReact Nativeなどにも対応しています。 プラットフォームを問わず スマホ アプリ全般をサポートし、充実した機能があるので、Bitriseを使っていて開発で困ることは無さそうに思います。 しかし、Bitriseが万能とはいえ、なんらかの理由でBitriseが停止した際にビルドやデリバリーに影響が出てしまっては困ります。 そんな時にも備えて対策を検討した際に出てきたのがBitrise CLI でした。 Bitrise CLI とは Bitrise CLI は読んで字の如く、Bitrise Command Line Interfaceのことです。 ローカル実行が可能で、コードが GitHub で公開されているため誰でも利用することができます。当然Bitriseのアカウントも不要です。 これを使えば懸念しているBitrise停止といった不慮の事故に見舞われたとしてもなんとかできそうです。 インストール方法 オープンソース なのでコードをビルドしないといけないのかと考えてしまうかもしれませんが、下記のコマンド一発です。 brew update && brew install bitrise Homebrewを使いたくない、あるいは Linux 環境にインストールしたい場合は下記。(v1.45.1の場合) curl -fL https://github.com/bitrise-io/bitrise/releases/download/1.45.1/bitrise-$(uname -s)-$(uname -m) > /usr/local/bin/bitrise 環境構築にまで気を配っているところが素敵ですね。 ローカルでの実行方法について 実行方法も至って簡単なのですが、その前にワークフローの設定が記載された bitrise.yml を用意する必要があります。 これはイチから作る必要は無く、Bitriseにログインしてワークフローエディタを開くとダウンロードできるようになっているので心配無用です。 具体的には下記のbitrise.ymlメニューからダウンロードできます。 bitrise.ymlの詳細をお見せすることはできませんが、内容は 公式サイトが参考になると思います 。 ここまで来れば実行は簡単で、bitrise.ymlを保存した ディレクト リで下記を実行するだけ。(下記の"SimpleBuild"はワークフロー名) bitrise run SimpleBuild すると下記のように アスキーアート で"BITRISE"のロゴが表示されたあと、ステップが実行されているのが分かります。 bitrise run ちなみに下記は同じワークフローをbitrise.ioで実行したログです。若干見た目が異なりますが、出力される内容はほぼ同じです。 bitrise.io Bitrise CLI 実行時の 環境変数 実行が簡単なことがお分かり頂けたと思いますが、通常BitriseでGit リポジトリ から ソースコード を取得する場合、 リポジトリ のURLとブランチ名はワークフローの設定ではなく、1つ上の階層にあるアプリケーションの設定に記載し、ステップの中では用意された 環境変数 を参照することになると思います。 残念ながらこの設定( 環境変数 )はbitrise.ymlに記載されないため、自前で 環境変数 としてexportしておく必要があります。 具体的な 環境変数 名は GIT_REPOSITORY_URL と BITRISE_GIT_BRANCH の2つです。 他にもbitrise.ioが使う環境変数 はいくつもありますが、弊社では今のところこの2つだけで足りています。 これを毎回exportするのが手間なので弊社では スクリプト を作って運用することにしました。 参考までに弊社で作成した スクリプト (一部抜粋版)を載せておきます。 #!/bin/sh WORKFLOW=$1 if [ -z "${WORKFLOW}" ]; then echo "第1引数でビルド対象のワークフローを指定してください" exit 1 fi export GIT_REPOSITORY_URL="{{リポジトリのURL}}" export BITRISE_GIT_BRANCH="{{ブランチ名}}" ssh-add ~/.ssh/id_rsa bitrise run ${WORKFLOW} スゴイと叫ばずにはいられなかった機能 最後に1つだけBitrise CLI でスゴイ!と思った機能を紹介したいと思います。 それがワークフローエディタです。とりあえず下記のコマンドを実行してみてください。 bitrise :workflow-editor すると、なんということでしょう、ブラウザが立ち上がってワークフローエディタが使えるじゃないですか! (下記のような画面が表示されます) bitrise workflow-editor スタックと呼ばれる 仮想マシン の管理機能が無いので言い過ぎになってしまいますが、これってBitriseの機能そのものだったりします。 オープンソース でここまで使えることに驚きを隠せませんでした。万が一に備えようと調べ始めた CLI 環境ですが、十分すぎる機能が提供されているので安心してBitriseを使えると思いました。 最後に いかがだったでしょうか。Bitrise CLI の情報でお役に立つ内容があったでしょうか。 実のところ、 スマホ アプリ開発 に着手した直後はビルドサーバーを社内に構築しようと考えていました。しかし、ビルドマシンのメンテコスト(機器、OS、 SDK 、その他ソフトウェアなどのメンテの手間)を考えると割りに合わないと判断し、CI/CD環境を提供してくれる クラウド サービスを利用することにしました。 今回、Bitriseには十分な機能と柔軟な環境が用意されていると改めて知ることができたので、この先もBitriseを活用していくことで開発を効率よく進めていくことができそうです。
アバター
目次 目次 はじめに styled-componentsとは 使用環境 環境構築 styled-componentsでの装飾 リンクの装飾 propsを使ったリンクの装飾 リンク以外の装飾 VSCodeの拡張機能 おわりに 参考 はじめに 初めまして。フロントエンドチーム 新卒1年目のhy094です。 業務でstyled-componentsを使う機会があったのでまとめたいと思います。 styled-componentsとは CSS in JSの一つです。 JSの中にスタイルを記述できるので、 CSS ファイルが必要なくなることや typo をエラーで教えてくれたりするメリットがあります。 また、 コンポーネント 単位で装飾するためReactとの親和性が高いです。 使用環境 上記の通り、Reactとの親和性が高いためReactを使用します。 折角なので弊社のブログ記事に書かれている内容を装飾していきます。 使用する環境・ ソースコード などはこちら↓ tech-blog.rakus.co.jp Reactの入門記事になります。ぜひご覧ください。 ※本記事は上の記事の内容を実施した直後という状態を想定して進めていきます。 環境構築 上記の記事だけだとstyled-componentsがインストールされていないので、 追加でインストールします。 npm install styled-components package. json に "styled-components": "^5.2.1", のような記述があれば問題なく成功しています。 styled-componentsでの装飾 リンクの装飾 元記事をそのまま実施していただいている場合、 フォルダ構成は以下のようになっていると思います。 ├── node_modules/ ├── package.json ├── public/ ├── README.md ├── src/ │ ├── page │ │ ├── one.js │ │ └── two.js │ └── routes.js └── yarn.lock まずは コンポーネント を配置するためのフォルダをsrc以下に作成します。 src/components 次に、src/components以下にLink コンポーネント を作成します。 src/components/Link.js import React from "react" ; import styled from 'styled-components' export const Link = ( { to,children } ) => { return <StyledLink href= { to } > { children } </StyledLink> } ; const StyledLink =styled.a` //ここにスタイルを記述 outline: none; text-decoration: none; display: inline-block; width: 19.5%; margin-right: 0.625%; text-align: center; line-height: 3; color: black; background: yellow; &:hover { background: orange; } &:active { background: red; color: white; } ` //ここにスタイルを記述 以降にお好きなスタイルを記述してください。 今回は MDNのリンクの装飾 をお借りしたいと思います。 次に作成したLink コンポーネント をアプリの方でimportします。 src/page/one.js import React from 'react'; - import { Link } from 'react-router-dom'; + import { Link } from '../components/Link'; import Two from './two'; class One extends React.Component{ render(){ return ( <div> test_one<br/> <Link to='/two'>twoへ</Link> </div> ) } } export default One; src/page/two.js import React from 'react'; - import { Link } from 'react-router-dom'; + import { Link } from '../components/Link'; import One from './one'; class Two extends React.Component{ render(){ return ( <div> test_two<br/> <Link to='/'>oneへ</Link> </div> ) } } export default Two; 見ていただくと分かる通り、差分は2行目のimport文のみです。 ここまでの作業でリンクのスタイルが変わっています。 propsを使ったリンクの装飾 「リンクといえばこのスタイル」という規則が決まっていれば、 今後は <Link>○○</Link> を使うことでclassなどを指定する必要がなくなります。 しかし今の表示だと自分がoneにいるのかtwoにいるのか分かりにくいですね。 そこでoneとtwoでスタイルを変えたいと思います。 src/components/Link.js import React from "react" ; import styled, { css } from 'styled-components' export const Link = ( { to,children,fontColor,isBluePattern } ) => { return <StyledLink href= { to } fontColor= { fontColor } isBluePattern= { isBluePattern } > { children } </StyledLink> } ; const StyledLink =styled.a` //ここにスタイルを記述 outline: none; text-decoration: none; display: inline-block; width: 19.5%; margin-right: 0.625%; text-align: center; line-height: 3; color:$ { (props) => props.fontColor } ; $ { (props) => props.isBluePattern ? (css` background: aquamarine; &:hover { background: aqua; } &:active { background: blue; color: white; } `): (css` background: yellow; &:hover { background: orange; } &:active { background: red; color: white; } `) } ` src/page/one.js import React from 'react' ; import { Link } from '../components/Link' ; import Two from './two' ; class One extends React.Component { render() { return ( <div> test_one<br/> <Link to= '/two' fontColor= "blue" isBluePattern= { false } >twoへ</Link> </div> ) } } export default One; src/page/two.js import React from 'react' ; import { Link } from '../components/Link' ; import One from './one' ; class Two extends React.Component { render() { return ( <div> test_two<br/> <Link to= '/' fontColor= "red" isBluePattern= { true } >oneへ</Link> </div> ) } } export default Two; 少し無理矢理感もありますが、これでページごとにスタイルが変わります。 color:${(props) => props.fontColor}; で、 <Link> に指定した fontColor の色が文字色となります。 oneは青、twoは赤ですね。 また、 $ { (props) => props.isBluePattern ? (css`    //以下略 三項演算子 と css によってまとめてスタイルを指定できます。 isBluePatternがtrueの時、 青背 景のボタン風リンクになります。 リンク以外の装飾 const StyledLink = styled.a でリンクの装飾ができました。 ではリンク以外の装飾はどうするのでしょうか。 答えは単純で h1 なら styled.h1 、 div なら styled.div です。 VSCode の 拡張機能 ここまで作業するとLink.jsは以下のような表示になっているのではないでしょうか。 見にくいですね。また、 CSS の補完も効きません。 そこで便利なのが VSCode の 拡張機能 vscode-styled-components です。 この 拡張機能 を使うと・・・ 非常に見やすくなり、補完も効くようになります。 styled-componentsを書くならぜひ抑えておきたい 拡張機能 だと思います。 おわりに 今回はstyled-componentsのさわりについて紹介させていただきました。 スタイルの話やデザイン関連の話はあまりできませんでしたが もしそちらにご興味ある方は弊社デザイナーチームのブログがあるので、 よかったらご覧ください!↓ note.com 現在はチームの話などが多いですが、今後記事数はどんどん増えていくと思います。 styled-componentsと親和性の高いatomic-designについて記事ができることを密かに楽しみにしています。 参考 https://qiita.com/snishinon/items/9ea9c2f981ef081e825f https://zenn.dev/syu/articles/0f92abf7f0b5c5 https://qiita.com/taneba/items/4547830b461d11a69a20 https://www.webprofessional.jp/style-react-components-styled-components/ エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは、開発エンジニアの amdaba_sk( ペンネ ーム未定)です。 今回は PHP のお話です。例えば以下のような配列があったとしましょう。 <?php $ target = [ 'ほげ' , 'ふが' , 'ぴよ' , ] ; これをソートしたいとします。ただそれだけなら、 <?php sort ( $ target ) でおしまい、 Q.E.D. ! でもいいのですが、 PHP には他にもいろいろな配列のソート方法が用意されていますよね。 この記事は、それらいろいろなソート方法を紹介し、特に日本語文字列を値に持つ配列の場合もっと「イイ感じ」にする方法がありますよというお話です。 なお、ソートに限らず PHP の配列機能について知りたいという方は、弊社ブログのこちらの記事も併せてチェックしてみてくださいね。 tech-blog.rakus.co.jp もくじ はじめに もくじ PHP の配列ソート関数いろいろ 標準の配列ソート関数 - sort SORT_REGULAR SORT_STRING 日本語文字列をイイ感じに並べる sort 関数に SORT_LOCALE_STRING フラグを指定する intl 拡張を使う - Collator::sort または collator_sort 独自ルールを実装する - usort 実装例:いろは順 おわりに PHP の配列ソート関数いろいろ お決まりですが、こういうときまずは公式の「 PHP マニュアル」を見に行くようにしましょう。 PHP は公式マニュアルがとても充実していますよね。配列のソートについても、それについてまとめた専用のページがあります。 www.php.net 関数名 ソートの基準 キーと値の相関関係 ソート順 関連する関数 array_multisort() 値 連想配列 の場合は維持し、数値添字配列の場合は維持しない 最初の配列、あるいはソートオプション array_walk() asort() 値 維持する 昇順 arsort() arsort() 値 維持する 降順 asort() krsort() キー 維持する 降順 ksort() ksort() キー 維持する 昇順 asort() natcasesort() 値 維持する 大文字小文字を区別しない自然順 natsort() natsort() 値 維持する 自然順 natcasesort() rsort() 値 維持しない 降順 sort() shuffle() 値 維持しない ランダム array_rand() sort() 値 維持しない 昇順 rsort() uasort() 値 維持する ユーザー定義 uksort() uksort() キー 維持する ユーザー定義 uasort() usort() 値 維持しない ユーザー定義 uasort() 配列をソートする関数が表でまとめられ、それぞれの関数のソートの基準(キー or 値)、キーと値の相関、ソート順、関連する関数が一覧できます。各関数ごとの詳細ページへのリンクもあって、勉強にも役立ちます。 余談ですが、ソートの基準とキーと値の相関が情報として入ってくるのは、すべての配列が 連想配列 な PHP の特徴的なところですね。 さて、マニュアルは PHP の一般の配列に関するものですが、本記事では文字列を値として持つ、一次元の連番整数値添字の配列について考えます。例えばいくつかの静的型付け言語だと、対象配列が次のように型付けされる場合です 1 。 Java : List<String> , C# : List<string> , TypeScript: string[] , Scala : List[String] , F#: string list , Haskell : [Text] また、すでに暗黙的に前提としていますが、特に日本語の文字を含む文字列を想定しています。 このような問題設定の下で、ここでは sort 関数について詳しく見ていくことにしましょう。 標準の配列ソート関数 - sort www.php.net sort ( array &$array , int $flags = SORT_REGULAR ) : bool sort は配列を昇順にソートする関数です。 第一引数 $array にソートしたい配列への参照を受け取って、ソートに成功したかどうかを真偽値で返します。引数で渡した配列は、この関数の実行によって変更されます。破壊的な作用を持つ関数ということです。 またオプションの第二引数 $flags に整数で表されたフラグを取り、要素の大小関係の判定方法を切り替えることが出来ます。使える値は以下の通りです。 SORT_REGULAR - 通常通りに項目を比較します。 詳細は 比較 演算子 で説明されています。 SORT_NUMERIC - 数値として項目を比較します。 SORT_STRING - 文字列として項目を比較します。 SORT_LOCALE_STRING - 現在の ロケール に基づいて、文字列として項目を比較します。 比較に使う ロケール は、setlocale() 関数で変更できます。 SORT_NATURAL - 要素の比較を文字列として行い、 natsort() と同様の「自然順」で比較します。 SORT_FLAG_CASE - SORT_STRING や SORT_NATURAL と (ビットORで) 組み合わせて使い、 文字列のソートで大文字小文字を区別しないようにします。 これらはすべて、 PHP のグローバルな定数として定義されています。いくつかピックアップしてみましょう。 SORT_REGULAR SORT_REGULAR は sort の第二引数 $flags のデフォルト値です。この値が指定された場合、要素の大小関係の判定には宇宙船 演算子 <=> (と同等の処理)が使われます。 宇宙船 演算子 の詳細については 公式マニュアルの該当するページ を参照してほしいのですが、 SORT_REGULAR が指定された場合の sort は、すべての要素が文字列である前提であれば、次に取り上げる SORT_STRING と変わりません。 ただしマニュアル上でも以下のような警告があるように、要素に複数の型が混在する場合は注意が必要です。 警告 flags が SORT_REGULAR の場合に 複数の型が混在する配列をソートする場合には、注意してください。 sort() が期待しない結果を出力することがあります。 この件に関する詳細は、かなり古い記事ではありますが、以下が参考になります。 hnw.hatenablog.com SORT_STRING SORT_STRING が指定された場合 sort 関数は各要素を文字列であるとして大小関係を比較します。 文字列であるとして比較するとはつまり、文字列を文字の配列であるとして、 文字コード の数値的大小関係に基づく辞書式順序で比較する 2 ということです。 例えば 'abc' と 'abd' を比較する場合、以下のように先頭から一文字ずつ文字の大小を比較していきます。 文字位置 'abc' 'abd' 比較 1 a a a = a 2 b b b = b 3 c d c < d 全体 'abc' 'abd' 'abc' < 'abd' 初めて違いの出た文字位置の文字の大小で、文字列全体の大小が決まります。ちなみに文字列の終端は、どんな文字よりも小さいと判定されます。 PHP においては、最終的に C 言語の memcmp 関数を用いる実装がされています 3 。 日本語文字列をイイ感じに並べる ここで以下のような配列をソートすることを考えてみましょう。 順位 文字列 読み UTF-8 文字コード 値 1 あぶりだし アブリダシ E38182 E381B6 E3828A E381A0 E38197 2 あまガミ アマガミ E38182 E381BE E382AC E3839F 3 ういーん ウイーン E38186 E38184 E383BC E38293 4 アフロディテ アフロディテ E382A2 E38395 E383AD E38387 E382A3 E38386 5 アフロディーテ アフロディーテ E382A2 E38395 E383AD E38387 E382A3 E383BC E38386 6 アプロディテ アプロディテ E382A2 E38397 E383AD E38387 E382A3 E38386 7 アマガミ アマガミ E382A2 E3839E E382AC E3839F 8 ウィーン ウィーン E382A6 E382A3 E383BC E383B3 9 一遍上人 イッペンショウニン E4B880 E9818D E4B88A E4BABA 10 悪人正機 アクニンショウキ E682AA E4BABA E6ADA3 E6A99F 11 阿闍梨 アジャリ E998BF E9978D E6A2A8 上表は sort 関数に SORT_STRING を指定して並べ替えた順に記載しています。各文字列の UTF-8 文字コード 値を 16 進数表示で付けました。これを見てもらうと、前節で説明した通りの順序に並んでいることが分かります。 一方で、ぱっと見てこの順序は「イイ感じ」でしょうか? 少し考えれば納得できそうですが、おそらくぱっと見だと「なんでこんな順序になっているの?」と思ってしまうのではないでしょうか。 世界にはいろいろな言語があり、使用している文字も違います。それぞれの言語での最適な文字の並び順が、 文字コード の大小とは一致しないこともあります。日本語だと、文字列は基本「読み」の順に並べるのが普通ですよね。 さて、ようやく本題にたどり着きました。日本語文字列を PHP で「イイ感じ」にソートするにはどのようにすればいいでしょうか? 先に結論をいうと、以下の 3 つの方法があります。 sort 関数に SORT_LOCALE_STRING フラグを指定する intl 拡張を使う - Collator::sort または collator_sort 独自ルールを実装する - usort 一つずつ見ていきましょう。 sort 関数に SORT_LOCALE_STRING フラグを指定する 標準のソート関数である sort は、先にも説明した通りオプションの第二引数 $flags によって要素の大小関係の判定方法を切り替えることが出来ます。 $flags に SORT_LOCALE_STRING が指定された場合、 SORT_STRING と同様各要素を文字列であるとして大小関係を比較しますが、その際、現在の ロケール に基づいて地域化された比較ルールを用います。 文字列を文字の配列であるとして先頭から一文字ずつ文字の大小を比較するという点は SORT_STRING の時と同様なのですが、文字同士の大小比較ルールが単純な 文字コード の大小比較ではありません。 SORT_LOCALE_STRING を使った場合、 PHP は内部的には C 言語の strcoll 関数を使って文字列の比較します 4 。これによって ロケール が示す地域の言語に適した方法で文字の大小比較をしてくれるというわけです。 先ほどのサンプル配列を ロケール が ja_JP.UTF-8 の環境下で、 SORT_STRING と SORT_LOCALE_STRING のそれぞれでソートした結果を比較すると、下のようになります。 --- SORT_STRING +++ SORT_LOCALE_STRING @@ @@ Array ( 0 => 'あぶりだし' 1 => 'あまガミ' 2 => 'ういーん' - 3 => 'アフロディテ' - 4 => 'アフロディーテ' + 3 => 'アフロディーテ' + 4 => 'アフロディテ' 5 => 'アプロディテ' 6 => 'アマガミ' 7 => 'ウィーン' - 8 => '一遍上人' + 8 => '阿闍梨' 9 => '悪人正機' - 10 => '阿闍梨' + 10 => '一遍上人' ) 比較結果を見ると、「ひらがな → カタカナ → 漢字」という文字種ごとの並び順は SORT_STRING の時と同様です。しかし漢字文字列だけを見ると、「ア → アク → イチ」と読みの順に並んでいて、より日本語の文字列として自然と思える並び順になっていると言えます。 より詳細にどのような比較ルールになっているのかというと、申し訳ないのですがにわか仕込みの C 言語ぢからが足りず、分かりませんでした。おそらく PHP を コンパイル した際の処理系に依存するものと考えているのですが、実際のところどうなのでしょう。教えてつよつよな人……! intl 拡張を使う - Collator::sort または collator_sort www.php.net オブジェクト指向 型 public Collator::sort ( array &$arr , int $sort_flag = ? ) : bool 手続き型 collator_sort ( Collator $coll , array &$arr , int $sort_flag = ? ) : bool PHP をビルドする際に、国際化用拡張モジュール (intl 拡張) を有効にすることで、 ICU ライブラリのラッパーを使うことが出来るようになります。ライブラリで提供される機能はいくつもありますが、ここで注目するのは Collator です。 Collator は、 ロケール に応じた適切な並び順を考慮した文字列比較機能を提供するクラスです。並び順のルールは UCA に準拠しています。 Collator を使って配列を並べ替えるには、 インスタンス メソッドの Collator::sort か、あるいは collator_sort 関数を使います。 使い方は標準の sort 関数とほぼ同じで、引数 $arr にソートしたい配列への参照を受け取って、ソートに成功したかどうかを真偽値で返します。引数で渡した配列は、この関数の実行によって変更されます。破壊的な作用を持つ関数というところも同じです。 またオプションの引数 $sort_flag に整数で表されたフラグを取り、要素の大小関係の判定方法を切り替えることが出来ます。使える値は以下の通りです。 Collator::SORT_REGULAR - 通常の比較 (型を変更しない) Collator::SORT_NUMERIC - 数値としての比較 Collator::SORT_STRING  - 文字列としての比較 省略した場合のデフォルトは Collator::SORT_REGULAR ですが、要素がすべて文字列である場合は Collator::SORT_STRING を指定した場合と結局のところ同じになります 5 。そして Collator::SORT_STRING が指定された場合、 Collator::sort は内部的に ICU ライブラリで提供される C 言語の関数 ucol_strcoll 6 を使用して文字列の比較をします 7 。 ロケール ja_JP.UTF-8 が有効な環境下で、サンプル配列を標準の sort + SORT_LOCALE_STRING と Collator::sort + Collator::SORT_STRING のそれぞれでソートして比較してみましょう。 <?php namespace Tests\Unit; use PHPUnit\Framework\TestCase; use Collator; class CollatorSortTest extends TestCase { /** * ロケールの設定(SORT_LOCALE_STRING 用) */ public static function setUpBeforeClass () : void { parent :: setUpBeforeClass () ; setlocale ( LC_COLLATE, 'ja_JP.UTF-8' ) ; } /** * sort + SORT_LOCALE_STRING と Collator::sort + Collator::SORT_STRING の * ソート結果を比較するテストケース * @return void */ public function testCollatorSort () { $ sample = [ 'あぶりだし' , 'あまガミ' , 'ういーん' , 'アフロディテ' , 'アフロディーテ' , 'アプロディテ' , 'アマガミ' , 'ウィーン' , '一遍上人' , '悪人正機' , '阿闍梨' , ] ; $ sortLocalString = $ this -> sortAndReturn ( fn ( &$ arr ) => sort ( $ arr , SORT_LOCALE_STRING ) , $ sample ) ; $ sortCollator = $ this -> sortAndReturn ( fn ( &$ arr ) => Collator :: create ( 'ja_JP.utf8' ) -> sort ( $ arr ) , $ sample ) ; $ this -> assertEquals ( $ sortLocalString , $ sortCollator ) ; } /** * 配列にソート関数を適用して返す * @param callable $sortFunc ソート関数 * @param array $arr 対象配列 * @return array */ private function sortAndReturn ( callable $ sortFunc , array $ arr ) : array { $ sortFunc ( $ arr ) ; return $ arr ; } } 実行するとこのテストケースは失敗して、以下の差分出力が出て来ます。 --- Expected (sort + SORT_LOCALE_STRING) +++ Actual (Collator::sort + Collator::SORT_STRING) @@ @@ Array ( 0 => 'あぶりだし' - 1 => 'あまガミ' - 2 => 'ういーん' - 3 => 'アフロディーテ' - 4 => 'アフロディテ' - 5 => 'アプロディテ' - 6 => 'アマガミ' - 7 => 'ウィーン' + 1 => 'アフロディーテ' + 2 => 'アフロディテ' + 3 => 'アプロディテ' + 4 => 'アマガミ' + 5 => 'あまガミ' + 6 => 'ウィーン' + 7 => 'ういーん' 8 => '阿闍梨' 9 => '悪人正機' 10 => '一遍上人' ) 比較結果を見ると、「かな文字 → 漢字」という並び順は相変わらずですが、 Collator::sort では sort + SORT_LOCALE_STRING とは違って、ひらがなとカタカナの区別がなくなり、音の順で並べられるようになりました。また漢字文字列同士であればやはり読みの順に並んでいて、 sort + SORT_LOCALE_STRING よりもさらに日本語の文字列として自然と思える並び順になっていると言えます。 独自ルールを実装する - usort www.php.net usort (array &$array , callable $callback ): bool sort + SORT_LOCAL_STRING や Collator::sort でも満足できないこだわり派の方には、独自に比較関数を実装する方法をご紹介しましょう。 usort は第一引数 $array にソートしたい配列への参照を受け取って、ソートに成功したかどうかを真偽値で返します。引数で渡した配列は、この関数の実行によって変更されます。破壊的な作用を持つ関数ということです。 sort と異なるのは第二引数がフラグではなく、要素を比較するための関数であり、かつ必須であるという点です。無理やり シグネチャ を上と同じように書くならば、第二引数のコールバック関数は以下のようになるでしょうか。 $callback (mixed $element1 , mixed $element2 ): int 配列の要素を引数として二つ取り、整数値を返す関数です。返り値の int は、最初の引数 $element1 のほうが二番目の引数 $element2 より大きい場合は正の数を、等しい場合はゼロを、そして小さい場合は負の数を返すように実装します。 このコールバック関数によって、好きなルールでソートを実現することが出来るという寸法です。要件に合わせて適切なルールを実装しましょう。ちなみに昇順降順の切替も、返り値の正負を反転させることで実現することが出来ます( ursort が無いのはそのためでしょう)。 実装例:いろは順 せっかくなので ursort を使った独自ソート関数の実装をやってみましょう。 ursort を使うのであれば、配列の各要素文字列からソートキー(よみがな等)を別途作成して付け加え、それを使ってソート、なんてこともできるでしょうが、ここでは以下の簡単な条件で実装してみます。 ソート対象配列の要素文字列はすべて「ひらがな」で構成される ただし濁音、半濁音、長音、拗音、促音に相当する文字は含まれないものとする 各文字列は辞書式順序で順序比較する 各文字の順序は「 いろは順 」に従う できあがりはこちらです。 <?php class IrohaOrder { /** @var array $iroha いろは順マスタ */ private $ iroha = [ 'い' , 'ろ' , 'は' , 'に' , 'ほ' , 'へ' , 'と' , 'ち' , 'り' , 'ぬ' , 'る' , 'を' , 'わ' , 'か' , 'よ' , 'た' , 'れ' , 'そ' , 'つ' , 'ね' , 'な' , 'ら' , 'む' , 'う' , 'ゐ' , 'の' , 'お' , 'く' , 'や' , 'ま' , 'け' , 'ふ' , 'こ' , 'え' , 'て' , 'あ' , 'さ' , 'き' , 'ゆ' , 'め' , 'み' , 'し' , 'ゑ' , 'ひ' , 'も' , 'せ' , 'す' , 'ん' , ] ; /** * 文字列配列をいろは順に並べる * @param array $arr * @return bool ソートできたかどうか */ public function sort ( array &$ arr ) : bool { try { usort ( $ arr , fn ( $ a , $ b ) => $ this -> compare ( $ a , $ b )) ; return true ; } catch ( RangeException $ e ) { return false ; } } /** * 文字列を二つ受け取っていろは順に従って順序を決める * @param string $a * @param string $b * @return int <=> の仕様に沿った整数値 * @throws RangeException 引数の文字列にいろは順が付けられない場合 */ public function compare ( string $ a , string $ b ) : int { $ lenA = mb_strlen ( $ a ) ; $ lenB = mb_strlen ( $ b ) ; $ ret = 0 ; for ( $ i = 0 ; $ i < min ([ $ lenA , $ lenB ]) ; $ i ++ ) { $ c = $ this -> getCharIndex ( mb_substr ( $ a , $ i , 1 )) ; $ d = $ this -> getCharIndex ( mb_substr ( $ b , $ i , 1 )) ; $ ret = $ c <=> $ d ; if ( $ ret !== 0 ) { break ; } } return $ ret ?: $ lenA <=> $ lenB ; } /** * ひらがな一文字を取っていろは順での順位を返す * @param string $c ひらがな一文字 * @return int いろは順で何番目か * @throws RangeException 引数の文字列にいろは順が付けられない場合 */ private function getCharIndex ( string $ c ) : int { $ ret = array_search ( $ c , $ this -> iroha, true ) ; if ( $ ret === false ) { throw new RangeException ( "Character ${$c} is not supported!" ) ; } return $ ret ; } } 使い方のサンプルも兼ねて、テストも簡単に作ってみました。 <?php namespace Tests\Unit; use PHPUnit\Framework\TestCase; use IrohaOrder; class IrohaOrderTest extends TestCase { /** @var IrohaOrder $irohaOrder */ private $ irohaOrder ; public function setUp () : void { $ this -> irohaOrder = new IrohaOrder () ; } /** * ひらがな文字列配列がいろは順に並ぶことの確認 * @return void */ public function testIrohaOrderSort () { $ expected = [ 'いしき' , 'いんかん' , 'ろうか' , 'はにわ' , 'におう' , 'におうもん' , 'ほとけ' , 'ほんき' , 'へいし' , 'へんろ' , 'とうき' , ] ; $ sorted = $ this -> shuffleAndSort ( fn ( &$ arr ) => $ this -> irohaOrder -> sort ( $ arr ) , $ expected ) ; $ this -> assertEquals ( $ expected , $ sorted ) ; } /** * 配列をシャッフルしてソート関数を適用して返す * @param callable $sortFunc ソート関数 * @param array $arr 対象配列 * @return array */ private function shuffleAndSort ( callable $ sortFunc , array $ arr ) : array { shuffle ( $ arr ) ; $ sortFunc ( $ arr ) ; return $ arr ; } } 実行してみましょう。 PHPUnit 9.5.2 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 00:00.004, Memory: 4.00 MB OK (1 test, 1 assertion) うまく動いているようです。 おわりに いかがでしたでしょうか? 本記事では、日本語文字列を値として持つ一次元の連番整数値添字の配列を対象として、 PHP で利用可能なソート方法をいくつか取り上げて具体例とともに紹介しました。ポイントをまとめると以下のようになります。 PHP で配列をソートする方法はいろいろある シンプルな sort 関数の結果は時に「イイ感じ」にならない PHP で日本語文字列を「イイ感じ」の順序で並べる方法は 3 通り その 3 通りの方法は以下の通りです。 sort 関数で SORT_LOCAL_STRING フラグを指定する 最も低コスト 実際の並び順は 処理系依存 (?) intl 拡張の Collator::sort または collator_sort を使う 拡張の有効化が必要で少し面倒 UCA に準拠した順序でのソートが可能 独自ルールを実装する 最も高コスト 並び順ルールを自由に決められる それでどの方法が一番「イイ感じ」なのかというと、結局要件次第にはなってしまうものの、個人的には 2 番の intl 拡張の Collator::sort または collator_sort を使う方法が以下の点で「イイ感じ」だと思います。 Unicode の技術標準として定められた仕様に準拠できる 外部のよくテストされたライブラリを使用できる 仕様の標準さと実装コストのバランスがよい 以上、 PHP で日本語の文字列配列をイイ感じにソートする話でした。少しでもみなさんの PHP ライフの参考にしていただけたら幸いです。 ※ 本記事に記載したサンプルコードは php 7.4.15 ( cli ) で動作を確認しています。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com 言語によっては他にもっといい型があるかもしれませんが、その辺はスルーでお願いします。 ↩ https://ja.wikipedia.org/wiki/ 辞書式順序 ↩ https://github.com/php/php-src/blob/94d96b3c979a60e47abe209506c3947a16abff40/Zend/zend_operators.c#L2684 ↩ https://github.com/php/php-src/blob/94d96b3c979a60e47abe209506c3947a16abff40/Zend/zend_operators.c#L1976 ↩ https://github.com/php/php-src/blob/94d96b3c979a60e47abe209506c3947a16abff40/ext/intl/collator/collator_sort.c#L51 ↩ https://unicode-org.github.io/icu/userguide/collation/api.html#compare ↩ https://github.com/php/php-src/blob/94d96b3c979a60e47abe209506c3947a16abff40/ext/intl/collator/collator_sort.c#L168 ↩
アバター
noriharu3 です。 業務で AWS の Lambda を使う機会があったので、簡単にまとめてみました。 Lambdaとは? Lambdaの嬉しいところ インフラの管理が不要 使った分だけの支払い Lambdaの場合 どういうことができるのか?苦手なのか? Lambdaが適さないケース Lambda関数開発 環境構築 ローカルPCでLambda関数を作成する ローカルPCでLambda関数を実行する AWSにデプロイする API Gateway+Lambdaにリクエストを送ってみる まとめ Lambdaとは? AWS のドキュメント *1 には、次のように記載されています。 AWS Lambda は、サーバーのプロビジョニングや管理の必要なしにコードを実行できるコンピューティングサービスです。 簡単に言うと、「サーバーを構築しなくとも、コードを実行できるよ」ということです。 図 *2 でいうと、こちらがわかりやすいと思います。 Lambdaの嬉しいところ では、Lambdaの嬉しいところ、メリットについて書いていきたいと思います。 インフラの管理が不要 インフラの管理といっても色々あると思います。 とりあえずセキュリティについて、次のことは AWS *3 が責任をもって対応すると言ってくれています。 AWS Lambda については、 AWS が基盤となるインフラスト ラク チャ、基盤サービス、 オペレーティングシステム 、アプリケーションプラットフォームを管理します。 なので、セキュリティについて開発者は主に次だけしっかりやればいいことになります。 IAMの権限 コードのセキュリティ( 脆弱性 ) 機密データの保管とアクセス管理 古いですが、セキュリティ以外では次のことを担保すると述べていますね。 *4 – キャパシティ – スケール – デプロイ – 耐障害性 – モニタリング – ロギング – セキュリティパッチの適用 なるほど。サーバーやOSの構築だけでなく、スケール管理、モニタリングなどもLambdaの標準サービスとして付帯している。 インフラのメンテナンス時間や定期的なダウンタイムもない。 たしかにこれは運用している人 からし たら嬉しい。 使った分だけの支払い 実際に、LambdaとEC2の月額費用を比較してみましょう。 毎日0時に Python コード(Lambdaで実行した場合の平均時間1分)を実行するものとします。 ※ 計算しやすくするため、1ヶ月は30日とする Lambdaの場合 ちなみに、Lambda では、指定してメモリ量に比例した CPU パワーとその他のリソースが割り当てられます。 メモリはLambdaで指定できる上限の10240MB(10GB)で計算します。 Lambda に512 MB のメモリを割り当て、30回実行し、毎回の実行時間が 60 秒間だった場合、 1 か月のコンピューティング料金 合計コンピューティング (秒) = 30 × 60 秒 = 1,800 秒 合計コンピューティング (GB-秒) = 1,800 × 10,240 MB ÷ 1024 = 18,000 GB-秒 合計コンピューティング – 無料利用枠 = 1 か月の請求コンピューティング GB-秒 18,000 GB-秒 - 400,000 GB-秒の無料利用枠 < 0 1 か月のリクエスト料金 30 件のリクエスト – 1,000,000 件の利用無料枠のリクエスト < 0 合計月額料金 合計料金 = コンピューティング料金 + リクエスト料金 = 0 USD + 0 USD = 0 USD/月 この段階で、EC2の負け確定ですねw 負け戦ですが、EC2の料金はこちら *5 を参照ください。 どういうことができるのか?苦手なのか? できることがたくさんあると思いますが、公式の資料 *6 には下記が記載されていました。 API サーバーの代わり アラート通知 etc Lambdaが適さないケース Lambdaにもいくつか制約があるので注意です。気になったものは下記になります。 大量のリソースが必要となる処理 Lambdaで指定できるメモリの上限 *7 は10240MB(10G) 900秒以上時間がかかる処理 Lambdaで指定できる タイムアウト の上限は900秒 *8 (15分)。900秒以上かかると、Lambdaはエラーを返します。 ペイロード *9 が6MB以上のリク エス トbodyまたはレスポンスbody リク エス トbodyまたはレスポンスbodyの ペイロード が6MB以上あった場合、Lambdaはエラーを返します。 OS、ネットワーク、ストレージレベルで制御したい 好みのOSを選定したい Lambdaは、 Amazon Linux 2 *10 がベースになっています。 Lambda関数開発 続いて、実際にLambdaで実行するコード(以下、Lambda関数)を作成し、動かしてみようと思います。 環境構築 ローカルPCでLambda関数を作成する ローカルPCでLambda関数を実行する AWS にデプロイする API Gateway +Lambdaにリク エス トを送ってみる 環境構築 公式 *11 を参考に、Docker と AWS SAM CLI をインストールします。 Windows , Mac , Linux で手順が異なるようなので、詳細は公式のドキュメントを見てください。以下、 Mac での手順です。 $ brew install docker $ brew tap aws/tap $ brew install aws-sam-cli また sam コマンドのリファレンスはこちら *12 。 ローカルPCでLambda関数を作成する sam init コマンドで、アプリケーションのプロジェクトを作成します。 このLambda関数は、 {"message":"hello world"} というレスポンスを返します。 $ sam init Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 What package type would you like to use? 1 - Zip (artifact is a zip uploaded to S3) 2 - Image (artifact is an image uploaded to an ECR image repository) Package type: 1 Which runtime would you like to use? 1 - nodejs12.x 2 - python3.8 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs10.x 8 - python3.7 9 - python3.6 10 - python2.7 11 - ruby2.5 12 - java8.al2 13 - java8 14 - dotnetcore2.1 Runtime: 1 Project name [sam-app]: Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates AWS quick start application templates: 1 - Hello World Example 2 - Step Functions Sample App (Stock Trader) 3 - Quick Start: From Scratch 4 - Quick Start: Scheduled Events 5 - Quick Start: S3 6 - Quick Start: SNS 7 - Quick Start: SQS 8 - Quick Start: Web Backend Template selection: 1 ----------------------- Generating application: ----------------------- Name: sam-app Runtime: nodejs12.x Dependency Manager: npm Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./sam-app/README.md sam build コマンドでビルドし、実行できる状態にします。 $ sam build Building codeuri: hello-world/ runtime: nodejs12.x metadata: {} functions: ['HelloWorldFunction'] Running NodejsNpmBuilder:NpmPack Running NodejsNpmBuilder:CopyNpmrc Running NodejsNpmBuilder:CopySource Running NodejsNpmBuilder:NpmInstall Running NodejsNpmBuilder:CleanUpNpmrc Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided ローカルPCでLambda関数を実行する sam local start-api コマンドで、 API Gateway を模したHTTPサーバーを作成し、擬似的な API Gateway を経由した動作確認を行うことができます。 $ sam local start-api Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2021-03-05 17:50:58 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit) Invoking app.lambdaHandler (nodejs12.x) 別ターミナルで http://127.0.0.1:3000/hello にアクセスします。 お、レスポンスが返ってきましたね。 $ curl http://127.0.0.1:3000/hello {"message":"hello world"} AWS にデプロイする sam deploy -g コマンドで、 AWS にデプロイします。 -g オプションを付けると、デプロイ設定を対話的にすすめることができます。 $ sam deploy -g Configuring SAM deploy ====================== Looking for samconfig.toml : Not found Setting default arguments for 'sam deploy' ========================================= Stack Name [sam-app]: AWS Region [us-east-1]: ap-northeast-1 #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: y #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: y HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to samconfig.toml [Y/n]: y Looking for resources needed for deployment: Found! (以下長いので、省略) API Gateway +Lambdaにリク エス トを送ってみる デプロイは完了しているので、後はリク エス トを送るだけです。 $ curl https://**********.execute-api.ap-northeast-1.amazonaws.com/Prod/hello {"message":"hello world"} まとめ EC2と比較して、Lambdaを使うと大幅なコスト削減ができるかもしれないので、魅力ですね。 また、インフラの管理を AWS に任せることで、運用業務にかかるコストの削減も見込めます。 ただ、Lambdaにはいくつか制約があり、適さないサービスや処理があるので、選定する際は注意です。 また SAM CLI を使えば、 AWS アカウント持っていなくても、さくっとローカルで Lambda 関数を動かすことができるので、興味のあるかたは是非やってみてください。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com *1 : https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html *2 : https://www.bit-drive.ne.jp/managed-cloud/column/images/column14/img_01.jpg *3 : https://d1.awsstatic.com/serverless-jp/Security%20Overview%20of%20AWS%20Lambda_JP.pdf *4 : https://d1.awsstatic.com/webinars/jp/pdf/services/20150701_AWS-BlackBelt-runcodeinthecloud.pdf *5 : https://aws.amazon.com/jp/ec2/pricing/on-demand/ *6 : https://d1.awsstatic.com/webinars/jp/pdf/services/20150701_AWS-BlackBelt-runcodeinthecloud.pdf *7 : https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-console.html *8 : https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-console.html *9 : https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html *10 : https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html *11 : https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html *12 : https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html
アバター
技術広報の syoneshin です。 今回は当社の開発組織メンバー達に 『おすすめのITエンジニア向けイベント』 と おすすめの理由を聞きました。 質問:皆さんの「おすすめのエンジニア向けイベント」 を教えてください。 【目次】 『PHPerKaigi』 『AWS Summit 』 『JJUG CCC』 『Web × PHP TechCafe』 『PostgreSQLアンカンファレンス』 『なるセミ』 『PHP Conference Japan』 『デブサミ・デブサミ関西・デブスト』 『Builders Box』 『プロダクトマネージャーカンファレンス』 『自動化大好きエンジニアLT会』 ITエンジニア向けイベント情報が検索できるサイト 6選 まとめ まず当社開発メンバーが選んだ「おすすめのITエンジニア向けイベント」は 『PHPerKaigi』 phperkaigi.jp 以下はおすすめ理由やポイント イベント自体はカジュアルな雰囲気で、参加者間での交流も盛んな印象です。 開催日3/27 に「 PHP でも アーキテクチャ テストしたい!」の発表テーマで登壇します! 開催日3/28に LT登壇します!是非、ご参加下さい! 次いで、当社開発メンバーが選んだ「おすすめのITエンジニア向けイベント」は 『 AWS Summit 』 partners.awscloud.com 以下はおすすめ理由やポイント AWS の最新情報、導入事例などが知れて勉強になりおすすめです。 基礎的な セミ ナー時間もあるので初心者にもおすすめです! 次いで、当社開発メンバーが選んだ「おすすめのITエンジニア向けイベント」は 『 JJUG CCC』 www.java-users.jp 以下はおすすめ理由やポイント 直近の Java の動向や流行りを知れるため、おすすめです。 Java 関連の技術や事例に関するセッションが豊富。 JJUG CCC 2020 Fallには当社メンバー2名が登壇しました! 次いで「おすすめのITエンジニア向けイベント」は 当社主催のイベント 『Web × PHP TechCafe』 rakus.connpass.com 以下はおすすめ理由やポイント レベルは高すぎず、会話ベースの進行のため、オンライン参加がしやすく、ラジオのように聞きながら情報をピックアップできる点がおススメです。 ゆるく雑談形式で最新のトピックを知ることができるので良いです。 外部から一緒に語り合ってくれる方も、聞き専門の方も増えているイベントです!是非、ご参加下さい! 次いで「おすすめのITエンジニア向けイベント」は 『 PostgreSQL アンカンファレンス』 pgunconf.connpass.com 以下はおすすめ理由やポイント PostgreSQL の新機能や内部的な仕組み、お試し系の検証結果などを知ることができて勉強になります。 毎年メジャーバージョンアップの時期に実施されるので、情報収集がてら参加するのは良いと思います。 次いで「おすすめのITエンジニア向けイベント」は 『なる セミ 』 nrs-seminar.connpass.com 以下は気になった理由やポイント 設計系ではよくお世話になっているイベントの一つです。 毎回テーマは違いますが、参考になるイベントが多いです! 次いで「おすすめのITエンジニア向けイベント」は 『 PHP Conference Japan』 phpcon.connpass.com 以下は気になった理由やポイント 2020開催のイベントには当社メンバー2名もLT登壇! 毎年、人も活気もすごいイベントです! 次いで「おすすめのITエンジニア向けイベント」は 『 デブサミ ・ デブサミ 関西・デブスト』 event.shoeisha.jp 以下はおすすめの理由やポイント デブサミ 関西は関西では数少ない大きなイベント。著名人も多く登壇されており、直接お話を伺え、刺激を受けやすいと思います。 デブストは同年代のエンジニアがやっていることを知れる、いい機会でした。 今期の デブサミ 関西とデブストにはそれぞれ当社メンバーも登壇しました! 次いで「おすすめのITエンジニア向けイベント」は 『Builders Box』 buildersbox-online.com 以下はおすすめの理由やポイント Sansan社が主催しており、著名な方のセッションが聞ける貴重なイベント。 国内の Saas 企業とコラボしたイベントなどもあり、内容はどれも面白くおすすめ。 次いで「おすすめのITエンジニア向けイベント」は 『プロダクトマネージャーカンファレンス』 2020.pmconf.jp 以下はおすすめの理由やポイント PdM意外の方でも、どうプロダクトへ貢献すべきかを理解する良い機会になります。 前回イベントで、PdMの最新情報をキャッチアップできました。 次いで「おすすめのITエンジニア向けイベント」は 当社主催のイベント 『自動化大好きエンジニアLT会』 rakus.connpass.com 以下は気になった理由やポイント テストやインフラ構成管理など、様々な分野における自動化が取り上げられていました。 LT登壇者が多く、豊富な自動化の事例が学べるイベントです! その他、以下のITエンジニア向けイベントを、当社エンジニア達が推薦しておりました。 『Spring Fest』 jsug.doorkeeper.jp 『 VSCode Meetup』 vscode.connpass.com 『Vue Fes Japan』 vuejs-jp.org 『CloudNative Days』 cloudnativedays.jp 『 スクラム フェス大阪』 www.scrumosaka.org 『 ソフトウェアテスト 自動化カンファレンス』 testautomationresearch.connpass.com 『 CSS Nite 』 cssnite.jp などなど 全50件近くの「おすすめのITエンジニア向けイベント」が 当社内で共有されました。 ITエンジニア向けイベント情報が検索できるサイト 6選 ここでは、ITエンジニア向けイベントの情報が探せるサイトをご紹介します。 TECHPLAY techplay.jp connpass connpass.com DoorKeeper www.doorkeeper.jp Street academy www.street-academy.com Peatix https://peatix.com/ peatix.com OpenCU www.opencu.com まとめ 以上、いかがだったでしょうか。 昨年からITエンジニア向けイベントもオンライン開催が主流になり 皆さんも、イベントを「はしご」する機会が増えたのではないかと思います。 当社も情報発信と交流を通じた見識を広める機会として、週1~2回ほど多様なテーマでイベントを主催しております。 もし、当社イベントに少しでもご興味をお持ちいただけましたら 、是非ご参加下さい。 主催イベント一覧 rakus.connpass.com 本記事でご紹介した おすすめのITエンジニア向けイベント が皆さまの情報探索の一助となれば幸いです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
アバター
はじめに 技術広報のyayawowoです。 いつも ラク スのエンジニアブログのご購読、そしてエンジニアイベントへのご参加、ありがとうございます。 今回は、『 開発戦略・マネジメント・設計 』をテーマに実施した、2021年最初の ラク スMeetup 2Daysの様子をお届けします! 本記事では、2/17(水)にお届けした 『PdM・インフラ戦略』 について紹介させていただきます。 ※2/16(火)【Meetup】PM・ リファクタリング 戦略は こちら はじめに イベントテーマ概要 発表の紹介 ロバストネス分析を用いた設計と工数見積もり JavaCCとSpring Expression Languageを用いたユーザ定義項目 開発を止めるという選択 インフラ業務のモデリングについて考えてみた おわりに イベントテーマ概要 Day2では人気の楽楽シリーズを担当しているエンジニア陣を中心に、大規模な SaaS 開発におけるエピソードや、多数のサービスを展開中の ラク スが構想しているインフラ戦略を紹介しました。 今回は以下サービスに携わるエンジニア3名と、 ラク ス全体のインフラ基盤を支えるエンジニア1名の計4名が登壇させていただきました。 楽楽勤怠 楽楽精算 楽楽明細 発表の紹介 それではここから各発表内容と資料を共有させていただきます! ロバストネス分析 を用いた設計と 工数 見積もり まずは楽楽精算のサブリーダである坂田が、設計工程で取り入れている ロバストネス分析 を用いた設計と 工数 見積もりについて、紹介させていただきました。 目新しい手法ではありませんが、システム構成を三つの要素で抽象化し、それらの関係性を非常に簡単な図で表現することができる優れた手法です。 またこれを行うことにより改修範囲の要 素数 が 定量 的に洗い出せることから、楽楽精算開発チームでは、これらの分析結果を応用した 下流 工程の 工数 見積もりも行っています。 今回はこれらの取り組みとその効果に関して紹介しました。 speakerdeck.com JavaCC とSpring Expression Languageを用いたユーザ定義項目 次に、楽楽勤怠チームの山口の発表です。 楽楽勤怠にてカスタム項目というユーザが楽楽勤怠に標準で持っているプロパティの値を利用して、独自に計算式を定義できる機能を開発しています。 この機能を開発する際は計算式をどうやって評価し、計算式からどうやって計算結果を算出するのかが肝となりました。 この肝となった部分についてフロントエンドとバックエンドの役割分担や実現方法を紹介しました。 speakerdeck.com 開発を止めるという選択 ラク スでは、開発チームに プロダクトマネジメント の人員を各商材配置し、意思決定から価値の提供までのスピードをあげる取り組みを行っています。 しかし、一言に「 プロダクトマネジメント 」といってもその取り組みに唯一解はなく広範囲に渡るため、試行錯誤しながら日々取り組んでいるのもまた現実です。 プロダクトマネジメント の大きな目的である、「求めている顧客に早く価値を届ける」という目的と、自分の過去の経験が邪魔をしたことで、失敗した 経験談 を超上流(サービス仕様)決定に携わっている松浦が紹介しました。 speakerdeck.com インフラ業務の モデリング について考えてみた 最後は、 ラク ス全体のインフラ基盤の責任者である竹田より、インフラ業務の モデリング についてです。 大規模な SaaS 開発の裏側では Saas サービスのサービス・機能特徴に合わせた独自技術がどんどん導入されています。 今回は アプリ開発 職よりはるかに少ないメンバーで組織運営を行う組織の中で、 増え続けるサービス、技術の深い理解 サービス・事業要件に応えるインフラ基盤 「安定・安心・安全(3A)」 を効率よく実現させ、コスト部門として設備管理にかかる費用の低減を続けるにはこれから何をしていくべきかを考察し、ご紹介しました。 speakerdeck.com おわりに 2021年最初の ラク スMeetupはどうでしたか? 今回の4名の発表が、皆さまにとって新しい気づきや成長につながる機会となっていますと幸いです! 今後も ラク スMeetupでは日々のエンジニアの取り組みを発信してまいりますので、次回もぜひご参加いただけますと幸いです。 また、前日に開催した 【Meetup】PM・リファクタリング戦略 も別記事にまとめておりますので、是非ご確認ください! そして直近ですが、 PdM がテーマのイベントを3/18(木)に開催予定です! プロダクト設計、開発、デザインの PdM にご興味のある方は、ぜひconnpassをご確認いただければと思います。 ご参加お待ちしております! rakus.connpass.com
アバター
はじめに 技術広報のyayawowoです。 いつも ラク スのエンジニアブログのご購読、そしてエンジニアイベントへのご参加、ありがとうございます。 今回は、『 開発戦略・マネジメント・設計 』をテーマに実施した、2021年最初の ラク スMeetup 2Daysの様子をお届けします! 本記事では、2/16(火)にお届けした 『PM・ リファクタリング 戦略』 について紹介させていただきます。 ※2/17(水)【Meetup】PdM・インフラ戦略は こちら はじめに イベントテーマ概要 発表の紹介 14年目のサービスと今後も歩むためのリファクタリング戦略 仮想基盤サーバのリプレイスに伴うインフラ設計 入社4ヶ月の新入りPdMの取り組み 変化の時代に活かす「みんなのプロジェクトマネジメント」 おわりに イベントテーマ概要 Day1では開発、インフラ、 プロダクトマネジメント 、プロジェクトマネジメントの4つの立場のエンジニアから、今年度 ラク スがチャレンジしてきました取り組みを紹介しました。 今回は以下サービスに携わるエンジニア4名が登壇させていただきました。 配配メール クルメル 楽楽販売 発表の紹介 それではここから各発表内容と資料を共有させていただきます! 14年目のサービスと今後も歩むための リファクタリング 戦略 現在14年目になる配配メールでも長年続くサービスならではの過去からの負債がありました。 また、過去の ソースコード を参考にすることも少なくないので負債は日々増えるばかり… いつか リファクタリング したいと思いながらも肥大化してしまった負債に手が出せないジレンマがありました。 しかし、このままではダメだと一念発起し問題に向き合う事を決め、リリースサイクルに影響を与えず円滑に リファクタリング を進める為に実施した工夫について、配配メール開発担当の西原が紹介しました。 speakerdeck.com 仮想基盤サーバのリプレイスに伴うインフラ設計 次に、「配配メール」「クルメル」のインフラ開発担当である小西の発表です。 ひと言で「設計」といっても様々な事を検討し、形にする必要があります。 基本設計・詳細設計・移行設計・運用設計などの各フェーズでの設計を行い、問題なく実行できるようにする必要があります。 今回は、仮想基盤サーバのリプレイスを通じて実際に行った設計の一部をご紹介し、今後のサービス運用と継続的な改善に繋がるインフラチームとしての設計方針を発表しました。 入社4ヶ月の新入りPdMの取り組み 楽楽販売(旧名:働くDB)はローンチから10年以上、たくさんのユーザ様に利用されているシステムです。 これまで営業 / CS / 企画 / 開発とお互いの領域を研ぎましていたフェーズから、今後のさらなる成長を目指し、各部門を超え組織横断でプロダクト強化を行うための活動について、入社直後の永橋がPdMとしてどう取り組んだかをご紹介しました。 
入社4ヶ月 新入りPdMの取り組み from Tsuyoshi Nagahashi www.slideshare.net 変化の時代に活かす「みんなのプロジェクトマネジメント」 最後に、配配メール開発チームのエンジニア リングマ ネージャである大塚の発表です。 「プロジェクトマネジメント」はプロダクト開発の役割であると同時にエンジニアのスキルセットでもあります。 プロジェクトをゴールに導くこのスキルセットは不確実な変化の時代にエンジニアが取るべき行動のヒントを与えてくれます。 今回はこれまでの SaaS プロダクト開発の現場で実践してきた事例を「プロジェクトマネジメント」の視点でご紹介し、チームのリーダーやマネージャーを目指す人はもちろん、変化の時代を生き抜く全てのエンジニアにおすすめしたい「みんなのプロジェクトマネジメント」についてお話ししました。 speakerdeck.com おわりに 今年度 ラク スがチャレンジしてきました取り組みは、いかがでしたでしょうか? ラク スの取り組みが、皆さまにとって新しい気づきや成長につながる機会となっていますと幸いです! 今後も ラク スMeetupでは日々のエンジニアの取り組みを発信してまいりますので、次回もぜひご参加いただけますと幸いです。 また、翌日に開催した 【Meetup】PdM・インフラ戦略 も別記事にまとめておりますので、是非ご確認ください! そして直近ですが、 PdM がテーマのイベントを3/18(木)に開催予定です! プロダクト設計、開発、デザインの PdM にご興味のある方は、ぜひconnpassをご確認いただければと思います。 ご参加お待ちしております! rakus.connpass.com
アバター
株式会社 ラクス のrakusMoritaです。 2021年2月22日にオンラインで開催された Laravel Meetup Tokyo Vol.13 に参加したので、内容のポイントをまとめてみました。 どの話も興味深く、そして実践しやすい内容だったので共有させていただきます。 laravel-meetup-tokyo.connpass.com Laravelと継続的デプロイ スピードと質を求められている状況下での基本構成 注意点等 継続的なデリバリのために組織として取り組むこと DDD入門とLaravel DDDの理解でよくある間違い 結局DDDって何? DDDのメリット どうやって実践するの? よく聞く軽量DDDとはどう違うの? 実装するには Laravel8で使えるJavaScriptパッケージ「Inertia.js」が便利 TDD視点から見るRequestクラスの依存性 まとめ Laravelと継続的デプロイ スピードと質を求められている状況下での基本構成 クラウド ・コスト削減 ・スケーラビリティが容易 コンテナ ・作業時間短縮 ・DevOpsとの相性良い 注意点等 ログの構造化は必須 AWS のCloudWatch等で、ログは構造化( JSON 化)しておくとのこと。 視認性が格段にアップして開発や デバッグ ・調査の時短になる。 vendor内をコンテナ化していると、ビルドに時間がかかったりする場合がある 安定性は向上するので、どちらを取るかは組織内で要相談。 デプロイを自動化していたとしても、突然デプロイが失敗することもある デプロイが短時間でできると思っていても、デプロイ当日にパニックになることもあるので、余裕を持ったスケジュール組むこと。 そして、定期的なデプロイをして、突然の「動かない」を極力なくす。 継続的なデリバリのために組織として取り組むこと 運用できる人を増やす 一部の人しか触れないではなく、なるべく多くの人が携わる。 関係者を巻き込みながら、少しずつやれることを増やしていく 体制面でも継続可能な状況を作ることが理想。 ログの構造化は確かに必須ですね。個人的に即効性があって良いと思いました。 DDD入門とLaravel 最近よく耳にする「 ドメイン 駆動設計」、通称「DDD(domain-driven design)」ですが、具体的にわかりにくいと思いませんか? 今回具体的にどういうことなのかを掘り下げた内容が聞けて勉強になりました。 DDDの理解でよくある間違い 「 アーキテクチャ で、層に分かれて実装すれば良い」  これ、間違いです 「 DDDでデータベース肥大化対策どうやるの?」  実は関係ありません 「 MVC ではDDDはできないのでは?」  これも全く関係ありません 「DDDで実装したけど、全然楽になりません」  長期的に見ると楽になるはず・・・ 結局DDDって何? 開発スタイルのひとつ 「実現したいこと」 や 「解決したい問題」 、それを 「 ドメイン 」 という。 その「やりたいこと」を起点として開発していくのがDDD。 実装言語や フレームワーク 、ストレージや アーキテクチャ などは含まれない 一般的な非機能要件は含まれない。 機能要件が含まれる。 DDDは、どういう アーキテクチャ で、どういう言語、 フレームワーク 、データベース・・・などの技術的な要素とは一切関係なく、 何を作るのか? それを考えて実現していくことだったのですね。 一般的な「 ドメイン 」と違うのが理解し難くしている原因の一つかもしれません。 DDDのメリット 何をしたいかをまず考えるため、先を見越した設計ができる 開発チームで共通の認識が生まれ、認識のずれからくるやり直しや確認が減る 突然の仕様変更に困ったことはありませんか?システムや機能の根幹を揺るがしかねない事態になる可能性だって往々にしてあります。 そういう事態を未然に防ぎやすいのがDDDの特徴です。 「何を実現したいか」が関係者で共有ができているため、設計段階から将来の変更を見越したものが作りやすいというわけです。 膨大な改修コストや、目的からずれた不要な機能開発とはこれでおさらばです。 どうやって実践するの? カスタマーサービス チームに加わったり企画会議に参加して、開発以外の概念理解や知識をつける ユーザー層や実現したいこと、チーム構成は変わっていくため、継続的にコミュニケーションや分析を行う チーム全員で ドメイン (会社が提供したい価値)モデルを導き出し、全員で同じ認識と同じ言語で共通理解をする 言葉の認識を合わせていくことが大切とのこと。 例えば「ユーザー」という言葉でも、エンジニアは「DBのユーザーテーブルのことだな」と思っていても、実際は「会員」を指しているのかもしれないし、「有料会員」のことを指すのかもしれない。 まずはその共通認識を行うことが第一歩。 文脈でもその意味は変わってくるので、そのバックグラウンドに基づく知識が共有できていれば、迷いやすれ違いが少なくなるとのこと。 よく聞く軽量DDDとはどう違うの? 軽量DDDとは、DDDで得られた知識を反映しやすい実装パターン、構造だけを採用したもので、DDDとは別物です。 こちらの意味だけが先行して、DDDの本質を見失う人も多そうな印象ですね。 実装するには Laravel等を用いて開発するときは、DDDの知識を落とし込みやすいパターンを無理に最初から実現しなくても良い 処理の共 通化 を行わず、まずは分析と知識に基づいて、クラスにする 同じような箇所はコピペでも良い。あとから共 通化 させるほうが収束しやすいし、 リファクタリング もしやすい。 まずは本当に共 通化 すべきもののみ共 通化 すること。 余談ですが、「設計をちゃんと考えるようになってからコピペが増えた」なんて話もちらほら聞きます。 DDDは詳しく知らなかったので、聞きながら実践イメージもわき、非常に興味深かったです。 Laravel8で使える JavaScript パッケージ「Inertia.js」が便利 Laravel8で使える、ビューとのデータのやり取りが容易になる JavaScript パッケージ「Inertia.js」でお手軽にモダンなSPAが構築できるというお話もありました。 軽く紹介があったので、調べて簡単にまとめてみました。 Inertia.jsとはLaravel8の標準認証ライブラリ「Jetstream」で使われている、フロントエンドとバックエンドのデータ連携技術 Vueファイルとのデータ連携がテンプレートエンジンと同じように扱えるため、SPA化も容易 Vue.jsなどでSPAを実現するには、 Ajax の制御やVue側でルーティングを行う必要があり、開発コストがかさみやすいですが、それが不要になります。 Laravelのルーティングの記述で、 Ajax 通信・ルーティングも一緒にコン トロール できるので楽にSPAを実現できるのが魅力的ですね。 TDD視点から見るRequestクラスの依存性 Requestクラスにはユーザー認証や情報の保持、バリデーションなど、色々な情報がありすぎて、 ユニットテスト が書きづらい バリデーションと認証が共存しているなど、テストクラスの文脈がぶれることがあるので、何をしているテストかわからない。何をテストすればいいかわかりにくいという内容で、そこから転じて 「なんでもやってるクラスはテストがつらい。」 この心の叫び、エンジニアあるあるではないでしょうか? 設計や実装時にはそう思われないクラスづくりが大切だと改めて考えさせられました。 まとめ 色々気づきや発見のある有意義な時間でした。 個人的にはDDDの概念が理解できたことが大きな収穫でした。 ドメイン 駆動はいわば「本質を見失わない開発」ということだったのですね。 また参加してみたいと思います。
アバター
はじめに こんにちは。itoken1013です! 度々お送りしています超入門シリーズ、今回ご紹介するのは Bootstrapの使い方 です! Bootstrapを利用することで、簡単にページデザインを仕上げることができます。 HTML/ CSS は分かるけどBootstrapには触れたことのない方、今まで体系的には理解せずにBootstrapを利用していた方にオススメの内容です。 それでは解説していきたいと思います! はじめに Bootstrapとは まず、フロントエンドとは? では、Bootstrapとは? Bootstrapを使うことによるメリット レスポンシブWebデザインに対応している 導入のしやすさ、汎用性 コンポーネントが多数用意されている Bootstrapのセットアップ グリッドシステム よく使うコンポーネント フォーム ボタン テーブル 画像 おわりに   Bootstrapとは まず、フロントエンドとは? Bootstrapはフロントエンドの開発で使うための フレームワーク ですが、まずはフロントエンドが何かを説明します。 (厳密な定義からは逸脱しますが)今回のご説明の範囲では、フロントエンドとは HTML、 CSS 、 JavaScript を使って開発するブラウザ上のUI(ユーザ・インタフェース)部分 と解釈いただければと思います。 ユーザが見ることができ、操作を行うことでシステムに命令を与えるための領域、とも言うことができます。 フロントエンドの開発で必要な技術要素は多岐に渡りますが、より詳細な情報については当社のエンジニアが書いた以下の記事を読むとご理解いただけるかと思います。 tech-blog.rakus.co.jp では、Bootstrapとは? それではBootstrapのご説明ですが、Bootstrapは フロントエンドの開発で利用する CSS フレームワーク に位置付けられます。 CSS は フレームワーク を利用せずとも自前のコーディングで装飾を施すことができますが、Bootstrapをはじめとした フレームワーク を導入することで効率的に CSS を仕上げることが可能です。 特にBootstrapは後述の通り、HTMLの各要素へ規定のクラス名を指定するだけでデザインを実現できるなど、 フレームワーク を利用しない場合と比べて圧倒的に早くデザインを仕上げることができてしまいます。 また フレームワーク であるため、一定のルールをもってコーディングできる点はチーム開発において有効であると考えることができます。 Bootstrapはもともと Twitter 社が開発し、2021年2月時点ではバージョン5.0.0が最新版となっています。 いまや世に CSS フレームワーク は数多く存在しますが、Bootstrapはその先駆けとなる フレームワーク でした。 getbootstrap.jp Bootstrapを使うことによるメリット 上記でBootstrapを使うメリットに触れましたが、より詳細に特徴3点を説明していきます。 レスポンシブWebデザインに対応している 多くの方がBootstrapを使う最大のメリットとして挙げるのが、レスポンシブWebデザインに対応している点です。 レスポンシブWebデザインとはPC、 タブレット 、 スマホ 等のデ バイス で異なる画面サイズにおいて、最適なレイアウトデザインへ切り替える手法です。 Bootstrapでは関単にレスポンシブWebデザインに対応したスタイル指定を行うことができ、デ バイス ごとに異なるHTMLを用意する手間を省くことができます。 導入のしやすさ、汎用性 Bootstrapの導入手順は後述しますが、とても簡単です。 また構築するプロダクトの規模や種類を問わずに利用できる汎用性があります。 このように手軽にデザインを反映できる点は大きなメリットと言えるでしょう。 コンポーネント が多数用意されている Bootstrapではデザイン性に優れた各 コンポーネント (ボタンやフォームなど)が提供されています。 さらにこれらの コンポーネント を利用するための手順は、各HTML要素に指定されたclassを追記するだけで実現できます。 非デザイナーのエンジニアでも、簡単な手順で一定レベルのデザインを実現できます。 以降では、定番となる コンポーネント を紹介したいと思います。 Bootstrapのセットアップ それではここから簡単な使い方を紹介していきます。 まずはBootstrapを使える状態とするため、セットアップを行いましょう。 ※今回は CDN (コンテンツ・デリバリー・ネットワーク)を使わず、Bootstrapをダウンロードして利用できる状態にしていきます。 まずは公式ページにアクセスし、Bootstrapをダウンロードしてください。 getbootstrap.jp 上記ページの「Compiled CSS and JS」配下のダウンロードボタンをクリックすると、Bootstrapのコード一式のダウンロードが開始されます。 ダウンロード完了後、Zipファイルを解凍すると css フォルダとjsフォルダが表示されるはずです。 今回はこの2フォルダを任意のフォルダ配下にコピーし、同列にindex.htmlを作成します。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title > Rakus </ title > < link href = "css/bootstrap.min.css" rel = "stylesheet" > </ head > < body > < h1 > Hello, Rakus! </ h1 > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > </ html > 上記index.htmlではダウンロードしてきた「bootstrap.min. css 」および「bootstrap.min.js」を読み込んでいる点をご確認ください。 またBootstrapでは jQuery を読み込む必要があり、「bootstrap.min.js」よりも前に「 jquery .min.js」を指定しています。 ファイルの作成が完了しましたら、ブラウザで確認してみましょう。 開発者コンソールも開き( Windows であればF12、 Mac であればCommand +Option + I )、特にエラーが表示されていなければセットアップ完了です。 グリッドシステム では実際にコーディングをしながら、Bootstrapの使い方を説明してまいります。 まずはBootstrapを使う上で重要な グリッドシステム からです。 グリッドシステムは先述したレスポンシブWebデザインを実現するために必要な仕組みであり、横幅を 12分割 してHTML要素を配置していきます。 HTML要素にはグリッド(格子)の数を指定していくことで、横幅を指定することができます。 コーディングを行うにあたっては、以下の4つの基本ルールに従う必要があります。 ① divタグを配置し、class=”container” または ”container-fluid”を指定する ② 1の中にdivタグを配置し、class=”row”を指定する ③ 2の中に要素を配置し(複数) 、class=”col-{prefix}-{columns}”を指定する ④ 3で配置した複数要素の{columns}を合計12とする 上記のルールに従ってindex.htmlを修正してみましょう。 今回は1行を4分割して要素を表示してみます。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title > Rakus </ title > < link href = "css/bootstrap.min.css" rel = "stylesheet" > </ head > < body > < header > ヘッダーです </ header > < div class = "container-fluid" > < div class = "row" > < div class = "col-md-2 border border-dark" > 2 </ div > < div class = "col-md-2 border border-dark" > 2 </ div > < div class = "col-md-2 border border-dark" > 2 </ div > < div class = "col-md-6 border border-dark" > 6 </ div > </ div > </ div > < footer > フッターです </ footer > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > </ html > 上記をブラウザで表示すると、指定したグリッドの個数分、行が分割されていることが確認できます。 ちなみにルール③における class=”col-{prefix}-{columns}” の prefix ですが、上記では「md」としています。 ここでコードを少し書き換え、「col-md-〇」の隣に「col- sm -〇」を追記してみます。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title > Rakus </ title > < link href = "css/bootstrap.min.css" rel = "stylesheet" > </ head > < body > < header > ヘッダーです </ header > < div class = "container-fluid" > < div class = "row" > < div class = "col-md-2 col-sm-6 border border-dark" > 2 </ div > < div class = "col-md-2 col-sm-2 border border-dark" > 2 </ div > < div class = "col-md-2 col-sm-2 border border-dark" > 2 </ div > < div class = "col-md-6 col-sm-2 border border-dark" > 6 </ div > </ div > </ div > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > </ html > 次に画面を表示し、画面サイズを小さくしてみましょう。 分割された4要素の比率が変わるタイミングがあるはずです。 このようにBootstrapでは 画面サイズごとにprefixを指定でき 、レスポンシブWebデザインを実現しています。 この画面サイズの境界を ブレークポイント と呼び、最新のv5.0.0では ブレークポイント を区切りに以下6種類のprefixを利用できます。 デ バイス 画面サイズ prefix スマホ 576px以下 xs (eXtra Small) タブレット 576px以上 ~ 768px未満 sm (SMall) PC(小) 768px以上 ~ 992px未満 md (MidDle) PC(中) 992px以上 ~ 1200px未満 lg (LarGe) PC(大) 1200px以上 ~ 1400px未満 xl (eXtra Large) PC(特大) 1400px以上 xxl (eXtra eXtra Large) 以上がグリッドシステムの基本となります。 Bootstrapを利用していく上でこのグリッドシステムへの理解は重要となりますので、ぜひ以下の公式ページもご確認ください。 getbootstrap.jp よく使う コンポーネント 上記「Bootstrapを使うことによるメリット」欄にて、Bootstrapには高いデザイン性の コンポーネント が用意されている点に触れました。 Bootstrapでは各HTML要素のclassに決められたクラス名を指定することで、 CSS を実装せずに簡単にデザインを適用することができます。 以降ではこれらの コンポーネント でもよく利用するであろうものについて、サンプルを紹介していきます。 フォーム まずはフォームです。とても簡単です。 formタグ内に以下2つのclassを指定していくことで、簡単にデザイン済のフォームを作成できます。 .form-group:formタグ内に用意するdivタグで指定し、フォーム要素のグルーピングを行います。 .form-control:inputタグなどの各フォーム要素に指定します。 サンプル画面、コードはこちらです。(コードはBody要素のみを記述します) < body > < div class = "container-fluid" > < form > < div class = "form-group" > < label > Email </ label > < input type = "email" class = "form-control" placeholder = "Email" > </ div > < div class = "form-group" > < label > Password </ label > < input type = "password" class = "form-control" placeholder = "Password" > </ div > < button type = "submit" class = "btn btn-primary" > Button </ button > </ form > </ div > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > ボタン 次はボタンです。ボタンは用途に応じ、"btn btn-XXXX"という形式でクラス名を指定します。 公式ページで紹介されているクラスを載せたコードを示します。 getbootstrap.jp < body > < div class = "container-fluid" > < form > < div class = "form-group" > < label > Email </ label > < input type = "email" class = "form-control" placeholder = "Email" > </ div > < div class = "form-group" > < label > Password </ label > < input type = "password" class = "form-control" placeholder = "Password" > </ div > < button type = "button" class = "btn btn-primary" > Primary </ button > < button type = "button" class = "btn btn-secondary" > Secondary </ button > < button type = "button" class = "btn btn-success" > Success </ button > < button type = "button" class = "btn btn-danger" > Danger </ button > < button type = "button" class = "btn btn-warning" > Warning </ button > < button type = "button" class = "btn btn-info" > Info </ button > < button type = "button" class = "btn btn-light" > Light </ button > < button type = "button" class = "btn btn-dark" > Dark </ button > < button type = "button" class = "btn btn-link" > Link </ button > </ form > </ div > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > ボタンについては上記用途別のクラス指定の他、背景色の透過やサイズ変更を実施できます。 詳細は上記公式ページのリファレンスをご確認ください。 テーブル テーブルへの指定方法もとても簡単です。 対象となるtableタグのclassに対して"table"を指定の上、デザインに応じて追加のクラス名を指定するだけです。 よく使われるクラス名を示します。 .table-bordered:テーブルセルに枠線をつける .table-striped:テーブル内の1行ずつ交互に色を変える .table-hover:カーソルの乗っている行をハイライト表示する .table-responsive:レスポンシブテーブルとし、水平方向にスクロールさせる 以下では個人的によく使う".table-striped"を指定したテーブルをサンプルに示します。 < body > < table class = "table table-striped" > < thead > < tr > < th scope = "col" > # </ th > < th scope = "col" > イベント名 </ th > < th scope = "col" > 開催日 </ th > </ tr > </ thead > < tbody > < tr > < th scope = "row" > 1 </ th > < td > UI/UXデザイナーLT会 - vol.2 </ td > < td > 3/3 </ td > </ tr > < tr > < th scope = "row" > 2 </ th > < td > フロントエンドTechCafe </ td > < td > 3/10 </ td > </ tr > < tr > < th scope = "row" > 3 </ th > < td > エディタ好きは語りたいLT会 </ td > < td > 3/17 </ td > </ tr > < tr > < th scope = "row" > 4 </ th > < td > PdM Tips LT会 </ td > < td > 3/18 </ td > </ tr > < tr > < th scope = "row" > 5 </ th > < td > おすすめの技術書LT会 </ td > < td > 3/24 </ td > </ tr > </ tbody > </ table > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > 画像 最後は画像です。画像の加工ももちろん CSS を編集して対応できますが、Bootstrapで指定のクラス名を付与することでとても簡単に実現できます。 対象となるimgタグに対して指定するクラス名のうち、よく使われるものを示します。 .img-responsive:画面サイズに応じてレスポンシブに画像サイズを変更する .img-thumbnail:画像の枠線に丸みを持たせ、サムネイル状に表示する .img-circle:画像をサークル形に加工して表示する .rounded float-left(right):画像の表示位置を左(右)に指定する。 サンプルは"img-thumbnail"です。サムネイルの枠線が判別しやすいよう、背景色も変更しております。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title > Rakus </ title > < link href = "css/bootstrap.min.css" rel = "stylesheet" > </ head > < body > < div class = "text-center" style = "background-color: black;" > < img src = "image/img-sample.png" class = "img-thumbnail rounded" > </ div > < script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></ script > < script src = "js/bootstrap.min.js" ></ script > </ body > </ html > おわりに 今回は超入門的なBootstrapの使い方を紹介しましたが、ご理解いただけましたでしょうか? Bootstrapは歴史の長い CSS フレームワーク ながら、今なお進化しております。 基本を理解すれば今後のバージョンアップにも追随していけるかと思いますので、ぜひこのブログをスタートラインとし、Bootstrapの便利機能の数々に触れていっていただけますと幸いです! ではでは!   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 https://rakus.hubspotpagebuilder.com/visit_engineer/ rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
株式会社ラクス でエンジニア リングマ ネージャをしている 間澤 です。 私達の組織では、ここ数年で管理職間やリーダー間でマネジメントやリーダーシップについてちょっとした勉強会をしています。 エンジニアは日頃そのテーマの学習の機会に触れるきっかけが少ないのかな、と感じていたのが始めたきっかけです。 開発をしていると技術のインプットだけでも大変ですよね。 そこで、私達が勉強会で本を読み合わせたうえで、議論や意見交換してみて良かったなと思う10冊をご紹介します。 先に言ってしまいますと殆どが技術本ではないです。エンジニアではない方にも有益だと思います。 単に本を読んだだけではあまり効果がないので、それを実際にやってみると人生がより良く変わっていきます。 仕事の中で、 「それ、本に書いてあったことができてるね。」 を言い合えることがポイントです。 1. 組織論:失敗の本質 2. リーダーシップ:マキアヴェッリ語録 3.問題解決:問題解決 4.論理思考力:ロジカル・プレゼンテーション 5.行動様式(自己啓発):人を動かす 6.心理学:嫌われる勇気 7.組織経営ポリシー:プロフェッショナルマネージャー 8.マネジメント:マネジメント(ドラッカー) 9.経営組織論:組織行動のマネジメント 10.プロダクトマネジメント:Inspired 1. 組織論:失敗の本質 一昔も二昔も前から著名なビジネス本です。 主に第二章を中心に旧日本型組織の陥りやすい弱点を理解します。 「目的が曖昧」であることが失敗を招きます。 「よかれ」や「雰囲気」で進めていくと簡単にあまり良くない状況に陥ります。 論理に基づいてタスクを進めているかが重要になります。 キーワード 戦略上の失敗:曖昧さ あいまいな戦略目的、短期思考、主観的で 帰納 的な戦略策定、アンバランスな技術体系 組織上の失敗:柔軟さの欠如 人的ネットワーク偏重、属人的な組織、学習の軽視、プロセスや動機を重視した評価 2. リーダーシップ: マキアヴェッリ 語録 勘違いされている方もいるかもしれませんが、強権な暴君の話ではありません。 過去の君主たちの実績が書かれた、必要な統率力の考え方とは何か、が書かれています。 戦える組織を作る。 軍隊(エンジニア)と法律(ルール)を整える。 良い法律とよい軍隊を持っていれば国は安定する。 このふたつこそが、国を保つための根幹である。 キーワード 必要な資質 変化への対応力、よからぬ者になりうる技、悪評から逃れるすべ、ケチであること、慈悲深さと冷酷さ、外見上は資質があるふりをする、気前の良さ 戦える組織をつくる 自分の軍を持つ、決断はひとりでする、有能な側近あるところに有能な君主あり、有能な側近に常に助言を求める、常に 武装 する 3.問題解決:問題解決 正しく問題を解決するプロセスを学びます。この本はすべてが重要なのですべてきちんと吸収したいです。 問題解決プロセス 現状把握(where)→課題抽出/特定(why)→あるべき姿(what)→対策立案(how)→モニタリング(KPI設定) 現状把握は非常に重要でここが誤っていたり、浅いと後続の各項目が矛盾したり、唐突だったりグダグダになります。 開発でいうと要求分析にあたるようなものなので全体の半分近くの力をここに注ぐのが望ましいです。 特に数値情報は説得力があり最後のKPIにも描きやすいです。 4.論理思考力:ロジカル・プレゼンテーション 前述の『問題解決』と同じ著者の方です。 ビジネスパーソン が価値の高い仕事を遂行していく上で必須となる能力が書かれています。 論理思考力:話をつなぐスキル 仮説検証力:疑問に答えるステップ 会議設計力:議論をまとめるスキル 資料作成力:紙に落とすステップ 5.行動様式( 自己啓発 ):人を動かす 言わずもがなの古典の名著。 目次に示された行動に対して自己診断の◯×をつけて、みんなで評価し合うと楽しいです。 もし、できていない項目があれば、それができている人を見つけて真似するだけで良くなっていきます。 6.心理学:嫌われる勇気 タイトルがキャッチーですが、嫌われるわけではないです。 正しく「共同体へ貢献」する考え方とは何かを理解できる本です。 他人を変えるための心理学ではなく、自分が変わるための心理学です。 自分を認めてあげると気持ちが楽になります。 キーワード 解決策は課題の分離 対人関係のゴールは「共同体感覚」 叱っても褒めてもいけない 「勇気づけ」と「感謝」 自己受容/他者信頼/他者貢献 7.組織経営ポリシー:プロフェッショナルマネージャー 高い視点の持ち方、視点をどこに置くべきか(経営の秘訣)、マネジメントとリーダーシップの違いなどが書かれています。 あなたのリーダーシップはどのようにして獲得できるか?高められるか?知りたい方は答えが書いてあります。 本を読む時は、初めから終わりへと読む ビジネスの経営はそれとは逆だ 終わりから始めて、そこへ到達するためにできる限りのことをする  - 自分は何をやりたいのかをしっかり見定め、それをやり始めよ。 すみません、10冊といいながらこのテーマについてはもう1冊紹介させてください。 HIGH OUTPUT MANAGEMENT マネージャーのアウトプットとは、     「自分の組織のアウトプット+自分の影響力が及ぶ隣接組織のアウトプット」 マネージャーは「テコ作用」を活用すべき。 マネージャーというテコをチームに入れることで、チームのパワーを何倍にもする点をなる。 マネージャーはテコを探し、作る。 8.マネジメント:マネジメント( ドラッカー ) こちらも誰もが知る名著。ただし、読むのは少し難しいです。 別冊にはなりますが、『実践する ドラッカー 【思考編】』と『実践する ドラッカー 【行動編】』だけでも 良質なピックアップがされていて読みやすいです。 主な論点は、貢献、強み、集中、時間管理、意思決定、目標管理、計画、です。 一番のおすすめは「強み」です。 『なすべきは自らが持っていないものではなく、  自らが持っているものを使って成果を上げることである。』 強みとは、個々人が持っている資質を磨いたものである。 自分を最高に生かす方法は、できること、強み、に集中することである。 そして、強みを生かす3ステップ  1.明らかになった強みに集中する事  2.その強みをさらに伸ばす事  3.無知の元凶ともいうべき知的な傲慢(もうこれで十分という気持ち)を正す事 9.経営組織論:組織行動のマネジメント こちらの本は重厚です。何回かに分けて、みんなで読んでいきました。 経営組織論に初めて触れる方は、なぜ経営理念があるのか、既にある人事制度や目標管理は論理に基づいている、組織文化を大事にする理由、などがわかります。 例えば、復数の管理職でこの本を読んだ際に、管理職の特性により以下の組織特性がありました。 グループシンク 安全志向重視が強い。→それが過度にならないようにどうするか注意が必要。 楽観的すぎる。→特定の人頼りになっていて管理も任せすぎた。 組織は、一時的に信頼が損なわれても説明でき、納得すれば関係を継続できる特徴があるので、早めの気づきからの説明と修正が重要になります。 また、 コンフリクト についても一概に悪いものと思い込んでいる点がありました。 ですが、調和的で平穏で協力的な集団は停滞しがちで変化や改革の必要性に対して無関心かつ鈍感になりやすいというものになります。 対策は、集団のリーダーに対して、集団を活性化し、 自己批判 的、創造的にするのに必要な最小限のコンフリクトを常に維持するよう促すのが大事と書かれていました。 10. プロダクトマネジメント :Inspired 最後に、急にエンジニアリングに戻ります。 製品やサービスに必要な役割と責務が書かれています。 プロダクトマネージャーの役割 製品の市場性を評価することと、開発すべき製品を定義することである。正しいもの(製品)は何かを定義する。(What) ユーザーエクス ペリエ ンスデザイナーの役割 製品が対象とするユーザー像(ペルソナ)を深く理解した上で、ユーザーのニーズに合うように要求仕様とデザインの調和を図ろうとする。 プロジェクトマネジメントの役割 製品が定義されれば、製品開発チームがプロジェクトを引き継ぎ、製品の開発に取りかかる。プロジェクトのスケジュールや工程・進捗の管理。正しくもの(製品)を作る。(How) エンジニアリングの役割 製品開発やソフトウェア開発(者)ともいうが、彼らは実際に製品を開発する人たちである。 サイト(システム)運用の役割 ウェブサイトを運用するチームは、ウェブ上のサービスを常時稼働させる任務を負っている。 プロダクト マーケティング の役割 製品を世界中に知らしめること、製品を対外的に発表すること、販売チャネルで製品を売り込むためのツールを提供すること、主要な マーケティング 活動を指揮することである。 以上、どの書籍も大変身になるものばかりでした。大事なのは読むだけではなく「やってみること」だと思います。 最後に、宣伝を失礼します。 株式会社ラクス では、このように人やプロセスを大事にして、正しく製品やサービスを作っております。 ご興味がありましたら是非お声がけください。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
アバター
こんにちは、株式会社 ラク スで楽楽勤怠の開発を行なっている goldminer です。 「楽楽勤怠」は昨年にリリースしたばかりの クラウド 型の勤怠管理システムです。 ラク スのサービスは BtoB のサービスが多く、勤怠管理システムも基本的にはバックオフィス業務をサポートするためのシステムです。 バックオフィス業務であれば(サービスの停止を伴う)メンテナンス時間を営業時間外となることが多い深夜時間帯にすれば影響が出にくいのですが、勤怠管理システムには打刻機能があり、これは次のような性質を持っています。 打刻には(いつ打刻したのかという)客観性が必要だが、打刻できずに従業員の自己申告に基づいて後から入力したものは客観性が低い 従業員の勤務時間帯は多様であり、深夜時間帯に行われる打刻も一定数ある このことから、楽楽勤怠では無停止リリースの重要性が高いです。直近のバージョンアップでたまたま無停止リリースを実施できる条件を満たしているものがありましたので、実際に無停止リリースのための開発を行ってみました。 今回の目的 無停止リリースの手順 無停止リリースに向けたアプリケーションの開発 DB変更パッチの作成 COLUMN の追加に関する注意点 TABLE の追加に関する注意点 アプリケーションの実装 COLUMN の追加に関する注意点 TABLE の追加に関する注意点 テスト 次回バージョンアップでの課題 DB変更パッチの作成 アプリケーションの実装 まとめ 今後の予定 今回の目的 無停止リリースについては弊社のかみせんプロジェクトでも検証しています。 同じ ラク ス エンジニアブログに 記事 があるので参考になさってください。 上記の記事では次の3つの要件があげられています。 セッション管理 アプリケーション構成 DB運用 この内、「セッション管理」「アプリケーション構成」は前に担当していたサービスでも対応済だったこともあり ナチュラ ルに対応しています。 つまり、DB変更がないリリースについてはすでに無停止リリースを達成しています。 今回は「DB運用」の検証で且つ 完全無停止 を目的としており、リリース中の更新リク エス トを制限しません。 「DB変更リリースを完全無停止で行う」ために何が必要であり、開発にどのような影響がでるのかを検証してみました。 無停止リリースの手順 DB変更リリースをサービスを停止して行う場合の手順は次のとおりです。 APサーバー1と2を停止する APサーバー1と2のアプリケーションを更新する DB変更パッチを適用する APサーバー1と2を再開する 無停止リリースの手順は次のとおりです。 DB変更パッチを適用する APサーバー1への振り分けを停止する APサーバー1のアプリケーションを更新する APサーバー1への振り分けを再開する APサーバー2への振り分けを停止する APサーバー2のアプリケーションを更新する APサーバー2への振り分けを再開する 手順の 2 から 7 まではDB変更がない場合の無停止リリースとまったく同じ手順です。 この手順の場合、アプリケーションとDBの組み合わせには次の3つがあります。 古いバージョンのアプリケーションと古いバージョンのDB 古いバージョンのアプリケーションと新しいバージョンのDB 新しいバージョンのアプリケーションと新しいバージョンのDB 1番目と3番目は当たり前のことですので、2番目の「古いバージョンのアプリケーションと新しいバージョンのDBで正常に動作する」ことが特徴的な要件です *1 。 無停止リリースに向けたアプリケーションの開発 DB変更パッチの作成 先に結論を言ってしまうと、DB変更パッチが以下だけの場合に限り完全無停止で行うことができます。 COLUMN の追加( ALTER TABLE ADD COLUMN ) TABLE の作成( CREATE TABLE ) 単純にデータ種別が増えて追加する場合に限る、既存の TABLE を分割するような場合は不可 大抵のORマッパーは追加された TABLE と COLUMN は無視しますので、特に何もしなくても 「古いバージョンのアプリケーションと新しいバージョンのDBで正常に動作する」を達成することができます。 それ以外の UPDATE や INSERT ~ SELECT などは、レコード数が多いと負荷が高くなったり容量が急に増えたりする可能性がありますので、運用しながらの実行はするべきではないと考えます。 COLUMN の追加に関する注意点 追加した COLUMN に初期値は設定せず、null のままにします OK 例 ALTER TABLE table_a ADD COLUMN col1 boolean ; NG 例 ALTER TABLE table_a ADD COLUMN col1 boolean ; UPDATE table_a SET col1 = true ; ALTER TABLE table_a ALTER COLUMN col1 SET NOT NULL ; NG 例は UPDATE を実行している点がよろしくありません。 UPDATE ではなく DEFAULT を使って初期値を設定するのも同様です。 今回は検証していませんが、全体設定のようなデータを保持する UPDATE ONLY な TABLE であれば レコード数が少なく、影響が把握しやすいので UPDATE も可能と考えています。 追加した COLUMN の INDEX は作成しません UPDATE と同じく、レコード数が多いと負荷が高くなったり容量が急に増えたりする可能性がありますので、運用しながらの作成はするべきではないと考えます。 やはりレコード数が少なければ作成しても問題ないですが、レコード数が少ないのであればそもそも INDEX を作る必要性が低いので、作成するという選択肢はないと考えています。 TABLE の追加に関する注意点 今回の検証では特に注意する点はありませんでした。 初期レコードを INSERT するかどうかについては次のように考えています。 基本は INSERT しない サンプルレコードや UPDATE ONLY な TABLE のレコードであれば、レコード数が少なく影響が把握しやすいので INSERT も可能 今回の検証では INSERT していません。 INDEX の作成はレコードがない、または数が少ないので問題はないと考えています。 プライマリーキーやユニークキー制約により自動的に作成される INDEX も同様です。 アプリケーションの実装 COLUMN の追加に関する注意点 上述したように追加した直後の COLUMN は null になっています。 これに対応する必要があります。 次のような実装でDBから取得した値が null だった場合に初期値に置換します。 Entity entity = /* DBから取得する処理 */ ; if (entity.col1 == null ) { entity.col1 = true ; } この置換処理は 、DataAccessObject(DAO) または Repository のどちらかで実行することになるでしょう。 そうすることで、それ以外の箇所では値が null である可能性を除外して実装することができます。 追加した COLUMN を抽出時の条件に使用する場合は 、null である可能性を考慮します。 Entity findByCol1() { // ↓「col1 IS NULL」を条件に付与している. String sql = "SELECT * FROM table_a WHERE col1 = true OR col1 IS NULL" ; } 追加した COLUMN でソートする場合、NULLS FIRST / NULLS LAST を指定する必要があります。 今回は検証した中にこのケースがなかったため実際にはやっていません。 TABLE の追加に関する注意点 今回の検証では初期レコードを INSERT しておらず、TABLE の中身が空なのは正常な状態のため特に注意する点はありませんでした。 テスト 無停止リリースに固有のものとして次のテストを行いました。 Apache JMeter を使って常時アクセスがある状況を作り、その中でDB変更パッチを実行するテスト 以下の観点で確認 DB変更パッチが正常終了するか Apache JMeter によるアクセスがすべて正常応答するか(エラー応答がないか、 応答時間 が正常な範囲内かどうか) 「古いバージョンのアプリケーションと新しいバージョンのDB」の組み合わせで機能テスト テスト結果はいずれも問題ありませんでした。 1 のテストは ミドルウェア やインフラ構成に大きな変更がない限りは、初回のみの実施でよく、毎バージョンアップで実施する必要はないと考えています。 次回バージョンアップでの課題 ここまでの内容で無停止リリースを実施することができます。 しかし、いくつかの課題を先送りにしており、次回以降のバージョンアップで対応する必要があります。 この対応はサービスを停止して行います。 DB変更パッチの作成 追加した COLUMN は null になっていますので、改めて初期値に更新します *2 。 緊急性は高くありませんが、今後の開発でノイズにならないために早めに対応しておくべきだと考えています。 UPDATE table_a SET col1 = true WHERE col1 IS NULL ; ALTER TABLE table_a ALTER COLUMN col1 SET NOT NULL ; INDEX の作成を見送っていた場合は INDEX も作成します。 アプリケーションの実装 null である可能性を考慮した実装がありますが、null がありえなくなりますので 本来あるべき実装にします。 Entity entity = /* DBから取得する処理 */ ; // 後処理は必要なし. Entity findByCol1() { String sql = "SELECT * FROM table_a WHERE col1 = true" ; } まとめ 今回の検証結果を踏まえての結論は次の通りです。 「DB変更リリースを完全無停止で行う」こと自体は条件が整えば可能 しかし、 工数 や 心理的 負担の増加を考えるとペイしない 次のような作業が必要になり、 工数 が増加します。 ★無停止リリースの手順を作成する 追加した COLUMN が null だった場合を考慮した実装を行う ★常時アクセスがある状況でのリリースのテストを行う 「古いバージョンのアプリケーションと新しいバージョンのDB」の組み合わせで機能テストを行う 追加した COLUMN の null を初期値に更新するDB変更パッチを作成する@次回バージョンアップ 追加した COLUMN が null だった場合を考慮した実装を本来あるべき実装に変更する@次回バージョンアップ 上記で★を付けたものは初回のみの実施でよく、毎バージョンアップで実施する必要はないと考えているものです。 ★が付いていないものが毎バージョンアップで 工数 が増加する原因になるもので、 工数 としてはざっくり1、2人日程度です。 工数 は飛び抜けて大きなものではありませんが、次回バージョンアップでの対応が発生することによる 心理的 負担の増加が嫌だなと、感じました。 効果の方はと言いますと、そもそもDB変更パッチが COLUMN と TABLE の追加のみの場合に限られるという問題があります。 さらに今後の予定に書いているように分散システム化することで打刻の無停止が達成できると、効果は「リリース担当エンジニアが深夜作業をしなくてよい」だけになってしまいます。 ビジネスサイドでの無停止リリースの要求が強くなるか開発の負担を減らすことができないと再チャレンジは難しいという所感です。 今後の予定 ビジネスサイドから無停止リリースの要求が出てきた際に応えられるようにと考えて実施した今回の検証ですが、毎バージョンアップで頑張ってDB変更パッチの作成と実装を行わなければならないのは継続性に難があると感じました。 今後の計画に無停止リリースは織り込まず、突発的に無停止リリースの必要が生じた場合に今回の知見を活かせればよいと考えています。 可用性の向上の観点ではシステム全体の完全無停止にはこだわらず、一度仕組みを作ってしまえば以降は再利用するだけでよくなる次のような方針で進めるべきだと考えています。 分散システムにして重要な機能は常時稼働できるようにする 勤怠管理システムでは打刻機能が該当 更新リク エス トのみ制限して参照リク エス トは行えるようにする 停止はするが停止時間が極力短くなるようにする *1 : DB変更パッチの適用とアプリケーションの更新の順序を入れ替えれば「新しいバージョンのアプリケーションと古いバージョンのDB」の組み合わせになります。 詳細は割愛しますが、この組み合わせで正常に動作させることは難易度が高く現実的ではありません。 例外的にDB変更が「 DROP COULMN」のみの場合は容易に達成できますが、「 DROP COULMN」に緊急性がある場合は少なく無停止リリースで行うメリットはないと思われます。 *2 : 新しいバージョンのアプリケーションで INSERT, UPDATE したレコードでは null ではありません。
アバター
今回は API について解説します。エンジニア/ プログラマ の方はよく聞く単語だと思いますが、 私自身、他の人にうまく説明できるかと考えた時にあまり自信がなかったのでまとめてみることにしました。 APIとは APIを使うメリット 開発コストの削減 開発の効率化 セキュリティの向上 APIの種類 実践 作ったもの 使用するAPI ぐるなびAPI LINE Messaging API 構成 作成手順 ぐるなびAPI登録 LINEBOT登録 Node.jsなどの環境構築 実装 おわりに API とは API は「 Application Programming Interface 」の略で、ざっくりいうとあるプログラムから別のソフトウェアを操作するための仕組みのことです。 企業などがアプリケーションの一部の機能を外部に向けて公開することで、第 三者 は開発したアプリで機能を利用することができます。 API を使うメリット 開発コストの削減 基本的にWeb API は API 自体の仕様が変更になる以外に、バグ修正等の運用コストも必要ありません。また、Web API は無料で公開されているものが多いので、開発時間の削減も可能です。 開発の効率化 アプリケーション開発で、ある機能を実装したいとなった時、一部の機能は他社が API として公開している場合があります。 例として、駅情報が必要になった時には既に駅情報を取得できる API が存在していますので、1から機能を実装する必要はありません。 1から作成する場合は、路線データ、地図データ、料金データ、アップデートなどを考慮する必要がありますが、 API を利用することでこれらの考慮が不要となり、効率よく開発を進めることができます。 セキュリティの向上 ログイン機能実装時には、 Facebook や Twitter 、 Google の API を活用することで該当アカウントを利用したログインができます。 大手企業の高いセキュリティ機能を利用することでセキュリティ面の担保ができます。 API の種類 ごく一部ですが、例として以下のようなものがあります。 ・ Twitter :アカウントと利用者や、ツイート、ダイレクトメッセージといった Twitter データの利用ができる ・ Amazon :商品情報、在庫情報、注文情報などを呼び出すことができる ・ Google :画像の分析やテキストの読み取り、手書き文字の読み取り、人や建造物の特定など、画像認識に関するさまざまな機能が使える ・LINE: BOT を作成して、応答メッセージの送受信を行ったりすることができる ・Slack:チャンネルや DM からのメッセージなど、イベントを送信したり、Slack チャンネルとユーザーグループを作成、変更したりできる 実践 作ったもの API を使って飲食店検索 BOT を作成しました。 仕様としては、位置情報を送った際に日本酒のある近くの居酒屋を検索しています。 使用する API ぐるなび API ぐるなび に掲載されている飲食店の基本情報を取得することができますので、飲食店を検索するようなアプリ作成時に使用するといいです。 LINE Messaging API Messaging API を使うことで BOT を作成することができます。ユーザーの反応によって何かしらの処理を行うことができますので、例えばオウム返しなどといったユーザーの送ったメッセージと同じメッセージを返す BOT が簡単に作れたりします。 構成 作成手順 ぐるなび API 登録 まず、アクセスキーを発行するために以下のサイトでアカウントを作成します。 ぐるなび Web Service - トップページ どのような API があるかは以下のサイトで確認できます。 ぐるなび Web Service - トップページ 次に受け取るデータの確認を行うのですが、 ぐるなび API はテストツールが用意されているため簡単にどのようなデータが返ってくるか確認することができます。 方法は、以下のサイトで使用する API 名とパラメータを指定して「クエリを送信ボタン」を押下するだけです。 ぐるなび Web Service - トップページ 今回はヒット件数を制限するhit_per_pageと、フリーワード検索を行うfreewordを指定しています。 画像の下部に JSON 形式でデータが返ってきていることが分かりますね。こちらからどのようなデータが返ってきているか確認します。 アプリに組み込むときは、URLをコピペすればとりあえずは動きます。あとは、必要に応じて検索パラメータを変更するように実装すればOKです。 LINEBOT登録 以下からLINE Developersにログインします。それからプロバイダーを作成して、Messaging API を選択してのチャネルを作成します。 ※LINE Developersへの登録にはLINE個人アカウントが必要になります。 LINE Developers Node.jsなどの環境構築 こちらについて今回は割愛させていただきます。 実装 今回実装したコードは以下となります。 'use strict'; const fetch = require('node-fetch'); const express = require('express'); const line = require('@line/bot-sdk'); var request = require('request'); const PORT = process.env.PORT || 3000; const config = { channelSecret: 'チャネルシークレット', channelAccessToken: 'チャネルアクセストークン' }; const app = express(); app.set('port', (process.env.PORT || 3000)); app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない) app.post('/webhook', line.middleware(config), (req, res) => { //console.log(req.body.events); //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。 if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){ res.send('Hello LINE BOT!(POST)'); console.log('疎通確認用'); return; } Promise .all(req.body.events.map(handleEvent),) .then((result) => res.json(result)); }); const client = new line.Client(config); async function handleEvent(event) { if (event.type !== 'message') { return Promise.resolve(null); } //位置情報が送られた場合 if(event.message.type == 'location') { //検索に必要なパラメータを定義 var query = { "keyid":"アクセスキー", "latitude": event.message.latitude, "longitude": event.message.longitude, "range": 3, "freeword": "日本酒", "hit_per_page": 10, }; //リクエストに必要なオプションを設定 var options = { url: "https://api.gnavi.co.jp/RestSearchAPI/v3/", qs: query, json: true }; } //ぐるなびAPIを叩く request.get(options, function(error, response, body){ if (!error && response.statusCode == 200 || body.rest == undefined) { if('error' in body){ console.log("検索エラー" + JSON.stringify(body)); return client.replyMessage(event.replyToken, { type: 'text', text: '検索結果が0件、または検索エラーです。' //実際に返信の言葉を入れる箇所 }); } } var random = Math.floor(Math.random() * (body.rest.length)); var message = { "type": "flex", "altText": "this is a flex message", "contents": { //中身記載 } }; return client.replyMessage(event.replyToken, message); }); } app.listen(PORT); console.log(`Server running at ${PORT}`); 上記のコードでポイントとなるところのみ解説します。 const config = { channelSecret: 'チャネルシークレット', channelAccessToken: 'チャネルアクセストークン' }; configにはLINE Developersで作成したチャネルの 秘密鍵 とアクセス トーク ンを設定しておきます。 //位置情報が送られた場合 if(event.message.type == 'location') { var query = { "keyid":"アクセスキー", "latitude": event.message.latitude, "longitude": event.message.longitude, "range": 3, "freeword": "日本酒", "hit_per_page": 10, }; Webhookイベントオブジェクトに送信されたメッセージが含まれており、messageプロパティに格納されています。 今回は位置情報が送られた際に処理を行いたいので、event.message.typeがlocationの時に処理します。 ちなみに位置情報が送られた際のメッセージオブジェクトは以下のようになります。 ・ID:メッセージID ・type:location ・title:タイトル ・address:住所 ・latitude:緯度 ・longitude:経度 また、queryには ぐるなび API を叩く際のリク エス トパラメータを設定しています。今回は以下となります。 ・keyId:アクセスキー ・latitude:緯度 ・longitude:経度 ・range:検索範囲 ・freeword:フリーワード検索 ・hit_per_page:検索件数 これで送った位置から半径1km以内の、日本酒のある飲食店を検索することができます。 request.get(options, function(error, response, body){ (中略) var message = { "type": "flex", "altText": "this is a flex message", "contents": { // Flex Message Simulatorで取得したJSONデータ } }; return client.replyMessage(event.replyToken, message); }); 初めはテキストで検索結果を返すようにしていたのですが、少し見た目を良くしたいと思いMessaging API の Flex Messageで返すようにしました。 そのためにtypeを flex としています。 また Flex Message Simulatorを使うことでメッセージを送信しなくてもレイアウトを確認することができます。 Flex Message Simulator 表示される JSON データをコピペして必要に応じて修正し、contentsに設定します。今回は以下のレスポンスデータを使用して表示しています。 ・rest.name:店名 ・rest.url_mobile:携帯サイトURL ・rest.image_url.shop_image1:店の画像 ・rest.address:住所 ・rest.opentime:営業時間 ※店によっては画像が空だったりするので注意が必要です。 おわりに 今回 API を使って飲食店検索 BOT を作ってみましたが、 ぐるなび API はテストツールでレスポンスを確認できるので使いやすさを感じました。 また、LINE Messaging API は送受信が簡単で、レイアウトは Flex Message Simulatorを使うことでわりとすぐ実装ができました。 記事作成時にいろいろな種類の API を見ていましたが使ってみたいものがありましたので、どのように利用するかを考え、また実践してみたいです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
アバター
皆様お初にお目にかかります。楽楽勤怠開発課のy_konnoと申します。2020年7月に入社したあまりフレッシュではない新入りです。 入社してからMQに関して取り扱う機会が多いのですが、 ラク スでMQというとRabbitMQがスタンダードになりつつあります。つい先日(1月22日)、RabbitMQ 3.8.11がリリースされ、なんともタイミングが良いので触れてみようと思います。 ちなみに3.8.9が2020年9月28日のリリースなので、約4ヶ月ぶりのバージョンアップになります。今回は文献のあまり多くないうえにアップデートのあったQuorum Queueについて触れてみます。 Quorum Queue以前のクラスタリング 1.ノード再起動時のミラーメッセージ消失 2.同期によるブロッキング Quorum Queueの特徴 Quorum Queueの概要 Quorum Queueがデータをレプリケーションする仕組み Quorum Queueでのノード障害発生時 Quorum Queueを使う Quorum Queueの制限・注意点 利用できない機能の存在 常にDurableなQueueとして扱われる ディスクI/O ノードの数 レイテンシ おわりに Quorum Queue以前の クラスタリング RabbitMQで クラスタ 構成を組む場合、ExchangeやBindingなどは各ノードへよしなに分散化してくれますが、Queueについては明示的に設定をしなければなにもしてくれません。Queueについての設定をしないまま クラスタ を組んで運用を始めてしまい、マスターノードが死んだ場合、そのままメッセージがロスするという恐ろしい事態が発生します。 バージョン3.8以前ではMirrored QueueがQueueの分散化について用意された唯一の選択肢でした。 Mirrored Queue Mirrored QueueはマスターのQueueでクライアントからのコマンド(Write、ACKなど)を処理し、 ミラーリング されたQueueに対してデータを レプリケーション していきます。もし、マスターが何らかの原因で落ちた場合は、ミラーのいずれかがマスターとしてプロモーションすることでサービスを維持します。 このようにMirrored Queueは クラスタ 内の各ノードにQueueを ミラーリング することで高可用性をもたらす機構ですが、データの一貫性については問題がある設計になっており、状況次第ではデータロスが生じる可能性が高まる危険があります。 1.ノード再起動時のミラーメッセージ消失 Mirrored Queueの1つ目の大きな問題は、あるノードが再起動した場合ミラーにあったデータ内容はすべて消去されるという挙動です。何らかの問題が生じたノードが再起動すると、ミラーにはデータが一切ないため、マスターノードからデータをすべて同期し直す必要があります。 これだけ見ると大して深刻な問題が起きそうにも見えませんが、このマスターからの同期がさらなる問題を引き起こします。 2.同期による ブロッキング 第2の問題はミラーへのデータ同期が ブロッキング で行われる点です。同期中は当該Queueへのメッセージ送受信がすべてブロックされるため、動機が完了するまでそのQueueは一切使えなくなります。 一般にRabbitMQが健全な運用状態であれば、Queueにメッセージが溜まった瞬間にConsumeされることがほとんどです。このため、Queueの状態はごく少数のメッセージが存在するか、もしくは空の状態になっていることがほとんどのはずです。しかし、何らかの問題でConsume側の処理停滞し、Queueに大量のメッセージが滞留した状態で同期による ブロッキング が発生すると、長時間に渡って該当のQueueが利用 不能 になってしまいます。ひどい場合には同期に膨大な時間とノードのリソースを消費した挙げ句に再度ノードがダウンするようなケースもありえます。こうなってしまうと、ミラーの同期を諦めるという選択をせざるを得なくなります。もしミラーの同期をしない場合は、再起動後から蓄積された新規のメッセージについては レプリケーション がされるものの、既存のメッセージについては同期しないため、データロスが発生する可能性が高まります。 MQで扱うメッセージが損失してしまうことが望ましくないシステムでは上記の事象は大きな問題となるでしょう。また、仮にメッセージ損失が許容できるシステムであったとしても、同期による ブロッキング が長時間に及んだ場合は全く影響を受けないわけではありません。 Quorum Queueの特徴 Quorum Queueの概要 Quorum QueueはRabbitMQ 3.8.0から搭載された新機能で、 Raft Consensus Algorithm を利用した高可用性・一貫性を実現するQueueです。特にデータの安全性・一貫性を重視されています。 Quorum Queue Quorum Queueのノードは1つのリーダーと複数のフォロワーから構成されます。このうち、クライアントと実際に対話を行うのはリーダーだけです。フォロワーは 冗長化 のためだけに存在し、万一リーダーが利用 不能 になった場合にはフォロワーの中から新たなリーダーが選出され、サービス全体のダウンを防ぎます。 Quorum Queueがデータを レプリケーション する仕組み Quorum Queueではログ レプリケーション という仕組みで各フォロワーにデータを連携しています。リーダーはクライアントからのコマンドを受け取ると、リーダーノード内のログに一時的にそのコマンドをコミットし、各フォロワーへログを レプリケーション します。この時点では クラスタ 全体として更新は確定していません。フォロワーはログの レプリケーション がなされると、自己のログにコミットを行い、リーダーへ返答を返します。リーダーはフォロワーからの返答の数をカウントし、フォロワーの 過半数 のコミットを以てリーダーもコミットし、 クラスタ 全体での値が一貫して更新されます。 Quorum Queueでのノード障害発生時 Mirrored Queueとは異なり、Quorum Queueではフォロワーノードが再起動しても保持しているデータを破棄しません。データはフォロワーノード上のディスクに書き込まれており、リーダーノードとの差分を レプリケーション でキャッチアップするだけです。 また、 レプリケーション 自体も非同期で行われます。このためMirrored Queueで発生しうるQueueが ブロッキング されてしまう事象ももはや発生しません。 さらに、新規にノードを クラスタ に追加した場合にも非同期で粛々と レプリケーション されるので、Mirrored Queueで発生していた問題は生じません。強いて言えば レプリケーション するデータ量が多い場合にはネットワークI/Oがやや増加する問題があることぐらいでしょうか。 Quorum Queueを使う 能書きが長々としてしまいましたが、実際にQuorum Queueを利用するのは非常に簡単です。クライアントにおいてQueue宣言時にパラメータを追加するだけで作成ができます。 以下は Java Clientでの宣言方法です。 Channel.queueDeclare() の arguments (第5引数)に x-queue-type=quorum を指定して宣言すればQuorum Queueとして作成されます。 Map<String, Object> extraQueueArgs = new HashMap<>(); extraQueueArgs.put( "x-queue-type" , "quorum" ); channel.queueDeclare( "test_queue" , true , false , false , extraQueueArgs); 簡単ですね。 なお、Quorum Queueを作成すると、Management Plugin上では以下の様に表示されます。 Management Console Type が Quorum になっていることが確認できますね。 また、メッセージの送受信については通常通りでよく、特別な処理は必要ありません。つまり、 Channel.basicPublish() や Channel.basicConsume() の内容は通常のQueueやMirrored Queueと何ら変わりません。 Quorum Queueの制限・注意点 ここまでQuorum Queueの利点ばかり述べてきましたが、残念なことにいくつかの欠点が存在します。 利用できない機能の存在 Mirrored Queueに比べて、Quorum Queueはサポートされている機能に制限があります。 以下はその一例です。 DurableではないQueue Exclusive Queue Queueのサイズ制限 drop-head と reject-publish (3.8.10から)のみサポート 公式サイト上では drop-head のみとの記載になっていますが、記述が古いです メッセージごとの永続化 メッセージの TTL メッセージ優先度 グローバル QoS 全く使えないものもあれば、一部のみサポートがされているものがあったりとバラバラな状態ですね。ただ、3.8.10でQueueのサイズ制限に reject-publish が追加されたり、Consumerの優先度がサポートされるようになったりと、今後は徐々にサポートが拡充されていくのかもしれません。 あまりケースとしては考えにくいですが、すでに構築済みのQueueをQuorum Queueに移行したい場合には、利用している機能がすべてサポートされているかは要確認です。最悪の場合は機能不足で移行できない可能性もありえるでしょう。 常にDurableなQueueとして扱われる 先述の通り、DurableではないQueueはQuorum Queueにおいてサポートされていません(そしておそらく今後もサポートされない可能性が高い)つまりQueueは常に永続化がされます。またメッセージもすべて永続化が強制的にされてしまいます。 メッセージの性質が損失を許容できないようなものであれば何ら問題ありませんが、メッセージの安全性よりもパフォーマンスに重きを置きたいような ユースケース には適していません。このあたりは新しい機能だからといって盲目的に採用するのではなく、採用するシステムに適した選択を心がけたいですね。 ディスクI/O Quorum Queueはメッセージを強制的に永続化する、すなわちディスクに書き込むため、ディスクI/Oについて注意をする必要があります。インフラ要件的にはなるべく SSD を採用することが求められます。 インフラ要件以外にも注意すべき点があります。それはExchangeでFANOUTを利用すべきでないということです。 FANOUTはすべてのQueueに対してメッセージを配送するExchangeです。Quorum QueueにおいてはQueueの数が増加するとそれだけディスクに書き込むメッセージ数が増加してしまうため、MQ全体に大量のQueueが存在するような場合には膨大なディスク書き込みを発生させることになります。 このため、Quorum QueueにおいてはFANOUTの利用は避けるべきでしょう。 ノードの数 Quorum QueueはRaft Consensus Algorithmを利用しているため、 クラスタ ーを構成するノード数に留意する必要があります。これを守らないとQuorum Queueの恩恵を受けられず、可用性・一貫性が損なわれる可能性があります。 とりあえず抑えておくべきは以下の2つです ノード数全体が奇数であること 最低ノード数は3 特に注意が必要なのはノード数が2以下の場合はノードが1台も落ちることが許されなくなる点です。また、偶数にしてしまうと、ネットワーク パーティション への耐性が失われる可能性があります。 また、7個以上のノードでQuorum Queueを構成すると性能が低下(レイテンシが増加)するとされています。これを考えると実用的なノード数が3か5ということになります。 レイテンシ Quorum Queueでは常にディスク書き込みが発生することと、Raft Consensus Algorithmのログ レプリケーション の仕組みの関係上、従来のQueueに比べるとレイテンシが高まる可能性があります。 ただ、Quorum Queueの主目的がデータの安全性としているので、パフォーマンスが若干犠牲になるのは致し方ないところではあります。 おわりに 以上、Quorum Queueの紹介でした。 Quorum Queueは日本語はおろか、英語でも文献が少ないこともあって、まだまだ広まっていない印象を受けます。Quorum Queueを利用すること自体はコード上では非常に簡単にできてしまいますが、性質についてはしっかりと理解した上で採用するかどうかを見極める必要があると思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
アバター
こんにちわ、インフラエンジニアをしている rakus-tatsu-kashi です。 今回は代理投稿です。同じ部署のインフラエンジニアが見つけてくれたテクニックをまとめてくれてました! この場を借りて、改めてお礼させていただきます! ありがとうございます!!! また、インフラエンジニアをやっていると、泥臭くなりやすい設定作業もあります。それを、 それを当たり前だと思いこまず、解決しようとするマインド 実際に解決する手段を見つけて、実現する実行力 が素晴らしい、また凄いなー、と思いました!!負けてはいられん...。 ということで、下記、本編です!! はじめに 今回は皆さんおなじみ、ZabbixのLLD(ローレベルディスカバリ)についてです。 ちょっと便利だなって思ったものがあるのでご紹介したいと思います。 ではさっそくですが、ご紹介する内容は以下2つです。 1.LLDに任意の監視を入れたい! 2.LLDのトリガの深刻度を変えたい! また、実施した環境はこちらになります。 利用するのは、以下の2つです。 いずれも Version 4.0.12 です。 ZabbixServer ZabbixAgent 1.LLDに任意の監視を入れたい! さっそく「1.LLDに任意の監視を入れたい!」からお話ししたいと思います。 皆さんご存じのとおりZabbixLLDはとても便利です。 サーバ(機器)によってデ バイス が何個ほどあるかを考慮せず、 データを取得できるようになったり、 トリガを設定できるようになったり、 をすることができます。 とっても便利です。 ただ、 意外と痒いところに手が届かない。 デフォルトで用意されているアイテムが多くない などはあります。 うちでいうと RAID 監視を作成するときに困りました。 環境によってコントローラの数やvdiskの数が異なるのに…。それぞれ用に結局作らなくちゃいけないの…? この面倒を、LLDの拡張というので解消を試みました。 任意のアイテムキーを json 形式で出力するようにして、そのアイテムキーをディスカバリで指定するとLLDを使えるようになります。 たとえば RAID のvdiskが複数個存在していて、そのvdiskごとにzabbixのアイテムをLLDで作りたい場合は、以下のような スクリプト を書きます #!/bin/bash LIST=`VDISKIDを取得するコマンド` if [ "${LIST}" = "" ] ;then echo "ZBX_NOTSUPPORTED" exit 1 fi echo "{" echo " \"data\":[" FIRST=1 for VER in ${LIST} do if [ ${FIRST} -eq 1 ] ; then echo "" FIRST=0 else echo "," fi echo -e -n "\t\t{ \"{#VDISKID}\":\"${VER}\" }" done echo "" echo " ]" echo "}" すると出力結果はこんな感じ { " data ": [ { " {#VDISKID} ":" 0 " } , { " {#VDISKID} ":" 2 " } ] } ここまで準備できたら、ZabbixAgentに以下の設定を読み込ませます。 UserParameter=vfs.vdisk.discovery,/etc/zabbix/vfs_vdisk_discovery.sh 次に、ZabbixServerでは、 「 vfs .adisk.discovery」をディスカバリルールのキーに指定 「storage.vdisk[{#VDISKID}]をアイテムのプロトタイプのキーに指定 すると! 任意の個数のVDISKがキーとして読み込める!!! という仕組みです。 実施してみると意外と簡単なので試してみてください。 2.LLDのトリガの深刻度を変えたい! つづいて「2.LLDのトリガの深刻度を変えたい!」について とっても便利なLLD、運用していく中で、監視の内容を変更したいと思うことが出来ました。 通常であればテンプレートのトリガの深刻度を修正して…と対応するのですが、LLDのトリガプロトタイプの深刻度を修正しても反映されない! 困りました。 大量にディスカバリで作成されたサーバたち、それらを一つずつ修正しろと…? 現実的ではない…と途方にくれました。 途方に暮れてしばらく空を眺めているうちにひらめきました。 LLDのトリガプロトタイプの深刻度は変更しても反映されない…が、LLDのトリガプロトタイプの条件式の変更は反映される。 ならば 深刻度を修正したうえで、条件式を修正して、また条件式を戻せば…? はやる気持ちを抑えて実験…! 無事深刻度が修正できました! キタ━━(☆∀☆)━━!!! どうやらトリガの無効などもうまく反映されない模様、同じやり方で試してみるのもいいかもしれませんね。 最後に 今回は、ZabbixのLLDについてのTips?を記載させていただきました。 Zabbixはせっかく機能が多いため、こういったTipsを身に身に付けていって、有効活用していきたいですね!! 以上!
アバター
こんにちは、masakaです。 前の会社でPWAについて少し調べていたのですが、特に発表することもなかったので 今回はその内容をブログにしてみました。 ざっくりとした説明と、簡単な実装をやってみたいと思います。 PWAとは PWAの代表的な機能 Webとネイティブアプリ、それぞれの特徴とPWAが求めるもの PWAを支える三つの柱 結局のところ、PWAとは PWAの対応状況 デスクトップ環境もPWAの対象に [実践編] PWA化への第一歩 PWA化のために必要なこと - [Web App Manifest]と[Service Worker] manifest Service Worker Webサイトからインストール出来るようにする - A2HS サンプルアプリとWebサーバの準備 A2HSを実装する オフラインでも実行できるようにする おわりに PWAとは ネイティブアプリみたいなWebアプリ PWAは「 progressive web apps」のことを指します。2015年に発表され、話題に上がってきました。 PWAに対応した代表的なサイトとして、 Twitter や国内だとSUUMOなどがよく例に挙げられます。 PWAというワードで検索すると、「ネイティブアプリみたいな」Webアプリである、という抽象的な内容が散見されます。 この表現は間違っていないです が、何をもって「ネイティブアプリみたい」というのか、いまいちはっきりしません。 もう少し詳しく理解してみることにします。 PWAの代表的な機能 機能的な面で調べると、PWAで出来ることとして代表的なものには以下があります。 ホーム画面への追加:アプリの様にインストールし、ホーム画面から起動できる オフライン起動:ネットワークに接続していなくても使用できる バックグラウンド同期:オフライン時に操作した内容を、ネットワーク接続されたタイミングでバックグラウンド送信する プッシュ通知:アプリからの通知 これらの機能が実装されることがPWAなのでしょうか? Webとネイティブアプリ、それぞれの特徴とPWAが求めるもの Google によるPWAの説明 には、ネイティブアプリとWeb アプリの特徴を次のように書いてあります。 ネイティブアプリの特徴 - 機能性・信頼性に優れる ホーム画面、タスクバーなどに存在しネットワークに影響されず使用できる 機能が豊富 Web アプリの特徴 - 到達性に優れる 単一コードベース デ バイス を問わず、誰でもどこでもアクセスできる その上で、PWAはWebアプリに双方の長所を持たせることを目指しているようで、以下のような記載がされています。 PWAはあくまでWeb アプリである モダンな API で構築されている デ バイス やブラウザを問わない到達性 PWAを支える三つの柱 加えてネイティブアプリのように感じさせるための三つの柱についても書かれています。 1.Capable (機能) Webは様々なことが出来るように機能拡張し、成長し続ける これらを取り入れることによりネイティブアプリでしか出来なかったことがWeb で可能になっていく 2.Reliable (信頼) ネットワークに影響されずに使用できる ネットワーク接続が出来ない、あるいは低速なネットワークでも使用できる 3.Installable (インストール可能) ブラウザの中で動くのではなく独立したアプリとして動く 1.はともかく、2.と 3.は具体的ですね。 ちなみに、以前は Google の デベロッパーページ では、 Reliable Fast Engaging という特徴を書いていたようなのですが、少し変わっているようです。 結局のところ、PWAとは これまでの内容で、つまるところPWAとは ネイティブアプリ的なユーザー体験を得られるWebアプリ であり、 ネットワーク状態に影響されず、 独立したアプリのように起動でき、 新しいWebAPIを採用し、機能的にネイティブアプリに大きく劣らないようにする といった要素を満たしておくと、結果的にそうなるという感じでしょうか。 PWAに求められる、より具体的な内容はPWAの チェックリスト を見ると分かります。 よりよいユーザー体験を提供しましょう的な内容が多い印象です。 PWAの対応状況 PWAはあくまでWebアプリなので、ブラウザによって 対応状況 が変わってきてしまいます。 What Web Can Do Today というサイトでは、アクセスされたブラウザでどの機能が使えるか表示してくれます。 Google が推進しているだけあって、 Android Chrome は早くからPWA対応 iOS Safari は一部の機能未対応 セキュリティ的な思惑もあり、すぐに対応は難しそう iOS でもプッシュ通知くらいは使えてほしいですが、iOS14でもまだ未対応です・・。 デスクトップ環境もPWAの対象に PWAは元々モバイル環境をターゲットにしていたようですが、 Chrome とEdgeはデスクトップ環境でもPWAに対応しています。 Chrome は version67 でPWAについての記載があります。 モバイルでPWA対応していたものは、特にデスクトップ用にコードを変更する必要はなく、そのままデスクトップPWAとして使えます。 PCでもPWAを利用できる状況になってきています。 [実践編] PWA化への第一歩 ここからは実践編です。今回は手始めに、PWAっぽくするために インストール可能にする オフラインで使用できる をやってみます。 デスクトップPWAとしての使用をターゲットにします。 PWA化のために必要なこと - [Web App Manifest]と[Service Worker] PWA化において欠かせない要素は、 Web App Manifest Service Worker の二つがあります。 manifest manifestファイルはウェブアプリについての情報や、挙動についての設定が記したファイルです。 ダウンロードし、アプリとして動かすために必要な情報が記載され、中身は JSON です。 ユーザーが取得できるよう、Web上に配置します。 Service Worker Service Workerはブラウザのメイン スクリプト 処理とは別に、バックグラウンドとして実行される スクリプト です。 PWAの肝となる部分かと思います。 ServiceWorkerはWebアプリ(ブラウザ)とネットワークの間で動作します。 必要に応じてキャッシュからデータを取得するか、ネットワークへリク エス トするかを制御できます。 sw オフライン時や、通信環境が悪い状況での実行に密接に影響しそうな機能ですね。 Webサイトからインストール出来るようにする - A2HS アプリとしてホーム画面などに追加する機能を 「A2HS」 と呼びます。(Add-to-Home-Screen の略) 早速、これをやってみましょう。 サンプルアプリとWebサーバの準備 まず、何かWebアプリが必要です。 htmlと JavaScript でごく簡単なクリック連打ゲームを作成しました。 これで試してみましょう。 GitHub にもあります。 クリック連打ゲームのソース index.html <!DOCTYPE html> < html > < head > < meta charset = "utf-8" /> < meta http-equiv = "X-UA-Compatible" content = "IE=edge" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" /> < title > button mashing </ title > < link rel = "stylesheet" type = "text/css" href = "app.css" /> </ head > < body > < div class = "wrapper" > < div > 目標クリック連打数: < span class = "js-target-count" > 10 </ span ></ div > < div class = "timer" > < span class = "js-timer-label" > 経過時間: </ span > < span class = "js-spend-time" ></ span > </ div > < div class = "click-btn-wrapper" > < button class = "js-click-target btn-click-target" > < span > Click! </ span >< br /> < span class = "js-counter" ></ span >< br /> </ button > </ div > < div > < button class = "js-reset btn-reset" > リセット </ button > </ div > < div class = "change-count-wrapper" > クリック数を変更する < ul > < li > < button class = "js-change-count btn-change-count" data -count= "10" > クリック数:10回 </ button > </ li > < li > < button class = "js-change-count btn-change-count" data -count= "20" > クリック数:20回 </ button > </ li > < li > < button class = "js-change-count btn-change-count" data -count= "30" > クリック数:30回 </ button > </ li > </ ul > </ div > </ div > < script src = "app.js" ></ script > </ body > </ html > app. css .wrapper { padding : 10px ; max-width : 400px ; margin : 0 auto ; text-align : center ; font-size : 14px ; } .click-btn-wrapper { margin : 10px auto ; } .btn-click-target { display : inline-block ; text-decoration : none ; color : #668ad8 ; background-color : #fff ; width : 80px ; height : 80px ; border-radius : 50 %; border : solid 2px #668ad8 ; text-align : center ; overflow : hidden ; font-weight : bold ; transition : 0.4s ; } .btn-click-target : hover { background : #b3e1ff ; } .change-count-wrapper { max-width : 240px ; margin : 20px auto 0px auto ; } .btn-change-count { position : relative ; display : inline-block ; font-weight : bold ; padding : 0.25em 0.5em ; text-decoration : none ; color : #00bcd4 ; background : #dbebf8 ; border : none ; transition : 0.4s ; } .btn-change-count : hover { background : #00bcd4 ; color : white ; } .header-change-count { border-bottom : solid 3px black ; } ul , ol { padding : 0 ; text-align : center ; margin : 3px 0px ; } ul li { position : relative ; list-style-type : none !important ; padding : 0.3em 0.3em 0.3em 0.3em ; margin-bottom : 1px ; vertical-align : middle ; } .btn-reset { position : relative ; display : inline-block ; font-weight : bold ; padding : 0.25em 0.5em ; text-decoration : none ; color : #00BCD4 ; background : #ECECEC ; border : none ; border-radius : 0 ; transition : . 4s ; } .btn-reset : hover { background : #636363 ; } app.js let clickCount = 0; let playing = false ; let targetCount = 10; let spendTime = 0; let timerId = 0; const clickTarget = document .querySelector( ".js-click-target" ); const resetBtn = document .querySelector( ".js-reset" ); const counterText = document .querySelector( ".js-counter" ); const targetCountText = document .querySelector( ".js-target-count" ); const spendTimeText = document .querySelector( ".js-spend-time" ); const timerLabelText = document .querySelector( ".js-timer-label" ); const countChanger = document .getElementsByClassName( "js-change-count" ); Array .prototype.forEach.call(countChanger, (btn) => { btn.addEventListener( "click" , (e) => { targetCount = btn.getAttribute( "data-count" ); targetCountText.innerHTML = targetCount; } ); } ); const start = () => { clickCount = 0; time = 0; playing = true ; timerLabelText.innerHTML = "経過時間:" ; counterText.innerText = clickCount; spendTimeText.innerHTML = time / 1000; timerId = setInterval(() => { time += 10; spendTimeText.innerHTML = (time / 1000).toFixed(2); } , 10); } ; const complete = () => { playing = false ; timerLabelText.innerHTML = "連打終了! 記録:" ; clearInterval(timerId); clickTarget.disabled = true ; setTimeout(() => (clickTarget.disabled = false ), 2000); } ; const reset = () => { playing = false ; clickCount = 0; counterText.innerText = clickCount; spendTimeText.innerHTML = 0; timerLabelText.innerHTML = "経過時間:" ; clickTarget.disabled = false ; clearInterval(timerId); } ; // 初回クリックでスタート clickTarget.addEventListener( "click" , () => { if (playing) return ; start(); playing = true ; } ); // クリック時のカウントアップ clickTarget.addEventListener( "click" , () => { if (!playing) return ; counterText.innerText = ++clickCount; if (clickCount >= targetCount) { complete(); } } ); // リセットボタン resetBtn.addEventListener( "click" , () => reset()); これらhtml, css , jsファイルを配置したらローカルでwebサーバを起動してアクセスできるようにします。 (ここではnode.jsの http-server を使いますが、なんでも構いません) npm install -g http-server ファイルを配置した場所で http-server -p 8001 これで http://localhost:8001/index.html `にアクセスできるようにします。 ※ちなみに、 GitHub Pagesを使うと HTTPS でアクセスできるので楽です。 A2HSを実装する やることはそう多くはありません。以下を実行すれば、インストールが可能になります。 1.manifest(Web App Manifest)を作成する htmlのヘッダにmanifest. json へのlinkを記述する 2.ServiceWorkerを登録し、fetchイベントをハンドリングさせる この他に HTTPS 経由で提供される必要があるようですが、 localhost の場合はHTTPでもOK です。 1.manifestの作成 まずmanifestを作成していきます。 中身は JSON ですが、拡張子は .webmanifest にするべきなようです。が、 .json でも動きます。 ここでは manifest.webmanifest として作成します。 manifest.webmanifest { " name ": " Button mashing ", " short_name ": " Bm ", " description ": " クリック連打 ", " icons ": [ { " src ": " ./icons/icon-192.png ", " sizes ": " 192x192 ", " type ": " image/png " } , { " src ": " ./icons/icon-512.png ", " sizes ": " 512x512 ", " type ": " image/png " } ] , " start_url ": " ./index.html ", " display ": " standalone ", " background_color ": " #FFFFFF ", " theme_color ": " #FFFFFF " } ざっと項目の説明です。 name: アプリの名称。 icons: ホーム画面やデスクトップに表示するアイコン。192 192と512 512があると良いらしいです。(とりあえず適当に作るか拾う) start_url: インストールして実行するファイルのパス。 display: 表示方法。アプリっぽく動かすには、 standalone か fullscreen を指定。fullscreenだとステータスバーなどのUIも表示されない。 パス系はmanifestファイルの場所からの 相対パス です。 作成したら、index.htmlにこのmanifestへのリンクを記載します。 index.html <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>button mashing</title> <link rel="stylesheet" type="text/css" href="app.css" /> + <link rel="manifest" href="manifest.webmanifest" /> </script> </head> 2.Service Workerの登録 SeriveWorkerの実態は JavaScript です。 sw.js にSeriveWorkerの本体を記載し、それをアプリ内から登録します。 sw.js self .addEventListener( 'fetch' , (e) => {} ) インストールに必要なのは、このfetchイベントをハンドリングさせるだけです。コールバックは空で構いません。 (ServiceWorker内では、selfに ServiceWorkerGlobalScope がbindされています。) これをアプリケーション内で登録します。 index.html <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>button mashing</title> <link rel="stylesheet" type="text/css" href="app.css" /> <link rel="manifest" href="manifest.webmanifest" /> + <!-- ServiceWorker --> + <script> + if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker.register("./sw.js").then( + function (registration) { + // Registration was successful + console.log( + "ServiceWorker registration successful with scope: ", + registration.scope + ); + }, + function (err) { + // registration failed :( + console.log("ServiceWorker registration failed: ", err); + } + ); + }); + } + </script> </head> ここまで変更したら、 localhost:8001/index.html にアクセスします。 アドレスバーの右端に、 + のアイコンが追加されました。 PWAのインストールボタン・・・わかりづらい クリックしてインストールしてみます。デスクトップにアイコンが追加され、起動してみると・・ デスクトップから起動したアプリ 起動できました。これでデスクトップに追加することができるようになりました。 オフラインでも実行できるようにする アプリとして起動できるようにはなりましたが、このままではオフラインでは使用できません。 コンテンツをオフラインで使用できるようにするため、キャッシュ処理をService Workerに書かなければならないのですが、 workbox を使うと簡単に試せます。 sw.jsにworkboxを利用したファイルのキャッシュ処理を書いてみましょう。 sw.jsに以下を追加します。 importScripts( 'https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js' ); workbox.precaching.precacheAndRoute( [ '/index.html' , '/app.css' , '/app.js' ] ); pre-cacheを使用しています。(インストール時にキャッシュされる機能) 本来はファイルにリビジョンを指定して、バージョン管理を行うことも必要ですが今回は割愛します。 キャッシュ処理を追加したところで、再度アプリをインストールし直してみます。(アンインストールはアプリ右上のメニューからできます) http-serverを停止したり、オフライン状態でもインストールしたゲームを起動できるようになっています。 どういう仕組みになっているのか軽く確認してみましょう。 開発者ツールを見るとキャッシュされたファイルはService Workerから取得していることがわかります cache storageに保存されています これで、オフライン実行が有効になります。 PWA化への第一歩を踏み出せました。 おわりに PWAとは何かの把握と実装の第一歩をやってみました。 機会があったらプッシュ通知あたりもやってみたいと思います。 PWA自体は対応するサイトも増えてきているようですが、普及に関しては iOS の対応が未だ不透明である点がネックですね。 とはいえ iOS も徐々に対応機能を増やしてきてはいるので、今後も発展が見込めそうな技術かと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 forms.gle イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com
アバター
はじめに 皆さん初めましてseahoseTです。 今回は Linux 上で文字列を処理することに便利な awk について紹介していきたいと思います。 目次 はじめに awkとは ここがすごいよ!awkのアレコレ! awkの基本的な仕様 awkのコマンド パターン 正規表現 BEGIN,END 評価式 アクション awkの組み込み変数 awkのオプション awkの注意点 問題編:行の指定 解決編:行の指定 問題編:複数のパターンを使用する 解決編:複数のパターンを使用する 問題編:NRとFNR 解決編:NRとFNR 問題編:awkでシェル変数を使用する 解決編:awkでシェル変数を使用する awkの実践 おわりに awk とは AWK (オーク)は、 プログラミング言語 の一つ。 テキストファイル、特に空白類(スペースの他、タブなど)やカンマなどで区切られたデータファイルの処理を念頭に置いた仕様となっているが、 一般的なプログラミングに用いることも可能である。 UNIX 上で開発された。( Wikipedia より) awk とは Linux や Unix ( Mac )で使用できるファイルの集計など文字列を扱う場合に便利な プログラミング言語 。 同様に文字列を処理できる sed などが非常に特殊な文法をしていた事に対して、 C言語 ライクで人が分かりやすいような文法をしている。 awk は プログラミング言語 であるが、 Linux のコマンドのように扱うことができる 。 目次へ ここがすごいよ! awk のアレコレ! 殆どの Linux や Unix ( Mac )上で標準で使用することができ、コマンドの使い回しができるなど互換性が高い 「 gawk 」をインストールするなどひと手間必要だが Windows でも問題なく使用することができる プログラミング言語 であるが、 コンパイル は不要 簡単 目次へ awk の基本的な仕様 awk の基本的な構文は下記のようになる。 # コマンドを直接入力する場合 awk ' コマンド ' [ 入力ファイルのパス ] # コマンドをファイルから入力する場合 awk -f コマンドファイルのパス [ 入力ファイルのパス ] また、入力ファイルを複数指定することも可能。 # 入力ファイルを複数設定したい場合 awk ' コマンド ' [ 入力ファイルのパス 1 ] [ 入力ファイルのパス 2 ] ..... 「入力ファイル」を設定せずに「パイプ」や「リダイレクト」での受け渡しも可能。 # 入力を「パイプ」で行う場合の例 echo | awk ' コマンド ' awk では 各行ごと に指定した列に対して処理を行う。 この時 awk では行の事を 「レコード」 、列のことを 「フィールド」 と呼ぶ。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 上記の例のような「date.txt」が存在する場合にフィールド1(列1)を表示したい場合には、 # 「date.txt」のフィールド1(列1)を表示 awk ' {print $1} ' date.txt と記述することで各レコード(行)のフィールド1(列1)を表示し、下記のような結果を得ることができる。 # 結果 1 4 また、「$0」を使用することで全てのレコード(行)、フィールド(列)を参照することが可能。 # 「date.txt」の全レコード(行)、フィールド(列)を表示 awk ' {print $0} ' date.txt # 結果 1 2 3 4 5 6 目次へ awk のコマンド awkの基本的な仕様 で awk の'コマンド'と記述している部分は本来「パターン」と「アクション」の二つから成る。「アクション」の部分は具体的にどのような処理を行うのかを内容を記述する。「パターン」の部分ではどのような場合にアクションを行うかの条件を決定している。また、 awkの基本的な仕様 で行った通り「パターン」の部分に何も記述しない場合でも問題なく動作する。 # 'コマンド'を「パターン」部分と「アクション」部分に分けた場合 awk ' パターン { アクション } ' [ 入力ファイルのパス ] パターンの種類 正規表現 BEGIN END 評価式 正規表現 記述した 正規表現 のパターンにマッチした行にアクション部分の処理を施す。 # 書き方 awk ' /正規表現/ { アクション } ' [ 入力ファイルのパス ] このように 正規表現 の左右にスラッシュを入れて記述する。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # 「date.txt」ファイル中の「4」で「終わる」フィールド(列)を含むレコード(行)がある場合 # そのフィールド(行)のフィールド2(列2)を表示する awk ' /4$/ { print $2 } ' date.txt # 結果 5 BEGIN,END awk では「BEGIN」、「END」を使用することで「メイン」を含めた三つのブロック構造を作ることが出来る。「BEGIN」と「END」は前処理、後処理の関係になっており、「BEGIN」では入力ファイルを 読み込む前 に処理が行われ、「END」では入力ファイルを 読み込み終わった後 の処理を記述する。 宣言した変数は「BEGIN」、「メイン」、「END」のブロックをまたいで使用することが可能。一般的には「BEGIN」で宣言を行う。 # 書き方 # 例1 awk ' BEGIN { 入力ファイルを読み込む前のアクション } { メイン処理 } ' [ 入力ファイルのパス ] # 例2 awk ' { メイン処理 } END { 入力ファイルを読み込んだ後のアクション } ' [ 入力ファイルのパス ] # 例3 awk ' BEGIN { 入力ファイルを読み込む前のアクション } { メイン処理 } END { 入力ファイルを読み込んだ後のアクション } ' [ 入力ファイルのパス ] 「BEGIN」、「END」は省略可能であるため「メイン」の処理のみを記述した際でも問題なく動作する。 評価式 評価式の評価にマッチした行にアクション部分の処理を施す。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # 「date.txt」ファイル中の「フィールド3(列3)」が「6」であるレコード(行)が存在する場合 # そのレコード(行)のフィールド3(列3)を表示する awk ' $3 == "6" { print $3 } ' date.txt # 結果 6 この結果だけ見ると前述した 正規表現 で良いように思えるが後述する組み込み変数や「-v」オプションと組み合わせた際に真価を発揮する。 アクション 「アクション」内で複数の処理を行う場合は セミ コロン(;)で区切る。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # 「アクション」内で複数の処理を行う例 awk ' {print $0; print $1} ' date.txt # 結果 1 2 3 4 5 6 1 4 同じような処理を連続で行う場合にはカンマ(,)での記述も可能。 # 「$0」と「$1」を表示 awk ' {print $0, $1} ' date.txt # 結果 1 2 3 4 5 6 1 4 変数を使用する場合は Linux コマンドと違い「$」をつけない。 # 変数の仕様 awk ' {age = 2021; print age} ' date.txt # 結果 2021 目次へ awk の組み込み変数 予め awk に準備されている変数。「パターン」、「アクション」両方で使える。シェルの場合と違い組み込み変数を使用する際には「$」をつける必要がない点に注意。 変数名 効果 デフォルト(未設定の場合) ARGC コマンドライン 引数の個数 ARGV コマンドライン 引数(配列に格納) ENVIRON シェルの 環境変数 を参照 FILENAME 現在処理しているファイルの名前 FNR 現在処理しているファイルのレコード数(行数) NR 現在処理しているレコード含む処理した総レコードの数(行数) ※入力ファイルが複数の時注意 RS 読み込み時のレコード(行)の区切り文字 改行 ORS 出力時のレコード(行)の区切り文字 改行 FS 読み込み時のフィールド(列)の区切り文字 ※「-F」オプションでも変更可能 空白 OFS 出力時のレコード(行)の区切り文字 空白 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # 「date.txt」の改行を,に変更して表示 awk ' ORS = "," {print $0} ' date.txt # 結果 1 2 3 , 4 5 6 目次へ awk のオプション awk のオプションは一般的な Linux コマンドと同様に使うことができる。 オプション名 効果 デフォルト(未設定の場合) -f コマンドファイル名 awk コマンドが書かれたファイルを指定する -F 区切り文字 区切り文字を指定する 空白 -v 変数名=値 変数を定義する # オプションの記述例 awk -オプション ' コマンド ' [ 入力ファイルのパス ] 目次へ awk の注意点 この項では筆者が awk を使用した際に躓いた事に対する注意点を挙げていく。 問題編:行の指定 awk を実行する時、「パターン」で行数を指定しない場合、デフォルトでは全ての行を対象に取る。 解決編:行の指定 # 「date.txt」のレコード1(行1)のフィールド2(列2)を表示 awk ' NR == 1{print $2} ' date.txt パターンの部分に「NR == 表示したい行数」を設定することで指定した特定の行数のみの結果を得ることができる。パターンには4種類の項目があることは コマンド で記載しているが、原理としては評価式として現在の行数を評価しているといった形になる。 問題編:複数のパターンを使用する 「パターン」は複数を組み合わせることができる。 解決編:複数のパターンを使用する 「&&」や「||」を使うと、複数のパターンを組み合わせることがでる。また、「()」を使用することで数式のようにパターンの優先度を変更できる。 # 複数個パターンを使用したawkの例 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # レコード1(行1)以降でかつフィールド2(列2)が2ではない物を表示 awk ' NR>1 && ! ( $2 ~ 2 ){ print $0} ' date.txt ※「~」はマッチ演算子で「 = 」とほとんど同じ効果を持つ。「 = 」に置き換えた場合でも動作する。 # 結果 4 5 6 問題編:NRとFNR 入力ファイルが複数個の場合のレコード数(行数)が加算されてしまう。 解決編:NRとFNR 組み込み変数「NR」を使用してある特定のレコード(行)にのみ変更を加えたい。そんな時に、「入力ファイル」が複数個存在する場合注意が必要だ。例えば下記のような二つの入力ファイルがあったとしよう、 # 一つ目の入力ファイル 例 ) date1.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # 二つ目の入力ファイル 例 ) date2.txt  列 1 列 2 列 3 行  7   8   9 行  10   11   12 二つの入力ファイルそれぞれのレコード2(行2)を表示したいので「NR == 2」として awk のコマンドを設定する。 # レコード2(行2)を表示したいコマンド awk ' NR == 2 {print $0} ' date1.txt date2.txt # 結果 4 5 6 「NR == 2」ではこのように「入力ファイル1」のレコード2(行2)だけしか表示することができていない。では、「FNR == 2」を使用してみるとどうだろう、 # レコード2(行2)を表示したいコマンド awk ' FNR == 2 {print $0} ' date1.txt date2.txt # 結果 4 5 6 10 11 12 期待した通りの結果、「date1.txt」と「date2.txt」のレコード2(行2)がそれぞれ表示していることが分かる。では次に「NR == 3」を行ってみる。 # レコード2(行2)を表示したいコマンド awk ' NR == 3 {print $0} ' date1.txt date2.txt # 結果 7 8 9 入力ファイル2のレコード1(行1)が表示されている。これはどこからレコード(行)のカウントが始まっているかの違いである。「NR」の場合は入力ファイル1の総レコードの2を処理した後入力ファイル2のレコード1(行1)を現在の行数に 加算 する。つまり入力ファイル2のレコード1(行1)は2+1で「NR = 3」にあたるということになる。 対して「FNR」は入力ファイル1の総レコードの2を処理した後行数のカウントを リセット する。つまり、レコード数(行数)は現在処理している入力ファイルのレコード数(行数)を参照することになる。 # 「NR」の処理イメージ(直列)   列 1 列 2 列 3 行 1   1   2   3 行 2   4   5   6 行 3   7   8   9 行 4   10   11   12 # 「FNR」のイメージ(並列)   列 1 列 2 列 3 行 1   1   2   3 行 2   4   5   6   列 1 列 2 列 3 行 1   7   8   9 行 2   10   11   12 問題編: awk でシェル変数を使用する awk 内ではデフォルトの状態でシェル変数を使用することはできない。 解決編: awk でシェル変数を使用する 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # シェル変数 var = 100 上記のように「date.txt」とシェル変数「var」が設定されている場合に「date.txt」のフィールド2(列2)と「var」の合計を awk を用い表示したいとする時、 # 「date.txt」のフィールド2(列2)とシェル変数「var」の合計を表示 awk ' {print $2 + var} ' date.txt と記述してもシェル変数である「var」を正しく認識できないため、正しい結果を得ることはできない。 awk は Linux のコマンドのように扱うことができるが、あくまで プログラミング言語 の一種であるため同様に扱うことができないのである。そこで解決策として「-v」オプションを使用する。 「-v」オプション オプション名 効果 -v 変数名=値 変数を定義する オプション の項で紹介している物と同様である。「-v」オプションでは任意の変数名に値を設定することで任意の変数を組み込み変数のように「パターン」や「アクション」で使用できるようにする効果がある。正確に言うと プログラミング言語 awk に対して 値を受け渡す オプションとなっている。値を設定する際、 シェル変数を受け渡すことも可能 なのでデフォルトではシェル変数を使用することが出来ないといった問題を解決することができる。 # 上記の例をもとにした「-v」オプション設定例 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # シェル変数 var = 100 # 「date.txt」のフィールド2(列2)とシェル変数「var」の合計を表示 awk -v awkVar = $var ' {print $2 + awkVar} ' date.txt # 結果 102 105 「-v」オプションを複数設定することで複数の変数を設定することもできる。 # 上記の例をもとにした「-v」オプション設定例 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # シェル変数 var = 100 var2 = 200 # 「date.txt」のフィールド2(列2)とシェル変数「var」、シェル変数「var2」の合計を表示 awk -v awkVar1 = $var -v awkVar2 = $var2 ' {print $2 + awkVar1 + awkVar2} ' date.txt # 結果 302 305 注意して欲しいのは設定した変数をコマンド内で使用する場合 組み込み変数 と同様に、「$」をつけずに、設定した変数名のみを記述することだ。シェルでの一般的な変数呼び出しとは勝手が違うので気を付ける必要がある。 ・・・しかし、実は「-v」オプションを使わなくても awk に値を受け渡すことは可能である。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # シェル変数 var = 100 # 「-v」オプションを使用せずに変数「awkVar3」を設定 awk ' {print $2 + awkVar3} ' awkVar3 = $var date.txt # 結果 102 105 これだけ見ると同様の動作をしているように見えるが次の例を見て欲しい。 # 「-v」オプションを使い「message」を設定 echo | awk -v message = " Test massage " ' BEGIN{print massage} {print massaeg} END{print massage} ' # BEGIN:結果 Test massage # メイン:結果 Test massage # END:結果 Test massage # 「-v」オプションを使わずに「message」を設定 echo | awk ' BEGIN{print massage} {print massaeg} END{print massage} ' message = " Test massage " # BEGIN:結果 # メイン:結果 Test massage # END:結果 Test massage おわかりいただけただろうか。「-v」オプションを使用せずに変数を設定した場合「パターン」の「BEGIN」ブロックにおいて変数が参照できていないのである。この違いにより「-v」オプションを使用することの方が安全であると言える。 また、処理を繰り返しを行う回数は「入力ファイル」のレコード数(行数)であるということに注意。 例 ) date.txt  列 1 列 2 列 3 行  1   2   3 行  4   5   6 # シェル変数 var = 100 # awkVar4の表示 awk -v awkVar4 = $var ' {print awkVar4} ' date.txt # 結果 100 100 # 「date.txt」の総レコード数(行数)である2回分の表示処理が繰り返し行われている。 他にも「-v」オプションを使わずに awk 内でシェル変数を使用する方法があるので紹介しておく。 # シェル変数 var = 100 # 変数名を「'」(シングルクォーテーション)で囲む echo | awk ' {pritn ' ${var} ' } ' # 結果 100 awk は「'」(シングルクォート)に囲まれたところを解析しようとするので、シェル変数の部分は「'」(シングルクォート)から外せばシェル変数として扱ってくれる。 目次へ awk の実践 「stress」コマンドを用いてCPUに負荷をかけそのログの統計情報を算出してみた。ログは長いので折り畳み先に記載する。 5分間の「vmstat」のログ 1 2021 / 01 / 18 09:56:00 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 2 2021 / 01 / 18 09:56:00 r b swpd free buff cache si so bi bo in cs us sy id wa st 3 2021 / 01 / 18 09:56:00 2 0 0 1152892 2284 471684 0 0 0 1 7 10 0 0 100 0 0 4 2021 / 01 / 18 09:56:01 0 0 0 1152752 2284 471680 0 0 0 0 152 333 0 0 100 0 0 5 2021 / 01 / 18 09:56:02 0 0 0 1152752 2284 471680 0 0 0 0 122 272 0 0 100 0 0 6 2021 / 01 / 18 09:56:03 0 0 0 1152752 2284 471680 0 0 0 432 166 345 0 0 100 0 0 7 2021 / 01 / 18 09:56:04 0 0 0 1152752 2284 471680 0 0 0 0 139 292 0 0 100 0 0 8 2021 / 01 / 18 09:56:05 4 0 0 1147864 2284 474028 0 0 0 0 827 263 73 2 25 0 0 9 2021 / 01 / 18 09:56:06 1 0 0 1147864 2284 474028 0 0 0 0 873 242 80 1 19 0 0 10 2021 / 01 / 18 09:56:07 1 0 0 1147864 2284 474028 0 0 0 0 867 241 80 0 20 0 0 11 2021 / 01 / 18 09:56:08 5 0 0 1147864 2284 474028 0 0 0 0 871 242 80 0 20 0 0 12 2021 / 01 / 18 09:56:09 0 0 0 1147864 2284 474028 0 0 0 0 865 241 81 0 19 0 0 13 2021 / 01 / 18 09:56:10 1 0 0 1147864 2284 474028 0 0 0 0 861 253 78 0 22 0 0 14 2021 / 01 / 18 09:56:11 1 0 0 1147864 2284 474028 0 0 0 0 874 241 80 1 19 0 0 15 2021 / 01 / 18 09:56:12 1 0 0 1147864 2284 474028 0 0 0 4 859 230 79 1 20 0 0 16 2021 / 01 / 18 09:56:13 1 0 0 1147864 2284 474028 0 0 0 0 873 250 81 0 19 0 0 17 2021 / 01 / 18 09:56:14 0 0 0 1147832 2284 474028 0 0 0 0 860 239 80 0 20 0 0 18 2021 / 01 / 18 09:56:15 1 0 0 1147832 2284 474028 0 0 0 0 871 255 79 0 21 0 0 19 2021 / 01 / 18 09:56:16 0 0 0 1147832 2284 474028 0 0 0 0 860 232 80 1 19 0 0 20 2021 / 01 / 18 09:56:17 2 0 0 1147832 2284 474028 0 0 0 0 868 239 80 0 20 0 0 21 2021 / 01 / 18 09:56:18 1 0 0 1147832 2284 474028 0 0 0 0 881 255 81 0 19 0 0 22 2021 / 01 / 18 09:56:19 0 0 0 1147832 2284 474028 0 0 0 0 852 236 79 0 21 0 0 23 2021 / 01 / 18 09:56:20 1 0 0 1147832 2284 474028 0 0 0 0 873 248 81 0 19 0 0 24 2021 / 01 / 18 09:56:21 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 25 2021 / 01 / 18 09:56:21 r b swpd free buff cache si so bi bo in cs us sy id wa st 26 2021 / 01 / 18 09:56:21 1 0 0 1147832 2284 474028 0 0 0 0 875 267 79 1 20 0 0 27 2021 / 01 / 18 09:56:22 2 0 0 1147832 2284 474028 0 0 0 0 860 235 78 1 21 0 0 28 2021 / 01 / 18 09:56:23 3 0 0 1147832 2284 474028 0 0 0 0 868 247 80 0 20 0 0 29 2021 / 01 / 18 09:56:24 1 0 0 1147832 2284 474028 0 0 0 0 871 221 81 0 19 0 0 30 2021 / 01 / 18 09:56:25 1 0 0 1147832 2284 474028 0 0 0 0 883 241 81 0 19 0 0 31 2021 / 01 / 18 09:56:26 0 0 0 1147832 2284 474028 0 0 0 0 861 249 78 1 21 0 0 32 2021 / 01 / 18 09:56:27 0 0 0 1147832 2284 474028 0 0 0 0 892 261 81 0 19 0 0 33 2021 / 01 / 18 09:56:28 0 0 0 1147832 2284 474028 0 0 0 0 869 250 79 0 21 0 0 34 2021 / 01 / 18 09:56:29 1 0 0 1147832 2284 474028 0 0 0 0 891 254 80 0 20 0 0 35 2021 / 01 / 18 09:56:30 1 0 0 1147832 2284 474028 0 0 0 0 879 250 79 2 19 0 0 36 2021 / 01 / 18 09:56:31 2 0 0 1147832 2284 474028 0 0 0 0 879 234 80 0 20 0 0 37 2021 / 01 / 18 09:56:32 0 0 0 1147832 2284 474028 0 0 0 0 887 248 81 0 19 0 0 38 2021 / 01 / 18 09:56:33 2 0 0 1147832 2284 474028 0 0 0 2 874 275 78 0 22 0 0 39 2021 / 01 / 18 09:56:34 1 0 0 1147832 2284 474028 0 0 0 0 887 261 81 0 19 0 0 40 2021 / 01 / 18 09:56:35 1 0 0 1147832 2284 474028 0 0 0 0 872 261 80 0 20 0 0 41 2021 / 01 / 18 09:56:36 4 0 0 1147832 2284 474028 0 0 0 0 891 258 80 1 19 0 0 42 2021 / 01 / 18 09:56:37 0 0 0 1147832 2284 474032 0 0 0 0 865 237 79 0 21 0 0 43 2021 / 01 / 18 09:56:38 1 0 0 1147832 2284 474032 0 0 0 0 885 260 80 0 20 0 0 44 2021 / 01 / 18 09:56:39 0 0 0 1147832 2284 474032 0 0 0 0 868 238 80 0 20 0 0 45 2021 / 01 / 18 09:56:40 1 0 0 1147832 2284 474032 0 0 0 0 883 264 78 2 20 0 0 46 2021 / 01 / 18 09:56:41 1 0 0 1147832 2284 474032 0 0 0 0 878 237 81 0 19 0 0 47 2021 / 01 / 18 09:56:42 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 48 2021 / 01 / 18 09:56:42 r b swpd free buff cache si so bi bo in cs us sy id wa st 49 2021 / 01 / 18 09:56:42 2 0 0 1147832 2284 474032 0 0 0 0 871 235 80 0 20 0 0 50 2021 / 01 / 18 09:56:43 1 0 0 1147832 2284 474032 0 0 0 8 880 223 81 0 19 0 0 51 2021 / 01 / 18 09:56:44 1 0 0 1147832 2284 474032 0 0 0 0 872 258 80 0 20 0 0 52 2021 / 01 / 18 09:56:45 2 0 0 1147832 2284 474032 0 0 0 0 856 231 78 0 22 0 0 53 2021 / 01 / 18 09:56:46 1 0 0 1147832 2284 474032 0 0 0 0 888 252 80 1 19 0 0 54 2021 / 01 / 18 09:56:47 1 0 0 1147832 2284 474032 0 0 0 0 872 242 80 0 20 0 0 55 2021 / 01 / 18 09:56:48 1 0 0 1147832 2284 474032 0 0 0 0 896 253 81 0 19 0 0 56 2021 / 01 / 18 09:56:49 0 0 0 1147832 2284 474032 0 0 0 0 874 240 81 0 19 0 0 57 2021 / 01 / 18 09:56:50 0 0 0 1147832 2284 474032 0 0 0 0 872 261 79 0 21 0 0 58 2021 / 01 / 18 09:56:51 1 0 0 1147832 2284 474032 0 0 0 0 875 229 80 1 19 0 0 59 2021 / 01 / 18 09:56:52 0 0 0 1147832 2284 474032 0 0 0 0 854 239 78 0 22 0 0 60 2021 / 01 / 18 09:56:53 1 0 0 1147832 2284 474032 0 0 0 0 869 223 81 0 19 0 0 61 2021 / 01 / 18 09:56:54 1 0 0 1147832 2284 474032 0 0 0 0 861 243 80 0 20 0 0 62 2021 / 01 / 18 09:56:55 1 0 0 1147832 2284 474032 0 0 0 0 870 227 80 0 20 0 0 63 2021 / 01 / 18 09:56:56 0 0 0 1147832 2284 474032 0 0 0 0 887 256 80 1 19 0 0 64 2021 / 01 / 18 09:56:57 1 0 0 1147832 2284 474032 0 0 0 9 856 228 79 0 21 0 0 65 2021 / 01 / 18 09:56:58 1 0 0 1147832 2284 474032 0 0 0 0 865 238 80 0 20 0 0 66 2021 / 01 / 18 09:56:59 1 0 0 1147832 2284 474032 0 0 0 0 872 219 80 0 20 0 0 67 2021 / 01 / 18 09:57:00 1 0 0 1147832 2284 474032 0 0 0 0 898 273 81 0 19 0 0 68 2021 / 01 / 18 09:57:01 0 0 0 1147832 2284 474032 0 0 0 0 868 243 79 2 19 0 0 69 2021 / 01 / 18 09:57:02 1 0 0 1147832 2284 474032 0 0 0 0 872 244 80 0 20 0 0 70 2021 / 01 / 18 09:57:03 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 71 2021 / 01 / 18 09:57:03 r b swpd free buff cache si so bi bo in cs us sy id wa st 72 2021 / 01 / 18 09:57:03 1 0 0 1147832 2284 474032 0 0 0 13 878 249 79 1 20 0 0 73 2021 / 01 / 18 09:57:04 3 0 0 1147832 2284 474032 0 0 0 0 861 232 79 0 21 0 0 74 2021 / 01 / 18 09:57:05 3 0 0 1147832 2284 474032 0 0 0 0 883 234 80 0 20 0 0 75 2021 / 01 / 18 09:57:06 5 0 0 1147832 2284 474032 0 0 0 0 880 238 79 1 20 0 0 76 2021 / 01 / 18 09:57:07 1 0 0 1147832 2284 474032 0 0 0 0 881 227 81 0 19 0 0 77 2021 / 01 / 18 09:57:08 0 0 0 1147832 2284 474032 0 0 0 0 868 235 80 0 20 0 0 78 2021 / 01 / 18 09:57:09 0 0 0 1147832 2284 474032 0 0 0 0 873 257 79 0 21 0 0 79 2021 / 01 / 18 09:57:10 0 0 0 1147832 2284 474032 0 0 0 0 901 273 81 0 19 0 0 80 2021 / 01 / 18 09:57:11 2 0 0 1147832 2284 474032 0 0 0 0 878 244 78 1 21 0 0 81 2021 / 01 / 18 09:57:12 1 0 0 1147832 2284 474032 0 0 0 0 882 242 81 0 19 0 0 82 2021 / 01 / 18 09:57:13 1 0 0 1147832 2284 474036 0 0 0 0 861 234 80 0 20 0 0 83 2021 / 01 / 18 09:57:14 1 0 0 1147832 2284 474036 0 0 0 8 876 209 81 0 19 0 0 84 2021 / 01 / 18 09:57:15 0 0 0 1147832 2284 474036 0 0 0 0 854 228 78 0 22 0 0 85 2021 / 01 / 18 09:57:16 1 0 0 1147832 2284 474036 0 0 0 0 886 265 80 1 19 0 0 86 2021 / 01 / 18 09:57:17 1 0 0 1147832 2284 474036 0 0 0 0 883 260 80 0 20 0 0 87 2021 / 01 / 18 09:57:18 1 0 0 1147832 2284 474036 0 0 0 0 878 241 80 0 20 0 0 88 2021 / 01 / 18 09:57:19 2 0 0 1147832 2284 474036 0 0 0 0 893 245 80 1 19 0 0 89 2021 / 01 / 18 09:57:20 0 0 0 1147832 2284 474036 0 0 0 0 870 235 79 1 20 0 0 90 2021 / 01 / 18 09:57:21 1 0 0 1147832 2284 474036 0 0 0 0 870 238 79 1 20 0 0 91 2021 / 01 / 18 09:57:22 1 0 0 1147832 2284 474036 0 0 0 0 878 244 81 0 19 0 0 92 2021 / 01 / 18 09:57:23 1 0 0 1147832 2284 474036 0 0 0 0 851 241 78 0 22 0 0 93 2021 / 01 / 18 09:57:24 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 94 2021 / 01 / 18 09:57:24 r b swpd free buff cache si so bi bo in cs us sy id wa st 95 2021 / 01 / 18 09:57:24 3 0 0 1147832 2284 474036 0 0 0 0 879 222 81 0 19 0 0 96 2021 / 01 / 18 09:57:25 1 0 0 1147832 2284 474036 0 0 0 0 879 244 80 0 20 0 0 97 2021 / 01 / 18 09:57:26 1 0 0 1147832 2284 474036 0 0 0 0 893 269 80 1 19 0 0 98 2021 / 01 / 18 09:57:27 0 0 0 1147832 2284 474036 0 0 0 0 882 276 79 0 21 0 0 99 2021 / 01 / 18 09:57:28 1 0 0 1147832 2284 474036 0 0 0 0 887 266 80 0 20 0 0 100 2021 / 01 / 18 09:57:29 1 0 0 1147832 2284 474036 0 0 0 0 878 250 80 0 20 0 0 101 2021 / 01 / 18 09:57:30 3 0 0 1147832 2284 474036 0 0 0 0 896 264 80 0 20 0 0 102 2021 / 01 / 18 09:57:31 2 0 0 1147832 2284 474036 0 0 0 0 886 239 80 1 19 0 0 103 2021 / 01 / 18 09:57:32 0 0 0 1147832 2284 474036 0 0 0 0 855 239 79 0 21 0 0 104 2021 / 01 / 18 09:57:33 1 0 0 1147832 2284 474036 0 0 0 67 914 280 82 0 18 0 0 105 2021 / 01 / 18 09:57:34 2 0 0 1147832 2284 474036 0 0 0 0 875 282 78 0 22 0 0 106 2021 / 01 / 18 09:57:35 1 0 0 1147832 2284 474036 0 0 0 0 891 258 81 0 19 0 0 107 2021 / 01 / 18 09:57:36 1 0 0 1147832 2284 474036 0 0 0 0 882 273 79 1 20 0 0 108 2021 / 01 / 18 09:57:37 1 0 0 1147832 2284 474036 0 0 0 0 883 256 80 0 20 0 0 109 2021 / 01 / 18 09:57:38 0 0 0 1147832 2284 474036 0 0 0 0 891 261 80 1 19 0 0 110 2021 / 01 / 18 09:57:39 3 0 0 1147832 2284 474036 0 0 0 0 881 273 78 1 21 0 0 111 2021 / 01 / 18 09:57:40 6 0 0 1147832 2284 474036 0 0 0 0 877 237 80 0 20 0 0 112 2021 / 01 / 18 09:57:41 1 0 0 1147832 2284 474036 0 0 0 0 883 249 79 1 20 0 0 113 2021 / 01 / 18 09:57:42 1 0 0 1147832 2284 474036 0 0 0 0 875 247 81 0 19 0 0 114 2021 / 01 / 18 09:57:43 0 0 0 1147832 2284 474036 0 0 0 0 886 254 81 0 19 0 0 115 2021 / 01 / 18 09:57:44 0 0 0 1147832 2284 474036 0 0 0 0 860 234 79 0 21 0 0 116 2021 / 01 / 18 09:57:45 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 117 2021 / 01 / 18 09:57:45 r b swpd free buff cache si so bi bo in cs us sy id wa st 118 2021 / 01 / 18 09:57:45 2 0 0 1147832 2284 474036 0 0 0 4 893 255 81 0 19 0 0 119 2021 / 01 / 18 09:57:46 2 0 0 1147832 2284 474036 0 0 0 0 881 302 77 1 22 0 0 120 2021 / 01 / 18 09:57:47 1 0 0 1147832 2284 474036 0 0 0 0 893 249 81 0 19 0 0 121 2021 / 01 / 18 09:57:48 1 0 0 1147832 2284 474036 0 0 0 0 866 257 80 0 20 0 0 122 2021 / 01 / 18 09:57:49 1 0 0 1147832 2284 474040 0 0 0 0 875 252 81 0 19 0 0 123 2021 / 01 / 18 09:57:50 0 0 0 1147832 2284 474040 0 0 0 0 870 247 80 0 20 0 0 124 2021 / 01 / 18 09:57:51 1 0 0 1147832 2284 474040 0 0 0 0 861 243 78 2 20 0 0 125 2021 / 01 / 18 09:57:52 0 0 0 1147832 2284 474040 0 0 0 0 861 243 80 0 20 0 0 126 2021 / 01 / 18 09:57:53 3 0 0 1147832 2284 474040 0 0 0 0 865 226 80 0 20 0 0 127 2021 / 01 / 18 09:57:54 1 0 0 1147832 2284 474040 0 0 0 0 871 221 81 0 19 0 0 128 2021 / 01 / 18 09:57:55 0 0 0 1147832 2284 474040 0 0 0 0 868 232 80 0 20 0 0 129 2021 / 01 / 18 09:57:56 1 0 0 1147832 2284 474040 0 0 0 0 879 243 79 1 20 0 0 130 2021 / 01 / 18 09:57:57 0 0 0 1147832 2284 474040 0 0 0 0 857 239 79 1 20 0 0 131 2021 / 01 / 18 09:57:58 1 0 0 1147832 2284 474040 0 0 0 0 876 264 80 0 20 0 0 132 2021 / 01 / 18 09:57:59 4 0 0 1147832 2284 474040 0 0 0 0 897 279 80 0 20 0 0 133 2021 / 01 / 18 09:58:00 3 0 0 1147832 2284 474040 0 0 0 0 875 249 80 0 20 0 0 134 2021 / 01 / 18 09:58:01 1 0 0 1147832 2284 474040 0 0 0 0 904 299 80 1 19 0 0 135 2021 / 01 / 18 09:58:02 2 0 0 1147832 2284 474040 0 0 0 0 867 260 79 0 21 0 0 136 2021 / 01 / 18 09:58:03 1 0 0 1147832 2284 474040 0 0 0 9 870 242 80 0 20 0 0 137 2021 / 01 / 18 09:58:04 1 0 0 1147832 2284 474040 0 0 0 0 863 245 80 0 20 0 0 138 2021 / 01 / 18 09:58:05 2 0 0 1147832 2284 474040 0 0 0 0 881 245 81 0 19 0 0 139 2021 / 01 / 18 09:58:06 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 140 2021 / 01 / 18 09:58:06 r b swpd free buff cache si so bi bo in cs us sy id wa st 141 2021 / 01 / 18 09:58:06 1 0 0 1147832 2284 474040 0 0 0 0 862 232 80 0 20 0 0 142 2021 / 01 / 18 09:58:07 1 0 0 1147832 2284 474040 0 0 0 0 870 269 78 1 21 0 0 143 2021 / 01 / 18 09:58:08 0 0 0 1147832 2284 474040 0 0 0 0 869 231 81 0 19 0 0 144 2021 / 01 / 18 09:58:09 1 0 0 1147832 2284 474040 0 0 0 0 862 230 80 0 20 0 0 145 2021 / 01 / 18 09:58:10 1 0 0 1147832 2284 474040 0 0 0 0 884 250 80 0 20 0 0 146 2021 / 01 / 18 09:58:11 1 0 0 1147832 2284 474040 0 0 0 0 861 239 80 0 20 0 0 147 2021 / 01 / 18 09:58:12 1 0 0 1147832 2284 474040 0 0 0 0 866 243 80 1 19 0 0 148 2021 / 01 / 18 09:58:13 1 0 0 1147832 2284 474040 0 0 0 0 859 228 80 1 19 0 0 149 2021 / 01 / 18 09:58:14 1 0 0 1147832 2284 474040 0 0 0 0 872 252 78 0 22 0 0 150 2021 / 01 / 18 09:58:15 1 0 0 1147832 2284 474040 0 0 0 0 869 232 80 0 20 0 0 151 2021 / 01 / 18 09:58:16 2 0 0 1147832 2284 474040 0 0 0 8 876 246 80 1 19 0 0 152 2021 / 01 / 18 09:58:17 1 0 0 1147832 2284 474040 0 0 0 0 876 234 80 1 19 0 0 153 2021 / 01 / 18 09:58:18 1 0 0 1147832 2284 474040 0 0 0 0 865 262 78 0 22 0 0 154 2021 / 01 / 18 09:58:19 2 0 0 1147832 2284 474040 0 0 0 0 861 231 80 0 20 0 0 155 2021 / 01 / 18 09:58:20 1 0 0 1147832 2284 474040 0 0 0 0 876 262 80 0 20 0 0 156 2021 / 01 / 18 09:58:21 1 0 0 1147832 2284 474040 0 0 0 0 876 242 81 0 19 0 0 157 2021 / 01 / 18 09:58:22 1 0 0 1147832 2284 474040 0 0 0 0 868 241 80 1 19 0 0 158 2021 / 01 / 18 09:58:23 1 0 0 1147832 2284 474040 0 0 0 0 854 230 80 0 20 0 0 159 2021 / 01 / 18 09:58:24 1 0 0 1147832 2284 474040 0 0 0 0 872 237 80 0 20 0 0 160 2021 / 01 / 18 09:58:25 2 0 0 1147832 2284 474040 0 0 0 0 840 208 79 0 21 0 0 161 2021 / 01 / 18 09:58:26 1 0 0 1147832 2284 474040 0 0 0 0 879 232 81 0 19 0 0 162 2021 / 01 / 18 09:58:27 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 163 2021 / 01 / 18 09:58:27 r b swpd free buff cache si so bi bo in cs us sy id wa st 164 2021 / 01 / 18 09:58:27 3 0 0 1147804 2284 474044 0 0 0 0 874 256 78 1 21 0 0 165 2021 / 01 / 18 09:58:28 1 0 0 1147804 2284 474044 0 0 0 0 870 237 81 0 19 0 0 166 2021 / 01 / 18 09:58:29 0 0 0 1147804 2284 474044 0 0 0 0 871 246 81 0 19 0 0 167 2021 / 01 / 18 09:58:30 0 0 0 1147804 2284 474044 0 0 0 0 850 232 79 0 21 0 0 168 2021 / 01 / 18 09:58:31 1 0 0 1147804 2284 474044 0 0 0 0 887 239 81 0 19 0 0 169 2021 / 01 / 18 09:58:32 1 0 0 1147804 2284 474044 0 0 0 0 858 241 78 1 21 0 0 170 2021 / 01 / 18 09:58:33 1 0 0 1147804 2284 474044 0 0 0 0 870 234 81 0 19 0 0 171 2021 / 01 / 18 09:58:34 1 0 0 1147804 2284 474044 0 0 0 2 873 259 80 0 20 0 0 172 2021 / 01 / 18 09:58:35 1 0 0 1147804 2284 474044 0 0 0 0 877 251 80 0 20 0 0 173 2021 / 01 / 18 09:58:36 1 0 0 1147804 2284 474044 0 0 0 0 884 251 80 1 19 0 0 174 2021 / 01 / 18 09:58:37 1 0 0 1147804 2284 474044 0 0 0 0 852 236 78 0 22 0 0 175 2021 / 01 / 18 09:58:38 1 0 0 1147804 2284 474044 0 0 0 0 856 218 79 1 20 0 0 176 2021 / 01 / 18 09:58:39 1 0 0 1147804 2284 474044 0 0 0 0 869 253 81 0 19 0 0 177 2021 / 01 / 18 09:58:40 1 0 0 1147804 2284 474044 0 0 0 0 868 243 79 1 20 0 0 178 2021 / 01 / 18 09:58:41 0 0 0 1147804 2284 474044 0 0 0 0 886 280 81 0 19 0 0 179 2021 / 01 / 18 09:58:42 1 0 0 1147804 2284 474044 0 0 0 0 857 239 79 0 21 0 0 180 2021 / 01 / 18 09:58:43 0 0 0 1147804 2284 474044 0 0 0 0 862 244 79 1 20 0 0 181 2021 / 01 / 18 09:58:44 1 0 0 1147804 2284 474044 0 0 0 0 865 236 80 0 20 0 0 182 2021 / 01 / 18 09:58:45 3 0 0 1147804 2284 474044 0 0 0 0 879 252 81 0 19 0 0 183 2021 / 01 / 18 09:58:46 1 0 0 1147804 2284 474044 0 0 0 0 860 251 80 0 20 0 0 184 2021 / 01 / 18 09:58:47 2 0 0 1147804 2284 474044 0 0 0 8 887 252 80 0 20 0 0 185 2021 / 01 / 18 09:58:48 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 186 2021 / 01 / 18 09:58:48 r b swpd free buff cache si so bi bo in cs us sy id wa st 187 2021 / 01 / 18 09:58:48 1 0 0 1147804 2284 474044 0 0 0 0 857 249 78 1 21 0 0 188 2021 / 01 / 18 09:58:49 1 0 0 1147804 2284 474044 0 0 0 0 883 250 81 0 19 0 0 189 2021 / 01 / 18 09:58:50 1 0 0 1147804 2284 474044 0 0 0 0 851 224 79 0 21 0 0 190 2021 / 01 / 18 09:58:51 4 0 0 1147804 2284 474044 0 0 0 0 881 238 81 0 19 0 0 191 2021 / 01 / 18 09:58:52 1 0 0 1147804 2284 474044 0 0 0 0 860 217 81 0 19 0 0 192 2021 / 01 / 18 09:58:53 0 0 0 1147804 2284 474044 0 0 0 0 836 215 78 1 21 0 0 193 2021 / 01 / 18 09:58:54 2 0 0 1147804 2284 474044 0 0 0 0 873 225 81 0 19 0 0 194 2021 / 01 / 18 09:58:55 0 0 0 1147804 2284 474044 0 0 0 0 857 232 79 1 20 0 0 195 2021 / 01 / 18 09:58:56 1 0 0 1147804 2284 474044 0 0 0 0 854 239 79 0 21 0 0 196 2021 / 01 / 18 09:58:57 1 0 0 1147804 2284 474044 0 0 0 0 875 234 81 0 19 0 0 197 2021 / 01 / 18 09:58:58 1 0 0 1147804 2284 474044 0 0 0 0 863 257 79 1 20 0 0 198 2021 / 01 / 18 09:58:59 1 0 0 1147804 2284 474044 0 0 0 0 857 201 81 0 19 0 0 199 2021 / 01 / 18 09:59:00 0 0 0 1147804 2284 474044 0 0 0 0 855 242 78 0 22 0 0 200 2021 / 01 / 18 09:59:01 2 0 0 1147804 2284 474044 0 0 0 0 873 247 80 0 20 0 0 201 2021 / 01 / 18 09:59:02 0 0 0 1147804 2284 474044 0 0 0 0 862 228 81 0 19 0 0 202 2021 / 01 / 18 09:59:03 2 0 0 1147804 2284 474048 0 0 0 0 868 238 79 1 20 0 0 203 2021 / 01 / 18 09:59:04 6 0 0 1147804 2284 474048 0 0 0 2 872 202 80 1 19 0 0 204 2021 / 01 / 18 09:59:05 0 0 0 1147804 2284 474048 0 0 0 0 857 225 79 0 21 0 0 205 2021 / 01 / 18 09:59:06 5 0 0 1147804 2284 474048 0 0 0 0 876 230 81 0 19 0 0 206 2021 / 01 / 18 09:59:07 2 0 0 1147804 2284 474048 0 0 0 0 861 245 79 0 21 0 0 207 2021 / 01 / 18 09:59:08 1 0 0 1147804 2284 474048 0 0 0 0 870 249 80 1 19 0 0 208 2021 / 01 / 18 09:59:09 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 209 2021 / 01 / 18 09:59:09 r b swpd free buff cache si so bi bo in cs us sy id wa st 210 2021 / 01 / 18 09:59:09 3 0 0 1147804 2284 474048 0 0 0 0 874 245 79 0 21 0 0 211 2021 / 01 / 18 09:59:10 1 0 0 1147772 2284 474048 0 0 0 0 865 226 81 0 19 0 0 212 2021 / 01 / 18 09:59:11 0 0 0 1147772 2284 474048 0 0 0 0 852 235 80 0 20 0 0 213 2021 / 01 / 18 09:59:12 1 0 0 1147772 2284 474048 0 0 0 0 866 247 79 0 21 0 0 214 2021 / 01 / 18 09:59:13 1 0 0 1147772 2284 474048 0 0 0 0 868 237 80 0 20 0 0 215 2021 / 01 / 18 09:59:14 2 0 0 1147772 2284 474048 0 0 0 0 876 239 80 1 19 0 0 216 2021 / 01 / 18 09:59:15 1 0 0 1147772 2284 474048 0 0 0 0 868 232 80 0 20 0 0 217 2021 / 01 / 18 09:59:16 0 0 0 1147772 2284 474048 0 0 0 0 870 235 80 1 19 0 0 218 2021 / 01 / 18 09:59:17 1 0 0 1147772 2284 474048 0 0 0 0 867 222 79 1 20 0 0 219 2021 / 01 / 18 09:59:18 0 0 0 1147772 2284 474048 0 0 0 8 872 264 79 0 21 0 0 220 2021 / 01 / 18 09:59:19 1 0 0 1147772 2284 474048 0 0 0 0 867 235 79 1 20 0 0 221 2021 / 01 / 18 09:59:20 1 0 0 1147772 2284 474048 0 0 0 0 875 230 81 0 19 0 0 222 2021 / 01 / 18 09:59:21 2 0 0 1147772 2284 474048 0 0 0 0 865 248 80 0 20 0 0 223 2021 / 01 / 18 09:59:22 1 0 0 1147772 2284 474048 0 0 0 0 864 205 81 0 19 0 0 224 2021 / 01 / 18 09:59:23 0 0 0 1147772 2284 474048 0 0 0 0 848 233 78 0 22 0 0 225 2021 / 01 / 18 09:59:24 1 0 0 1147772 2284 474048 0 0 0 0 866 230 80 1 19 0 0 226 2021 / 01 / 18 09:59:25 1 0 0 1147772 2284 474048 0 0 0 0 849 221 79 0 21 0 0 227 2021 / 01 / 18 09:59:26 1 0 0 1147772 2284 474048 0 0 0 0 894 259 81 0 19 0 0 228 2021 / 01 / 18 09:59:27 1 0 0 1147772 2284 474048 0 0 0 0 871 234 81 0 19 0 0 229 2021 / 01 / 18 09:59:28 0 0 0 1147772 2284 474048 0 0 0 0 844 232 79 0 21 0 0 230 2021 / 01 / 18 09:59:29 0 0 0 1147772 2284 474048 0 0 0 0 886 251 81 0 19 0 0 231 2021 / 01 / 18 09:59:30 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 232 2021 / 01 / 18 09:59:30 r b swpd free buff cache si so bi bo in cs us sy id wa st 233 2021 / 01 / 18 09:59:30 1 0 0 1147772 2284 474048 0 0 0 0 854 244 78 1 21 0 0 234 2021 / 01 / 18 09:59:31 1 0 0 1147772 2284 474048 0 0 0 0 877 259 81 0 19 0 0 235 2021 / 01 / 18 09:59:32 1 0 0 1147772 2284 474048 0 0 0 0 868 237 80 0 20 0 0 236 2021 / 01 / 18 09:59:33 3 0 0 1147772 2284 474048 0 0 0 0 875 250 80 0 20 0 0 237 2021 / 01 / 18 09:59:34 1 0 0 1147772 2284 474048 0 0 0 3 899 295 81 0 19 0 0 238 2021 / 01 / 18 09:59:35 1 0 0 1147772 2284 474048 0 0 0 0 854 236 77 1 22 0 0 239 2021 / 01 / 18 09:59:36 1 0 0 1147772 2284 474048 0 0 0 0 867 238 81 0 19 0 0 240 2021 / 01 / 18 09:59:37 3 0 0 1147772 2284 474048 0 0 0 0 871 244 79 1 20 0 0 241 2021 / 01 / 18 09:59:38 1 0 0 1147772 2284 474048 0 0 0 0 876 251 80 0 20 0 0 242 2021 / 01 / 18 09:59:39 1 0 0 1147772 2284 474048 0 0 0 0 876 247 81 0 19 0 0 243 2021 / 01 / 18 09:59:40 0 0 0 1147772 2284 474052 0 0 0 0 851 226 79 1 20 0 0 244 2021 / 01 / 18 09:59:41 0 0 0 1147772 2284 474052 0 0 0 0 886 270 81 0 19 0 0 245 2021 / 01 / 18 09:59:42 4 0 0 1147772 2284 474052 0 0 0 0 847 217 78 0 22 0 0 246 2021 / 01 / 18 09:59:43 1 0 0 1147772 2284 474052 0 0 0 0 867 236 80 1 19 0 0 247 2021 / 01 / 18 09:59:44 1 0 0 1147772 2284 474052 0 0 0 0 854 233 79 0 21 0 0 248 2021 / 01 / 18 09:59:45 4 0 0 1147772 2284 474052 0 0 0 0 886 246 80 1 19 0 0 249 2021 / 01 / 18 09:59:46 0 0 0 1147772 2284 474052 0 0 0 0 875 246 81 0 19 0 0 250 2021 / 01 / 18 09:59:47 1 0 0 1147772 2284 474052 0 0 0 0 880 280 79 0 21 0 0 251 2021 / 01 / 18 09:59:48 2 0 0 1147772 2284 474052 0 0 0 0 872 247 79 0 21 0 0 252 2021 / 01 / 18 09:59:49 2 0 0 1147772 2284 474052 0 0 0 8 886 260 81 0 19 0 0 253 2021 / 01 / 18 09:59:50 2 0 0 1147772 2284 474052 0 0 0 0 884 242 80 1 19 0 0 254 2021 / 01 / 18 09:59:51 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 255 2021 / 01 / 18 09:59:51 r b swpd free buff cache si so bi bo in cs us sy id wa st 256 2021 / 01 / 18 09:59:51 2 0 0 1147772 2284 474052 0 0 0 0 864 237 78 0 22 0 0 257 2021 / 01 / 18 09:59:52 0 0 0 1147772 2284 474052 0 0 0 0 859 216 81 0 19 0 0 258 2021 / 01 / 18 09:59:53 1 0 0 1147772 2284 474052 0 0 0 0 877 255 80 0 20 0 0 259 2021 / 01 / 18 09:59:54 1 0 0 1147772 2284 474052 0 0 0 0 869 235 81 0 19 0 0 260 2021 / 01 / 18 09:59:55 1 0 0 1147772 2284 474052 0 0 0 0 866 237 79 1 20 0 0 261 2021 / 01 / 18 09:59:56 1 0 0 1147772 2284 474052 0 0 0 0 879 278 79 1 20 0 0 262 2021 / 01 / 18 09:59:58 1 0 0 1147772 2284 474052 0 0 0 0 864 232 80 1 19 0 0 263 2021 / 01 / 18 09:59:59 3 0 0 1147772 2284 474052 0 0 0 0 871 267 78 0 22 0 0 264 2021 / 01 / 18 10:00:00 1 0 0 1147772 2284 474052 0 0 0 0 877 252 81 0 19 0 0 265 2021 / 01 / 18 10:00:01 4 0 0 1147772 2284 474052 0 0 0 0 878 263 79 1 20 0 0 266 2021 / 01 / 18 10:00:02 4 0 0 1147772 2284 474052 0 0 0 0 908 286 81 0 19 0 0 267 2021 / 01 / 18 10:00:03 2 0 0 1147772 2284 474052 0 0 0 0 863 242 79 0 21 0 0 268 2021 / 01 / 18 10:00:04 1 0 0 1147772 2284 474052 0 0 0 0 874 234 80 1 19 0 0 269 2021 / 01 / 18 10:00:05 2 0 0 1147772 2284 474052 0 0 0 4 872 240 81 0 19 0 0 270 2021 / 01 / 18 10:00:06 0 0 0 1147772 2284 474052 0 0 0 0 861 269 78 0 22 0 0 271 2021 / 01 / 18 10:00:07 1 0 0 1147772 2284 474052 0 0 0 0 884 260 81 0 19 0 0 272 2021 / 01 / 18 10:00:08 2 0 0 1147772 2284 474052 0 0 0 0 894 275 81 0 19 0 0 273 2021 / 01 / 18 10:00:09 0 0 0 1147772 2284 474052 0 0 0 0 845 245 77 1 22 0 0 274 2021 / 01 / 18 10:00:10 0 0 0 1147772 2284 474052 0 0 0 0 882 280 81 0 19 0 0 275 2021 / 01 / 18 10:00:11 1 0 0 1147772 2284 474052 0 0 0 0 867 228 80 0 20 0 0 276 2021 / 01 / 18 10:00:12 1 0 0 1147772 2284 474052 0 0 0 0 873 240 80 0 20 0 0 277 2021 / 01 / 18 10:00:13 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 278 2021 / 01 / 18 10:00:13 r b swpd free buff cache si so bi bo in cs us sy id wa st 279 2021 / 01 / 18 10:00:13 3 0 0 1147772 2284 474052 0 0 0 0 885 245 80 1 19 0 0 280 2021 / 01 / 18 10:00:14 1 0 0 1147772 2284 474052 0 0 0 0 852 232 78 2 20 0 0 281 2021 / 01 / 18 10:00:15 2 0 0 1147772 2284 474052 0 0 0 0 881 242 80 0 20 0 0 282 2021 / 01 / 18 10:00:16 0 0 0 1147772 2284 474052 0 0 0 0 870 286 79 0 21 0 0 283 2021 / 01 / 18 10:00:17 1 0 0 1147772 2284 474056 0 0 0 0 869 226 81 0 19 0 0 284 2021 / 01 / 18 10:00:18 2 0 0 1147740 2284 474056 0 0 0 0 871 249 78 1 21 0 0 285 2021 / 01 / 18 10:00:19 1 0 0 1147740 2284 474056 0 0 0 0 872 242 81 0 19 0 0 286 2021 / 01 / 18 10:00:20 2 0 0 1147740 2284 474056 0 0 0 0 864 241 81 0 19 0 0 287 2021 / 01 / 18 10:00:21 0 0 0 1147740 2284 474056 0 0 0 8 876 291 79 0 21 0 0 288 2021 / 01 / 18 10:00:22 1 0 0 1147740 2284 474056 0 0 0 0 868 220 81 0 19 0 0 289 2021 / 01 / 18 10:00:23 0 0 0 1147740 2284 474056 0 0 0 0 864 229 79 1 20 0 0 290 2021 / 01 / 18 10:00:24 1 0 0 1147740 2284 474056 0 0 0 0 854 240 80 0 20 0 0 291 2021 / 01 / 18 10:00:25 4 0 0 1147740 2284 474056 0 0 0 0 869 245 80 0 20 0 0 292 2021 / 01 / 18 10:00:26 1 0 0 1147740 2284 474056 0 0 0 0 863 231 80 0 20 0 0 293 2021 / 01 / 18 10:00:27 2 0 0 1147740 2284 474056 0 0 0 0 880 237 81 0 19 0 0 294 2021 / 01 / 18 10:00:28 2 0 0 1147740 2284 474056 0 0 0 0 864 253 77 1 22 0 0 295 2021 / 01 / 18 10:00:29 1 0 0 1147740 2284 474056 0 0 0 0 876 236 81 0 19 0 0 296 2021 / 01 / 18 10:00:30 2 0 0 1147740 2284 474056 0 0 0 0 862 229 80 0 20 0 0 297 2021 / 01 / 18 10:00:31 1 0 0 1147740 2284 474056 0 0 0 0 873 236 81 0 19 0 0 298 2021 / 01 / 18 10:00:32 0 0 0 1147740 2284 474056 0 0 0 0 853 215 80 0 20 0 0 299 2021 / 01 / 18 10:00:33 1 0 0 1147740 2284 474056 0 0 0 0 855 230 77 2 21 0 0 300 2021 / 01 / 18 10:00:34 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 301 2021 / 01 / 18 10:00:34 r b swpd free buff cache si so bi bo in cs us sy id wa st 302 2021 / 01 / 18 10:00:34 1 0 0 1147740 2284 474056 0 0 0 0 857 241 80 0 20 0 0 303 2021 / 01 / 18 10:00:35 1 0 0 1147740 2284 474056 0 0 0 47 884 256 79 1 20 0 0 304 2021 / 01 / 18 10:00:36 1 0 0 1147740 2284 474056 0 0 0 0 877 253 81 0 19 0 0 305 2021 / 01 / 18 10:00:37 1 0 0 1147740 2284 474056 0 0 0 0 867 249 80 0 20 0 0 306 2021 / 01 / 18 10:00:38 1 0 0 1147740 2284 474056 0 0 0 0 875 254 79 1 20 0 0 307 2021 / 01 / 18 10:00:39 1 0 0 1147740 2284 474056 0 0 0 0 872 231 81 0 19 0 0 308 2021 / 01 / 18 10:00:40 1 0 0 1147740 2284 474056 0 0 0 0 848 256 79 0 21 0 0 309 2021 / 01 / 18 10:00:41 0 0 0 1147740 2284 474056 0 0 0 0 874 253 81 0 19 0 0 310 2021 / 01 / 18 10:00:42 1 0 0 1147740 2284 474056 0 0 0 0 853 237 79 0 21 0 0 311 2021 / 01 / 18 10:00:43 1 0 0 1147740 2284 474056 0 0 0 0 874 243 80 1 19 0 0 312 2021 / 01 / 18 10:00:44 5 0 0 1147740 2284 474056 0 0 0 0 881 254 80 0 20 0 0 313 2021 / 01 / 18 10:00:45 1 0 0 1147740 2284 474056 0 0 0 0 870 252 80 0 20 0 0 314 2021 / 01 / 18 10:00:46 1 0 0 1147740 2284 474056 0 0 0 0 862 237 81 0 19 0 0 315 2021 / 01 / 18 10:00:47 0 0 0 1147740 2284 474056 0 0 0 0 886 284 79 1 20 0 0 316 2021 / 01 / 18 10:00:48 0 0 0 1147740 2284 474056 0 0 0 0 862 246 80 0 20 0 0 317 2021 / 01 / 18 10:00:49 1 0 0 1147740 2284 474056 0 0 0 0 873 245 79 1 20 0 0 318 2021 / 01 / 18 10:00:50 0 0 0 1147740 2284 474056 0 0 0 0 865 241 81 0 19 0 0 319 2021 / 01 / 18 10:00:51 1 0 0 1147740 2284 474056 0 0 0 0 853 231 77 1 22 0 0 320 2021 / 01 / 18 10:00:52 0 0 0 1147740 2284 474056 0 0 0 4 877 246 80 1 19 0 0 321 2021 / 01 / 18 10:00:53 1 0 0 1147740 2284 474056 0 0 0 0 858 243 80 0 20 0 0 322 2021 / 01 / 18 10:00:54 1 0 0 1147740 2284 474056 0 0 0 0 876 241 80 0 20 0 0 323 2021 / 01 / 18 10:00:55 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 324 2021 / 01 / 18 10:00:55 r b swpd free buff cache si so bi bo in cs us sy id wa st 325 2021 / 01 / 18 10:00:55 2 0 0 1147740 2284 474060 0 0 0 0 853 204 81 0 19 0 0 326 2021 / 01 / 18 10:00:56 1 0 0 1147740 2284 474060 0 0 0 0 870 248 79 1 20 0 0 327 2021 / 01 / 18 10:00:57 1 0 0 1147740 2284 474060 0 0 0 0 868 232 81 0 19 0 0 328 2021 / 01 / 18 10:00:58 0 0 0 1147740 2284 474060 0 0 0 0 852 241 78 0 22 0 0 329 2021 / 01 / 18 10:00:59 1 0 0 1147740 2284 474060 0 0 0 0 866 242 81 0 19 0 0 330 2021 / 01 / 18 10:01:00 1 0 0 1147740 2284 474060 0 0 0 0 867 238 80 0 20 0 0 331 2021 / 01 / 18 10:01:01 1 0 0 1147740 2284 474060 0 0 0 0 868 234 79 1 20 0 0 332 2021 / 01 / 18 10:01:02 0 0 0 1147592 2284 474068 0 0 0 0 873 304 79 2 19 0 0 333 2021 / 01 / 18 10:01:03 1 0 0 1147592 2284 474068 0 0 0 0 886 247 82 0 18 0 0 334 2021 / 01 / 18 10:01:04 1 0 0 1147592 2284 474068 0 0 0 0 856 228 81 0 19 0 0 335 2021 / 01 / 18 10:01:05 1 0 0 1147592 2284 474068 0 0 0 10 850 232 77 1 22 0 0 336 2021 / 01 / 18 10:01:06 0 0 0 1147592 2284 474068 0 0 0 0 164 270 4 0 96 0 0 337 2021 / 01 / 18 10:01:07 0 0 0 1147592 2284 474068 0 0 0 0 133 290 0 1 99 0 0 338 2021 / 01 / 18 10:01:08 0 0 0 1147592 2284 474068 0 0 0 0 143 313 0 0 100 0 0 339 2021 / 01 / 18 10:01:09 0 0 0 1147592 2284 474068 0 0 0 0 126 274 0 1 99 0 0 340 2021 / 01 / 18 10:01:10 0 0 0 1147592 2284 474068 0 0 0 0 121 264 0 0 100 0 0 341 2021 / 01 / 18 10:01:11 0 0 0 1147592 2284 474068 0 0 0 0 125 268 0 0 100 0 0 342 2021 / 01 / 18 10:01:12 0 0 0 1147592 2284 474068 0 0 0 0 120 269 0 0 100 0 0 343 2021 / 01 / 18 10:01:13 1 0 0 1147592 2284 474068 0 0 0 0 107 230 0 0 100 0 0 344 2021 / 01 / 18 10:01:14 0 0 0 1147592 2284 474068 0 0 0 0 108 244 1 0 99 0 0 345 2021 / 01 / 18 10:01:15 0 0 0 1147560 2284 474068 0 0 0 0 112 243 0 0 100 0 0 346 2021 / 01 / 18 10:01:16 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 347 2021 / 01 / 18 10:01:16 r b swpd free buff cache si so bi bo in cs us sy id wa st 348 2021 / 01 / 18 10:01:16 1 0 0 1147560 2284 474068 0 0 0 0 116 260 0 0 100 0 0 349 2021 / 01 / 18 10:01:17 0 0 0 1147560 2284 474068 0 0 0 0 122 265 0 0 100 0 0 350 2021 / 01 / 18 10:01:18 0 0 0 1147560 2284 474068 0 0 0 0 114 255 0 0 100 0 0 351 2021 / 01 / 18 10:01:19 0 0 0 1147560 2284 474068 0 0 0 0 149 316 0 0 100 0 0 352 2021 / 01 / 18 10:01:20 0 0 0 1147560 2284 474068 0 0 0 0 147 321 0 1 99 0 0 353 2021 / 01 / 18 10:01:21 1 0 0 1147560 2284 474068 0 0 0 0 146 318 0 0 100 0 0 354 2021 / 01 / 18 10:01:22 0 0 0 1147560 2284 474068 0 0 0 0 133 294 0 0 100 0 0 355 2021 / 01 / 18 10:01:23 0 0 0 1147560 2284 474068 0 0 0 8 130 281 0 0 100 0 0 356 2021 / 01 / 18 10:01:24 0 0 0 1147560 2284 474068 0 0 0 0 118 254 1 1 98 0 0 357 2021 / 01 / 18 10:01:25 0 0 0 1147560 2284 474068 0 0 0 0 111 246 0 0 100 0 0 358 2021 / 01 / 18 10:01:26 0 0 0 1147560 2284 474068 0 0 0 0 125 275 0 0 100 0 0 359 2021 / 01 / 18 10:01:27 0 0 0 1147560 2284 474068 0 0 0 0 138 297 0 1 99 0 0 360 2021 / 01 / 18 10:01:28 0 0 0 1147560 2284 474068 0 0 0 0 115 256 0 0 100 0 0 361 2021 / 01 / 18 10:01:29 0 0 0 1147560 2284 474068 0 0 0 0 114 250 0 0 100 0 0 362 2021 / 01 / 18 10:01:30 0 0 0 1147560 2284 474068 0 0 0 0 128 280 0 0 100 0 0 363 2021 / 01 / 18 10:01:31 0 0 0 1147560 2284 474072 0 0 0 0 121 269 0 0 100 0 0 364 2021 / 01 / 18 10:01:32 0 0 0 1147560 2284 474072 0 0 0 0 99 220 0 0 100 0 0 365 2021 / 01 / 18 10:01:33 0 0 0 1147560 2284 474072 0 0 0 0 111 240 0 0 100 0 0 366 2021 / 01 / 18 10:01:34 0 0 0 1147560 2284 474072 0 0 0 0 130 282 0 0 100 0 0 367 2021 / 01 / 18 10:01:35 0 0 0 1147560 2284 474072 0 0 0 50 112 239 0 0 100 0 0 368 2021 / 01 / 18 10:01:36 0 0 0 1147560 2284 474072 0 0 0 0 130 282 0 0 100 0 0 実務でよく使用されるのが「r(実行中と実行待ち中のプロセス数の合計)の欄」と「CPU」の欄であるため、今回はその二つの平均を awk を用いて算出する。 「rの平均(プロセスの平均)」 awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sum += $3; count +=1} END{print "プロセス数合計:"sum, "プロセス数平均:"sum/count} ' vmstat_0956_1001.log ※「vmstat_0956_1001.log」はログファイル名 # 結果 プロセス数合計: 363 プロセス数平均: 1 . 08036 「CPUの各情報毎の平均」 # us(カーネルコード以外の実行に使用した時間) awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sumUs += $15; count +=1} END{print "実行に使用した時間の合計:"sumUs, "実行に使用した時間の平均:"sumUs/count} ' vmstat_0956_1001.log # 結果 実行に使用した時間の合計: 23939 実行に使用した時間の平均: 71 . 247 # sy(カーネルコード以外の実行に使用した時間) awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sumSy += $16; count +=1} END{print "実行に使用した時間の合計:"sumSy, "実行に使用した時間の平均:"sumSy/count} ' vmstat_0956_1001.log # 結果 実行に使用した時間の合計: 100 実行に使用した時間の平均: 0 . 297619 # id(アイドル時間) awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sumId += $17; count +=1} END{print "アイドル時間の合計:"sumId, "アイドル時間の平均:"sumId/count} ' vmstat_0956_1001.log # 結果 アイドル時間の合計: 9561 アイドル時間の平均: 28 . 4554 # wa(I/O待ち時間) awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sumWa += $18; count +=1} END{print "I/O待ち時間の合計:"sumWa, "I/O待ち時間の平均:"sumWa/count} ' vmstat_0956_1001.log # 結果 I/O待ち時間の合計: 0 I/O待ち時間の平均: 0 # st(CPUリソースを割当ててもらえなかった時間の割合) awk ' (NR % 23 < 1 ) || (NR % 23 > 2) {sumSt += $19; count +=1} END{print "CPUリソースを割当ててもらえなかった時間の割合の合計:"sumSt, "CPUリソースを割当ててもらえなかった時間の割合の平均:"sumSt/count} ' vmstat_0956_1001.log # 結果 CPUリソースを割当ててもらえなかった時間の割合の合計: 0 CPUリソースを割当ててもらえなかった時間の割合の平均: 0 目次へ おわりに 今回は awk コマンドについての説明を執筆させていただきました。 awk の魅力が少しでも伝わっていれば幸いです。少しでも興味を持った方はとてもとっつきやすいので触ってみてはどうでしょうか。解説に関しては長々となってしまい申し訳ありません。本当を言えば筆者の書きたいことはまだまだあるのですが、これ以上長くなってしまうのは忍びないのでこのあたりで〆させていただきます。ご高覧ありがとうございました。 また、 Linux コマンドの一覧表を以下ブログにてまとめておりますので、ご参考ください。 よく使うLinuxコマンド一覧【最新版】 Linux の理解をより深めたい方へ以下関連おすすめブログ ・ ls コマンド 【使い方 まとめ】 ・ find コマンド 【使い方 まとめ】 ・ iptables まとめ【Linux ファイアウォール】 ・ sed コマンド【使い方 まとめ】 ・ vi コマンド【使い方まとめ】 ・ Linuxのファイル操作でよく使うLinuxコマンド ・ 実務で使える!基本的なシェル(Linux)コマンドの話 ~forとsed~ ・ 【Linux】今振り返りたい、プロセスって何?   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 https://rakus.hubspotpagebuilder.com/visit_engineer/ rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに 感想 ドメイン駆動設計との出会い 今ではそんなに悪い奴ではないかなと思えるように 知見 なぜドメイン駆動設計は難しく感じられるのか 原典の重厚さ 導入タイミングの難しさ ドメインエキスパートの協力が得られない ドメインモデルは育てるもの 自社サービスにおけるドメインの捉え方 アーキテクチャには適切なフェーズがある これからドメイン駆動設計を実践する方へ 初めは軽量DDDからでも良い すでにある程度開発が進んでおり、ドメイン知識と技術的負債をコードがたっぷり蓄えているプロジェクト これから立ち上げるプロジェクト おすすめはユビキタス言語から いかにしてドメイン知識を得るか 最後に はじめに こんにちは。logyです。 私は前職で一年と少し、 ラク スに入社してから一年間 ドメイン 駆動設計に関連する開発に関わってきました。 今回は、そこから得られた知見や感想、また、これから ドメイン 駆動設計を実践する方におすすめの方法について書こうと思います。 感想 ドメイン 駆動設計との出会い 私が ドメイン 駆動設計に出会ったのは、前職での部署異動がきっかけでした。 新しく関わることになるプロジェクトが ドメイン 駆動設計を採用しており、当然、メンバーになった私もその思想を習得する必要があったのですが、 当時、まだ オブジェクト指向 ですらあまりしっくりと来ていなかった私にとって、分厚い エヴァ ンス本 *1 は解読困難な古文書のようでした。 並行して実践 ドメイン 駆動設計も読み始めましたが、こちらはまだ幾分わかりやすかった気がします。 そのプロジェクトは所謂業務システムとは少し異なる抽象的な領域をターゲットにしており、 ドメイン 自体を理解するのも非常に難易度が高かったと思います。 一方で、戦術的設計が至るところで用いられており、今思えば、実践 ドメイン 駆動設計を辞書代わりにコードリーディングを行うには最高の環境でした。 そんな環境で1年ほど過ごして様々なノウハウを身に着けていきました。 今ではそんなに悪い奴ではないかなと思えるように ドメイン 駆動設計に出会ってから2年以上経った今でも、「完全に理解した」とは到底言えず、むしろ「全くわからない」状態に近いのが正直なところです。 しかし、これまで目にしてきたような、すっかり ドメイン が流出してしまっており、何度も ヒアリ ングを行い想像を重ねることでやっと理解できるようなあのシステム達も、 ドメイン 駆動設計を取り入れれば、もしかしたら救ってあげられるのかも、と思うようになったり、実装を進める際にはこれは果たして ドメイン の関心事だろうか、と意識するようになりました。 。 当然 ドメイン 駆動設計も 銀の弾丸 ではありませんが、うまく使えば、複雑なシステムに立ち向かうための強力な戦力になってくれるはずです。 知見 なぜ ドメイン 駆動設計は難しく感じられるのか 最近はいろんな記事などで触れられることが多い ドメイン 駆動設計ですが、その内容もさることながら、よく同時に語られるのがその難しさについてです。 導入を試みたが、難しすぎて駄目だった、思ったような効果が得られなかったといった声をよく耳にします。 なぜこのようなことになってしまうのでしょうか。私は次の3つが大きな原因ではないかと考えています。 原典の重厚さ その当時の私も同じくこれに心が折られました。 頑張って読み進めてもなかなか頭に入ってこない。 原典で語られている話は抽象的なものが多く、一読しただけでは「じゃあ、どうすれば?」となりがちです。 特に初学者の場合は、実践 ドメイン 駆動設計から読むことをおすすめします。 最近は他にも初学者向けに日本語で書かれたものがいくつか出ているので、分厚い本に抵抗感のある方はそちらから入っても良いかもしません。 ドメイン 駆動設計を学べる書籍について、別の ラク スエンジニアがまとめた記事がありますので、こちらもどうぞ。 tech-blog.rakus.co.jp 導入タイミングの難しさ 正直言って、 ドメイン 駆動設計を導入する際の初期コストは大きいです。 ドメイン モデリング を行うコストを払う必要があったり、大量の ドメイン クラスを作ることで実装コストも膨らみます。 その分、保守コストを削減できるのがメリットなのですが、その効果を実感できるのは、ある程度システムが複雑化してからになるため、 開発初期の段階で導入判断を行うのは難しいのではないでしょうか。 一方で、すでに複雑化してしまったシステムに、徐々に ドメイン 駆動設計を適用することも、かなり骨の折れる作業となり、導入を妨げている要因といえそうです。 ドメイン エキスパートの協力が得られない ドメイン 駆動設計の導入に際して、一番のハードルと言っても良いのではないでしょうか。 ビジネスサイドと開発サイドが肩を並べて隣で仕事をしているような現場であれば、大きな問題にはならないですが、 サイロ化が進んでいる組織では、定期的に時間を割いてもらうのがやっとで、必要なときに十分な協力を得られないことがよくあります。 たとえ、開発サイドが ドメイン 駆動設計の導入に意気込んでいたとしても、ビジネスサイドの理解が得られず、いつの間にか自然消滅…なんてことにも。 ドメイン 駆動設計の文脈から外れてしまうため、このあたりにしておきますが、組織に関する問題は、 システム開発 につきものです。 *2 幸い、 ラク スでは、ビジネスサイドはもちろん、実際のユーザーでもあるコーポレート部門の方たちにも積極的に協力いただきながら開発を進めることができています。 非常に恵まれた環境で開発を進められてることに、この場を借りて感謝したいと思います。 ドメイン モデルは育てるもの とりあえず モデリング をやってみたはいいものの、あまりしっくりこなかったり、すぐに実状と合わなくなってしまったという経験をお持ちの方も多いのではないでしょうか。 しかし、そこで諦めてはいけません。むしろそこからモデルを進化させ続けることこそ ドメイン 駆動設計の醍醐味です。 特にプロジェクト初期のように、業務理解が浅いうちは、それ相応のモデルしか作成できないはずです。 しかし、 ドメイン モデリング を行うことそれ自体が、 ドメイン へのより深い理解を促し、新たな概念の発見を通じて、既存のモデルをより洗練されたモデルへと進化させます。 自社サービスにおける ドメイン の捉え方 ラク スのように自社サービスを開発している現場では、 ドメイン を分析する際に業務 ドメイン とはまた違った難しさが存在します。 それは ドメイン がすぐに変化しやすいという点です。 SaaS の開発では、数多くのお客様に使っていただくため、ある程度、どの現場でも共通する業務をターゲットに開発を行います。 つまり、製品が扱う ドメイン としては、特定のお客様の ドメイン ではなく、ある程度、最大公約数的なものをイメージして定義することになります。 それぞれ微妙に異なる ドメイン すると、「このお客様の現場では正しいけど、別の現場では不適切」となるような ドメイン 知識を得ることもあり、製品としてどうあるべきか常に折り合いをつけ続ける必要があります。 サービスが成長し続ける限り、その検討範囲は拡大し、時折自己矛盾を生むこともあるでしょう。 それらを反映した ドメイン モデルを維持し続けることの大変さは、想像に難くないはずです。 また、これまで存在しなかったような、革新的な機能を提供するサービスでは、そもそも新たな ドメイン そのものを作り出す作業が必要となるため、これもまた大変な仕事になりそうです。 アーキテクチャ には適切なフェーズがある 実践 ドメイン 駆動設計で触れられているような、CQRS+ESといった アーキテクチャ は、複雑なシステムを制御する手段として有効です。 私は実際にこれを採用しているプロジェクトに関わったこともありますが、確かに ドメイン イベントとイベントソーシングの相性は抜群で、どんな要件にも柔軟に対応できるのではという気持ちにもなりました。 集約の設計を突き詰めると、そのうちCQRS+ESにたどり着くといった話も時折耳にします。 しかし、あくまでそれは、そのプロジェクトがいくつもの検討の末にたどり着いた解決手段の一つであるということを忘れてはいけません。 プロジェクトの初期から「DDDをやるならCQRS+ESで決まりでしょ!」みたいな発想で初めるのは明らかに アンチパターン です。 私が関わったプロジェクトでも、初めからCQRS+ESが採用されていたわけではなく、発展を続けるうちに、いろんなパターンを経てたどり着いた、とのことでした。 RDSの限界やNoSQLの発展がCQRS+ESを生み出したように、別の技術の発展がそのうちまた新たな方式の アーキテクチャ を生み、発展させていくのでしょう。 現行の アーキテクチャ が将来の発展に耐えられそうにないと判断したとき、課題に応じた解決策を選択できるようにしたいですね。 これから ドメイン 駆動設計を実践する方へ 初めは軽量DDDからでも良い 所謂、軽量DDDは アンチパターン と言われることが多いですが、個人的には、初めは軽量DDDから導入を進めるのも良い選択だと思います。 というのも、上記で述べたように、 ドメイン 駆動設計は導入、学習コストが非常に高く付く点がネックです。 そこで、戦術的設計を先に導入することで、徐々に理解を深め、最終的に戦略的設計の適用を目指せば、初期コストを低く抑えつつ導入を進められます。 また、評価の結果、 ドメイン 駆動設計がプロジェクトに合わないと判断された場合でも、戦術的設計は オブジェクト指向設計 の1パターンとしてその先も利用でき、負債になりづらいです。 とはいえ、将来的に戦略的設計を全く採用する気がないのであれば、 ドメイン 駆動設計の導入はおすすめしません。 様々な記事で触れられているように、戦術的設計はあくまで ドメイン と向き合うための足掛かりとなる手段であり、最終的には ドメイン を通じてソフトウェアの価値を高めることが ドメイン 駆動設計の目的だからです。 そこで、おすすめしたいのは、次のような導入方法です。 すでにある程度開発が進んでおり、 ドメイン 知識と技術的負債をコードがたっぷり蓄えているプロジェクト 戦術的設計(軽量DDD)を用いて、既存のコードを整理し、 ドメイン 知識を抽出する。 DDDに慣れてきたら戦略的設計を適用してみる。 これから立ち上げるプロジェクト 戦略的設計を積極的に適用し、 ドメイン モデルと共に成長を目指す。 おすすめは ユビキタス 言語から 仮に戦略的設計を導入することになった場合、まずは ユビキタス 言語の徹底から始めることをおすすめします。 通常の システム開発 でも、用語集や辞書といったものを用いて用語の統一を図ることが多いですね。 しかし、 ユビキタス 言語は単なる用語集づくりではありません。 ユビキタス 言語の特徴としては、統一した用語を開発者だけでなく、 ドメイン エキスパートとの会話でも使用する点が挙げられます。 これを徹底することで、仕様とコードの行き来が圧倒的に楽になり、コミュニケーションミスも減ります。 また、 ドメイン エキスパートと開発者が使用する僅かな用語の違いから、新たな ドメイン 知識を発見するなど、 ドメイン のより深い理解に繋がります。 他の戦略的設計である「境界づけられたコンテキスト」の実践にも、 ユビキタス 言語は非常に重要な役割を果たすため、初めに適用することで後続につなげることもできます。 いかにして ドメイン 知識を得るか もちろん ドメイン エキスパートとの会話が一番の機会になるでしょう。 仕様の相談はもちろん、そのときは開発と直接関係がないような業務上の話題まで、 ドメイン エキスパートと同じ目線で ドメイン を捉えることができるようになればなるほど、 開発はスムーズに進むようになるはずです。 また、 ドメイン エキスパートが普段触れている情報源に触れてみるのも一つの手段です。 日々のニュースや、必読とされている書籍など、少しでも同じ知識を手に入れることで、普段の会話にもついていきやすくなります。 関連する資格取得を目指すのも良いかもしれません。銀行検定、証券外務員、簿記、社労士など、資格勉強を通じてたくさんの ドメイン 知識に触れることが出来ます。 最後に この記事では、私が2年間と少し ドメイン 駆動設計に関わって得られた、感じたことについてまとめてきました。 ドメイン 駆動設計については、多くの方がさまざまな媒体を通じて情報発信されています。 私も色々見聞きはしたものの、まだ実践できていないようなプ ラク ティスもたくさんありますので、引き続き経験を積んでいきたいと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com *1 : エリック・ エヴァ ンスの ドメイン 駆動設計。 ドメイン 駆動設計の原典と言われる。 *2 : コンウェイ の法則や 割れ窓理論 、 パーキンソンの法則 を初めとして健全な開発を続けるには人間の性質とうまく付き合っていく必要がありますね。
アバター