<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>fujimisakari blog</title>
  <icon>https://www.gravatar.com/avatar/e8298aa752035f534e26ff4313ea31dd</icon>
  
  <link href="http://blog.fujimisakari.com/atom.xml" rel="self"/>
  
  <link href="http://blog.fujimisakari.com/"/>
  <updated>2025-02-16T14:19:10.235Z</updated>
  <id>http://blog.fujimisakari.com/</id>
  
  <author>
    <name>fujimisakari</name>
    <email>fujimisakari@gmail.com</email>
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>久しぶりの投稿</title>
    <link href="http://blog.fujimisakari.com/look_back_on_past/"/>
    <id>http://blog.fujimisakari.com/look_back_on_past/</id>
    <published>2025-02-16T00:00:00.000Z</published>
    <updated>2025-02-16T14:19:10.235Z</updated>
    
    <content type="html"><![CDATA[<p>ここ数年、仕事の忙しさとか環境の変化とか、いろんな要因が重なって、業務以外でTechを触るモチベーションが下がってた。2017年くらいからPostの頻度が落ち始めて、2018〜2020年は仕事に追われ、2020〜2024年は完全に趣味の沼にどっぷり。</p><p>久々に昔のPostを見返してみたら、当時はTechを楽しんでたな〜って改めて思う。そんな中、最近 “エンジニアの楽園vim-jp” っていうpodcastを知った。昔から知ってるエンジニアの人たちや、上の世代の人たちが今もバリバリやってるのを聞いて、いろいろ刺激を受けた。</p><p>今は家庭や趣味もあるし、前みたいにTech漬けにはなれないけど、また少しずつやっていこうかなって思ってる。</p><h2 id="2019"><a href="#2019" class="headerlink" title="2019"></a>2019</h2><ul><li>家を購入<ul><li>コロナ前だったので、運よく高騰前に買えた</li></ul></li><li>芝生にハマりだす<ul><li>全面青い芝生を目指す</li><li>ここから長い戦いとなり、結局全面青い芝生を手に入れたのは2024年だったw</li><li>写真は2024年のもの</li></ul></li></ul><img src="/image/life/2025-02-16-shibafu.jpg" width="420" /><h2 id="2020"><a href="#2020" class="headerlink" title="2020"></a>2020</h2><ul><li>コロナでフルリモート生活が始まる</li><li>たぶんこの辺りでインスタ以外のSNSはほぼやめた。趣味に振った感じ</li><li>観葉植物にハマりだす</li></ul><img src="/image/life/2025-02-16-plants-1.jpg" width="420" /><h2 id="2021"><a href="#2021" class="headerlink" title="2021"></a>2021</h2><ul><li>観葉植物の沼へ<ul><li>普通の植物では物足りず、壁にかけることができるビカクシダにハマりだす</li></ul></li></ul><img src="/image/life/2025-02-16-plants-2.jpg" width="420" /><h2 id="2022"><a href="#2022" class="headerlink" title="2022"></a>2022</h2><ul><li>植物に飽き足らず、アクアリウムの沼へ<ul><li>水槽は前々から興味あったので思い切って踏み込んだ</li><li>ネイチャーアクアリムというジャンルで、森林の一部を切り取ったイメージの水槽</li></ul></li></ul><img src="/image/life/2025-02-16-aquarium.jpg" width="420" /><h2 id="2023"><a href="#2023" class="headerlink" title="2023"></a>2023</h2><ul><li>アクアリウム<ul><li>半年ぐらいはモチベ高かったけど、水質をキープするため週1、2回の水換えが大変と感じてくるようなってきた</li></ul></li><li>観葉植物を減らす<ul><li>水やりや剪定、害虫対策など日々のメンテが大変すぎて管理が難しくなってきた</li><li>大きめなのが60鉢以上はあったと思うけど、20鉢ぐらい減らした</li></ul></li><li>アガベ、塊根<ul><li>これも前々から興味があったので手出してしまった</li><li>他に比べると割と手間がかからないことに気づく</li><li>そして沼にハマっていく</li></ul></li></ul><img src="/image/life/2025-02-16-agave-shelf-1.jpg" width="420" /><h2 id="2024"><a href="#2024" class="headerlink" title="2024"></a>2024</h2><ul><li>アクアリウムを処分<ul><li>1年半ぐらいやったところでモチベがなくなり、業者に取りに来てもらった</li></ul></li><li>ビカクシダを減らす<ul><li>水やりコストが高かった。部屋の中で水やりできないので毎回外へ持っていっていた</li><li>最大20数個はあったと思うけど半分ぐらいにした</li></ul></li><li>アガベ<ul><li>過去一ハマったと思う</li><li>高級株や園芸資材をたくさん揃えた</li><li>大して変わらないのだけど、毎朝起きて観察するのが楽しみ</li></ul></li></ul><img src="/image/life/2025-02-16-agave-shelf-2.jpg" width="420" /><h2 id="2025"><a href="#2025" class="headerlink" title="2025"></a>2025</h2><ul><li>フルリモートから週2出社のハイブリットへ<ul><li>ずっとフルリモートやってると、社会から外れていく疎外感を感じてたので</li><li>個人的には週1、2ぐらいの出社がちょうど合ってる気がしてる</li></ul></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;ここ数年、仕事の忙しさとか環境の変化とか、いろんな要因が重なって、業務以外でTechを触るモチベーションが下がってた。2017年くらいからPostの頻度が落ち始めて、2018〜2020年は仕事に追われ、2020〜2024年は完全に趣味の沼にどっぷり。&lt;/p&gt;
&lt;p&gt;久々に昔</summary>
      
    
    
    
    
    <category term="life" scheme="http://blog.fujimisakari.com/tags/life/"/>
    
  </entry>
  
  <entry>
    <title>APIリクエストの各認証方式</title>
    <link href="http://blog.fujimisakari.com/api-authentication/"/>
    <id>http://blog.fujimisakari.com/api-authentication/</id>
    <published>2020-12-29T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.841Z</updated>
    
    <content type="html"><![CDATA[<p>APIリクエストの認証っていろいろあって、よくわからなくなるので整理してみました。<br>（認証フローとかは省いてます）</p><h2 id="認証-認可について"><a href="#認証-認可について" class="headerlink" title="認証&#x2F;認可について"></a>認証&#x2F;認可について</h2><p>認証を整理するためには、認証&#x2F;認可の役割は知っとく必要があります。</p><h3 id="認証（Authentication）とは"><a href="#認証（Authentication）とは" class="headerlink" title="認証（Authentication）とは"></a>認証（Authentication）とは</h3><p>「誰であるか」を本人確認すること。<br>ID、PWでの認証が一般的な方法ですが、APIだとどのような認証方法があるかは後述してます。</p><h3 id="認可（Authorization）とは"><a href="#認可（Authorization）とは" class="headerlink" title="認可（Authorization）とは"></a>認可（Authorization）とは</h3><p>「何の権限を持っているか」を確認、実証すること。<br>認可する過程で、認証処理が含まれてるので混乱しやすいです。<br>たとえば、OAuthで認可する場合、クライアント（サードパーティAPP）はユーザーから権限を与えてもらい認可を得ます。この権限を与える行為は事前にユーザー認証しないと行えないので、ややこしくなってます。</p><h2 id="さまざまな認証方式"><a href="#さまざまな認証方式" class="headerlink" title="さまざまな認証方式"></a>さまざまな認証方式</h2><p>自分がよく見る認証方式をまとめてみました。</p><h3 id="■-標準化されている認証方式"><a href="#■-標準化されている認証方式" class="headerlink" title="■ 標準化されている認証方式"></a>■ 標準化されている認証方式</h3><p>認証方式を標準化をしている団体はいくつもあって、有名どこだとIETF（RFC）やOASIS（SAML）、OpenID財団（OpenID）ってとこでしょうか。</p><h4 id="・Basic（RFC-2617）"><a href="#・Basic（RFC-2617）" class="headerlink" title="・Basic（RFC 2617）"></a>・Basic（<a href="https://tex2e.github.io/rfc-translater/html/rfc2617.html">RFC 2617</a>）</h4><p>ユーザ名とパスワードをBase64方式でエンコードして送る。<br>TLSではデータは見ませんが、平文と同じなんでユーザー認証として安全な方法ではないのでので、開発環境など内部システム認証によく使われてたりします。</p><h4 id="・Digest（RFC-2617）"><a href="#・Digest（RFC-2617）" class="headerlink" title="・Digest（RFC 2617）"></a>・Digest（<a href="https://tex2e.github.io/rfc-translater/html/rfc2617.html">RFC 2617</a>）</h4><p>サーバーとクライアントで共有シークレット（パスワード）を知っていることが前提の方式。<br>クライアントでユーザー名、パスワード、ランダムな文字列をMD5でハッシュ化し、サーバーに送信する。 サーバー側でもハッシュ値を計算し、 クライアントから送信されたハッシュ値と合致するかを検証する。Basic最大の弱点である平文でパスワードを送信することなく行うことができるメリットはありますが、MD5ハッシュなので強度が強くないです。</p><h4 id="・Bearer（RFC-6750）"><a href="#・Bearer（RFC-6750）" class="headerlink" title="・Bearer（RFC 6750）"></a>・Bearer（<a href="https://openid-foundation-japan.github.io/rfc6750.ja.html">RFC 6750</a>）</h4><p>RFC 6750は認可サーバーにより発行されたアクセストークンを、Web APIに渡す方法を定めた仕様です。<br>OAuth 2.0の認可機構として設計されたものですが、OAuthに限らず汎用的なHTTP認可で使ってよいと書いてあります。</p><p>次の3つの方法を定義しています。</p><ul><li>ヘッダーのAuthorizationに埋め込む方法</li><li>リクエストボディーへ埋め込む方法（access_tokenパラメータ）</li><li>クエリーパラメーターとして渡す方法（access_tokenパラメータ）</li></ul><h4 id="・OAuth1-0（RFC-5849）"><a href="#・OAuth1-0（RFC-5849）" class="headerlink" title="・OAuth1.0（RFC 5849）"></a>・OAuth1.0（<a href="https://openid-foundation-japan.github.io/rfc5849.ja.html">RFC 5849</a>）</h4><p>OAuthは、2007年に標準化されたユーザの同意のもと任意のサービスへ認可情報を移譲する仕様です。<br>クライアント（サードパーティAPP）へのアクセストークン付与可否をユーザに確認する仕組みですが、認可する過程で認証処理が含まてるので、OAuthを認証と扱っているケースは多数見受けられます。<br>※ RFC 5849で定められていますが、この仕様はOAuth2.0（RFC 6749）の策定をもって廃止されました。</p><h4 id="・OAuth2-0（RFC-6749）"><a href="#・OAuth2-0（RFC-6749）" class="headerlink" title="・OAuth2.0（RFC 6749）"></a>・OAuth2.0（<a href="https://openid-foundation-japan.github.io/rfc6749.ja.html">RFC 6749</a>）</h4><p>OAuth2.0は、OAuth1.0の脆弱性の対策を施して2012年に改定されたものです。<br>個人的には、このOAuthの認可が認証コンテキストを複雑化させている印象でした。<br>また、OAuth 2.0を認証利用することは問題があります（<a href="https://www.sakimura.org/2012/02/1487/">車が通れるほどのどでかいセキュリティー・ホールができる</a>）。<br>implicit flowを使って「認証」すると、トークンの正当性を確かめる術がないので、なりすましできることがわかります。</p><h4 id="・OpenID-Authentication（OpenID-Authentication-2-0-最終版）"><a href="#・OpenID-Authentication（OpenID-Authentication-2-0-最終版）" class="headerlink" title="・OpenID Authentication（OpenID Authentication 2.0 - 最終版）"></a>・OpenID Authentication（<a href="https://openid-foundation-japan.github.io/openid-authentication.html">OpenID Authentication 2.0 - 最終版</a>）</h4><p>OAuthが認可を目的にした仕様でしたが、OpenID Authenticationは認証を目的とした仕様になります。<br>ユーザ認証された旨の情報、認証をどのように行ったかの情報、当該ユーザの属性情報を、クライアント（サードパーティAPP）に対して転送する仕組みです。認証のみを提供しているため、権限を認可するような仕組みはありません。<br>各サービスに散在したID&#x2F;PASS管理の煩雑さを解決策する仕様として広がっていきましたが、「認証だけでなく機能（権限）まで連携させたい」という思想が多く、OAuthが一気に普及しました。<br>※ 2014年にOpenID Connectによって置換えられました。</p><h4 id="・OpenID-Connect（OpenID-Connect-Core-1-0）"><a href="#・OpenID-Connect（OpenID-Connect-Core-1-0）" class="headerlink" title="・OpenID Connect（OpenID Connect Core 1.0）"></a>・OpenID Connect（<a href="https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html">OpenID Connect Core 1.0</a>）</h4><blockquote><p>OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.</p></blockquote><p><a href="https://openid.net/connect/">Webページ</a>には、OpenID ConnectはOAuth2.0プロトコル上にシンプルなアイデンティティレイヤーを付与したものとのこと（<a href="https://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa#oauth-20-%E3%81%A8-openid-connect">実際は全然違うようですw</a>）</p><p>OpenIDは、OAuth認証の手軽さから普及に至りませんでしたが、OAuth認証による認証レベルは低く問題がありました。そこでOAuth認証の手軽さとOpenIDの高い認証レベルを備えたOpenID Connectが策定されました。<br>これによりOAuthによる認可と同時に、OpenID Connectによる認証もできるようになりました。</p><h3 id="■-セキュリティトークン（ワンタイムパスワード）"><a href="#■-セキュリティトークン（ワンタイムパスワード）" class="headerlink" title="■ セキュリティトークン（ワンタイムパスワード）"></a>■ セキュリティトークン（ワンタイムパスワード）</h3><p>セキュリティトークンとは「ワンタイムパスワード」を生成する機械やソフトウェアのことで、ワンタイムパスワードとは、一度限りの使い捨てのパスワードのことになります。<br>強固なセキュリティが必要なWebサービスでは通常の認証ではセキュリティの観点から安全とは言い切れません。そのため、2要素認証（or多要素認証）サポートをしているところが多く、通常認証+ワンタイムパスワードの組み合わせはよく見かけます。<br>セキュリティトークンの種類はいくつかあって、物理カード型とアプリ（Google Authenticator）は自分もよく使ってます。<br>また、文脈は違いますがAPI通信でも一時的なワンタイムパスワードを発行して、認証利用しているとこはよく見かけますね。</p><h3 id="■-APIキー認証"><a href="#■-APIキー認証" class="headerlink" title="■ APIキー認証"></a>■ APIキー認証</h3><p>APIキーは、アカウント情報などはなくアプリケーションを識別する暗号化された単純な文字列です。<br>クライアントがRequest内にAPIキーを含めて送る認証方式で、仕込み先はヘッダーやクエリー、bodyなどサービスによって異なります。一般公開データに匿名でアクセスする場合に便利で広く利用されています。<br>非常にシンプルに実装できる一方、APIキーはクライアントからアクセス可能で簡単に盗まれる可能性があったり、キーがあれば誰でも認証を通過できることから、安全性が低い認証方式です。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa">OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る</a></li><li><a href="https://qiita.com/TakahikoKawasaki/items/185d34814eb9f7ac7ef3">OAuth &amp; OpenID Connect 関連仕様まとめ</a></li><li><a href="https://www.nttpc.co.jp/technology/oauth.html">OAuthの概要とそれを取り巻く環境</a></li><li><a href="https://www.atmarkit.co.jp/ait/articles/1209/27/news138.html">「OpenID Connect」を理解する</a></li><li><a href="https://yyh-gl.github.io/tech-blog/blog/id_token_and_access_token/">【OAuth 2.0 &#x2F; OIDC】アクセストークンとIDトークンの違い ＋ OIDC誕生の歴史</a></li><li><a href="https://cybersecurity-jp.com/security-measures/30358">セキュリティトークンとは？仕組みやメリット、種類について徹底解説</a></li><li><a href="https://architecting.hateblo.jp/entry/2020/03/27/033758">Web API認証方式のパターン</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;APIリクエストの認証っていろいろあって、よくわからなくなるので整理してみました。&lt;br&gt;（認証フローとかは省いてます）&lt;/p&gt;
&lt;h2 id=&quot;認証-認可について&quot;&gt;&lt;a href=&quot;#認証-認可について&quot; class=&quot;headerlink&quot; title=&quot;認証&amp;#x2</summary>
      
    
    
    
    
    <category term="Security" scheme="http://blog.fujimisakari.com/tags/Security/"/>
    
  </entry>
  
  <entry>
    <title>SSL/TLS通信の仕組み</title>
    <link href="http://blog.fujimisakari.com/ssl-tls-flow/"/>
    <id>http://blog.fujimisakari.com/ssl-tls-flow/</id>
    <published>2020-12-13T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.841Z</updated>
    
    <content type="html"><![CDATA[<p>SSL&#x2F;TLS通信の仕組みってよく忘れてしまい毎回ググってるのでメモがてら整理してみた。<br>仕組みを理解するには、デジタル署名、デジタル証明書の理解が肝となる。</p><h2 id="デジタル署名"><a href="#デジタル署名" class="headerlink" title="デジタル署名"></a>デジタル署名</h2><p>デジタル署名は、送信されてきたデータが間違いなく本人のものであるのかを証明するのための技術で公開鍵暗号よって実現してます。<br>送信者の送信データをハッシュ値に算出し暗号化したものがデジタル署名にあたり、受信者は受信データをハッシュ値に算出しデジタル署名を復号して結果と比較します。ハッシュ値と一致するば送信者のもの判断しデータの改ざん確認を行えます。</p><img src="/image/security/digital_sign.png" width="800" /><ol><li>送信者が公開鍵、秘密鍵を発行する</li><li>事前に送信者が受信者へ公開鍵を渡す</li><li>送信者がメッセージを作成</li><li>メッセージからハッシュ値を算出</li><li>ハッシュ値を秘密鍵で暗号化しデジタル署名を作成</li><li>メッセージとデジタル署名を送信</li><li>受信したメッセージからハッシュ値を算出</li><li>公開鍵を利用してデジタル署名を復号する</li><li>算出したハッシュ値と復号したハッシュ値を比較する</li></ol><h3 id="デジタル署名のアルゴリズム"><a href="#デジタル署名のアルゴリズム" class="headerlink" title="デジタル署名のアルゴリズム"></a>デジタル署名のアルゴリズム</h3><p>デジタル署名アルゴリズムは、公開鍵暗号方式とハッシュ関数を組み合わせたもので代表的なものに次がある。</p><table><thead><tr><th>アルゴリズム</th><th>組み合わせ</th></tr></thead><tbody><tr><td>Sha-1WithRSAEncryption</td><td>ハッシュ関数にSHA-1、公開鍵暗号方式にRSA</td></tr><tr><td>id-dsa-with-sha1</td><td>ハッシュ関数にSHA-1、公開鍵暗号方式にDSA</td></tr><tr><td>ecdsa-with-SHA1</td><td>ハッシュ関数にSHA-1、公開鍵暗号方式にECDSA</td></tr><tr><td>Sha-1WithRSAEncryption</td><td>ハッシュ関数にSHA-1、公開鍵暗号方式にRSA</td></tr><tr><td>Sha-256WithRSAEncryption</td><td>ハッシュ関数にSHA-256、公開鍵暗号方式にRSA</td></tr></tbody></table><h2 id="デジタル証明書"><a href="#デジタル証明書" class="headerlink" title="デジタル証明書"></a>デジタル証明書</h2><p>デジタル証明書は、認証局（CA）へCSR（公開鍵と申請情報）を送付し発行されたもので、通信時に相手に渡して自身がなりすましでなく本人と証明することができます。SSL&#x2F;TLS通信では事前に安全に公開鍵を渡す必要があり、公開鍵とデジタル署名を組み合わせたデジタル証明書が実現してくれます。</p><img src="/image/security/digital_certificate.png" width="800" /><ol><li>公開鍵、秘密鍵を発行</li><li>CSR（公開鍵と申請情報）を含め認証局へ送付し、デジタル証明書の発行依頼する</li><li>認証局はCSRをハッシュ化して秘密鍵でデジタル署名したデジタル証明書を生成する</li><li>クライアントへデジタル証明書を発行する</li><li>クライアントがサーバーへリクエスト</li><li>サーバーはクライアントへデジタル証明書を渡す</li><li>ブラウザがプリインストールされている認証局の公開鍵でデジタル証明書のデジタル署名を復号し、なりすまし確認をする</li><li>サーバーの正当確認がとれればデジタル証明書の公開鍵でSSL&#x2F;TLS通信を開始する</li></ol><h2 id="SSL-TLS通信の仕組み"><a href="#SSL-TLS通信の仕組み" class="headerlink" title="SSL&#x2F;TLS通信の仕組み"></a>SSL&#x2F;TLS通信の仕組み</h2><p>SSL&#x2F;TLS通信は、インターネット上で通信相手を証明し通信データを暗号化して安全にやりとりするプロトコルです。<br>これまでのデジタル署名、デジタル証明書を組み合わせて実現されています。</p><ul><li>ざっくり要点だけ<ul><li>デジタル証明書からデジタル署名でなりすまし確認が行え、安全に公開鍵を渡せる</li><li>公開鍵暗号で安全に共通鍵をサーバへ渡せる</li><li>共通鍵でデータ暗号化させた通信を行える</li></ul></li></ul><img src="/image/security/ssl_flow.png" width="800" /><ol><li>SSLリクエスト</li><li>デジタル証明証を送る</li><li>クライアントが共通鍵を生成（正確には共通鍵のSecretKeyを生成）</li><li>サーバの公開鍵で共通鍵を暗号化</li><li>サーバへ共通鍵を送る</li><li>サーバは秘密鍵で共通鍵を復号</li><li>サーバー、クライアントが共に共通鍵を持てたのでデータ暗号化通信を行います</li></ol><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.infraexpert.com/study/security5.html">デジタル署名の仕組み</a><br><a href="https://www.infraexpert.com/study/security6.html">デジタル証明書の仕組み</a><br><a href="https://www.infraexpert.com/study/security7.html">SSLとは</a><br><a href="https://www.ipa.go.jp/security/pki/024.html">PKI関連技術に関するコンテンツ</a><br><a href="https://www.jstage.jst.go.jp/article/itej/69/3/69_228/_pdf/-char/ja">SSL&#x2F;TLSの仕組みを知っていますか？</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;SSL&amp;#x2F;TLS通信の仕組みってよく忘れてしまい毎回ググってるのでメモがてら整理してみた。&lt;br&gt;仕組みを理解するには、デジタル署名、デジタル証明書の理解が肝となる。&lt;/p&gt;
&lt;h2 id=&quot;デジタル署名&quot;&gt;&lt;a href=&quot;#デジタル署名&quot; class=&quot;head</summary>
      
    
    
    
    
    <category term="Security" scheme="http://blog.fujimisakari.com/tags/Security/"/>
    
  </entry>
  
  <entry>
    <title>k8sのServiceMeshとルーティング、LB</title>
    <link href="http://blog.fujimisakari.com/service_mesh_and_routing_and_lb/"/>
    <id>http://blog.fujimisakari.com/service_mesh_and_routing_and_lb/</id>
    <published>2020-11-24T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.840Z</updated>
    
    <content type="html"><![CDATA[<p>最近よく聞くk8sのServiceMesh。業務では使ってるけどいまいち理解が追いついてないのでルーティング方法とLBについて調べてみました。</p><h2 id="ServiceMeshとは"><a href="#ServiceMeshとは" class="headerlink" title="ServiceMeshとは"></a>ServiceMeshとは</h2><p>マイクロサービス間に張り巡らされたメッシュ状の通信やその経路を制御する考え方です。<br>OSSしてはIstio &#x2F; Conduit &#x2F; Linkerdなどが有名で、今回はIstioについて調べました。</p><h2 id="Istio"><a href="#Istio" class="headerlink" title="Istio"></a>Istio</h2><img src="/image/k8s/istio.svg" width="220" /><p><a href="https://istio.io/">https://istio.io/</a></p><p>Google &#x2F; IBM &#x2F; Lyftが中心となって開発しているOSSで、ServiceMeshのフレームワークになります。<br>MS間のトラフィック管理機能などを持つEnvoyと組み合わせてServiceMesh機能を実現しています。</p><p>いろいろ機能ありますがよく使われそうなものとしては次のようなものがありました。</p><ul><li>負荷分散（v1へ90％、v2へ10%とか）</li><li>動的な通信制御（ルールベースでのルーティング）</li><li>サービス間のアクセス制御</li><li>Timeout制御</li><li>Retry制御</li><li>Rate Limit</li><li>Circuit Breaker</li><li>Fault Injection</li><li>通信メトリクス収集</li></ul><h3 id="仕組み"><a href="#仕組み" class="headerlink" title="仕組み"></a>仕組み</h3><p>各PodにSidecarとしてEnvoyプロキシ(コンテナ)が埋め込み、トラフィックが流れていく際には同じPodに内包されているEnvoyが一度トラフィックを受けてからトラフィックを転送します。<br>API → Serviceと連動している場合だと次のようなフローとなります</p><img src="/image/k8s/sidecar_envoy.png" width="720" height="420" /><h3 id="IstioとEnvoyの役割"><a href="#IstioとEnvoyの役割" class="headerlink" title="IstioとEnvoyの役割"></a>IstioとEnvoyの役割</h3><p>IstioはControlPlane, EnvoyはDataPlaneで役割を持っています。<br>ControlPlaneはトラフィックコントロールの定義（Pilot）, メトリクスの収集先（Mixer）, MS間の認証設定（mTLS）。<br>DataPlaneが実際の実務処理を行っています。定義に基づいたトラフィックコントロール、メトリクス送信、MS間認証。</p><h2 id="ルーティングについて"><a href="#ルーティングについて" class="headerlink" title="ルーティングについて"></a>ルーティングについて</h2><p>Istioのルーティング（Virtual Service）について調べだすと、Envoy Proxyでルーティングやロードバランシングなどいろいろ登場してきて混乱するので整理しました。</p><h3 id="IstioのVirtualService"><a href="#IstioのVirtualService" class="headerlink" title="IstioのVirtualService"></a>IstioのVirtualService</h3><p>VirtualServiceはService(L4)へルールベースでルーティングするアプローチです。<br>このルールベースでルーティングはEnvoyを利用して実現されています。L7まで受けてからルーティングしてるので、HTTP Headerなどもルールとして利用できます。</p><img src="/image/k8s/istio_virtual_service.png" width="720" height="420" /><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">networking.istio.io/v1beta1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">VirtualService</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">microservice-B</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">hosts:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">microservice-B.default.svc.cluster.local</span></span><br><span class="line">  <span class="attr">http:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">match:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">dev1</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">headers:</span></span><br><span class="line">        <span class="attr">microservice-B-router:</span></span><br><span class="line">          <span class="attr">exact:</span> <span class="string">dev1</span></span><br><span class="line">    <span class="attr">route:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">destination:</span></span><br><span class="line">        <span class="attr">host:</span> <span class="string">microservice-B-dev1.default.svc.cluster.local</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">match:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">dev2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">headers:</span></span><br><span class="line">        <span class="attr">microservice-B-router:</span></span><br><span class="line">          <span class="attr">exact:</span> <span class="string">dev2</span></span><br><span class="line">    <span class="attr">route:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">destination:</span></span><br><span class="line">        <span class="attr">host:</span> <span class="string">microservice-B-dev2.default.svc.cluster.local</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">match:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">dev3</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">headers:</span></span><br><span class="line">        <span class="attr">microservice-B-router:</span></span><br><span class="line">          <span class="attr">exact:</span> <span class="string">dev3</span></span><br><span class="line">    <span class="attr">route:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">destination:</span></span><br><span class="line">        <span class="attr">host:</span> <span class="string">microservice-B-dev3.default.svc.cluster.local</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">default</span></span><br><span class="line">    <span class="attr">route:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">destination:</span></span><br><span class="line">        <span class="attr">host:</span> <span class="string">microservice-B.default.svc.cluster.local</span></span><br></pre></td></tr></table></figure><h3 id="EnvoyのFront-Proxy"><a href="#EnvoyのFront-Proxy" class="headerlink" title="EnvoyのFront Proxy"></a>EnvoyのFront Proxy</h3><p>EnvoyのFront proxyは次のように、Istioとは関係のないEnvoyのみでのProxy機能を利用するケースになります。<br>定義方法が異なりますが、機能的にはEnvoyを内包しているIstioのVirtualServiceと同様のものとなります。<br>使いどことしては、リクエストを受ける最前段Pod（GWなど）が考えられると思います。IstioのVirtualServiceは、upstreemにIstioが導入されている必要があり、リクエストを受ける最前段PodではVirtualServiceを利用することができないので。</p><img src="/image/k8s/envoy_proxy.png" width="720" height="420" /><h3 id="EnvoyはLBもできる"><a href="#EnvoyはLBもできる" class="headerlink" title="EnvoyはLBもできる"></a>EnvoyはLBもできる</h3><p>gRPC通信（http2）などコネクションが貼られっぱなしになるプロトコルでは、常に同じPodに対してリクエスト送るのでLBが難しくなりますが、Envoyを中継させることで、upstreemにいるpodへのLBが容易になり相性が良いです。</p><h3 id="Headless-ServiceでのLB方法"><a href="#Headless-ServiceでのLB方法" class="headerlink" title="Headless ServiceでのLB方法"></a>Headless ServiceでのLB方法</h3><p>VirtualService(EnvoyのProxy)を使わずに、k8sのHeadless Service（内部DNS経由で各PodのIPが取得できる）+ gRPC Client Side LBを利用して、LBすることもできます。Headless Serviceを利用してUpStreemにいるすべてのPodとコネクションを貼り、gRPC Client Sideからリクエストに利用するコネクションをRound RobinしてLBします。</p><img src="/image/k8s/headless_service_lb.png" width="720" height="420" /><h2 id="参考書籍、URL"><a href="#参考書籍、URL" class="headerlink" title="参考書籍、URL"></a>参考書籍、URL</h2><ul><li><a href="https://www.amazon.co.jp/Kubernetes%E5%AE%8C%E5%85%A8%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-Top-Gear-%E9%9D%92%E5%B1%B1/dp/4295009792">Kubernetes完全ガイド</a></li><li><a href="https://speakerdeck.com/ido_kara_deru/benefits-and-usage-notes-of-istio-based-on-our-system-operation-experience">CNDK2019 1年間のシステム運用を通して分かったIstioの嬉しさと活用における注意点</a></li><li><a href="https://deeeet.com/writing/2018/03/30/kubernetes-grpc/">Kubernetes上でgRPCサービスを動かす</a></li><li><a href="https://i-beam.org/2019/01/22/hello-envoy/">Envoy Proxyに入門した</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近よく聞くk8sのServiceMesh。業務では使ってるけどいまいち理解が追いついてないのでルーティング方法とLBについて調べてみました。&lt;/p&gt;
&lt;h2 id=&quot;ServiceMeshとは&quot;&gt;&lt;a href=&quot;#ServiceMeshとは&quot; class=&quot;header</summary>
      
    
    
    
    
    <category term="Kubernetes" scheme="http://blog.fujimisakari.com/tags/Kubernetes/"/>
    
  </entry>
  
  <entry>
    <title>公開鍵暗号</title>
    <link href="http://blog.fujimisakari.com/public-key/"/>
    <id>http://blog.fujimisakari.com/public-key/</id>
    <published>2020-11-01T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.840Z</updated>
    
    <content type="html"><![CDATA[<p>暗号化技術の勉強で前回は、<a href="https://blog.fujimisakari.com/aes-key/">共通鍵</a> をやったので公開鍵暗号をやりました。</p><h2 id="公開鍵暗号"><a href="#公開鍵暗号" class="headerlink" title="公開鍵暗号"></a>公開鍵暗号</h2><p>暗号化と復号に別々の鍵（公開鍵、秘密鍵）で行う暗号方式です。暗号化を公開鍵で行い、復号を秘密鍵で行なわれます。暗号化アルゴリズムにはRSAや楕円曲線（Elliptic Curve）暗号などがあります。</p><p>RSAの鍵長サイズは1,024から4,096ビッドで、サイズは256の倍数でなければなりません。<br>鍵長が大きくなるほど暗号強度が向上しますが、データ処理に時間と負荷がかかるので安全性と処理負荷を考慮したうえで、鍵長を決めることが大切です。現在鍵長は2048ビット以上が推奨されています。</p><p>共通鍵暗号と公開鍵暗号のデータ処理時間に特徴があり、共通鍵暗号は処理が早く、公開鍵暗号は処理が遅いです。<br>そのため、データ通信なのでは共通鍵暗号でデータを暗号化し、公開鍵で共通鍵を暗号化するハイブリット方式により安全性と高速化を実現しています。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">privateKey, _ := rsa.GenerateKey(rand.Reader, <span class="number">2048</span>)</span><br><span class="line">publicKey := &amp;privateKey.PublicKey</span><br><span class="line"></span><br><span class="line"><span class="comment">// Encrypt</span></span><br><span class="line">plainText := []<span class="type">byte</span>(<span class="string">&quot;fizzbuzzfizzbuzz...&quot;</span>)</span><br><span class="line">cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Cipher text: %x\n&quot;</span>, cipherText)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Decrypt</span></span><br><span class="line">decryptedText, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Decrypted text: %s\n&quot;</span>, decryptedText)</span><br></pre></td></tr></table></figure><h2 id="参考URL"><a href="#参考URL" class="headerlink" title="参考URL"></a>参考URL</h2><ul><li><a href="https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7">https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7</a></li><li><a href="https://deeeet.com/writing/2015/11/10/go-crypto/">https://deeeet.com/writing/2015/11/10/go-crypto/</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;暗号化技術の勉強で前回は、&lt;a href=&quot;https://blog.fujimisakari.com/aes-key/&quot;&gt;共通鍵&lt;/a&gt; をやったので公開鍵暗号をやりました。&lt;/p&gt;
&lt;h2 id=&quot;公開鍵暗号&quot;&gt;&lt;a href=&quot;#公開鍵暗号&quot; class=&quot;heade</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
    <category term="Security" scheme="http://blog.fujimisakari.com/tags/Security/"/>
    
  </entry>
  
  <entry>
    <title>共通鍵暗号</title>
    <link href="http://blog.fujimisakari.com/aes-key/"/>
    <id>http://blog.fujimisakari.com/aes-key/</id>
    <published>2020-10-29T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.839Z</updated>
    
    <content type="html"><![CDATA[<p>パブリックなネットワーク上でデータのやり取りを行う場合には、第三者にデータを盗み見や改ざんさせない仕組みが必要になります。<br>その仕組みとしてデータの暗号化があります。暗号化方式には共通鍵暗号と公開鍵暗号が2種類あり、まずは共通鍵暗号を勉強してみました。</p><h2 id="共通鍵暗号"><a href="#共通鍵暗号" class="headerlink" title="共通鍵暗号"></a>共通鍵暗号</h2><p>暗号化と復号を同一鍵で行う暗号方式です。<br>共通鍵暗号の暗号化アルゴリズムでは長い間DESが利用されてきましたが、コンピューター性能が上がってきたことでDESの安全性が低下してきました。そこで後継となる次世代アルゴリズムの公募があり2000年にAESが採択されました。<br>現在はこのAESが利用されおり、ブロック暗号で鍵長が128bit &#x2F; 192bit &#x2F; 256bitから選択でき、また平文は128bitずつ暗号化されます。</p><h2 id="暗号モード"><a href="#暗号モード" class="headerlink" title="暗号モード"></a>暗号モード</h2><p>DESやAESといったブロック暗号は一定のビット数を暗号化する方式でしたが、メッセージはビット数までしか暗号化できませんでした。そこで暗号化したい長いメッセージをビット長のブロックに分割し、繰り返しブロック暗号化を行えるようにするため暗号モードが必要になりました。</p><p>暗号モードには、秘匿用の利用モードと、認証用の利用モードがあります。</p><h3 id="秘匿用の利用モード"><a href="#秘匿用の利用モード" class="headerlink" title="秘匿用の利用モード"></a>秘匿用の利用モード</h3><p>暗号化モードでECB, CBC, OFB, CFB, CTRのモードがあり、ブロック長とストリームで分類できます。</p><ul><li>ECB, CBCは、特定のブロック長で処理を行う暗号化</li><li>OFB, CFB, CTRは、ストリームで順次処理を行う暗号化</li></ul><p>今回はブロック暗号化に絞って調べてみました</p><h4 id="ECB（Electronic-CodeBook）"><a href="#ECB（Electronic-CodeBook）" class="headerlink" title="ECB（Electronic CodeBook）"></a>ECB（Electronic CodeBook）</h4><p>ECBモードは、単純な暗号利用モードでメッセージがブロックに分割され、それぞれのブロックは独立して暗号化されます。<br>このモードは欠点がって、長いメッセージ（画像データなど）のある部分が他の部分と同じであるかどうかが暗号文の比較によって判断できてしまうので、他のCBCモードなどが考えられました。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">plainText := <span class="string">&quot;fizzbuzzfizzbuzz&quot;</span>  <span class="comment">// 16bye</span></span><br><span class="line">key := []<span class="type">byte</span>(<span class="string">&quot;passpasspasspass&quot;</span>) <span class="comment">// 16bye</span></span><br><span class="line"></span><br><span class="line">block, err := aes.NewCipher(key)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cipherText []<span class="type">byte</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Encrypt</span></span><br><span class="line">cipherText = <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="built_in">len</span>(plainText))</span><br><span class="line">block.Encrypt(cipherText, []<span class="type">byte</span>(plainText))</span><br><span class="line">fmt.Printf(<span class="string">&quot;Cipher text: %x\n&quot;</span>, cipherText)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Decrypt</span></span><br><span class="line">decryptedText := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="built_in">len</span>(cipherText))</span><br><span class="line">block.Decrypt(decryptedText, cipherText)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Decrypted text: %s\n&quot;</span>, <span class="type">string</span>(decryptedText))</span><br></pre></td></tr></table></figure><h4 id="CBC（Cipher-Block-Chaining）"><a href="#CBC（Cipher-Block-Chaining）" class="headerlink" title="CBC（Cipher Block Chaining）"></a>CBC（Cipher Block Chaining）</h4><p>CBCモードは、ECBモードの欠点を補われたモードで最も広く用いられている暗号利用モードになります。<br>1つ前の暗号文ブロックと平文ブロックをXORしたものを暗号化することを繰り返す手法になります。またメッセージごとのユニーク性を確保するため、最初のブロックの暗号化には初期化ベクトル（iv）が利用されます。<br>ただブロック暗号化の仕様上しかたない部分ではありますが、ブロックのビット長の整数倍となるようメッセージはパディング調整する必要があります。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">plainText := <span class="string">&quot;fizzbuzzfizzbuzz&quot;</span>  <span class="comment">// 16bye</span></span><br><span class="line">key := []<span class="type">byte</span>(<span class="string">&quot;passpasspasspass&quot;</span>) <span class="comment">// 16bye</span></span><br><span class="line"></span><br><span class="line">block, err := aes.NewCipher(key)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cipherText []<span class="type">byte</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Encrypt</span></span><br><span class="line">cipherText = <span class="built_in">make</span>([]<span class="type">byte</span>, aes.BlockSize+<span class="built_in">len</span>(plainText))</span><br><span class="line">iv := cipherText[:aes.BlockSize]</span><br><span class="line">_, err = rand.Read(iv)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">cbc := cipher.NewCBCEncrypter(block, iv)</span><br><span class="line">cbc.CryptBlocks(cipherText[aes.BlockSize:], []<span class="type">byte</span>(plainText))</span><br><span class="line">fmt.Printf(<span class="string">&quot;Cipher text: %x\n&quot;</span>, cipherText)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Decrypt</span></span><br><span class="line">_ = cipherText[:aes.BlockSize] <span class="comment">// iv</span></span><br><span class="line">cipherText = cipherText[aes.BlockSize:]</span><br><span class="line">decryptedText := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="built_in">len</span>(cipherText))</span><br><span class="line">cbc = cipher.NewCBCDecrypter(block, iv)</span><br><span class="line">cbc.CryptBlocks(decryptedText, cipherText)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Decrypted text: %s\n&quot;</span>, <span class="type">string</span>(decryptedText))</span><br></pre></td></tr></table></figure><h3 id="認証用の利用モード"><a href="#認証用の利用モード" class="headerlink" title="認証用の利用モード"></a>認証用の利用モード</h3><p>暗号化と同時に完全性や認証性も実現するための暗号モードで、総称してAEDA (Authenticated Encryption with Asocciated Data) と呼ばれます。<br>次のものなどが知られていて、今回はGCMについて調べてみました。</p><ul><li>CCM (Counter with CBC-MAC)</li><li>GCM (Galois&#x2F;Counter Mode)</li><li>OCB (Offset CodeBook)</li><li>XCBC (eXtended Ciphertext Block Chaining)</li></ul><h4 id="GCM-Galois-Counter-Mode"><a href="#GCM-Galois-Counter-Mode" class="headerlink" title="GCM (Galois&#x2F;Counter Mode)"></a>GCM (Galois&#x2F;Counter Mode)</h4><p>GCMは暗号化としてCTRモードを、認証としてTAG(Galois mode)を生成してを組み合わせたもので、これによりデータ保護と認証性（完全性確認）が実現されます。<br>ブロック長128ビットのブロック暗号に適用可能でパディングは不要です。最初に任意長の初期化ベクトル（iv）を必要とします。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">plainText := <span class="string">&quot;fizzbuzzfizzbuzz...&quot;</span></span><br><span class="line">key := []<span class="type">byte</span>(<span class="string">&quot;passpasspasspass&quot;</span>) <span class="comment">// 16bye</span></span><br><span class="line"></span><br><span class="line">block, err := aes.NewCipher(key)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">aesGCM, err := cipher.NewGCM(block)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Encrypt</span></span><br><span class="line">nonce := <span class="built_in">make</span>([]<span class="type">byte</span>, aesGCM.NonceSize())</span><br><span class="line"><span class="keyword">if</span> _, err = io.ReadFull(rand.Reader, nonce); err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Printf(<span class="string">&quot;err: %s\n&quot;</span>, err)</span><br><span class="line">&#125;</span><br><span class="line">cipherText := aesGCM.Seal(nonce, nonce, []<span class="type">byte</span>(plainText), <span class="literal">nil</span>)</span><br><span class="line">fmt.Printf(<span class="string">&quot;Cipher text: %x\n&quot;</span>, cipherText)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Decrypt</span></span><br><span class="line">nonceSize := aesGCM.NonceSize()</span><br><span class="line">nonce, cipherText = cipherText[:nonceSize], cipherText[nonceSize:]</span><br><span class="line">decryptedText, err := aesGCM.Open(<span class="literal">nil</span>, nonce, cipherText, <span class="literal">nil</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">fmt.Printf(<span class="string">&quot;Decrypted text: %s\n&quot;</span>, <span class="type">string</span>(decryptedText))</span><br></pre></td></tr></table></figure><h2 id="参考URL"><a href="#参考URL" class="headerlink" title="参考URL"></a>参考URL</h2><ul><li><a href="https://tkengo.github.io/blog/2015/12/01/https-details/">https://tkengo.github.io/blog/2015/12/01/https-details/</a></li><li><a href="https://deeeet.com/writing/2015/11/10/go-crypto/">https://deeeet.com/writing/2015/11/10/go-crypto/</a></li><li><a href="https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E5%88%A9%E7%94%A8%E3%83%A2%E3%83%BC%E3%83%89#CTR">https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E5%88%A9%E7%94%A8%E3%83%A2%E3%83%BC%E3%83%89#CTR</a></li><li><a href="https://ja.wikipedia.org/wiki/Galois/Counter_Mode">https://ja.wikipedia.org/wiki/Galois/Counter_Mode</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;パブリックなネットワーク上でデータのやり取りを行う場合には、第三者にデータを盗み見や改ざんさせない仕組みが必要になります。&lt;br&gt;その仕組みとしてデータの暗号化があります。暗号化方式には共通鍵暗号と公開鍵暗号が2種類あり、まずは共通鍵暗号を勉強してみました。&lt;/p&gt;
&lt;h2</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
    <category term="Security" scheme="http://blog.fujimisakari.com/tags/Security/"/>
    
  </entry>
  
  <entry>
    <title>TCPのSocket Closeでハマった</title>
    <link href="http://blog.fujimisakari.com/tcp-socket-close/"/>
    <id>http://blog.fujimisakari.com/tcp-socket-close/</id>
    <published>2020-01-25T00:00:00.000Z</published>
    <updated>2025-02-09T16:50:04.706Z</updated>
    
    <content type="html"><![CDATA[<p>TCP通信開発に慣れていないのとTCP状態遷移を理解できていなかったのでSocket Closeしようとしてハマってしまった。</p><p>前提</p><ul><li>やろうとしていたこと<ul><li>TCP ServerとClientを開発していて、Serverが落ちたときClientからリトライで再接続するような仕組み</li></ul></li><li>ハマったっこと<ul><li>Clientからリトライで再接続するとき、<code>bind: address already in use</code> のエラーで失敗し続けてしまい原因がわからなかった</li></ul></li></ul><h2 id="再現させるために同等のミニマムコードを用意"><a href="#再現させるために同等のミニマムコードを用意" class="headerlink" title="再現させるために同等のミニマムコードを用意"></a>再現させるために同等のミニマムコードを用意</h2><h3 id="Server側"><a href="#Server側" class="headerlink" title="Server側"></a>Server側</h3><p>Clientからの接続を受けてすぐCloseさせる</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;net&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">listener, _ := net.Listen(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;:7070&quot;</span>)</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">conn, err := listener.Accept()</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;failed to connection&quot;</span>)</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line">&#125;</span><br><span class="line">conn.Close()</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Client側"><a href="#Client側" class="headerlink" title="Client側"></a>Client側</h3><p>Serverへ2回接続をトライする</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;net&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">newConn</span><span class="params">()</span></span> &#123;</span><br><span class="line">remote, _ := net.ResolveTCPAddr(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;0.0.0.0:7070&quot;</span>)</span><br><span class="line">local, _ := net.ResolveTCPAddr(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;0.0.0.0:10001&quot;</span>)</span><br><span class="line">_, err := net.DialTCP(<span class="string">&quot;tcp&quot;</span>, local, remote)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Println(err)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;take1&quot;</span>)</span><br><span class="line">newConn()</span><br><span class="line">time.Sleep(<span class="number">5</span> * time.Second)</span><br><span class="line">fmt.Println(<span class="string">&quot;take2&quot;</span>)</span><br><span class="line">newConn()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="実行してみる"><a href="#実行してみる" class="headerlink" title="実行してみる"></a>実行してみる</h4><p>2回目のServer接続時にすでに使用済みエラーとなり、接続に失敗してしまう</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ go run client/main.go</span><br><span class="line">take1</span><br><span class="line">take2</span><br><span class="line">dial tcp 0.0.0.0:10001-&gt;0.0.0.0:7070: bind: address already in use</span><br></pre></td></tr></table></figure><h4 id="TCP状態遷移を確認してみる"><a href="#TCP状態遷移を確認してみる" class="headerlink" title="TCP状態遷移を確認してみる"></a>TCP状態遷移を確認してみる</h4><p>① まずServerのみを起動した状態</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      20256/main</span><br></pre></td></tr></table></figure><p>② 1回目のServer接続直後の状態</p><ul><li>Client側が <code>CLOSE_WAIT</code><ul><li>(ここが重要だった)Server側が接続をCloseしてもClient側は自動でCloseされないので、Client側のSocketのClose処理待ちしてる状態</li></ul></li><li>Server側は <code>FIN_WAIT2</code><ul><li>Client側へClose処理(FIN)を送りACKをもらった状態。Client側の<code>CLOSE_WAIT</code>が完了するのを待っている</li></ul></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$  netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp        1      0 127.0.0.1:10001         127.0.0.1:7070          CLOSE_WAIT  22909/main      </span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      20256/main      </span><br><span class="line">tcp6       0      0 127.0.0.1:7070          127.0.0.1:10001         FIN_WAIT2   -</span><br></pre></td></tr></table></figure><p>③ 2回目のServer接続失敗し、Clientコマンド終了直後の状態</p><ul><li>Client側は、コマンドのプロセスが終了してるので <code>CLOSE_WAIT</code> がなくなっている</li><li>Server側は、Client側の終了により<code>FIN_WAIT2</code> → <code>TIME_WAIT</code>(終了待ち)の状態へ切り替わっている</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$  netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      20256/main      </span><br><span class="line">tcp6       0      0 127.0.0.1:7070          127.0.0.1:10001         TIME_WAIT   -</span><br></pre></td></tr></table></figure><p>④ さらに60秒後</p><ul><li>Server側は、<code>TIME_WAIT</code>(終了待ち)の期限を越えたのでSocketはCloseされている</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      20256/main      </span><br></pre></td></tr></table></figure><p>この調査から、Server側が接続をCloseしてもClient側が自動でCloseされないし自前でやる必要があることがわかった。<br>(これに気づくのに2,3時間かかった…)</p><h2 id="コードを修正して再実行"><a href="#コードを修正して再実行" class="headerlink" title="コードを修正して再実行"></a>コードを修正して再実行</h2><p>Client側のコードに、Close処理を追加</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;net&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">newConn</span><span class="params">()</span></span> &#123;</span><br><span class="line">remote, _ := net.ResolveTCPAddr(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;0.0.0.0:7070&quot;</span>)</span><br><span class="line">local, _ := net.ResolveTCPAddr(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;0.0.0.0:10001&quot;</span>)</span><br><span class="line">-_, err := net.DialTCP(<span class="string">&quot;tcp&quot;</span>, local, remote)</span><br><span class="line">+conn, err := net.DialTCP(<span class="string">&quot;tcp&quot;</span>, local, remote)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">fmt.Println(err)</span><br><span class="line">&#125;</span><br><span class="line">+conn.Close()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;take1&quot;</span>)</span><br><span class="line">newConn()</span><br><span class="line">-time.Sleep(<span class="number">5</span> * time.Second)</span><br><span class="line">+time.Sleep(<span class="number">65</span> * time.Second)</span><br><span class="line">fmt.Println(<span class="string">&quot;take2&quot;</span>)</span><br><span class="line">newConn()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="実行してみる-1"><a href="#実行してみる-1" class="headerlink" title="実行してみる"></a>実行してみる</h4><p>2回目のServer接続も成功した!</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ go run client/main.go</span><br><span class="line">take1</span><br><span class="line">take2</span><br></pre></td></tr></table></figure><h4 id="TCP状態遷移を確認してみる-1"><a href="#TCP状態遷移を確認してみる-1" class="headerlink" title="TCP状態遷移を確認してみる"></a>TCP状態遷移を確認してみる</h4><p>① まずServerのみを起動した状態</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      32548/main      </span><br></pre></td></tr></table></figure><p>② 1回目のServer接続直後の状態</p><ul><li>Client側にClose処理を追加したので、前回 <code>CLOSE_WAIT</code>だったが <code>TIME_WAIT</code>に変わってる</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp        0      0 127.0.0.1:10001         127.0.0.1:7070          TIME_WAIT   -               </span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      32548/main      </span><br></pre></td></tr></table></figure><p>③ 2回目のServer接続へ成功し、Clientコマンド終了直後の状態</p><ul><li>Client側、Server側共に<code>TIME_WAIT</code>(終了待ち)の状態へ切り替わっている</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp        0      0 127.0.0.1:10001         127.0.0.1:7070          TIME_WAIT   -               </span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      32548/main      </span><br><span class="line">tcp6       0      0 127.0.0.1:7070          127.0.0.1:10001         TIME_WAIT   -               </span><br></pre></td></tr></table></figure><p>④ さらに60秒後</p><ul><li><code>TIME_WAIT</code>(終了待ち)の期限を越えたのでSocketはCloseされている</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ netstat -antlp | grep -e 7070 -e 10001</span><br><span class="line">tcp6       0      0 :::7070                 :::*                    LISTEN      20256/main      </span><br></pre></td></tr></table></figure><h2 id="TCP状態遷移の備忘録"><a href="#TCP状態遷移の備忘録" class="headerlink" title="TCP状態遷移の備忘録"></a>TCP状態遷移の備忘録</h2><p><strong>TCP状態遷移図</strong></p><img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F2813%2F2169c473-a7d3-56fd-d748-282c14a84674.jpeg?ixlib=rb-1.2.2&auto=compress%2Cformat&gif-q=60&s=dbc5139eaa0657f4032e707cac03238e" width=30%><p>図は<a href="https://qiita.com/mogulla3">@mogulla3</a> さんの <a href="https://qiita.com/mogulla3/items/196124b9fb36578e5c80">TCPの状態遷移</a> の記事から参照</p><p>クローズにはアクティブクローズとパッシブクローズがあり、名前の通りアクティブの方はクローズを開始した側、パッシッブの方はクローズを受けた側になる。今回だとCloseを先に開始してるServer側がアクティブで、Client側がパッシッブの状態遷移となる。</p><h2 id="所感"><a href="#所感" class="headerlink" title="所感"></a>所感</h2><p>TCP状態遷移の理解が低いまま実装を進めたのでハマってしまったが、こうゆう場合はググって解決するよりは遠回りと思わずしっかり理解して解決方法を出すとよかった。これまで断片的に理解していた点が線として繋がりを感じれた。</p><h2 id="参考リンク"><a href="#参考リンク" class="headerlink" title="参考リンク"></a>参考リンク</h2><p><a href="https://qiita.com/mogulla3/items/196124b9fb36578e5c80">https://qiita.com/mogulla3/items/196124b9fb36578e5c80</a><br><a href="https://qiita.com/kuni-nakaji/items/c07004c7d9e5bb683bc2">https://qiita.com/kuni-nakaji/items/c07004c7d9e5bb683bc2</a><br><a href="https://blog.ybbo.net/2013/05/29/tcp%E3%81%AEclose_wait%E3%81%A8%E3%81%AF%EF%BC%9F/">https://blog.ybbo.net/2013/05/29/tcp%E3%81%AEclose_wait%E3%81%A8%E3%81%AF%EF%BC%9F/</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;TCP通信開発に慣れていないのとTCP状態遷移を理解できていなかったのでSocket Closeしようとしてハマってしまった。&lt;/p&gt;
&lt;p&gt;前提&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;やろうとしていたこと&lt;ul&gt;
&lt;li&gt;TCP ServerとClientを開発していて、Server</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
    <category term="TCP" scheme="http://blog.fujimisakari.com/tags/TCP/"/>
    
  </entry>
  
  <entry>
    <title>Test Double パターン</title>
    <link href="http://blog.fujimisakari.com/test_double_pattern/"/>
    <id>http://blog.fujimisakari.com/test_double_pattern/</id>
    <published>2018-11-24T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.838Z</updated>
    
    <content type="html"><![CDATA[<p>ユニットテストでよく mock や stub, fake など見かけるが理解度が低かったので、テストでは有名な <a href="http://xunitpatterns.com/Test%20Double.html">xUnit Test Patterns</a> を漁ってみると<br>これらの用語は Test Double パターン がそれに該当するようだったので調べてみた</p><h2 id="Test-Double-パターン-について"><a href="#Test-Double-パターン-について" class="headerlink" title="Test Double パターン について"></a>Test Double パターン について</h2><p>対象コードをテストするために依存してるコンポーネントを別で用意するパターンの総称となる。<br>もともと由来は映画のスタントマンから来てるようで、現場では俳優の代わりに「スタント・ダブル」を雇います。<br>彼らは単独で行動こそできませんが、どのように大きな高さから落ちるか、車を墜落させるか、シーンが求めているものを知っています。<br>コードも同様でテスト用に任意の振る舞いを持たせた依存コンポーネントを用意してテストに入ります。</p><p>Test Doubleでは、どのように&#x2F;なぜ使用するかに基づいて次の分類されます</p><p><img src="http://xunitpatterns.com/Types%20Of%20Test%20Doubles.gif" alt="testdoubles"><br>(<a href="http://xunitpatterns.com/Test%20Double.html">http://xunitpatterns.com/Test%20Double.html</a> より参照)</p><p>今は、「Test Stub」「Mock Oject」「Fake Object」 のパターンについて調べた</p><h2 id="各テストパターン"><a href="#各テストパターン" class="headerlink" title="各テストパターン"></a>各テストパターン</h2><ul><li>用語<ul><li>SUT: system under test の略でテスト対象のこと</li><li>間接入力: テスト対象ロジックが外部コンポーネントに依存している場合、外部コンポーネントからの返り値のこと</li><li>間接出力: テスト対象ロジック中の出力を依存してる外部コンポーネントに引数などから渡したりすること</li></ul></li></ul><h3 id="Test-Stub"><a href="#Test-Stub" class="headerlink" title="Test Stub"></a>Test Stub</h3><p>Test StubはSUTが依存するコンポーネントを置き換えてテストするパターン</p><p>特徴としては</p><ul><li>テストケースにSUTの間接入力用の制御ポイントを用意する</li><li>間接出力を検証する必要ない</li></ul><p><strong>SUT</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> teststub</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> PostCode <span class="keyword">struct</span> &#123;</span><br><span class="line">code    <span class="type">string</span></span><br><span class="line">address <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> PostCodeRepo <span class="keyword">interface</span> &#123;</span><br><span class="line">GetByCode(code <span class="type">string</span>) *PostCode</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> AddressService <span class="keyword">struct</span> &#123;</span><br><span class="line">repo PostCodeRepo</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *AddressService)</span></span> GetAddress(code <span class="type">string</span>) <span class="type">string</span> &#123;</span><br><span class="line">pc := s.repo.GetByCode(code)</span><br><span class="line"><span class="keyword">return</span> pc.address</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Test</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> teststub</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;testing&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> testRepo <span class="keyword">struct</span> &#123;</span><br><span class="line">address <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *testRepo)</span></span> GetByCode(code <span class="type">string</span>) *PostCode &#123;</span><br><span class="line"><span class="keyword">return</span> &amp;PostCode&#123;address: r.address&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestGetAddress</span><span class="params">(t *testing.T)</span></span> &#123;</span><br><span class="line">wantAddress := <span class="string">&quot;AAA県BBB市CCC町&quot;</span></span><br><span class="line">repo := &amp;testRepo&#123;address: wantAddress&#125;</span><br><span class="line"></span><br><span class="line">as := AddressService&#123;repo: repo&#125;</span><br><span class="line">address := as.GetAddress(<span class="string">&quot;123-4567&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> got, want := address, wantAddress; got != want &#123;</span><br><span class="line">t.Fatalf(<span class="string">&quot;got %v, want %v&quot;</span>, got, want)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Mock-Object"><a href="#Mock-Object" class="headerlink" title="Mock Object"></a>Mock Object</h3><p>Mock Objectは、SUTの動作検証のほかに間接的出力の副作用も検証に含ませて実施するパターン</p><p>特徴としては</p><ul><li>SUTが依存するobjectと同じインタフェースを実装するMock Objectを定義されてること</li><li>SUTを実行する前にメソッド呼び出しの大部分またはすべての引数の値を予測できること</li><li>SUTから期待するメソッド呼び出し（期待される引数を含む）とSUTに応答する必要がある戻り値を使用して、Mock Objectを構成してること</li><li>関節出力を検証できること。Mock ObjecがSUTの実行中に呼び出されると受け取った実際の引数を期待される引数と比較し、一致しなければテストに失敗する</li></ul><p><strong>SUT</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> mockobject</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> User <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> UserRepo <span class="keyword">interface</span> &#123;</span><br><span class="line">GetByID(id <span class="type">int</span>) (*User, <span class="type">error</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetUser</span><span class="params">(id <span class="type">int</span>, repo UserRepo)</span></span> (*User, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> repo.GetByID(id)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Test</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> mockobject</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;errors&quot;</span></span><br><span class="line"><span class="string">&quot;testing&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="string">&quot;github.com/golang/mock/gomock&quot;</span></span><br><span class="line"></span><br><span class="line">mock <span class="string">&quot;./mock_mockobject&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestGetUser</span><span class="params">(t *testing.T)</span></span> &#123;</span><br><span class="line">ctrl := gomock.NewController(t)</span><br><span class="line"><span class="keyword">defer</span> ctrl.Finish()</span><br><span class="line"></span><br><span class="line">userErr := errors.New(<span class="string">&quot;user error&quot;</span>)</span><br><span class="line">mockUserRepo := mock.NewMockUserRepo(ctrl)</span><br><span class="line">mockUserRepo.EXPECT().GetByID(<span class="number">10</span>).Return(&amp;User&#123;&#125;, <span class="literal">nil</span>)</span><br><span class="line">mockUserRepo.EXPECT().GetByID(<span class="number">20</span>).Return(<span class="literal">nil</span>, userErr)</span><br><span class="line"></span><br><span class="line"><span class="comment">// valid test</span></span><br><span class="line">u, err := GetUser(<span class="number">10</span>, mockUserRepo)</span><br><span class="line"><span class="keyword">if</span> u == <span class="literal">nil</span> &#123;</span><br><span class="line">t.Fatal(<span class="string">&quot;expect User, but nil&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">t.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// invalid test</span></span><br><span class="line">_, err = GetUser(<span class="number">20</span>, mockUserRepo)</span><br><span class="line"><span class="keyword">if</span> err != userErr &#123;</span><br><span class="line">t.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Fake-Object"><a href="#Fake-Object" class="headerlink" title="Fake Object"></a>Fake Object</h3><p>Fake Objectは、本物の依存コンポーネントと同じI&#x2F;Fを持つ疑似コンポーネントを作成しSUTに注入するパターンで<br>SUTの間接的な入力と出力の検証以外の場合に利用する。</p><p>Stubと似てる印象を受けましたが、Stubはテスト時に依存したコンポーネントを差し替える用途で利用するのに対して<br>Fakeは本物の疑似コンポーネントなのでテスト以外にもローカル開発時の外部サービスと疑似接続の用途として利用したりと用途が広い。</p><p>特徴</p><ul><li>Fake Objectは、Test Stubの一種で代替可能な依存関係をSUTに注入する</li><li>SUTとFake Object間で発生しうる処理を提供するだけ</li><li>間接出力を検証する必要ない</li><li>Fake Objectからの戻り値はハードコードされているか、テストによって設定される</li><li>DBや外部サービス、テストが困難または遅くなるような他のコンポーネントに依存する場合に利用する</li></ul><p><strong>SUT</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> fakeobject</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> User <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetUser</span><span class="params">(id <span class="type">int</span>, repo UserRepo)</span></span> (*User, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> repo.GetByID(id)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> UserRepo <span class="keyword">interface</span> &#123;</span><br><span class="line">GetByID(id <span class="type">int</span>) (*User, <span class="type">error</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> UserRepoImpl <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *UserRepoImpl)</span></span> GetByID(id <span class="type">int</span>) (*User, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> &amp;User&#123;&#125;, <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> FakeUserRepoImpl <span class="keyword">struct</span> &#123;</span><br><span class="line">GetByIDFunc <span class="function"><span class="keyword">func</span><span class="params">(id <span class="type">int</span>)</span></span> (*User, <span class="type">error</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *FakeUserRepoImpl)</span></span> GetByID(id <span class="type">int</span>) (*User, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> r.GetByIDFunc != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span> r.GetByIDFunc(id)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> &amp;User&#123;&#125;, <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Test</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> fakeobject</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;errors&quot;</span></span><br><span class="line"><span class="string">&quot;testing&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestGetUser</span><span class="params">(t *testing.T)</span></span> &#123;</span><br><span class="line">repo := &amp;FakeUserRepoImple&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// valid test</span></span><br><span class="line">u, err := GetUser(<span class="number">10</span>, repo)</span><br><span class="line"><span class="keyword">if</span> u == <span class="literal">nil</span> &#123;</span><br><span class="line">t.Fatal(<span class="string">&quot;expect User, but nil&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">t.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// invalid test</span></span><br><span class="line">userErr := errors.New(<span class="string">&quot;user error&quot;</span>)</span><br><span class="line">repo.GetByIDFunc = <span class="function"><span class="keyword">func</span><span class="params">(id <span class="type">int</span>)</span></span> (*User, <span class="type">error</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, userErr</span><br><span class="line">&#125;</span><br><span class="line">_, err = GetUser(<span class="number">10</span>, repo)</span><br><span class="line"><span class="keyword">if</span> err != userErr &#123;</span><br><span class="line">t.Fatal(err)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="http://goyoki.hatenablog.com/entry/20120301/1330608789">http://goyoki.hatenablog.com/entry/20120301/1330608789</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;ユニットテストでよく mock や stub, fake など見かけるが理解度が低かったので、テストでは有名な &lt;a href=&quot;http://xunitpatterns.com/Test%20Double.html&quot;&gt;xUnit Test Patterns&lt;/a&gt; を漁っ</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
    <category term="Test" scheme="http://blog.fujimisakari.com/tags/Test/"/>
    
  </entry>
  
  <entry>
    <title>Go Linter</title>
    <link href="http://blog.fujimisakari.com/go_linter/"/>
    <id>http://blog.fujimisakari.com/go_linter/</id>
    <published>2018-08-18T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.838Z</updated>
    
    <content type="html"><![CDATA[<p>定期的にブログ書いてないとなかなか書く気が起きなくなるので敷居が低そうだった Go の Linter について調べてみた。<br>書こうと思って調べた矢先、非常にまとまった良い記事を発見してしまったが気にせず書いてみる。</p><ul><li><a href="http://haya14busa.com/ci-for-go-in-end-of-2016/">Go の CI で lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年 - haya14busa</a></li><li><a href="http://tech.sideci.com/entry/2017/01/10/110000">Go Meta Linter がサポートするツールまとめ</a></li></ul><p>Linter の印象して、他の言語では大体の鉄板機能を網羅している大きな Linter が2, 3個あるのに対して<br>Go では機能毎に小さくパッケージ化されていて組み合せて Linter としての機能を実現してる印象がある。<br>この複数の Linter を組み合せた状態で Lint 実行できると有名なのが <a href="https://github.com/alecthomas/gometalinter">gometalinter</a> で<br>最大30以上の Linter を実行させるこができる。(もちろん有効&#x2F;無効もできる)<br>ただ、全部が必須なわけでなく、どのゆう機能の Lint が走ってるかを把握していて<br>ブラックボックス状態でないことが重要と思ってるので自分では単体の Linter をそれぞれ実行してる。<br>ということ現状利用してる Linter を上げみる。</p><h2 id="golint"><a href="#golint" class="headerlink" title="golint"></a>golint</h2><p><a href="https://github.com/golang/lint">https://github.com/golang/lint</a></p><p>Go のコーティングスタイルにあっているか確認してくれる Linter。</p><p>入れるとすべての関数にコメントをしなければならなくなる。<br>Lint の一部を無効にするような機能はなく、有効&#x2F;無効 のどちらかの選択になる。<br>Issue を見ると、作者は Lint を一部無効するような機能を実装する気はなく<br>Go らしく書くか書かないかどちらかだみたいなこと言ってるので、grep でコメントは無効にしてる</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ golint `go list ./...` | grep -v &quot; should have comment or be unexported&quot;</span><br></pre></td></tr></table></figure><h2 id="go-vet"><a href="#go-vet" class="headerlink" title="go vet"></a>go vet</h2><p>Go の標準ツールの一つで、コンパイラで検知できないエラーをチェックしてくれる Linter。</p><p>Printf のコールで文字列フォーマットと値がアンマッチしてる場合や struct のタグの定義エラー、<br>他には、経験上から問題点と推測されるようなもを指摘してくれる。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Fizz <span class="keyword">struct</span> &#123;</span><br><span class="line">Buzz <span class="type">string</span> <span class="string">`json: hoge`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;hoge %s&quot;</span>, <span class="number">1</span>)</span><br><span class="line">fmt.Printf(<span class="string">&quot;hoge %d&quot;</span>, <span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ go vet main.go</span><br><span class="line"># command-line-arguments</span><br><span class="line">./main.go:6: struct field tag `json: &quot;hoge&quot;` not compatible with reflect.StructTag.Get: bad syntax for struct tag value</span><br><span class="line">./main.go:11: unreachable code</span><br><span class="line">./main.go:11: Printf format %s has arg 1 of wrong type int</span><br><span class="line">./main.go:12: Printf call needs 1 arg but has 2 args</span><br></pre></td></tr></table></figure><h2 id="unused"><a href="#unused" class="headerlink" title="unused"></a>unused</h2><p>利用していないを定数や変数、型、関数を教えてくれる Linter。</p><p>変数とかはコンパイル時に not used としてチェックする機構はあるが<br>他はしてくれるないので助かってる</p><h2 id="errcheck"><a href="#errcheck" class="headerlink" title="errcheck"></a>errcheck</h2><p><a href="https://github.com/dominikh/errcheck">https://github.com/dominikh/errcheck</a></p><p>戻り値の error のチェック抜けがないか検知してくれる Linter。</p><p>このチェックは、よく defer 時の error チェックの扱いで議論になる。<br>ファイルやネットワーク接続を閉じる際に <code>defer client.Close()</code> みたいにすると error チェック漏れ扱いになる。</p><p>この時、excludeオプションを使ってチェックを除外するか</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ errcheck -exclude .errcheck_excludes.txt main.go</span><br></pre></td></tr></table></figure><p>きちんと error ハンドリングするかの選択になる。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">closer</span><span class="params">(c io.Closer)</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> err := c.Close(); err != <span class="literal">nil</span> &#123;</span><br><span class="line">log.Error(<span class="string">&quot;failed to close error.&quot;</span>, err)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">defer</span> closer(client)</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>自分としては、error をスルーしてコードスッキリさせるよりも検知したいので後者の選択を取るようにしてる。</p><h2 id="go-staticcheck"><a href="#go-staticcheck" class="headerlink" title="go-staticcheck"></a>go-staticcheck</h2><p><a href="https://github.com/dominikh/go-staticcheck">https://github.com/dominikh/go-staticcheck</a></p><p>静的解析でさまざまな種類のクラッシュと不正な動作を検出できる Linter。</p><p>どうゆうパターンのチェックがあるかはここから確認できる。<br><a href="https://staticcheck.io/docs/staticcheck#overview">https://staticcheck.io/docs/staticcheck#overview</a></p><h2 id="go-simple"><a href="#go-simple" class="headerlink" title="go-simple"></a>go-simple</h2><p><a href="https://github.com/dominikh/go-simple">https://github.com/dominikh/go-simple</a></p><p>シンプルに書くことができる方法を教えてくれる Linter。</p><p>たとえば、main.go 中でこんな感じなコードがあると</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> _, e := <span class="keyword">range</span> y &#123;</span><br><span class="line">  x = <span class="built_in">append</span>(x, e)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>append が良いよって教えてくれる。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ gosimple main.go</span><br><span class="line">main.go:15:2: should replace loop with x = append(x, y...) (S1011)</span><br></pre></td></tr></table></figure><p>どうゆうパターンのチェックがあるかはここ確認できる<br><a href="https://staticcheck.io/docs/gosimple">https://staticcheck.io/docs/gosimple</a></p><h2 id="misspell"><a href="#misspell" class="headerlink" title="misspell"></a>misspell</h2><p><a href="https://github.com/client9/misspell">https://github.com/client9/misspell</a></p><p>英語のタイプミスを教えてくれる Linter。</p><p>misspell はエディタ上でも範囲選択してチェックできるようにして使ってる</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ misspell fujimisakari/</span><br><span class="line">fujimisakari/go-crawler/Makefile:18:25: &quot;infrastracture&quot; is a misspelling of &quot;infrastructure&quot;</span><br><span class="line">fujimisakari/go-crawler/backend/domain/crawlentry/dao.go:4:45: &quot;infrastracture&quot; is a misspelling of &quot;infrastructure&quot;</span><br><span class="line">fujimisakari/go-crawler/backend/domain/hatenahotentry/dao.go:5:45: &quot;infrastracture&quot; is a misspelling of &quot;infrastructure&quot;</span><br><span class="line">fujimisakari/go-crawler/backend/domain/qiitaentry/dao.go:5:45: &quot;infrastracture&quot; is a misspelling of &quot;infrastructure&quot;</span><br><span class="line">fujimisakari/grpc-todo/api/vendor/github.com/grpc-ecosystem/grpc-gateway/runtime/context.go:155:3: &quot;permenant&quot; is a misspelling of &quot;permanent&quot;</span><br><span class="line">fujimisakari/grpc-todo/api/vendor/github.com/grpc-ecosystem/grpc-gateway/runtime/marshaler.go:46:33: &quot;seperator&quot; is a misspelling of &quot;separator&quot;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;定期的にブログ書いてないとなかなか書く気が起きなくなるので敷居が低そうだった Go の Linter について調べてみた。&lt;br&gt;書こうと思って調べた矢先、非常にまとまった良い記事を発見してしまったが気にせず書いてみる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>赤黒木を実装</title>
    <link href="http://blog.fujimisakari.com/red_black_tree/"/>
    <id>http://blog.fujimisakari.com/red_black_tree/</id>
    <published>2018-04-30T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.837Z</updated>
    
    <content type="html"><![CDATA[<p>最近、業務委託で参画させていただいてる会社の方から新たな言語を使うときは<br>まず赤黒木を作ってみるといいよと聞きGoで実装してみましたー</p><p><img src="/image/go/red-black-tree.png" alt="red-black-tree"><br>red-black-treeコマンドに引数で数字を渡すと赤黒木として表示させる実装になってます</p><h2 id="赤黒木について"><a href="#赤黒木について" class="headerlink" title="赤黒木について"></a>赤黒木について</h2><p>赤黒木は「平衡木」というデータ構造のひとつで、ツリー構造のうち、根ノードから子を持たない末端の葉ノードまでの深さがなるべく等しくなるような構造になります。</p><p>ざっくりとした特徴でいえば</p><ul><li>二分木の一種ですが、ノードを赤と黒で色分けする</li><li>根から葉までがほぼ同じ数のノードを経由するため探索する際の平均の計算時間を短縮することができる</li><li>ノードの挿入や削除時に深さのバランスが保たれるようバランス(回転)と呼ばれる操作で、ツリーの再構築を行う</li></ul><h3 id="赤黒木の実装に次の条件を満たす必要がある"><a href="#赤黒木の実装に次の条件を満たす必要がある" class="headerlink" title="赤黒木の実装に次の条件を満たす必要がある"></a>赤黒木の実装に次の条件を満たす必要がある</h3><ul><li>各ノードは赤または黒に区分される</li><li>あるノードが赤ならばその子ノードは必ず黒である。黒の子ノードには制限はない</li><li>根ノードから末端の葉ノードまでの黒ノードの個数（黒深さ）はどの経路でも同じ</li><li>根ノードは黒である</li></ul><h2 id="実装"><a href="#実装" class="headerlink" title="実装"></a>実装</h2><p>lispで書かれてますがこちらを参考にGoで実装しました<br><a href="http://www.geocities.jp/m_hiroi/clisp/clispb03.html">http://www.geocities.jp/m_hiroi/clisp/clispb03.html</a></p><p>実装コードはこちら<br><a href="https://github.com/fujimisakari/red-black-tree">https://github.com/fujimisakari/red-black-tree</a></p><p>実装自体は、引数から取得した数字(ノード)配列を 「ツリーへ挿入 → 回転」 を再帰的に繰り返しいくだけなのでアルゴリズムを理解できていれば実装自体の難しさはないと思います。<br>自分の場合は回転させるとなぜ深さのバランスが整っていくのかを理解するのが大変でした。<br>回転させるパターンはいくつもあるのでコードを書く前に紙でいろんな条件を書いて回転させてみないと理解が進みませんでした。</p><p>ツリー構造出力を実装する際の空白スペース幅の計算が結構苦労しました。<br>いろんな方法で実装してみましたが、根ノードから末端の葉ノードへ再帰で進め行く経路毎に親ノードと子ノードの情報(数字)を保存して<br>出力時はその情報から空白スペース幅を計算して実装する方法が一番シンプルで良さげでした。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近、業務委託で参画させていただいてる会社の方から新たな言語を使うときは&lt;br&gt;まず赤黒木を作ってみるといいよと聞きGoで実装してみましたー&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/image/go/red-black-tree.png&quot; alt=&quot;red-black-tree</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>pecoでDockerを操作する</title>
    <link href="http://blog.fujimisakari.com/go_gdic_command/"/>
    <id>http://blog.fujimisakari.com/go_gdic_command/</id>
    <published>2018-03-11T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.836Z</updated>
    
    <content type="html"><![CDATA[<p>タイトルの通りですが、<a href="https://github.com/peco/peco">peco</a>でDockerを操作するコマンドを作りました。<br>はじめに理想を言うと、素のDockerコマンドをタイポせずすごい勢いで打てるのが一番カッコイイと思ってるのですが自分には無理なので最高のインタフェースを持つpeco(本当はpercol)でDocker操作を効率化できるようにしました。</p><p>これまではよく紹介されてるshell設定のエイリアスやスクリプト、pecoと組み合せた操作を設定して満足できてたんですが、ただ目的の操作のために毎回コマンドを実行しないといけないのが微妙にめんどいなって思ってたので1回のコマンド操作できるよう作りました。</p><p>だいたいの雰囲気はこんな感じです</p><ul><li><code>docker rm</code>を連続で選択した場合</li></ul><p><img src="/image/go/gdic-rm.gif" alt="gdic rm"></p><ul><li><code>docker exec</code>を実行した場合(zsh設定でCtr-x rにバインドして起動してます)</li></ul><p><img src="/image/go/gdic-exec.gif" alt="gdic exec"></p><h2 id="特徴"><a href="#特徴" class="headerlink" title="特徴"></a>特徴</h2><p><a href="https://github.com/fujimisakari/gdic">https://github.com/fujimisakari/gdic</a></p><ul><li>コマンド名は go docker incremental cli の略から <code>gdic</code> としました。(特徴から単語を並べてみただけです)</li><li>次のDockerコマンドをpecoで起動 → 選択できるようになる<ul><li><code>docker exec</code></li><li><code>docker run</code></li><li><code>docker stop</code></li><li><code>docker rm</code></li><li><code>docker rmi</code></li></ul></li><li><code>stop</code>、<code>rm</code>、<code>rmi</code>の場合は一度コマンドを起動すると<code>exit</code>しない限りは連続で選択できる</li><li><code>run</code>、<code>exec</code> の場合は、選択したコンテナ名で実行コマンドを生成できる</li><li><code>rm</code>、 <code>rmi</code> のコマンド失敗時もpeco上でエラーメッセージを確認することができる</li></ul><h2 id="インストール方法"><a href="#インストール方法" class="headerlink" title="インストール方法"></a>インストール方法</h2><p>pecoをインストールして、gdicを<code>go get</code>します(バイナリの配布はしていません)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># macの場合</span><br><span class="line">$ brew install peco</span><br><span class="line"></span><br><span class="line"># Linuxの場合</span><br><span class="line">https://github.com/peco/peco/releasesからバイナリをダウンロードして実行Pathへ配置</span><br><span class="line"></span><br><span class="line">$ go get github.com/fujimisakari/gdic</span><br></pre></td></tr></table></figure><h2 id="使い方"><a href="#使い方" class="headerlink" title="使い方"></a>使い方</h2><p>Dockerコマンドをgdicに変更して実行すると、コマンドに関連した候補がpecoで起動されます。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ gdic <span class="built_in">exec</span></span><br><span class="line">$ gdic run</span><br><span class="line">$ gdic stop</span><br><span class="line">$ gdic <span class="built_in">rm</span></span><br><span class="line">$ gdic rmi</span><br></pre></td></tr></table></figure><p>あと、お好みですがShell設定(zsh)に以下を追加してます。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># alias設定</span></span><br><span class="line"><span class="built_in">alias</span> dstop=<span class="string">&#x27;gdic stop&#x27;</span></span><br><span class="line"><span class="built_in">alias</span> drm=<span class="string">&#x27;gdic rm&#x27;</span></span><br><span class="line"><span class="built_in">alias</span> drmi=<span class="string">&#x27;gdic rmi&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># dockerコンテナのRun先を選択</span></span><br><span class="line"><span class="keyword">function</span> peco-docker-<span class="function"><span class="title">run</span></span> () &#123;</span><br><span class="line">   BUFFER=$(gdic run)</span><br><span class="line">   CURSOR=<span class="variable">$#BUFFER</span></span><br><span class="line">   <span class="built_in">zle</span> clear-screen</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">zle</span> -N peco-docker-run</span><br><span class="line"><span class="built_in">bindkey</span> <span class="string">&#x27;^Xr&#x27;</span> peco-docker-run</span><br><span class="line"></span><br><span class="line"><span class="comment"># dockerコンテナのexec先を選択</span></span><br><span class="line"><span class="keyword">function</span> peco-docker-<span class="function"><span class="title">exec</span></span> () &#123;</span><br><span class="line">   BUFFER=$(gdic <span class="built_in">exec</span>)</span><br><span class="line">   CURSOR=<span class="variable">$#BUFFER</span></span><br><span class="line">   <span class="built_in">zle</span> clear-screen</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">zle</span> -N peco-docker-exec</span><br><span class="line"><span class="built_in">bindkey</span> <span class="string">&#x27;^Xe&#x27;</span> peco-docker-exec</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;タイトルの通りですが、&lt;a href=&quot;https://github.com/peco/peco&quot;&gt;peco&lt;/a&gt;でDockerを操作するコマンドを作りました。&lt;br&gt;はじめに理想を言うと、素のDockerコマンドをタイポせずすごい勢いで打てるのが一番カッコイイと思ってる</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
    <category term="Docker" scheme="http://blog.fujimisakari.com/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>ゴルーチン、チャネルを利用した並行パターン</title>
    <link href="http://blog.fujimisakari.com/go_goroutine_channel/"/>
    <id>http://blog.fujimisakari.com/go_goroutine_channel/</id>
    <published>2018-03-10T00:00:00.000Z</published>
    <updated>2025-02-09T16:50:26.706Z</updated>
    
    <content type="html"><![CDATA[<p>最近以下の書籍でゴルーチン、チャネルを利用した並行・並列ロジックに勉強しましたので整理してみます。</p><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4621300253/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/41BaAiMmrnL._SL160_.jpg" alt="プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4621300253/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)</a><div class="amazlet-detail" style="margin-top: 20px">Alan A.A. Donovan Brian W. Kernighan <br />丸善出版 <br />売り上げランキング: 162,350<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4621300253/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4908686033/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/515xkIcDgXL._SL160_.jpg" alt="Goならわかるシステムプログラミング" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4908686033/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Goならわかるシステムプログラミング</a><div class="amazlet-detail" style="margin-top: 20px">渋川よしき <br />Lambda Note <br />売り上げランキング: 62,948<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4908686033/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><p>もともと並行プログラミングはGoなども含め勉強したことがありましたが、ゴルーチンやチャネルなどの理解度が低いまま扱っており、今読んでるコード、書いているコードがどうゆう並行ロジックパターンに元づいて実装してるのかロジック像が掴みきれず時間を取ってしまう状況でした。そのため、この並行ロジック辺りのパターンをある程度まとめてみることにしました。</p><p>実装パターンは次のように整理してみました。</p><ul><li>基本的な並行パターン</li><li>バッファなしチャネルパターン</li><li>バッファありチャネルパターン</li><li>並列forループパターン</li><li>タスク生成と処理を分けるパターン(Producer-Consumerパターン)</li><li>パイプラインパターン(fan-outパターン)</li><li>selectを利用したチャネル多重化パターン</li><li>selectとワーカープールを組み合せた多重化パターン</li><li>Future&#x2F;Promiseパターン</li></ul><h2 id="基本的な並行パターン"><a href="#基本的な並行パターン" class="headerlink" title="基本的な並行パターン"></a>基本的な並行パターン</h2><p>Goでまず一番最初に学ぶ並行パターンです。<br><code>main()</code>も(メイン)ゴルーチンなので、無名関数でそれとは別のゴルーチンを生成して並行処理を実現してます。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="string">&quot;fmt&quot;</span></span><br><span class="line">    <span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    fmt.Println(<span class="string">&quot;-- 1 --&quot;</span>)</span><br><span class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">        fmt.Println(<span class="string">&quot;-- 2 --&quot;</span>)</span><br><span class="line">        time.Sleep(time.Second)</span><br><span class="line">        fmt.Println((<span class="string">&quot;-- 3 --&quot;</span>))</span><br><span class="line">    &#125;()</span><br><span class="line">    time.Sleep(<span class="number">2</span> * time.Second)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="バッファなしチャネルパターン"><a href="#バッファなしチャネルパターン" class="headerlink" title="バッファなしチャネルパターン"></a>バッファなしチャネルパターン</h2><p>このパターンは重い処理(ネットワークIOやファイルIOなど)を非同期で実行したい場合など利用されます。<br>重い処理自体は無名関数ゴルーチンが非同期で行いますが終了時はチャネルを通じて別ゴルーチンで受信させますので処理フローは同期化が保証されてます。この特定条件の際に読み込み・書き込みがブロックされる特性は、並行処理制御の手法としてさまざまな形で使えます。サンプルは最も基本的な「別のgoroutineの終了を待つ」パターンですが、<code>sync.Mutex</code>のLockなどで制御するしくみもあります。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;log&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span><br><span class="line">log.Println(<span class="string">&quot;start&quot;</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">log.Println(<span class="string">&quot;done&quot;</span>)</span><br><span class="line">done &lt;- <span class="keyword">struct</span>&#123;&#125;&#123;&#125;</span><br><span class="line">&#125;()</span><br><span class="line">log.Println(<span class="string">&quot;between&quot;</span>)</span><br><span class="line">&lt;-done</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="バッファありチャネルパターン"><a href="#バッファありチャネルパターン" class="headerlink" title="バッファありチャネルパターン"></a>バッファありチャネルパターン</h2><p>このパターンは、同時に実行できるタスク数(非同期I&#x2F;O)を制限したい場合の利用するパターンです。<br>要素に数を指定した生成したチャネルをキューとしてい扱い、タスク(ゴルーチン)実行の開始時にチャネルへ値を送信(エンキュー)して終了時に受信(デキュー)するサイクルで、チャネル送信時に上限数まで逹していた場合は実行が待たされます(チャネルの送受信操作はFIFO)。また、チャネルをキューとして扱いましたがセマフォ相当にも例えられたりします(チャネル要素数がセマフォ計数)。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;net/http&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fetch</span><span class="params">(sem <span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;, url <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">sem &lt;- <span class="keyword">struct</span>&#123;&#125;&#123;&#125;</span><br><span class="line"><span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123; &lt;-sem &#125;()</span><br><span class="line"></span><br><span class="line">http.Get(url)</span><br><span class="line">fmt.Println(<span class="string">&quot;fetched&quot;</span>, url)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">sem := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;, <span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">10</span>; i++ &#123;</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">url := <span class="string">&quot;http://blog.fujimisakari.com&quot;</span></span><br><span class="line">fetch(sem, url)</span><br><span class="line">&#125;()</span><br><span class="line">&#125;</span><br><span class="line">wg.Wait()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="並列forループパターン"><a href="#並列forループパターン" class="headerlink" title="並列forループパターン"></a>並列forループパターン</h2><p>ループ毎の処理すべてを並列に実行したい場合に利用するパターンです。<br>waitを入れずに実行すると、<code>main()</code>(メインゴールチン)が終了してしまうので<code>sync.WaitGroup</code>を使って各ゴルーチン終了まで待ちます。<br>(ループの内部処理が小さすぎると、オーバヘッドのほうが大きくなり効率があがらないことがある)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">var</span> messages = []<span class="type">string</span>&#123;</span><br><span class="line"><span class="string">&quot;test1&quot;</span>,</span><br><span class="line"><span class="string">&quot;test2&quot;</span>,</span><br><span class="line"><span class="string">&quot;test3&quot;</span>,</span><br><span class="line"><span class="string">&quot;test4&quot;</span>,</span><br><span class="line"><span class="string">&quot;test5&quot;</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"><span class="keyword">for</span> _, msg := <span class="keyword">range</span> messages &#123;</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line"><span class="comment">// レキシカルスコープ参照だと最後のループ変数をゴルーチンが評価するので引数で渡すようにすること</span></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(m <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">printer(m)</span><br><span class="line">&#125;(msg)</span><br><span class="line">&#125;</span><br><span class="line">wg.Wait()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">printer</span><span class="params">(msg <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">fmt.Println(msg)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="タスク生成と処理を分けるパターン-Producer-Consumerパターン"><a href="#タスク生成と処理を分けるパターン-Producer-Consumerパターン" class="headerlink" title="タスク生成と処理を分けるパターン(Producer-Consumerパターン)"></a>タスク生成と処理を分けるパターン(Producer-Consumerパターン)</h2><p>このパターンは、<code>バッファありチャネルパターン</code>と同様に同時に複数のタスク(非同期I&#x2F;O)を実行したい場合の利用するパターンです。<br><code>バッファありチャネルパターン</code>との違いはタスク数の制限方法がバッファチャネルでなく、ワーカー数になってることです。タスク生成(Producer)時にチャネルを通じてワーカープール(Consumer)へ処理を送信していきます。ワーカー数を増やことで安全に処理速度をスケールすることができます。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(sem &lt;-<span class="keyword">chan</span> <span class="type">int</span>, wg *sync.WaitGroup)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> num := <span class="keyword">range</span> sem &#123;</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">fmt.Println(<span class="string">&quot;process&quot;</span>, num)</span><br><span class="line">wg.Done()</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">sem := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="comment">// Consumer</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">20</span>; i++ &#123;</span><br><span class="line"><span class="keyword">go</span> worker(sem, &amp;wg)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Produder</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i &lt;= <span class="number">200</span>; i++ &#123;</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(x <span class="type">int</span>)</span></span> &#123;</span><br><span class="line">sem &lt;- x</span><br><span class="line">&#125;(i)</span><br><span class="line">&#125;</span><br><span class="line">wg.Wait()</span><br><span class="line"><span class="built_in">close</span>(sem)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="パイプラインパターン-fan-outパターン"><a href="#パイプラインパターン-fan-outパターン" class="headerlink" title="パイプラインパターン(fan-outパターン)"></a>パイプラインパターン(fan-outパターン)</h2><p>このパターンはタスク分割したゴルーチンを順序付けて処理したい場合に利用するパターンです。<br>1つのゴルーチン内のチャネル送信(出力)が別のゴルーチン内のチャネル受信(入力)になるように、複数のゴルーチンを接続して実装します。<br>終ったチャネルは閉じていないと送信するとパニックになりますし、受信すると待たされることなくゼロ値を生成するので<br>ループが終わりのないゼロ値を受け取りを繰り返すことになり<code>fatal error: all goroutines are asleep - deadlock!</code>となってしまいます。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">double</span><span class="params">(naturals &lt;-<span class="keyword">chan</span> <span class="type">int</span>, doubles <span class="keyword">chan</span>&lt;- <span class="type">int</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">x, ok := &lt;-naturals</span><br><span class="line"><span class="keyword">if</span> !ok &#123;</span><br><span class="line"><span class="built_in">close</span>(doubles)</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">&#125;</span><br><span class="line">doubles &lt;- x * <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">squarer</span><span class="params">(doubles &lt;-<span class="keyword">chan</span> <span class="type">int</span>, squares <span class="keyword">chan</span>&lt;- <span class="type">int</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">x, ok := &lt;-doubles</span><br><span class="line"><span class="keyword">if</span> !ok &#123;</span><br><span class="line"><span class="built_in">close</span>(squares)</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">&#125;</span><br><span class="line">squares &lt;- x * x</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">naturals := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line">doubles := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line">squares := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i &lt; <span class="number">10</span>; i++ &#123;</span><br><span class="line">naturals &lt;- i</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">close</span>(naturals)</span><br><span class="line">&#125;()</span><br><span class="line"><span class="keyword">go</span> double(naturals, doubles)</span><br><span class="line"><span class="keyword">go</span> squarer(doubles, squares)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> result := <span class="keyword">range</span> squares &#123;</span><br><span class="line">fmt.Println(result)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="selectを利用したチャネル多重化パターン"><a href="#selectを利用したチャネル多重化パターン" class="headerlink" title="selectを利用したチャネル多重化パターン"></a>selectを利用したチャネル多重化パターン</h2><p>複数のチャネルを同時に扱いたい場合に利用します。<br>selectは複数のチャネルのレディ(受信&#x2F;送信可能)の状態をノンブロッキングで同時に監視することができ、レディになったもの返してくれます。複数のcaseがレディ状態の場合はランダムで1つ選ばれます。それにより、すべてのチャネルが平等に選択されることが保証されます。レディになってない場合はselectスコープをブロッキングし続けてくれるのでforループなどでも無駄なCPUリソースを消費しません。(defalutがあればブロッキングはしません)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Server <span class="keyword">struct</span> &#123;</span><br><span class="line">req <span class="keyword">chan</span> <span class="type">string</span></span><br><span class="line">res <span class="keyword">chan</span> <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">serverStart</span><span class="params">(server Server)</span></span> &#123;</span><br><span class="line">worker := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>, <span class="number">30</span>)</span><br><span class="line">result := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)</span><br><span class="line"><span class="keyword">go</span> listen(server, worker, result)</span><br><span class="line"><span class="keyword">go</span> response(worker, result)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">listen</span><span class="params">(server Server, worker <span class="keyword">chan</span>&lt;- <span class="type">string</span>, result &lt;-<span class="keyword">chan</span> <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line"><span class="keyword">select</span> &#123;</span><br><span class="line"><span class="keyword">case</span> req := &lt;-server.req:</span><br><span class="line">worker &lt;- req</span><br><span class="line"><span class="keyword">case</span> res := &lt;-result:</span><br><span class="line">server.res &lt;- res</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">response</span><span class="params">(worker &lt;-<span class="keyword">chan</span> <span class="type">string</span>, result <span class="keyword">chan</span>&lt;- <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> request := <span class="keyword">range</span> worker &#123;</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(req <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">result &lt;- fmt.Sprintf(<span class="string">&quot;response from %s&quot;</span>, req)</span><br><span class="line">&#125;(request)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">request</span><span class="params">(server Server, reqNum <span class="type">int</span>, wg *sync.WaitGroup)</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">server.req &lt;- fmt.Sprintf(<span class="string">&quot;reqest %d&quot;</span>, reqNum)</span><br><span class="line">fmt.Println(&lt;-server.res)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">server := Server&#123;req: <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>), res: <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)&#125;</span><br><span class="line">serverStart(server)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i &lt; <span class="number">1000</span>; i++ &#123;</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">go</span> request(server, i, &amp;wg)</span><br><span class="line">&#125;</span><br><span class="line">wg.Wait()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="selectとワーカープールを組み合せた多重化"><a href="#selectとワーカープールを組み合せた多重化" class="headerlink" title="selectとワーカープールを組み合せた多重化"></a>selectとワーカープールを組み合せた多重化</h2><p>このパターンは、selectのチャネル多重化と目的は変わらないです。<br>selectでのチャネル受信後は、そのまま同期処理、ゴルーチンで並行処理、ワーカープールで処理するなど考えれますが後者の実装となります。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;runtime&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Server <span class="keyword">struct</span> &#123;</span><br><span class="line">req <span class="keyword">chan</span> <span class="type">string</span></span><br><span class="line">res <span class="keyword">chan</span> <span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">serverStart</span><span class="params">(server Server)</span></span> &#123;</span><br><span class="line">worker := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>, <span class="number">30</span>)</span><br><span class="line">result := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)</span><br><span class="line"><span class="keyword">go</span> listen(server, worker, result)</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt; runtime.NumCPU(); i++ &#123;</span><br><span class="line"><span class="keyword">go</span> response(i, worker, result)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">listen</span><span class="params">(server Server, worker <span class="keyword">chan</span>&lt;- <span class="type">string</span>, result &lt;-<span class="keyword">chan</span> <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line"><span class="keyword">select</span> &#123;</span><br><span class="line"><span class="keyword">case</span> req := &lt;-server.req:</span><br><span class="line">worker &lt;- req</span><br><span class="line"><span class="keyword">case</span> res := &lt;-result:</span><br><span class="line">server.res &lt;- res</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">response</span><span class="params">(id <span class="type">int</span>, worker &lt;-<span class="keyword">chan</span> <span class="type">string</span>, result <span class="keyword">chan</span>&lt;- <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"><span class="keyword">for</span> reqest := <span class="keyword">range</span> worker &#123;</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(req <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">result &lt;- fmt.Sprintf(<span class="string">&quot;id=%d response from %s&quot;</span>, id, req)</span><br><span class="line">&#125;(reqest)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">request</span><span class="params">(server Server, reqNum <span class="type">int</span>, wg *sync.WaitGroup)</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">server.req &lt;- fmt.Sprintf(<span class="string">&quot;reqest %d&quot;</span>, reqNum)</span><br><span class="line">fmt.Println(&lt;-server.res)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">server := Server&#123;req: <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>), res: <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)&#125;</span><br><span class="line">serverStart(server)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">1</span>; i &lt; <span class="number">1000</span>; i++ &#123;</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">go</span> request(server, i, &amp;wg)</span><br><span class="line">&#125;</span><br><span class="line">wg.Wait()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Future-Promiseパターン"><a href="#Future-Promiseパターン" class="headerlink" title="Future&#x2F;Promiseパターン"></a>Future&#x2F;Promiseパターン</h2><p>このパターンは、目的は<code>パイプラインパターン</code>と同様でチャネルの送受信を通じて処理結果の取得を必要になるところまで後回しにする手法です。<br>実装では、Futureが「今はまだ得られないけど将来得られるはずの入力」、Promiseが「将来、値を提供するという約束」で表現されおり、並列デザインパターンとしてもあるので馴染がある人にとってはわかりやすい表現なのかも。</p><p>ちなみにゲーム開発などでも画面表示時のゲームコンポーネントLoadingを効率よくするために Future&#x2F;Promiseパターンでシーケンス制御しながら各タスクでの必要オブジェクト郡を並列Loadすることで遅延を短縮させ最適化をやってたりします。(バトル画面とかで、フィールドオブジェクト生成 → バトル初期値設定 → プレイヤー、敵オブジェクトを生成 → バトルシーントランザクション → バトル開始 みたいな流れ)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="string">&quot;strings&quot;</span></span><br><span class="line"><span class="string">&quot;sync&quot;</span></span><br><span class="line"><span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loadGameStage</span><span class="params">()</span></span> <span class="keyword">chan</span> <span class="type">string</span> &#123;</span><br><span class="line">promise := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">&quot;load stage&quot;</span>)</span><br><span class="line">time.Sleep(<span class="number">2</span> * time.Second)</span><br><span class="line">promise &lt;- <span class="string">&quot;done stage&quot;</span></span><br><span class="line">&#125;()</span><br><span class="line"><span class="keyword">return</span> promise</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loadGameResource</span><span class="params">(featureStage &lt;-<span class="keyword">chan</span> <span class="type">string</span>)</span></span> <span class="keyword">chan</span> []<span class="type">string</span> &#123;</span><br><span class="line">promise := <span class="built_in">make</span>(<span class="keyword">chan</span> []<span class="type">string</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">resource := []<span class="type">string</span>&#123;&lt;-featureStage&#125;</span><br><span class="line"><span class="keyword">var</span> mu sync.Mutex</span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line">wg.Add(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">fmt.Println(<span class="string">&quot;load character&quot;</span>)</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">mu.Lock()</span><br><span class="line">resource = <span class="built_in">append</span>(resource, <span class="string">&quot;done character&quot;</span>)</span><br><span class="line">mu.Unlock()</span><br><span class="line">&#125;()</span><br><span class="line"></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">fmt.Println(<span class="string">&quot;load field material&quot;</span>)</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">mu.Lock()</span><br><span class="line">resource = <span class="built_in">append</span>(resource, <span class="string">&quot;done field material&quot;</span>)</span><br><span class="line">mu.Unlock()</span><br><span class="line">&#125;()</span><br><span class="line"></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">defer</span> wg.Done()</span><br><span class="line">fmt.Println(<span class="string">&quot;load property&quot;</span>)</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">mu.Lock()</span><br><span class="line">resource = <span class="built_in">append</span>(resource, <span class="string">&quot;done property&quot;</span>)</span><br><span class="line">mu.Unlock()</span><br><span class="line">&#125;()</span><br><span class="line"></span><br><span class="line">wg.Wait()</span><br><span class="line">promise &lt;- resource</span><br><span class="line">&#125;()</span><br><span class="line"><span class="keyword">return</span> promise</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loadGameMenu</span><span class="params">(featureResource &lt;-<span class="keyword">chan</span> []<span class="type">string</span>)</span></span> <span class="keyword">chan</span> []<span class="type">string</span> &#123;</span><br><span class="line">promise := <span class="built_in">make</span>(<span class="keyword">chan</span> []<span class="type">string</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">resource := &lt;-featureResource</span><br><span class="line">fmt.Println(<span class="string">&quot;load menu frame&quot;</span>)</span><br><span class="line">time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">meue := <span class="built_in">append</span>(resource, <span class="string">&quot;done menu frame&quot;</span>)</span><br><span class="line">promise &lt;- meue</span><br><span class="line">&#125;()</span><br><span class="line"><span class="keyword">return</span> promise</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">featureStage := loadGameStage()</span><br><span class="line">featureResource := loadGameResource(featureStage)</span><br><span class="line">featureMenu := loadGameMenu(featureResource)</span><br><span class="line">fmt.Println(strings.Join(&lt;-featureMenu, <span class="string">&quot;\n&quot;</span>))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="所感"><a href="#所感" class="headerlink" title="所感"></a>所感</h2><p>並行プログラミングは少しは経験があったのである程度わかるだろうと踏むでたんですが全然まだまだでした。<br>本を見て理解したつもりになってたのですが、実際コード書いてみるとdeadlock!、書くたびにdeadlock! なんとなくコツを掴んできたぞと思ったらdeadlock!のループでした。Emacs以来のツンデレ感を味わったのですが今回整理したことで少しは仲よくなれた気がします。Goの並列プログラミングを習得するにはまだエラーハンドリングやロック機構、テスト方法など覚えることが盛りだくさんですが学んだパターンをベースにこれもっと幅を広げていけたらと思ってる次第です。</p><h2 id="参考リンク"><a href="#参考リンク" class="headerlink" title="参考リンク"></a>参考リンク</h2><ul><li><a href="http://ascii.jp/elem/000/001/475/1475360/">http://ascii.jp/elem/000/001/475/1475360/</a></li><li><a href="https://mattn.kaoriya.net/software/lang/go/20160706165757.htm">https://mattn.kaoriya.net/software/lang/go/20160706165757.htm</a></li><li><a href="https://qiita.com/hayajo/items/4cd75f87e35e60ae11a9">https://qiita.com/hayajo/items/4cd75f87e35e60ae11a9</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近以下の書籍でゴルーチン、チャネルを利用した並行・並列ロジックに勉強しましたので整理してみます。&lt;/p&gt;
&lt;br /&gt;

&lt;div class=&quot;amazlet-box&quot; style=&quot;margin-bottom:0px;&quot;&gt;&lt;div class=&quot;amazlet-ima</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Pythonエンジニアファーストブックを読んで</title>
    <link href="http://blog.fujimisakari.com/read_pyfirst/"/>
    <id>http://blog.fujimisakari.com/read_pyfirst/</id>
    <published>2018-03-04T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.834Z</updated>
    
    <content type="html"><![CDATA[<p>去年、<a href="https://twitter.com/checkpoint">@checkpoint</a>さんから頂いてたのに今ごろ感想上げて申し訳ありませんorz<br>そしてありがとうございました!!</p><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774192228/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51he2gtbQXL._SL160_.jpg" alt="Pythonエンジニア ファーストブック" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774192228/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Pythonエンジニア ファーストブック</a></div><div class="amazlet-detail">鈴木 たかのり 清原 弘貴 嶋田 健志 池内 孝啓 関根 裕紀 <br />技術評論社 <br />売り上げランキング: 44,136<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774192228/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><p>この本は、ファーストブックとつくようにこれからPythonをさわり始める方にとって、とてもバランスがとれた一冊と思いました。<br>内容としては、前半ではPythonの言語としての特徴や歴史、コミュニティを紹介してから言語の基本的な機能とよく利用するライブラリ、そして開発環境、開発サイクルが解説されてます。ここまでで一通り開発するまでの準備や知見が整います。後半では、活用事例としてScraping、データ分析、Webアプリケーションの実装がサンプルコードを含め詳細に解説されてます。</p><p>それぞれの節では、どれもほどよい長さにまとまっていて読みやすく、あまり使わないような機能の紹介などもなく実際によく利用する機能に絞って書かれているので、Python初心者や別言語のメインで使っているけど、これからPythonを学びたいって思っている方に特に良いかなと思いました。最後の節では、読み終えた方が次のステップへ進むときの書籍、サイトを紹介してくれてるので興味を持った技術にスムーズに目指せれる点も良さげでした。</p><p>個人的には、1章が好きでPythonの歴史とかは何度か読んだことがありますが実際に使うことがあまり無くすぐ忘れてしまっていて再認識するためのちょうど良い分量にまとまってました、またコミュニティまわりがあまり知らなかったので各コミュティ活動状況を紹介してくれてるのが嬉しかったです。</p><p>Pythonを始めたときに大体の入門本は読みましたが、簡単過ぎず且つ実践的な要素をこれだけバランス良くまとめられてる書籍は他になくオススメの1冊です!</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;去年、&lt;a href=&quot;https://twitter.com/checkpoint&quot;&gt;@checkpoint&lt;/a&gt;さんから頂いてたのに今ごろ感想上げて申し訳ありませんorz&lt;br&gt;そしてありがとうございました!!&lt;/p&gt;
&lt;br /&gt;

&lt;div class=&quot;amaz</summary>
      
    
    
    
    
    <category term="Python" scheme="http://blog.fujimisakari.com/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>EmacsでGoの開発環境構築</title>
    <link href="http://blog.fujimisakari.com/go_development_with_emacs/"/>
    <id>http://blog.fujimisakari.com/go_development_with_emacs/</id>
    <published>2018-02-04T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.833Z</updated>
    
    <content type="html"><![CDATA[<p>今さら感がありますが、最近よくGoを使っててあらためてEmacs環境を整備したのでざっくりまとめてみます。</p><h2 id="メジャーモードの導入-go-mode"><a href="#メジャーモードの導入-go-mode" class="headerlink" title="メジャーモードの導入(go-mode)"></a>メジャーモードの導入(go-mode)</h2><p>まずGo言語のためのメジャーモードのgo-modeで入れます。<br>シンタックスハイライトやインデント、goコマンドをelispインタフェース通して利用できるようになります。</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">M-x package-install go-mode</span><br></pre></td></tr></table></figure><img src="/image/go/go-mode.png" width="720" height="420" /><h2 id="環境変数設定"><a href="#環境変数設定" class="headerlink" title="環境変数設定"></a>環境変数設定</h2><p>go-modeやGoに関連する作業に<code>GOPATH</code>が必要になるのでEmacsにも環境変数を設定します。</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">setenv</span> <span class="string">&quot;GOPATH&quot;</span> <span class="string">&quot;/home/dev/go&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="タグジャンプ設定-helm-gtags"><a href="#タグジャンプ設定-helm-gtags" class="headerlink" title="タグジャンプ設定(helm-gtags)"></a>タグジャンプ設定(helm-gtags)</h2><p>定義元へのジャンプは <code>godef</code>がよく紹介されてますが他言語の開発とタグジャンプ操作は同じインタフェースにしたいので<code>Gnu Global + Pygments</code>を利用した<code>helm-gtags</code>でタグジャンプ設定します。<br>GOPATH直下でタグDBを作成するのですべての定義元へジャンプできるようになります。</p><p>導入方法はこの辺りを参考<br><a href="https://qiita.com/yoshizow/items/9cc0236ac0249e0638ff">https://qiita.com/yoshizow/items/9cc0236ac0249e0638ff</a><br><a href="https://github.com/syohex/emacs-helm-gtags">https://github.com/syohex/emacs-helm-gtags</a><br><a href="http://blog.10rane.com/2014/09/17/to-reading-comprehension-of-the-source-code-by-introducing-the-helm-gtags-mode/">http://blog.10rane.com/2014/09/17/to-reading-comprehension-of-the-source-code-by-introducing-the-helm-gtags-mode/</a></p><p>Go言語はまだPygmentsでサポートされていないので<code>.ctags</code>に以下を追加します</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">--langdef=Go</span><br><span class="line">--langmap=Go:.go</span><br><span class="line">--regex-Go=/func([ \t]+\([^)]+\))?[ \t]+([a-zA-Z0-9_]+)/\2/d,func/</span><br><span class="line">--regex-Go=/var[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,var/</span><br><span class="line">--regex-Go=/const[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,const/</span><br><span class="line">--regex-Go=/type[ \t]+([a-zA-Z_][a-zA-Z0-9_]+)/\1/d,type/</span><br></pre></td></tr></table></figure><h2 id="コード補完-go-autocomplete"><a href="#コード補完-go-autocomplete" class="headerlink" title="コード補完(go-autocomplete)"></a>コード補完(go-autocomplete)</h2><p>コード補完は<code>gocode</code>を利用するので事前インストールします</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ go get -u github.com/nsf/gocode</span><br></pre></td></tr></table></figure><p><code>auto-complete.el</code>を利用したインタフェースで補完しますのでこちらをインストールして、<br>その後にGo言語の補完で利用する<code>go-autocomplete</code>をインストールします。</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">require</span> &#x27;go-autocomplete)</span><br></pre></td></tr></table></figure><h2 id="エラーチェック-go-flymake"><a href="#エラーチェック-go-flymake" class="headerlink" title="エラーチェック(go-flymake)"></a>エラーチェック(go-flymake)</h2><p>エラーチェックはgo-flymakeを利用します。<br>入力のたびにGoのSyntaxを自動チェックして知らせてくれるようになります。</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">require</span> &#x27;go-flymake)</span><br></pre></td></tr></table></figure><h2 id="ドキュメント確認"><a href="#ドキュメント確認" class="headerlink" title="ドキュメント確認"></a>ドキュメント確認</h2><h3 id="go-eldoc"><a href="#go-eldoc" class="headerlink" title="go-eldoc"></a>go-eldoc</h3><p>カーソル位置のシンボルのドキュメントをミニバッファに表示してくれます</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">M-x package-install go-eldoc</span><br><span class="line"></span><br><span class="line">(<span class="name">require</span> &#x27;go-eldoc)</span><br><span class="line">(<span class="name">add-hook</span> &#x27;go-mode-hook &#x27;go-eldoc-setup)</span><br></pre></td></tr></table></figure><h3 id="パッケージ単位でドキュメントを確認"><a href="#パッケージ単位でドキュメントを確認" class="headerlink" title="パッケージ単位でドキュメントを確認"></a>パッケージ単位でドキュメントを確認</h3><p>ここのやつをちょっといじってるだけです<br><a href="http://syohex.hatenablog.com/entry/20130618/1371567527">http://syohex.hatenablog.com/entry/20130618/1371567527</a></p><p>helm経由でパッケージを選んでドキュメントを確認できるようになります</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">defvar</span> helm-go-packages-source</span><br><span class="line">  (<span class="name">helm-build-sync-source</span> <span class="string">&quot;Helm Go Packages&quot;</span></span><br><span class="line">    <span class="symbol">:candidates</span> (<span class="name">go-packages</span>)</span><br><span class="line">    <span class="symbol">:candidate-number-limit</span> <span class="number">300</span></span><br><span class="line">    <span class="symbol">:action</span> &#x27;godoc))</span><br><span class="line"></span><br><span class="line">(<span class="name">defun</span> helm-go-packages ()</span><br><span class="line">  (<span class="name">interactive</span>)</span><br><span class="line">  (<span class="name">helm</span> <span class="symbol">:sources</span> &#x27;(helm-go-packages-source) <span class="symbol">:buffer</span> <span class="string">&quot;*helm go packages*&quot;</span>))</span><br></pre></td></tr></table></figure><h3 id="ポップアップでドキュメントを確認"><a href="#ポップアップでドキュメントを確認" class="headerlink" title="ポップアップでドキュメントを確認"></a>ポップアップでドキュメントを確認</h3><p><code>go-eldoc</code>とほぼ同じ機能なのですが、ミニバッファだと見にくかったり、詳細がなかったり、他とイベントとカブったとき表示されないのでPopupで確認できるようにしました。<br>操作はリージョン選択して<code>godoc-popup</code>を実行するだけです。(popup.elが必要になります)</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">defun</span> godoc-popup ()</span><br><span class="line">  (<span class="name">interactive</span>)</span><br><span class="line">  (<span class="name">unless</span> (<span class="name">use-region-p</span>)</span><br><span class="line">    (<span class="name">error</span> <span class="string">&quot;Dose not region selection&quot;</span>))</span><br><span class="line">  (<span class="name">let</span> ((<span class="name">query</span> (<span class="name">buffer-substring-no-properties</span> (<span class="name">region-beginning</span>) (<span class="name">region-end</span>))))</span><br><span class="line">    (<span class="name">run-at-time</span> <span class="number">0.1</span> <span class="literal">nil</span> &#x27;deactivate-mark)</span><br><span class="line">    (<span class="name">popup-tip</span></span><br><span class="line">     (<span class="name">with-temp-buffer</span></span><br><span class="line">       (<span class="name">let</span> ((<span class="name">standard-output</span> (<span class="name">current-buffer</span>))</span><br><span class="line">             (<span class="name">help-xref-following</span> <span class="literal">t</span>))</span><br><span class="line">         (<span class="name">prin1</span> (<span class="name">funcall</span> &#x27;shell-command-to-string (<span class="name">concat</span> <span class="string">&quot;go doc &quot;</span> query)))</span><br><span class="line">         (<span class="name">buffer-substring-no-properties</span> (<span class="name">+</span> (<span class="name">point-min</span>) <span class="number">1</span>) (<span class="name">-</span> (<span class="name">point-max</span>) <span class="number">3</span>)))))))</span><br></pre></td></tr></table></figure><img src="/image/go/godoc-popup.png" width="720" height="420" /><h2 id="任意パッケージをdiredで開く"><a href="#任意パッケージをdiredで開く" class="headerlink" title="任意パッケージをdiredで開く"></a>任意パッケージをdiredで開く</h2><p>こちらはhelm-ghq.elで実現できたのですが、目的のパッケージをdiredで開くだけのシンプルなものがほしかったので用意しました。(事前にghqコマンドをインストールしておく必要があります)</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">defun</span> helm-ghq--get-candidates ()</span><br><span class="line">  (<span class="name">let*</span> ((<span class="name">cmd-result</span> (<span class="name">funcall</span> &#x27;shell-command-to-string <span class="string">&quot;ghq list&quot;</span>))</span><br><span class="line">         (<span class="name">candidates</span> (<span class="name">split-string</span> cmd-result <span class="string">&quot;\n&quot;</span>))</span><br><span class="line">         (<span class="name">candidates</span> (<span class="name">sort</span> candidates &#x27;string&lt;))</span><br><span class="line">         (<span class="name">candidates</span> (<span class="name">cdr</span> candidates)))</span><br><span class="line">    candidates))</span><br><span class="line"></span><br><span class="line">(<span class="name">defvar</span> helm-ghq-list-source</span><br><span class="line">  (<span class="name">helm-build-sync-source</span> <span class="string">&quot;Helm ghq list&quot;</span></span><br><span class="line">    <span class="symbol">:candidates</span> (<span class="name">helm-ghq--get-candidates</span>)</span><br><span class="line">    <span class="symbol">:candidate-number-limit</span> <span class="number">300</span></span><br><span class="line">    <span class="symbol">:action</span> &#x27;helm-ghq--dired))</span><br><span class="line"></span><br><span class="line">(<span class="name">defun</span> helm-ghq--dired (<span class="name">name</span>)</span><br><span class="line">  (<span class="name">when</span> (<span class="name">one-window-p</span>)(<span class="name">split-window-horizontally</span>))</span><br><span class="line">  (<span class="name">other-window</span> <span class="number">1</span>)</span><br><span class="line">  (<span class="name">dired</span> (<span class="name">concat</span> (<span class="name">getenv</span> <span class="string">&quot;GOPATH&quot;</span>) <span class="string">&quot;/src/&quot;</span> name)))</span><br><span class="line"></span><br><span class="line">(<span class="name">defun</span> helm-ghq-list ()</span><br><span class="line">  (<span class="name">interactive</span>)</span><br><span class="line">  (<span class="name">helm</span> <span class="symbol">:sources</span> &#x27;(helm-ghq-list-source) <span class="symbol">:buffer</span> <span class="string">&quot;*helm ghq list*&quot;</span>))</span><br></pre></td></tr></table></figure><img src="/image/go/ghq-list.png" width="1000" /><h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2><h3 id="コード整形"><a href="#コード整形" class="headerlink" title="コード整形"></a>コード整形</h3><p>go-modeのデフォルト機能の<code>gofmt</code>を利用する。現在のバッファに対してコード整形が適用されます。<br>また保存時自動的にこのコマンドを実行したい場合は 次のようにhookに登録します。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(add-hook &#x27;before-save-hook &#x27;gofmt-before-save)</span><br></pre></td></tr></table></figure><h3 id="パッケージのimport"><a href="#パッケージのimport" class="headerlink" title="パッケージのimport"></a>パッケージのimport</h3><p>こちらもgo-modeのデフォルト機能の<code>go-import-add</code>を利用します。helm経由で選択できるので便利。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go-import-add (Default: C-c C-a)</span><br></pre></td></tr></table></figure><p>また、使っていないimportも <code>go-remove-unused-imports</code> を使うと使用していないpackageたちをimportから一括で外してくれるはずだがなぜかできない。。</p><h3 id="コード整形とimportの整理を同時にしてくれる"><a href="#コード整形とimportの整理を同時にしてくれる" class="headerlink" title="コード整形とimportの整理を同時にしてくれる"></a>コード整形とimportの整理を同時にしてくれる</h3><p><code>goimports</code>はgofmtの関連ツールで標準のディストリビューションには入ってませんが<br>コード整形と必要に応じてインポートの宣言の挿入と削除を同時に行ってくれます。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ go get golang.org/x/tools/cmd/goimports</span><br><span class="line"></span><br><span class="line">gofmtコマンドの上書き設定します</span><br><span class="line">(setq gofmt-command &quot;goimports&quot;)</span><br></pre></td></tr></table></figure><h3 id="godocのバッファー名は-godoc-にする"><a href="#godocのバッファー名は-godoc-にする" class="headerlink" title="godocのバッファー名は*godoc*にする"></a>godocのバッファー名は<code>*godoc*</code>にする</h3><p>go-mode機能のgodoc使った場合に、新たにバッファが生成されどんどん増えていくのでpopwinで無駄なバッファを生成しないようにしたいのですが<br>バッファー名が<code>*godoc xxxxx*</code>みたいに毎回変わりpopwin設定ができないのでバッファー名は<code>*godoc*</code>固定となるように関数を上書きます。</p><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">defun</span> godoc--get-buffer (<span class="name">query</span>)</span><br><span class="line">  <span class="string">&quot;Get an empty buffer for a godoc QUERY.&quot;</span></span><br><span class="line">  (<span class="name">let*</span> ((<span class="name">buffer-name</span> <span class="string">&quot;*godoc*&quot;</span>)</span><br><span class="line">         (<span class="name">buffer</span> (<span class="name">get-buffer</span> buffer-name)))</span><br><span class="line">    <span class="comment">;; Kill the existing buffer if it already exists.</span></span><br><span class="line">    (<span class="name">when</span> buffer (<span class="name">kill-buffer</span> buffer))</span><br><span class="line">    (<span class="name">get-buffer-create</span> buffer-name)))</span><br></pre></td></tr></table></figure><h3 id="ショートカット"><a href="#ショートカット" class="headerlink" title="ショートカット"></a>ショートカット</h3><p>若干めんどうだったのでショートカットを用意しました</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(defun insert-go-channel-arrow ()</span><br><span class="line">  (interactive)</span><br><span class="line">  (insert &quot;&lt;-&quot;))</span><br><span class="line"></span><br><span class="line">(defun insert-go-expression ()</span><br><span class="line">  (interactive)</span><br><span class="line">  (insert &quot;:=&quot;))</span><br></pre></td></tr></table></figure><h2 id="現在の設定"><a href="#現在の設定" class="headerlink" title="現在の設定"></a>現在の設定</h2><p><a href="https://github.com/fujimisakari/.emacs.d/blob/master/inits/46-go-mode.el">https://github.com/fujimisakari/.emacs.d/blob/master/inits/46-go-mode.el</a></p><h2 id="参考サイト"><a href="#参考サイト" class="headerlink" title="参考サイト"></a>参考サイト</h2><p><a href="http://emacs-jp.github.io/programming/golang.html">http://emacs-jp.github.io/programming/golang.html</a><br><a href="http://syohex.hatenablog.com/entry/20130618/1371567527">http://syohex.hatenablog.com/entry/20130618/1371567527</a><br><a href="http://unknownplace.org/archives/golang-editing-with-emacs.html">http://unknownplace.org/archives/golang-editing-with-emacs.html</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今さら感がありますが、最近よくGoを使っててあらためてEmacs環境を整備したのでざっくりまとめてみます。&lt;/p&gt;
&lt;h2 id=&quot;メジャーモードの導入-go-mode&quot;&gt;&lt;a href=&quot;#メジャーモードの導入-go-mode&quot; class=&quot;headerlink&quot; ti</summary>
      
    
    
    
    
    <category term="Emacs" scheme="http://blog.fujimisakari.com/tags/Emacs/"/>
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>2017年を振り返る</title>
    <link href="http://blog.fujimisakari.com/look_back_on_2017/"/>
    <id>http://blog.fujimisakari.com/look_back_on_2017/</id>
    <published>2017-12-31T22:20:40.000Z</published>
    <updated>2025-02-09T16:06:40.825Z</updated>
    
    <content type="html"><![CDATA[<p>去年に引き続き今年も一月づつ印象に残ってることを振り返ってみる。</p><h2 id="1月"><a href="#1月" class="headerlink" title="1月"></a>1月</h2><ul><li>新しい会社に入社</li><li><a href="https://github.com/fujimisakari/emacs-code-skeletons">helm-code-skelton</a>を作る</li><li><a href="http://blog.fujimisakari.com/read_language_mechanism/">言語のしくみを読んで</a></li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4822239179/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51vGKhInMkL._SL160_.jpg" alt="まつもとゆきひろ 言語のしくみ" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4822239179/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">まつもとゆきひろ 言語のしくみ</a><div class="amazlet-detail" style="margin-top:20px;">まつもとゆきひろ <br />日経BP社 <br />売り上げランキング: 156,517<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4822239179/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="2月"><a href="#2月" class="headerlink" title="2月"></a>2月</h2><ul><li>プライベートでいろいろあってフリーランスを考えだす</li><li>Head First C でC言語の勉強する</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873116090/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51mOeOgWS4L._SL160_.jpg" alt="Head First C ―頭とからだで覚えるCの基本" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873116090/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Head First C ―頭とからだで覚えるCの基本</a><div class="amazlet-detail" style="margin-top:20px;">David Griffiths Dawn Griffiths <br />オライリージャパン <br />売り上げランキング: 349,133<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873116090/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="3月"><a href="#3月" class="headerlink" title="3月"></a>3月</h2><ul><li><a href="https://github.com/fujimisakari/emacs-helm-find-cmd">helm-find-cmd</a>を作る</li><li>Go言語によるWebアプリケーション開発 で勉強</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873117526/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51UoREcNrnL._SL160_.jpg" alt="Go言語によるWebアプリケーション開発" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873117526/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Go言語によるWebアプリケーション開発</a><div class="amazlet-detail" style="margin-top:20px;">Mat Ryer <br />オライリージャパン <br />売り上げランキング: 124,276<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873117526/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="4月"><a href="#4月" class="headerlink" title="4月"></a>4月</h2><ul><li>プログラミングErlang で勉強<ul><li><a href="http://blog.fujimisakari.com/start_erlang_study/">Erlangの学習を始めた</a></li></ul></li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274067149/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51bZh24YhqL._SL160_.jpg" alt="プログラミングErlang" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274067149/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">プログラミングErlang</a><div class="amazlet-detail" style="margin-top:20px;">Joe Armstrong <br />オーム社 <br />売り上げランキング: 188,900<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274067149/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="5月"><a href="#5月" class="headerlink" title="5月"></a>5月</h2><ul><li>奥さんが入院して子供と2人で10日間過ごして育児の大変さを知る</li><li>Erlangの勉強<ul><li><a href="http://blog.fujimisakari.com/erlang_port_with_python/">ErlangからPythonに外部接続</a></li><li><a href="http://blog.fujimisakari.com/network_programing_with_erlang_tcp/">Erlangでネットワークプログラミング - TCP</a></li><li><a href="http://blog.fujimisakari.com/network_programing_with_erlang_udp/">Erlangでネットワークプログラミング - UDP</a></li></ul></li></ul><h2 id="6月"><a href="#6月" class="headerlink" title="6月"></a>6月</h2><ul><li>フリーランスになるための勉強</li><li>会社が上場した</li><li><a href="https://github.com/fujimisakari/sql-generator">sql-generator</a>を作成</li><li>Elixir(Phoenix)の勉強</li><li>SQLアンチパターンで勉強</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B01KFCXP04/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51I5KmDJdkL._SL160_.jpg" alt="プログラミングElixir" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B01KFCXP04/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">プログラミングElixir</a><div class="amazlet-detail" style="margin-top:20px;">オーム社 (2017-07-14)<br />売り上げランキング: 70,341<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/B01KFCXP04/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/41qHKrFZi0L._SL160_.jpg" alt="SQLアンチパターン" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">SQLアンチパターン</a><div class="amazlet-detail" style="margin-top:20px;">Bill Karwin <br />オライリージャパン <br />売り上げランキング: 11,573<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="7月"><a href="#7月" class="headerlink" title="7月"></a>7月</h2><ul><li>フリーランスになるための勉強</li><li>macがSierraになってkarabiner使えくなったので開発環境をLinux(ThinkPad)にする</li><li>LinuxのWMは5,6年くらい前に使ってた<a href="http://www.nongnu.org/ratpoison/">Ratpoison</a>に戻る</li><li>基礎力アップのためセキュリティ関連を勉強<ul><li><a href="http://blog.fujimisakari.com/web_applicbation_security/">Webアプリケーションの脆弱性</a></li><li><a href="http://blog.fujimisakari.com/user_authentication_flow/">ユーザ認証フロー</a></li></ul></li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361190/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51wWshX%2BoIL._SL160_.jpg" alt="体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361190/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践</a><div class="amazlet-detail" style="margin-top:20px;">徳丸 浩 <br />SBクリエイティブ <br />売り上げランキング: 36,745<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4797361190/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="8月"><a href="#8月" class="headerlink" title="8月"></a>8月</h2><ul><li>3年ぶりぐらいにちゃんと夏季休暇とってみた(お盆に実家に帰る)</li><li>フリーランスになろうと思ってたけど転職という選択をした</li><li><a href="http://blog.fujimisakari.com/mysql_performance_tuning/">MySQLパフォーマンスチューニング</a></li><li><a href="http://blog.fujimisakari.com/web_application_architecture_pattebrn/">webアプリケーションアーキテクチャ</a></li><li><a href="https://github.com/fujimisakari/go-tree">go-tree</a>を作成</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51pl3HrLCjL._SL160_.jpg" alt="SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)</a><div class="amazlet-detail" style="margin-top:20px;">ミック <br />技術評論社 <br />売り上げランキング: 61,735<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51TVM1CFHKL._SL160_.jpg" alt="エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)</a><div class="amazlet-detail" style="margin-top:20px;">マーチン・ファウラー <br />翔泳社 <br />売り上げランキング: 322,401<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="9月"><a href="#9月" class="headerlink" title="9月"></a>9月</h2><ul><li>クローリング、スクレイピングの勉強</li><li>達人に学ぶDB設計徹底指南書</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774183679/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51IiWeYB-7L._SL160_.jpg" alt="Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774183679/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-</a><div class="amazlet-detail" style="margin-top:20px;">加藤 耕太 <br />技術評論社 <br />売り上げランキング: 4,691<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774183679/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798124702/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/61bMuQNwkoL._SL160_.jpg" alt="達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798124702/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ</a><div class="amazlet-detail" style="margin-top:20px;">ミック <br />翔泳社 <br />売り上げランキング: 70,869<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798124702/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="10月"><a href="#10月" class="headerlink" title="10月"></a>10月</h2><ul><li>新しい会社に入社</li><li>設計をした。とにかくリファクタをやりつづける</li><li>isuconに初めて出場した<ul><li>設計準備でisuconの準備時間がほとんど作れず、3人で出たが自分だけ完全に準備不足で全然貢献できず本当に申し訳なかった</li></ul></li></ul><h2 id="11月"><a href="#11月" class="headerlink" title="11月"></a>11月</h2><ul><li>とにかくリファクタをやりつづける<ul><li>こんな感じでやってた → <a href="http://blog.fujimisakari.com/team_development/">チーム開発</a></li></ul></li><li>テスト駆動開発を読み、TDDを実践始める<ul><li><a href="http://blog.fujimisakari.com/read_test_driven_development/">テスト駆動開発を読んで</a></li></ul></li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51hsd-b1RTL._SL160_.jpg" alt="テスト駆動開発" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">テスト駆動開発</a><div class="amazlet-detail" style="margin-top:20px;">Kent Beck <br />オーム社 <br />売り上げランキング: 4,659<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="12月"><a href="#12月" class="headerlink" title="12月"></a>12月</h2><ul><li>よく利用するデザインパターンが偏ったり忘れてきたので、Pythonで勉強してみる(<a href="https://github.com/fujimisakari/design-pattern">design-pattern</a>)<ul><li>一日1パターンやろうと思ってたけで全然できてない…</li></ul></li><li>Unixの考え方 を読む</li><li>プリンシプル オブ エンジニア を読む</li><li>実践ドメイン駆動設計 を読む</li></ul><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274064069/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/518ME653H3L._SL160_.jpg" alt="UNIXという考え方―その設計思想と哲学" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274064069/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">UNIXという考え方―その設計思想と哲学</a><div class="amazlet-detail" style="margin-top:20px;">Mike Gancarz <br />オーム社 <br />売り上げランキング: 4,102<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274064069/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798046140/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51Ss443d4hL._SL160_.jpg" alt="プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798046140/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則</a><div class="amazlet-detail" style="margin-top:20px;">上田 勲 <br />秀和システム <br />売り上げランキング: 4,206<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798046140/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/479813161X/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/61Y5Po5i8lL._SL160_.jpg" alt="実践ドメイン駆動設計 (Object Oriented SELECTION)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/479813161X/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">実践ドメイン駆動設計 (Object Oriented SELECTION)</a><div class="amazlet-detail" style="margin-top:20px;">ヴァーン・ヴァーノン <br />翔泳社 <br />売り上げランキング: 44,217<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/479813161X/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><h2 id="今年振り返ってみて"><a href="#今年振り返ってみて" class="headerlink" title="今年振り返ってみて"></a>今年振り返ってみて</h2><p>去年に引き続き2回転職した。それもあってか、自分の働き方を考える1年だった。個人的には正社員で長く続ける仕事よりは、<br>単発だったり期間で仕事を受けたりする方が仕事量も調整できプライベートとの両立しやすそうな気もしてたし迷走した感じだった。<br>エンジニアとしては基礎力アップさせることに努めた1年だった。なんとなくわかってる技術が多かったのでその辺りを解消できる<br>ようにした。なので、技術的な学習では新しめな技術よりは低レイヤーの陳腐化しない技術に学習する機会が多く、来年はもう少し<br>自分の興味がある技術を延せる1年にしたいと思ってる。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;去年に引き続き今年も一月づつ印象に残ってることを振り返ってみる。&lt;/p&gt;
&lt;h2 id=&quot;1月&quot;&gt;&lt;a href=&quot;#1月&quot; class=&quot;headerlink&quot; title=&quot;1月&quot;&gt;&lt;/a&gt;1月&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;新しい会社に入社&lt;/li&gt;
&lt;li&gt;&lt;a hre</summary>
      
    
    
    
    
    <category term="life" scheme="http://blog.fujimisakari.com/tags/life/"/>
    
  </entry>
  
  <entry>
    <title>テスト駆動開発を読んで</title>
    <link href="http://blog.fujimisakari.com/read_test_driven_development/"/>
    <id>http://blog.fujimisakari.com/read_test_driven_development/</id>
    <published>2017-12-04T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.821Z</updated>
    
    <content type="html"><![CDATA[<p>ほしいと思ってたんですが気付いたらamazonとジュンク堂には無く、第2刷でようやく手に入れました。</p><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51hsd-b1RTL._SL160_.jpg" alt="テスト駆動開発" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">テスト駆動開発</a></div><div class="amazlet-detail">Kent Beck <br />オーム社 <br />売り上げランキング: 4,007<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4274217884/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><p>自分はTDDについての知識は、ネット上の誰かの記事でつまみ読み程度でこうした本を読んで勉強するのは初めてでした。そして、普段の開発でテストを書いてるかというとそんなに書いてなく必要に応じて書く感じでした。率先して書いてなかった理由としてあまりテストの知識を持ってなく勉強してキレイなテストを書けるようになってからっと思ってたので、時が来たって感じでた。</p><p>読んでみると最初にすごくビックリしたのがTDDの概念を誤認してたことです。まずテストを書いて開発を進めるテストファーストな開発方法がTDDと思ってたのですがそれは間違いでした。TDDは開発者が設計の治具としてテストコードを同時に書きながら開発と改善を回していくのが目的でした。なので、この本にはテストとリファクタを回しながらどうやって動作するキレイなコードになるかまでが書かれています。</p><p>コードの設計やロジックを美しく書くことは日々意識してますが、設計がよければテストも書きやすくなるみたいな思想でしたがそれは間違っていることに気付せられます。自分の場合、アーキテクチャ駆動でまず「キレイな」を最初に取り組み、そのうえであちこち設計の辻褄を合せながらどうにか「動作する」を実現させていました。TDDは、最初に「動作する」に取り組み、その後で「キレイな」に取り組むのでこれまで行っていたアーキテクチャ駆動の開発とは正反対でとても興味深い発見でした。</p><p>TDDの開発手法以外にもテスト自体のことも触れられており、「仮実装」、「三角測量」、「明白な実装」や「良いテストを見分ける方法」などテストをどうキレイに機能させるかというアプローチは今後テストを書く上で非常に参考になりそうでした。</p><p>そして、付録として<a href="https://twitter.com/t_wada">t_wada</a>さんの訳者解説が非常に良かったです。<br>とても印象に残った文で</p><blockquote><p>テスト駆動開発とは練習によって獲得できる技術です。最初はテストコードを書くのを難しいと感じることもあるでしょう。時間がかかることもあるしょう。「量は質に転化する」と言われています。誰でも最初から良いテストコードをすらすら書けるわけではありません。量をこなすうちに、テストを書くコツが身につき、テストから考え、テストコードをストレスなく書けるようになります。(付録C 訳者解説より引用)</p></blockquote><p>テストと関連ありませんが、これまでプログラマとしてもっと成長したいと思い、業務などとは関係ない分野も好きだから勉強してましたがどこかで意味あんのかなって思ったり、もっと効率良く勉強する方法を探した方がいいのではなど、疑心暗記になることがありました。これを読んで直接は影響なくとも成長するために幅広くやることに間違いはないと思えるようになりもっと質の高いプログラムを書けるようになりたいという気持ちになりました。</p><p>まとめとして、この本はもっとプログラミングの質を上げたいと思っている人に特にオススメと感じました。テストを通じてコードをキレイに書くためのパターンがいくつも紹介されてるので実装のヒントになったり新しい発見をできるのではと思います。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;ほしいと思ってたんですが気付いたらamazonとジュンク堂には無く、第2刷でようやく手に入れました。&lt;/p&gt;
&lt;br /&gt;

&lt;div class=&quot;amazlet-box&quot; style=&quot;margin-bottom:0px;&quot;&gt;&lt;div class=&quot;amazlet-im</summary>
      
    
    
    
    
    <category term="TDD" scheme="http://blog.fujimisakari.com/tags/TDD/"/>
    
  </entry>
  
  <entry>
    <title>チーム開発</title>
    <link href="http://blog.fujimisakari.com/team_development/"/>
    <id>http://blog.fujimisakari.com/team_development/</id>
    <published>2017-11-30T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.821Z</updated>
    
    <content type="html"><![CDATA[<p>最近ブログサボってて久し振りの投稿になります。<br>というのも業務で大規模リファクタリングを行っていて忙しくてなかなか時間が取れませんでした。<br>このリファクタリングを通してチーム開発の良さ、楽しさをあらためて感じました。</p><p>大規模リファクタリングした動機としては、現状のコードが個人のフリーライティングで書かれた部分が多く、<br>システムを把握するのに時間がかかり、どこにどう実装 or 修正すればよいのか迷う状態でした。<br>今後メンバーが増えて行く中で開発スピードの維持しながら開発規模を拡大していくためには設計から見直す必要があり、<br>DDDのレイヤー化アーキテクチャーでシステム設計を再考していきました。<br>作業内容としては、メンバー3名で設計やコーディング規約など事前にある程度決めて1ヶ月半ぐらいひらすらリファクタする感じでした。</p><h3 id="チーム開発のやってよかったこと"><a href="#チーム開発のやってよかったこと" class="headerlink" title="チーム開発のやってよかったこと"></a>チーム開発のやってよかったこと</h3><h4 id="・-よく議論したこと"><a href="#・-よく議論したこと" class="headerlink" title="・ よく議論したこと"></a>・ よく議論したこと</h4><p>リファクタ時に、設計がどうしても合わない部分や見直したい部分というのは出てきます。<br>その際に、どうすべきかを設計者などが決めずに議論して決めれてたことが良かったと思ってます。<br>設計者は大体ここはこうすべきっていう一般的な知見は持ってますが、議論して問題を堀り下げていくと<br>それが合っていなかったり、こうした方が良かったということも多々ありました。<br>また議論することでメンバーが何か気になりながらコーディングせずに納得した感じで作業できるのも良いと思いました。<br>このコードなんか統一されてないよなーとか思ってるコードでも何も議論せずにモヤモヤした気持ちのままと議論した上での<br>結果であればストレスの持ち方が違います。<br>そして、こうした何でも議論しやすい環境を作るのがチーム開発では特に大事だと感じました。</p><h4 id="・-特にタスク担当を決めなかったこと"><a href="#・-特にタスク担当を決めなかったこと" class="headerlink" title="・ 特にタスク担当を決めなかったこと"></a>・ 特にタスク担当を決めなかったこと</h4><p>結果オーライ的なとこがあるのですが、明確なタスク担当決めずにタスク終了したら次のタスクを取るみたいな流れで<br>作業してたので(得意不得意出ますが)各メンバーがシステムの広範囲の把握することができました。<br>終って見るとメンバーがシステム構成など全体を理解するのによい機会となってました。</p><h4 id="・-エンジニア全員でできたこと"><a href="#・-エンジニア全員でできたこと" class="headerlink" title="・ エンジニア全員でできたこと"></a>・ エンジニア全員でできたこと</h4><p>もともと3人しかいかなったので全員でやるのは自然な流れだったのですが、<br>設計やコーディング規約を決めていくとメンバーへ理解してもらえるまでレビューなどで伝えていかなければなりませんが<br>メンバー全員に入ってもらうことで決めたことに対する共通認識を持てるので以降の作業がスムーズになります。<br>もう少し大きい組織だったとしても、別チームのメンバーにも入れ替わりでサポートに入ってもらい認識の共有するのはアリかと。</p><h4 id="・-1人で抱えこまなかったこと"><a href="#・-1人で抱えこまなかったこと" class="headerlink" title="・ 1人で抱えこまなかったこと"></a>・ 1人で抱えこまなかったこと</h4><p>これは自分に対してなのですが、これまで一人でリファクタを進める機会が多く、大規模なリファクタをずっとやってると<br>全然進捗が進まなかった日やゴールの遠さに心が折れそうになる日もあり精神的に不安になる時がありました。<br>チームでリファクタを進めると、自分の進みが悪くても他のメンバーが頑張ってくれてたりしてするので進捗的にも非常に助かり、<br>精神的に不安になることは少なくゴールに向って良いモチベーションを維持することができました。<br>メンバーを信頼することやチーム開発の良さを感じれました。</p><h4 id="・-一緒にご飯に行ったこと"><a href="#・-一緒にご飯に行ったこと" class="headerlink" title="・ 一緒にご飯に行ったこと"></a>・ 一緒にご飯に行ったこと</h4><p>昼も夜も一緒に食事してたのでチームの結束力が強くなった(と感じてます)<br>実は一番大事なとこだっと思ってます。(コミュニケーション大事)</p><h3 id="所感"><a href="#所感" class="headerlink" title="所感"></a>所感</h3><p>ただ振られたタスクを個人個人が淡々とこなしていくのはチーム開発でなく<br>チームが同じ目的意識を持って進めることができるのが本当のチーム開発だなとあらためて感じました。<br>これまでいろんなプロジェクトでチーム開発してきましたがこうゆう気持ちになれるプロジェクトは<br>久々でしたのでとても良い経験をさせてもらいました。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近ブログサボってて久し振りの投稿になります。&lt;br&gt;というのも業務で大規模リファクタリングを行っていて忙しくてなかなか時間が取れませんでした。&lt;br&gt;このリファクタリングを通してチーム開発の良さ、楽しさをあらためて感じました。&lt;/p&gt;
&lt;p&gt;大規模リファクタリングした動機</summary>
      
    
    
    
    
    <category term="Refactoring" scheme="http://blog.fujimisakari.com/tags/Refactoring/"/>
    
  </entry>
  
  <entry>
    <title>Webアプリケーションの設計パターン</title>
    <link href="http://blog.fujimisakari.com/web_application_architecture_pattern/"/>
    <id>http://blog.fujimisakari.com/web_application_architecture_pattern/</id>
    <published>2017-08-27T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.820Z</updated>
    
    <content type="html"><![CDATA[<p>Webアプリケーションを開発する上での設計パターンついて<br>ある程度自分の中で知見がまとまったのでつらつら書いてみる。</p><p>ここで指してる設計パターンが何なのかというと、<br>機能を開発するための業務ロジック設計ではなく<br>システム全体の構造の大枠を定義するための設計パターンになります。</p><h2 id="なぜ設計が必要なのか"><a href="#なぜ設計が必要なのか" class="headerlink" title="なぜ設計が必要なのか"></a>なぜ設計が必要なのか</h2><p>設計方針を決めないまま開発していると、個々それぞれの実装経験やドメイン知識などが異なるので<br>スキルに応じたフリーライティングとなり実装パターンがバラつきコードの可読性や保守性が下ってきてます。<br>これを防ぐために、システム構造の大枠の設計を定義します。</p><p>設計を導入することにより、どの機能はどこの書けばよいのかが明確になるので<br>誰が実装してもある程度同様の実装方針となります。<br>また、局所的な視点から見て不恰好なコードがあっても、<br>全体的な視点から見ればコードのレイアウトがきれいに整った構造になるので<br>設計方針さえブレなれば開発規模を拡大しても可読性や保守性は保持することができます。</p><h2 id="現在の設計が機能しているかの判断ポイント"><a href="#現在の設計が機能しているかの判断ポイント" class="headerlink" title="現在の設計が機能しているかの判断ポイント"></a>現在の設計が機能しているかの判断ポイント</h2><p>自分の経験上ですが、業務で扱う設計は以下のケースがあると思います</p><ul><li>設計パターンを組み合せて利用してるケース</li><li>設計パターン + オレオレ設計のケース</li><li>完全オレオレ設計のケース</li><li>そもそも設計自体を入れていないケース</li></ul><p>設計自体を入れていないケース以外はどれが良いケースというのはケースバイケースなので明言できませんが<br>個人的には以下について思う節があった場合は設計があまりうまく機能していないと思ってます</p><ul><li>新しいドメインロジックを追加しようと思ってるけど何処に定義すればいいか迷ったりわからない</li><li>参照方法が幾通りある(例えば、DBデータを取得するのに、domain経由だったり、model経由だったりしてる)</li><li>重複してるコードが多いと感じる</li><li>各レイヤ間やドメイン間の依存関係が多い</li><li>コントローラに業務ロジックなどのベタ書きが多いと感じる</li><li>この処理は何処に定義するなどのルールが決まっていない</li></ul><h2 id="利用している設計について"><a href="#利用している設計について" class="headerlink" title="利用している設計について"></a>利用している設計について</h2><p>チーム開発では王道の「レイヤー化アーキテクチャ」を利用してます。</p><p>レイヤ化とは、依存の原則を強い制約を適用したアーキテクチャです。<br>機能をはっきり定義されたモジュールに分割し、明快に設計されたインタフェースを<br>モジュール間に定めることで依存関係も最小限に収めることができます。</p><p>大きな特徴としては</p><ul><li>上位下位の関係になるように全体を分割する</li><li>上位レイヤは直下のレイヤにしかアクセスしないよう強制する</li><li>双方向の依存は禁止</li></ul><p>こうすることで依存方向を一方向に限定できます<br>直下レイヤを飛び越えた下位レイヤへのアクセスは禁止です<br>当然ながら上位レイヤーへのアクセスも禁止です<br>また下位レイヤーを安定するように設計することで安定したほうへの依存も達成できます。</p><h3 id="レイヤ化では、主な3つのレイヤに分けられます"><a href="#レイヤ化では、主な3つのレイヤに分けられます" class="headerlink" title="レイヤ化では、主な3つのレイヤに分けられます"></a>レイヤ化では、主な3つのレイヤに分けられます</h3><h4 id="・プレゼンテーションレイヤ"><a href="#・プレゼンテーションレイヤ" class="headerlink" title="・プレゼンテーションレイヤ"></a>・プレゼンテーションレイヤ</h4><p>WebアプリケーションではHTTP処理を行うレイヤになります。<br>リクエストを受けてテンプレートへレンダリング、レスポンスを返す一連の流れを実装します。<br>処理的なとこでは、Get、Postパラメータの取得、リダイレクト処理などHTTP関連を扱い<br>ビジネスロジックはこのレイヤーでは定義せずHTTPの処理の流れのみがシンプルに確認できるようにします。</p><h4 id="・ドメインレイヤ"><a href="#・ドメインレイヤ" class="headerlink" title="・ドメインレイヤ"></a>・ドメインレイヤ</h4><p>データベースへのデータの登録、取得などの操作を行ったり、<br>あるいはデータの計算、整形などシステム機能の中核ロジックを定義する場所です。<br>ドメインロジックやビジネスロジックとも呼ばれコード量も多く一番複雑になるレイヤです。</p><h4 id="・データソースレイヤ"><a href="#・データソースレイヤ" class="headerlink" title="・データソースレイヤ"></a>・データソースレイヤ</h4><p>データベースへのアクセスロジック、取得したレコードのエンティティオブジェクト化、<br>SQLを隠蔽するマッパ機能を実装するレイヤで、リポジトリとなるデータへの操作を直で行う処理が定義されます。</p><h2 id="レイヤー化アーキテクチャの設計パターンについて"><a href="#レイヤー化アーキテクチャの設計パターンについて" class="headerlink" title="レイヤー化アーキテクチャの設計パターンについて"></a>レイヤー化アーキテクチャの設計パターンについて</h2><p>上記でレイヤー化アーキテクチャについての概要を書きましたが<br>各レイヤ毎に設計パターンがいくつか存在します。<br>そして、システム構造の大枠の設計はこのパターンの組み合せで成り立っています。</p><p>実際にはフレームワーク自体がすでに設計パターン取り入れてる場合もありますが<br>設計パターンを知っておくと全体的なコードのレイアウトを揃えるために<br>何が必要なのかを気付くことができたりもします。</p><h3 id="プレゼンテーションレイヤ"><a href="#プレゼンテーションレイヤ" class="headerlink" title="プレゼンテーションレイヤ"></a>プレゼンテーションレイヤ</h3><p>このレイヤのパターン詳細は割愛します<br>というのも、Webアプリケーションを開発する際は何かしらのフレームワークは利用すると思いますが<br>そのフレームワークがコントローラやビューのいずれかの設計パターンを実装されていることがほとんどですので<br>意図的に設計パターンを変更する機会はそこまでないのではっという感じです</p><ul><li><p>コントローラ系のパターン</p><ul><li>モデルビューコントローラ</li><li>ページコントローラ</li><li>フロントコントローラ</li></ul></li><li><p>ビュー系のパターン</p><ul><li>トランスフォームビュー</li><li>テンプレートビュー</li><li>ツーステップビュー</li></ul></li></ul><h3 id="ドメインレイヤ"><a href="#ドメインレイヤ" class="headerlink" title="ドメインレイヤ"></a>ドメインレイヤ</h3><p>日本語訳では問題領域とされてて、非常に抽象的な表現なのですが<br>責務、役割、関心事などの単位で機能(ドメインロジック)を実装するレイヤです。<br>プレゼンテーション層やデータソース層から分離して実装するので外部の層から影響されてはならず<br>ドメインでカプセル化された機能のみに注目して実装を行なわなければならないです。</p><p>実装では機能として粒度の大きな単位をドメインモジュールで分割していき<br>その中でさらに粒度の小さいドメインロジックをコンポーネントとして分けていきます。<br>ディレクトリ構造もプロジェクトによってまちまちだと思いますが良く見る構造だと</p><p><strong>横割りのフラット構造</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">domain</span><br><span class="line">├── module1</span><br><span class="line">│   ├── component1-1(service)</span><br><span class="line">│   ├── component1-2</span><br><span class="line">│   └── component1-3</span><br><span class="line">├── module2</span><br><span class="line">│   ├── component2-1(service)</span><br><span class="line">│   └── component2-2</span><br><span class="line">└── module3</span><br><span class="line">    ├── component3-1(service)</span><br><span class="line">    ├── component3-2</span><br><span class="line">    ├── component3-3</span><br><span class="line">    └── component3-4</span><br></pre></td></tr></table></figure><p><strong>横割りのネスト構造</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">domain</span><br><span class="line">├── module1</span><br><span class="line">│   ├── component1-1(service)</span><br><span class="line">│   ├── component1-2</span><br><span class="line">│   └── component1-3</span><br><span class="line">├── module2</span><br><span class="line">│   ├── component2-1(service)</span><br><span class="line">│   ├── component2-2</span><br><span class="line">│   └── module3</span><br><span class="line">│       ├── component3-1(service)</span><br><span class="line">│       ├── component3-2</span><br><span class="line">│       └── module4</span><br><span class="line">│           ├── component4-1(service)</span><br><span class="line">│           └── component4-2</span><br><span class="line">└── module5</span><br><span class="line">    ├── component5-1(service)</span><br><span class="line">    ├── component5-2</span><br><span class="line">    └── component5-3</span><br></pre></td></tr></table></figure><p><strong>縦割り構造</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">domain</span><br><span class="line">├── service</span><br><span class="line">│   ├── component1</span><br><span class="line">│   ├── component2</span><br><span class="line">│   └── component3</span><br><span class="line">├── model</span><br><span class="line">│   ├── component1</span><br><span class="line">│   ├── component2</span><br><span class="line">│   ├── component3</span><br><span class="line">│   ├── component4</span><br><span class="line">│   └── component5</span><br><span class="line">├── entity</span><br><span class="line">│   ├── component1</span><br><span class="line">│   ├── component2</span><br><span class="line">│   ├── component3</span><br><span class="line">│   └── component4</span><br><span class="line">└── dao</span><br><span class="line">    ├── component1</span><br><span class="line">    ├── component2</span><br><span class="line">    └── component3</span><br></pre></td></tr></table></figure><p>とかがあります。<br>ドメインレイヤは複雑性が高くなりがちで設計時点では今後どのような利用のされ方になるかを測ることが難く<br>王道のパターンなども無いので、チームの方針や設計者の経験よって採用されてる感じです。</p><p>ドメインのパターンは以下に分類されます</p><h4 id="・トランザクションスクリプト"><a href="#・トランザクションスクリプト" class="headerlink" title="・トランザクションスクリプト"></a>・トランザクションスクリプト</h4><p>これパターンは、ドメインロジックを一連の手続きで定義していくパターンとなります。<br>もっともシンプルで一番良く見るパターンではないでしょうか。<br>個人としては、ドメインのインタフェースとなるサービスレイヤをこのパターンで実装してます。<br>サービス内の一連の手続きの流れの中はほぼドメインモデルの呼び出しに集約させて、<br>複雑にならないよう極力ビジネスロジックは定義せずに実装します。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_all_player_cards</span>(<span class="params">player</span>):</span><br><span class="line">    cards = &#123;&#125;</span><br><span class="line">    cards[<span class="string">&#x27;attack&#x27;</span>] = AttackPlayerCard.get_cards(player)</span><br><span class="line">    cards[<span class="string">&#x27;defense&#x27;</span>] = DefensePlayerCard.get_cards(player)</span><br><span class="line">    cards[<span class="string">&#x27;special&#x27;</span>] = SpecialPlayerCard.get_cards(player)</span><br><span class="line">    <span class="keyword">return</span> cards</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_strongest_attack_player_card</span>(<span class="params">player</span>):</span><br><span class="line">    player_cards = get_player_cards(player)</span><br><span class="line">    player_cards.sort(key=operator.attrgetter(<span class="string">&#x27;attack&#x27;</span>), reverse=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">return</span> player_cards[<span class="number">0</span>]</span><br></pre></td></tr></table></figure><h4 id="・ドメインモデル"><a href="#・ドメインモデル" class="headerlink" title="・ドメインモデル"></a>・ドメインモデル</h4><p>このパターンは、目的や役割、関心などの括りをドメインモデルとして切り分けてオブジェクトで扱います。<br>そのため、データをモデル化するオブジェクトもあれば、ビジネスで使用するルールを把握するオブジェクトなどもあります。</p><p>このドメインモデルの実装は結構難易度が高いです。<br>あまりドメイン知識がなく目的や役割、関心などでドメインモデルを切り分けていっても<br>ただ依存関係が絡み合った結合度が高いカオスな構造が出来上ってしまいます。<br>ドメインモデルベースで実装する場合は、まずデザインパターン形式に落しながら<br>ドメイン知識を身に付けて実装していくのがベターかなと思います。<br>あとドメインモデルを使う際は、サービスレイヤをセットで使用するようにします。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Service</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">send_mail</span>(<span class="params">user, message</span>):</span><br><span class="line">    mailhandle = MailHandle()</span><br><span class="line">    mailhandle.add(AdminMailHandler(user.name, message))</span><br><span class="line">    mailhandle.add(UserMailHandler(user.name, user.email))</span><br><span class="line">    mailhandle.send()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Domain Model</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MailHandle</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.mail_handlers = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">self, mail_handler</span>):</span><br><span class="line">        <span class="variable language_">self</span>.mail_handlers.append(mail_handler)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">self</span>):</span><br><span class="line">        mailer = MailService()</span><br><span class="line">        <span class="keyword">for</span> m <span class="keyword">in</span> <span class="variable language_">self</span>.mail_handlers:</span><br><span class="line">            to_address = m.get_to_address()</span><br><span class="line">            from_address = m.get_from_address()</span><br><span class="line">            title = m.get_title()</span><br><span class="line">            body = m.get_body()</span><br><span class="line">            mailer.async_send(to_address, from_address, title, body)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MailStrategy</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">    __metaclass__ = ABCMeta</span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_to_address</span>(): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_from_address</span>(): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_title</span>(): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abstractmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_body</span>(): <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AdminMailHandler</span>(<span class="title class_ inherited__">MailStrategy</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user_name, msg</span>):</span><br><span class="line">        <span class="variable language_">self</span>.user_name = user_name</span><br><span class="line">        <span class="variable language_">self</span>.message = msg</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_to_address</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;admin@xxxx.com&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_from_address</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;support@xxxx.com&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_title</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;問い合わせがありました&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_body</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;&#123;&#125; 様より 問い合わせがありました \n &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="variable language_">self</span>.user_name, <span class="variable language_">self</span>.message)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserMailHandler</span>(<span class="title class_ inherited__">MailStrategy</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user_name, to_address</span>):</span><br><span class="line">        <span class="variable language_">self</span>.user_name = user_name</span><br><span class="line">        <span class="variable language_">self</span>.to_address = to_address</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_to_address</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.to_address</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_from_address</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;support@xxxx.com&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_title</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;問い合わせ受け付けました&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_body</span>():</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;&#123;&#125; 様 \n 問い合わせ受け付けました&#x27;</span>.<span class="built_in">format</span>(<span class="variable language_">self</span>.user_name)</span><br></pre></td></tr></table></figure><h4 id="・テーブルモジュール"><a href="#・テーブルモジュール" class="headerlink" title="・テーブルモジュール"></a>・テーブルモジュール</h4><p>このパターンは、データベース内のテーブルをドメインクラスで模倣するオブジェクトを用意するパターンです。<br>テーブルモジュールの特徴は、テーブルモジュールのオブジェクトが一意性の概念を持たないことです。<br>メンバ変数にテーブルレコード相当に該当するデータセット情報を保持しており、<br>このデータセット情報に対して、findやcreate, udpate, deleteなど振る舞い用意してアクセスすることになります。</p><p>テーブルモジュールは複雑なビジネスロジックを実装するは苦手ですが、異なるデータソースからのデータを<br>1つのオブジェクトで扱えたりするので、データ操作が中心の振る舞いであれば得意です。<br>個人的なとこでは、RDBを利用した実装ではドメインモデルとアクティブレコードを組み合わせるパターンがほとんどで<br>テーブルモジュールはNoSQLなどデータスキーマを持たないデータがリポジトリの場合に使ったりします。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Domain Layer</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserTableModule</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="variable language_">self</span>._user = user</span><br><span class="line">        <span class="variable language_">self</span>._profile_kvs = ProfileKVS(user)</span><br><span class="line">        <span class="variable language_">self</span>._favorite_kvs = FavoriteKVS(user)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">user</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._user.to_dict()</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">profile</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._profile_kvs.get()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_profile</span>(<span class="params">self, birthday, living, gender, comment</span>):</span><br><span class="line">        init_data = &#123;<span class="string">&#x27;birthday&#x27;</span>: birthday,</span><br><span class="line">                     <span class="string">&#x27;living&#x27;</span>: living,</span><br><span class="line">                     <span class="string">&#x27;gender&#x27;</span>: gender,</span><br><span class="line">                     <span class="string">&#x27;comment&#x27;</span>: comment,</span><br><span class="line">                     <span class="string">&#x27;type&#x27;</span>: <span class="number">1</span>,</span><br><span class="line">                     <span class="string">&#x27;created_at&#x27;</span>: datetime.datetime.now().strftime(<span class="string">&#x27;%Y-%m-%d %H:%M&#x27;</span>),</span><br><span class="line">                     <span class="string">&#x27;updated_at&#x27;</span>: datetime.datetime.now().strftime(<span class="string">&#x27;%Y-%m-%d %H:%M&#x27;</span>)&#125;</span><br><span class="line">        <span class="variable language_">self</span>._profile_kvs.save(init_data)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update_profile</span>(<span class="params">self, profile_data</span>):</span><br><span class="line">        profile_data[<span class="string">&#x27;updated_at&#x27;</span>] = datetime.datetime.now().strftime(<span class="string">&#x27;%Y-%m-%d %H:%M&#x27;</span>)</span><br><span class="line">        <span class="variable language_">self</span>._profile_kvs.save(profile_data)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_favorite</span>(<span class="params">self, data_id</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._favorite_kvs.get_by(data_id)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_favorites</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._favorite_kvs.get_list()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create_favorite</span>(<span class="params">self, target_id</span>):</span><br><span class="line">        init_data = &#123;<span class="string">&#x27;data_id&#x27;</span>: <span class="built_in">str</span>(uuid4()),</span><br><span class="line">                     <span class="string">&#x27;target_id&#x27;</span>: target_id,</span><br><span class="line">                     <span class="string">&#x27;created_at&#x27;</span>: datetime.datetime.now().strftime(<span class="string">&#x27;%Y-%m-%d %H:%M&#x27;</span>)&#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._favorite_kvs.save(init_data)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete_favorite</span>(<span class="params">self, data_id</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._favorite_kvs.delete(data_id)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># DataSource Layer</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ProfileKVS</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="variable language_">self</span>._kvs = user.get_kvs(KVS_PROFILE_KEY)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._kvs.get()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, profile_data</span>):</span><br><span class="line">        <span class="variable language_">self</span>._kvs.<span class="built_in">set</span>(profile_data)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FavoriteKVS</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, user</span>):</span><br><span class="line">        <span class="variable language_">self</span>._kvs = user.get_kvs(KVS_FAVORITE_KEY)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_list</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._kvs.get([])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_by</span>(<span class="params">self, data_id</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._kvs.get(data_id, <span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save</span>(<span class="params">self, data_id, data</span>):</span><br><span class="line">        data_map = <span class="variable language_">self</span>._kvs.get()</span><br><span class="line">        data_map[data_id] = data</span><br><span class="line">        <span class="variable language_">self</span>._kvs.<span class="built_in">set</span>(data_map)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self, data_id</span>):</span><br><span class="line">        data_map = <span class="variable language_">self</span>._kvs.get()</span><br><span class="line">        <span class="keyword">if</span> data_map.get(data_id, <span class="literal">False</span>):</span><br><span class="line">            <span class="keyword">del</span> data_map[data_id]</span><br><span class="line">        <span class="variable language_">self</span>._kvs.<span class="built_in">set</span>(data_map)</span><br></pre></td></tr></table></figure><h4 id="・サービスレイヤ"><a href="#・サービスレイヤ" class="headerlink" title="・サービスレイヤ"></a>・サービスレイヤ</h4><p>サービスレイヤはドメインロジックを構成する1つでドメインレイヤのインタフェースとなる部分で利用されます。<br>このレイヤを導入する背景には以下が考えられます</p><ul><li>ドメインモジュール内のコンポーネントを外部と依存(直接参照)させないようにする</li><li>純粋なドメインロジックに外部レイヤからの呼び出しロジックも共存させると再利用の可能性が低下する</li></ul><p>サービスレイヤの実装パターンは2種類あります</p><p><strong>ドメインファサード手法</strong><br>ドメインモデル上の薄いファサードのセットとして実装します。<br>ビジネスロジックはすべてドメインモデルによって実装されるので<br>サービス内ではビジネスロジックは一切実装しません。</p><p><strong>操作スクリプト手法</strong><br>外部レイヤからの利用できる操作をスクリプトとして実装します。<br>この手法では、ビジネスロジックは実装できるものの薄いロジックのみにとどめて<br>重いドメインロジックはカプセル化したドメインモデルなどに任せます。</p><h3 id="データソースレイヤ"><a href="#データソースレイヤ" class="headerlink" title="データソースレイヤ"></a>データソースレイヤ</h3><h4 id="・テーブルデータゲートウェイパターン"><a href="#・テーブルデータゲートウェイパターン" class="headerlink" title="・テーブルデータゲートウェイパターン"></a>・テーブルデータゲートウェイパターン</h4><p>テーブルごとにデータへアクセスするためオブジェクトを用意するパターンです。</p><ul><li>特徴としては<ul><li>テーブルごとに1つのインスタンスを所持して、このインスタンス以外からデータへアクセスは行わない</li><li>データベースからデータを取得するための数種類のfind、update、 insert、 deleteなどのメソッドから構成されるインタフェースを備えている</li><li>データの受け渡しのみが役割であるためステートレスです</li><li>データにアクセスするオブジェクトの振る舞いから短縮してDAOパターンとも呼ばれるてます</li><li>ドメインパターンのテーブルモジュールパターンと組み合わせて利用されるケースが多いです</li></ul></li></ul><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PersonGateWayMixin</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_query_with_exception</span>(<span class="params">cls, sql</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_all</span>(<span class="params">cls</span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person&#x27;</span></span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">return</span> rows <span class="keyword">if</span> rows <span class="keyword">else</span> []</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find</span>(<span class="params">cls, <span class="built_in">id</span></span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">return</span> rows[<span class="number">0</span>] <span class="keyword">if</span> rows <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_by_age</span>(<span class="params">cls, age</span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE age = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(age)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">return</span> rows <span class="keyword">if</span> rows <span class="keyword">else</span> []</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">cls, **kwargs</span>):</span><br><span class="line">        sql = <span class="string">&#x27;INSERT INTO person VALUES (&#123;&#125;, &#123;&#125;, &#123;&#125;)&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(kwargs[<span class="string">&#x27;lastname&#x27;</span>], kwargs[<span class="string">&#x27;firstname&#x27;</span>], kwargs[<span class="string">&#x27;age&#x27;</span>])</span><br><span class="line">        <span class="keyword">return</span> cls._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">cls, **kwargs</span>):</span><br><span class="line">        sql = <span class="string">&#x27;UPDATE person SET lastname = &#123;&#125;, firstname = &#123;&#125;, age = &#123;&#125; WHERE id = &#123;&#125;&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(kwargs[<span class="string">&#x27;lastname&#x27;</span>], kwargs[<span class="string">&#x27;firstname&#x27;</span>], kwargs[<span class="string">&#x27;age&#x27;</span>], kwargs[<span class="string">&#x27;id&#x27;</span>])</span><br><span class="line">        <span class="keyword">return</span> cls._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">cls, <span class="built_in">id</span></span>):</span><br><span class="line">        sql = <span class="string">&#x27;DELETE FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        <span class="keyword">return</span> cls._query_with_exception(sql)</span><br></pre></td></tr></table></figure><h4 id="・行データゲートウェイ"><a href="#・行データゲートウェイ" class="headerlink" title="・行データゲートウェイ"></a>・行データゲートウェイ</h4><p>テーブルの1レコードを模倣するオブジェクトを用意するパターンです</p><ul><li>特徴としては<ul><li>テーブルの1レコードごとに1インスタンスを生成します</li><li>オブジェクトにはテーブルの列と紐づくデータフィールドとアクセッサが定義されている</li><li>オブジェクトの振る舞いは、insert, update, deleteを持ちます</li><li>オブジェクトの振る舞いには、ドメイン系のロジックは定義しません。</li></ul></li></ul><p><strong>find(検索)について</strong><br>このパターン内を利用する際にfind(検索)をどこに定義するかという問題があります。<br>明確な答えはないのですが考え方としては、以下のパターンで実装されてる傾向です</p><ul><li>行データゲートウェイオブジェクトに静的findメソッドを追加して実装</li><li>findオブジェクトを定義して結果を行データゲートウェイオブジェクトにバインドさせる</li></ul><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Domain(Service) Layer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_person</span>(<span class="params"><span class="built_in">id</span></span>):</span><br><span class="line">    <span class="keyword">return</span> PersonFinder.find(<span class="built_in">id</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_person_by</span>(<span class="params">age</span>):</span><br><span class="line">    <span class="keyword">return</span> PersonFinder.find_by(age)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># DataSource Layer</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PersonFinder</span>(<span class="title class_ inherited__">DBdriver</span>):</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find</span>(<span class="params">cls, <span class="built_in">id</span></span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">return</span> PersonGateway(rows[<span class="number">0</span>]) <span class="keyword">if</span> rows <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_by</span>(<span class="params">cls, age</span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE age = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(age)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> rows:</span><br><span class="line">            <span class="keyword">return</span> []</span><br><span class="line">        <span class="keyword">return</span> [PersonGateway(r) <span class="keyword">for</span> r <span class="keyword">in</span> rows]</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PersonGateway</span>(<span class="title class_ inherited__">DBdriver</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, **kwargs</span>):</span><br><span class="line">        <span class="variable language_">self</span>.<span class="built_in">id</span> = kwargs.get(<span class="string">&#x27;id&#x27;</span>, <span class="literal">None</span>)</span><br><span class="line">        <span class="variable language_">self</span>.lastname = kwargs[<span class="string">&#x27;lastname&#x27;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.firstname = kwargs[<span class="string">&#x27;firstname&#x27;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.age = kwargs[<span class="string">&#x27;age&#x27;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_query_with_exception</span>(<span class="params">self, sql</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="variable language_">self</span>.db.execute(sql)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;INSERT INTO person VALUES (&#123;&#125;, &#123;&#125;, &#123;&#125;)&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(<span class="variable language_">self</span>.lastname, <span class="variable language_">self</span>.firstname, <span class="variable language_">self</span>.age)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;UPDATE person SET lastname = &#123;&#125;, firstname = &#123;&#125;, age = &#123;&#125; WHERE id = &#123;&#125;&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(<span class="variable language_">self</span>.lastname, <span class="variable language_">self</span>.firstname, <span class="variable language_">self</span>.age, <span class="variable language_">self</span>.<span class="built_in">id</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;DELETE FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br></pre></td></tr></table></figure><h4 id="・アクティブレコード"><a href="#・アクティブレコード" class="headerlink" title="・アクティブレコード"></a>・アクティブレコード</h4><p>行データゲートウェイパターンとほぼ同じです。<br>異なる点としては、ドメインロジックを定義することが可能というです。<br>そのため、findメソッドやテーブルデータを処理するビジネスロジックを定義したりします。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PersonGateway</span>(<span class="title class_ inherited__">DBdriver</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, **kwargs</span>):</span><br><span class="line">        <span class="variable language_">self</span>.<span class="built_in">id</span> = kwargs.get(<span class="string">&#x27;id&#x27;</span>, <span class="literal">None</span>)</span><br><span class="line">        <span class="variable language_">self</span>.lastname = kwargs[<span class="string">&#x27;lastname&#x27;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.firstname = kwargs[<span class="string">&#x27;firstname&#x27;</span>]</span><br><span class="line">        <span class="variable language_">self</span>.age = kwargs[<span class="string">&#x27;age&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fullname</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;&#123;&#125; &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="variable language_">self</span>.lastname, <span class="variable language_">self</span>.firstname)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_query_with_exception</span>(<span class="params">self, sql</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="variable language_">self</span>.db.execute(sql)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;INSERT INTO person VALUES (&#123;&#125;, &#123;&#125;, &#123;&#125;)&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(<span class="variable language_">self</span>.lastname, <span class="variable language_">self</span>.firstname, <span class="variable language_">self</span>.age)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">update</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;UPDATE person SET lastname = &#123;&#125;, firstname = &#123;&#125;, age = &#123;&#125; WHERE id = &#123;&#125;&#x27;</span></span><br><span class="line">        sql = sql.<span class="built_in">format</span>(<span class="variable language_">self</span>.lastname, <span class="variable language_">self</span>.firstname, <span class="variable language_">self</span>.age, <span class="variable language_">self</span>.<span class="built_in">id</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">self</span>):</span><br><span class="line">        sql = <span class="string">&#x27;DELETE FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._query_with_exception(sql)</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find</span>(<span class="params">cls, <span class="built_in">id</span></span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE id = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">return</span> cls(rows[<span class="number">0</span>]) <span class="keyword">if</span> rows <span class="keyword">else</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find_by</span>(<span class="params">cls, age</span>):</span><br><span class="line">        sql = <span class="string">&#x27;SELECT * FROM person WHERE age = &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(age)</span><br><span class="line">        rows = cls.db.execute(sql)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> rows:</span><br><span class="line">            <span class="keyword">return</span> []</span><br><span class="line">        <span class="keyword">return</span> [cls(r) <span class="keyword">for</span> r <span class="keyword">in</span> rows]</span><br></pre></td></tr></table></figure><h2 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h2><p>ここまでPofEAA(Patterns of Enterprise Application Architecture)に元づいた設計パターンを<br>自分の経験を踏まえて紹介してきましたが設計手法はこれ以外にもたくさんあります。<br>すべて覚える必要はありませんが代表的なものをいくつか知っておくことで他の設計パターンに出会った時に<br>そのパターンの特徴を早く抑えることができコード全体の規則性に気付くことができるようになります。<br>規則性を理解できるようになると見える世界も変ってくるので、どこにロジックをかけばよいのかが<br>明確にイメージできるようになり悩んだりする時間も短縮されるのでコーディングの速さも上がります。<br>このように設計を理解していくと、これまで見えていなかった視野が広がったことを実感できるようになりました。<br>そして、こういった技術は陳腐化することはなく、どの言語でも共通して通じるのでものですので<br>学習する価値は非常に高いものと感じてます。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51TVM1CFHKL._SL160_.jpg" alt="エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)</a><div class="amazlet-detail" style="margin-top:20px;">マーチン・ファウラー <br />翔泳社 <br />売り上げランキング: 322,401<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4798105538/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Webアプリケーションを開発する上での設計パターンついて&lt;br&gt;ある程度自分の中で知見がまとまったのでつらつら書いてみる。&lt;/p&gt;
&lt;p&gt;ここで指してる設計パターンが何なのかというと、&lt;br&gt;機能を開発するための業務ロジック設計ではなく&lt;br&gt;システム全体の構造の大枠を定義</summary>
      
    
    
    
    
    <category term="DesignPatterns" scheme="http://blog.fujimisakari.com/tags/DesignPatterns/"/>
    
    <category term="Python" scheme="http://blog.fujimisakari.com/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Goでtreeコマンド</title>
    <link href="http://blog.fujimisakari.com/go_tree_command/"/>
    <id>http://blog.fujimisakari.com/go_tree_command/</id>
    <published>2017-08-10T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.817Z</updated>
    
    <content type="html"><![CDATA[<p>最近、Goでtreeコマンドを作ってみた。<br>自分はディレクトリとファイルのツリー構造をREADMEなど仕様上の資料に含めることが多いのですが、<br>その際に必要とするディレクトリとファイルを<code>mkdir</code>、<code>touch</code>してから、ツリー構造の先頭に行き<br>treeコマンドを実行して用意するのが手間だったので作りました。</p><p><a href="https://github.com/fujimisakari/go-tree">https://github.com/fujimisakari/go-tree</a></p><p>使い方は、yamlでtree構造を定義するだけです。<br><code>go-tree</code>以降からyamlの配列で定義していき、ディレクトリには末尾にセミコロン付けます。<br>また、<code>root-dir</code>でrootディレクトリ名を定義することができます(defaultでは<code>.</code>になります)</p><ul><li>sample.yaml</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">root-dir:</span> <span class="string">&quot;Sample&quot;</span></span><br><span class="line"><span class="attr">go-tree:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">fizz</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">buzz</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">dir1:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp1-1</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp1-2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp1-3</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp1-4</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">dir2:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-1</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-3</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-4</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">dir3:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">comp3-1</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">comp3-2</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">dir4:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">comp4-1</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">comp4-2</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">comp4-3</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">comp3-3</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">comp3-4</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-5</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp2-6</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">dir5:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp4-1</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp4-2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp4-3</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">comp4-4</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">foo</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">bar</span></span><br></pre></td></tr></table></figure><p>あとは、go-treeコマンドのパラメータにyamlファイルのpathに渡して実行。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">$ go-tree ./sample.yaml</span><br><span class="line">Sample</span><br><span class="line">├── fizz</span><br><span class="line">├── buzz</span><br><span class="line">├── dir1</span><br><span class="line">│   ├── comp1-1</span><br><span class="line">│   ├── comp1-2</span><br><span class="line">│   ├── comp1-3</span><br><span class="line">│   └── comp1-4</span><br><span class="line">├── dir2</span><br><span class="line">│   ├── comp2-1</span><br><span class="line">│   ├── comp2-2</span><br><span class="line">│   ├── comp2-3</span><br><span class="line">│   ├── comp2-4</span><br><span class="line">│   ├── dir3</span><br><span class="line">│   │   ├── comp3-1</span><br><span class="line">│   │   ├── comp3-2</span><br><span class="line">│   │   ├── dir4</span><br><span class="line">│   │   │   ├── comp4-1</span><br><span class="line">│   │   │   ├── comp4-2</span><br><span class="line">│   │   │   └── comp4-3</span><br><span class="line">│   │   ├── comp3-3</span><br><span class="line">│   │   └── comp3-4</span><br><span class="line">│   ├── comp2-5</span><br><span class="line">│   └── comp2-6</span><br><span class="line">├── dir5</span><br><span class="line">│   ├── comp4-1</span><br><span class="line">│   ├── comp4-2</span><br><span class="line">│   ├── comp4-3</span><br><span class="line">│   └── comp4-4</span><br><span class="line">├── foo</span><br><span class="line">└── bar</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近、Goでtreeコマンドを作ってみた。&lt;br&gt;自分はディレクトリとファイルのツリー構造をREADMEなど仕様上の資料に含めることが多いのですが、&lt;br&gt;その際に必要とするディレクトリとファイルを&lt;code&gt;mkdir&lt;/code&gt;、&lt;code&gt;touch&lt;/code&gt;し</summary>
      
    
    
    
    
    <category term="Go" scheme="http://blog.fujimisakari.com/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>MySQLパフォーマンスチューニング</title>
    <link href="http://blog.fujimisakari.com/mysql_performance_tuning/"/>
    <id>http://blog.fujimisakari.com/mysql_performance_tuning/</id>
    <published>2017-08-06T00:00:00.000Z</published>
    <updated>2025-02-09T16:06:40.816Z</updated>
    
    <content type="html"><![CDATA[<p>開発時にMySQLのパフォーマンスで気を付けてることのまとめました。</p><ul><li>メモリ設定</li><li>テーブル定義</li><li>クエリー発行</li><li>アプリケーション側</li><li>まとめ</li></ul><h2 id="メモリ設定"><a href="#メモリ設定" class="headerlink" title="メモリ設定"></a>メモリ設定</h2><p>MySQLは、データを保存するために利用するメモリとして<br>データキャッシュ(バッファプール)、ログバッファの2つ持ってます。</p><p>データキャッシュはSELECT文のキャッシュするための領域で<br>ログバッファは更新処理(INSERT, DELETE, UPDATE, MERGE)時の依頼を溜めてる領域です。<br>更新処理は即時反映でなくログバッファに溜めて、あとでまとめてストレージに書き込んでるので。</p><p>上記を踏まて、目的によって以下のデフォルトメモリのサイズをチューニングします。</p><ul><li>データキャッシュ(バッファプール)<ul><li><code>innodb_buffer_pool_size</code>: 128Mバイト</li></ul></li><li>ログバッファ<ul><li><code>innodb_log_buffer_size</code>: 8Mバイト</li></ul></li></ul><p>ログバッファのチューニングとしては、通常のWebアプリケーションでは<br>一気に8M以上の書き込まれることは無いと思うので必要ないかなって印象です。<br>逆に更新処理が多いバッチ系のアプリケーションであれば調整すると効果が期待できそうです。</p><p>データキャッシュでは、単独のDBサーバだったら物理メモリの60〜70%ぐらい割り当てて<br>あとは、DBサーバのメモリー使用量で枯渇しないかなど見ながら微調整していきます。</p><h2 id="テーブル定義"><a href="#テーブル定義" class="headerlink" title="テーブル定義"></a>テーブル定義</h2><h3 id="INDEXについて"><a href="#INDEXについて" class="headerlink" title="INDEXについて"></a>INDEXについて</h3><h4 id="・INDEX効果"><a href="#・INDEX効果" class="headerlink" title="・INDEX効果"></a>・INDEX効果</h4><p>TABLEに対して発行される、SELECT句の種類に応じたINDEXは作らずINDEX効果が期待できるもののみを厳選します。</p><p>一般的には、INDEX効果が効く場合としては検索結果数が全体の5〜10%前後というのが目安で<br>それ以上の結果が返ってくる場合は大きな効果を期待できずテーブルフルスキャンの方が速いと言われてたりします。<br>とはいえ、WHERE句によって検索結果数は変わることがほとんどなので、<br>大規模なデータ量となるテーブルなのか、もしくはカーディナリティ(値の種類がどれくらいかの指標)が<br>比較的高い検索結果になるのかでINDEXを貼る価値を考えたりします。</p><h4 id="・INDEX数"><a href="#・INDEX数" class="headerlink" title="・INDEX数"></a>・INDEX数</h4><p>INDEXを貼ると「INSERT, UPDATE, DELETE」の速度が低下するので更新頻度が多いテーブルほどINDEXは少いほうがいいです。<br>データ量とかにもよるけど、テーブルあたり4つぐらいまでがひとつの目安と思ってて<br>これ以上貼ってたら、ちょっときな臭いと思ってボトルネックになっていないか調査してみていいと思います。<br>あと、INDEX数が多くなるとデータ量が増えるのでデータキャッシュに載せれるデータが減ってしまうデメリットもあります。</p><h4 id="・INDEX確認"><a href="#・INDEX確認" class="headerlink" title="・INDEX確認"></a>・INDEX確認</h4><p>たまにUNIQUE制約とINDEXを同時に同一カラムに定義してることがあるので<br>TABLEを作成した後は、INDEXが重複して定義していないか確認します。<br><code>SHOW INDEX FROM &lt;TabelName&gt;;</code></p><h3 id="パーティショニング"><a href="#パーティショニング" class="headerlink" title="パーティショニング"></a>パーティショニング</h3><p>実際の運用ではデータが青天井で勢いよく増加つづけるテーブルが出てきます(解析系や履歴系とか)<br>このようなテーブルはデータ量に比例してパフォーマンスが落ちるのでパーティショニングします。</p><p>よく利用するパーティショニングの種類は、Rangeパーティショニングで<code>id</code>や<code>date</code>を利用して<br>テーブルの1日のデータ増加量よって単位は異りますが100万〜1000万ぐらい間でパーティショニングを切ってました。<br>パーティショニングを分ける単位の基準としては、1日のデータ増加量から逆算して2ヶ月くらいで<br>1パーティションを使いきるぐらいを目安としていました。</p><h2 id="クエリー発行"><a href="#クエリー発行" class="headerlink" title="クエリー発行"></a>クエリー発行</h2><h3 id="INDEXが期待できる条件を理解しておく"><a href="#INDEXが期待できる条件を理解しておく" class="headerlink" title="INDEXが期待できる条件を理解しておく"></a>INDEXが期待できる条件を理解しておく</h3><h4 id="・INDEXが効くクエリー"><a href="#・INDEXが効くクエリー" class="headerlink" title="・INDEXが効くクエリー"></a>・INDEXが効くクエリー</h4><ul><li>&#x3D;, &gt;, &lt;, AND, IN, ORDER BY, GROUP BY, BETWEEN</li><li>LIKE述語の前方一致のみ<ul><li><code>SELECT * FROM account WHERE name like &#39;fuji%&#39;;</code></li></ul></li><li>索引列で右側で演算や関数を行っている場合<ul><li><code>select * from account where 50 &gt; point * 1.1;</code></li></ul></li></ul><h4 id="・INDEXが効かないクエリー"><a href="#・INDEXが効かないクエリー" class="headerlink" title="・INDEXが効かないクエリー"></a>・INDEXが効かないクエリー</h4><ul><li>否定形(&lt;&gt;, !&#x3D;, NOT IN)</li><li>LIKE述語の中間一致、後方一致<ul><li><code>SELECT * FROM account WHERE name like &#39;%fuji%&#39;;</code></li><li><code>SELECT * FROM account WHERE name like &#39;%fuji&#39;;</code></li></ul></li><li>WHERE句で左側で演算や関数を行っている場合<ul><li><code>SELECT * FROM account WHERE point * 1.1 &gt; 100;</code></li><li><code>SELECT * FROM account WHERE LENGTH(point)b = 10;</code></li></ul></li><li>WHERE句でIs Null述語を使ってる<ul><li><code>SELECT * FROM account WHERE name IS NULL;</code></li></ul></li></ul><h3 id="ワイルドカード-のSELECT句"><a href="#ワイルドカード-のSELECT句" class="headerlink" title="*(ワイルドカード)のSELECT句"></a>*(ワイルドカード)のSELECT句</h3><p>カラム数が多いテーブル or 大きなデータを持ってるカラムが存在するテーブルなどでは<br>「*」を指定するのはできるだけ避けて必要なカラムのみを定義をするようします。<br>この手のSELECT句はIOにとても時間を取る上、データキャッシュに無駄なリソースが取られてしまいます。</p><h3 id="JOINの仕組みを理解しておく"><a href="#JOINの仕組みを理解しておく" class="headerlink" title="JOINの仕組みを理解しておく"></a>JOINの仕組みを理解しておく</h3><p>JOINを利用する場合は仕組みを理解しておくことが重要と思ってます。<br>MySQLのJOINアルゴリズムにはNested Loop Join(NLJ)が採用されてます。<br>NLJは、外部表(JOINする側)が内部表(JOINされる側)を1行ずつループして処理するアルゴリズムで<br>JOINの回数が増えると急激に重くなります。</p><p>外部表100行と内部表100行の状態でINDEXが効かないJOINした場合<br><code>100行 + 100行x100行 = 10100行</code>のような計算量になります。<br>逆に外部表と内部表に一意なINDEXが効いた状態でJOINした場合<br><code>100行 + 100行(100x1) = 200行</code>となり無駄なく高速に処理できます。</p><p>JOINは外部表と内部表のかけ算になるのでパフォーマンスの観点から見ると<br>一意なINDEXが効いている前提で利用するのがベターと思ってます。<br>多段JOINや一意なINDEXが効かないJOINはついてはバッチ処理など<br>パフォーマンスを求めれない処理などであれば利用するのはありかと思います。</p><h3 id="サブクエリーの使いどこ"><a href="#サブクエリーの使いどこ" class="headerlink" title="サブクエリーの使いどこ"></a>サブクエリーの使いどこ</h3><p>まず、サブクエリーは以下の問題点があるので積極的には使われない傾向があります。</p><ul><li>可読性が悪い</li><li>SELECT句にサブクエリー分のコストが上乗せされるので実行コストが増える</li><li>サブクエリー結果からINやテーブル結合する場合、サブクエリー結果にインデック情報などを持たないので最適なパフォーマンスを得られない</li><li>サブクエリー結果がすべてメモリに乗らなかった場合はSwapが発生し、急激な性能劣化が起きる</li><li>サブクエリーが実行されて初めて結果がわかるので、事前にEXPLAINで実行計画を確認できない</li></ul><p>ということ問題があるので、SQL側で頑張って1クエリーで完結させようとせずに<br>アプリケーション側から素直に本クエリー分とサブクエリー分を発行して<br>データを組み立てた方がシンプルに読みやすいコードになります。</p><h4 id="・サブクエリーでパフォーマンスを改善できる場合"><a href="#・サブクエリーでパフォーマンスを改善できる場合" class="headerlink" title="・サブクエリーでパフォーマンスを改善できる場合"></a>・サブクエリーでパフォーマンスを改善できる場合</h4><p>サブクエリーでパフォーマンスを改善の期待できるのは、<br>テーブル結合を行う前に対象行数を小さく絞り込むことができる場合です。</p><p>典型的なケースでは、1:Nの関係持った所属テーブルとユーザテーブルがある状況下で<br>所属毎のユーザ数を取得したいケースがあります。</p><ul><li>パターン1: 所属テーブルとユーザテーブルをJOINしてからGROUP BYで絞る</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  g.id,</span><br><span class="line">  g.name,</span><br><span class="line">  <span class="built_in">SUM</span>(u.group_id) <span class="keyword">AS</span> user_count</span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  `<span class="keyword">group</span>` <span class="keyword">AS</span> g</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span></span><br><span class="line">  <span class="keyword">user</span> <span class="keyword">AS</span> u</span><br><span class="line"><span class="keyword">ON</span></span><br><span class="line">  g.id <span class="operator">=</span> u.group_id</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  g.id</span><br></pre></td></tr></table></figure><ul><li>パターン2: サブクエリで事前にユーザテーブル対象を絞ってから所属テーブルとをJOINする</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  g.id,</span><br><span class="line">  g.name,</span><br><span class="line">  u.user_count</span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  `<span class="keyword">group</span>` <span class="keyword">AS</span> g</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span></span><br><span class="line">  (<span class="keyword">SELECT</span></span><br><span class="line">     group_id,</span><br><span class="line">     <span class="built_in">SUM</span>(group_id) <span class="keyword">as</span> user_count</span><br><span class="line">   <span class="keyword">FROM</span></span><br><span class="line">     <span class="keyword">user</span></span><br><span class="line">   <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">     group_id) <span class="keyword">AS</span> u</span><br><span class="line"><span class="keyword">ON</span></span><br><span class="line">  g.id <span class="operator">=</span> u.group_id</span><br></pre></td></tr></table></figure><p>パターン1とパターン2では結合行数が異ります。<br>パターン1では、ユーザテーブルのレコード数分の結合が行われますが<br>パターン2では、サブクエリで事前にユーザテーブルの結合対象を所属テーブル分に絞ってます。<br>そのため、パターン2の方が結合コストが低くパフォーマンス改善が期待できるようになります。</p><p>ただ、数百万件ぐらいの件数がないと、パターン1 &lt; パターン2のパフォーマンスにならないと思います。<br>パターン2は結合コストが低いもののクエリー2回発行してて、パターン1は1クエリーしか発行してないので<br>件数が少いケースではパターン1が有利になります。</p><h2 id="アプリケーション側"><a href="#アプリケーション側" class="headerlink" title="アプリケーション側"></a>アプリケーション側</h2><h3 id="アプリケーション側でグルグル検索をしない-N-1"><a href="#アプリケーション側でグルグル検索をしない-N-1" class="headerlink" title="アプリケーション側でグルグル検索をしない(N+1)"></a>アプリケーション側でグルグル検索をしない(N+1)</h3><p>for文とかで都度クエリーを発行するとDBへの負荷が高いです。<br>解決策のアプローチとしては、JOINで1回のクエリーで取得する方法がありますが<br>多段JOINで複雑なSQLになる場合は、アプリケーション側で<code>IN</code>を利用したクエリー結果で<br>データを組み立てるのも1つの手です。(コードの文法は適当です)</p><ul><li>Before</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># usersの回数分、SELECTが2回づつ発行される</span></span><br><span class="line"><span class="keyword">for</span> user <span class="keyword">in</span> users:</span><br><span class="line">    group = `SELECT * FROM group WHERE user_id = user.<span class="built_in">id</span>`</span><br><span class="line">    detail = `SELECT * FROM user_detail WHERE user_id = user.<span class="built_in">id</span>`</span><br></pre></td></tr></table></figure><ul><li>After</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># SELECTが2回だけしか実行されない</span></span><br><span class="line">all_groups = &#123;g.user_id: g <span class="keyword">for</span> g <span class="keyword">in</span> `SELECT * FROM group`&#125;</span><br><span class="line"></span><br><span class="line">user_ids = [user.<span class="built_in">id</span> <span class="keyword">for</span> user <span class="keyword">in</span> users]</span><br><span class="line">user_details = &#123;d.user_id: d <span class="keyword">for</span> d <span class="keyword">in</span> `SELECT * FROM user_detail WHERE user_id IN (user_ids)`</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> user <span class="keyword">in</span> users:</span><br><span class="line">    group = all_groups[user.<span class="built_in">id</span>]</span><br><span class="line">    detail = user_details[user.<span class="built_in">id</span>]</span><br></pre></td></tr></table></figure><h3 id="アプリケーション側のキャッシュを利用する"><a href="#アプリケーション側のキャッシュを利用する" class="headerlink" title="アプリケーション側のキャッシュを利用する"></a>アプリケーション側のキャッシュを利用する</h3><p>DBへのクエリーを減らすためにアプリケーション側のミドルウェア(redisやmemcachedなど)<br>を利用してクエリー結果をキャッシュします。</p><p>よくキャッシュ対象としてされるのは</p><ul><li>頻繁に発行されるクエリー</li><li>Primaryキーでの参照クエリー</li><li>fixtureデータ系のフルスキャンクエリー</li><li>時間がかかる重いクエリー<br>などがあります。</li></ul><p>基本はリードスルーのみでキャッシュ生成を行います。<br>ライトスルーは個人的には限定的パターンしか使いません。<br>というのも、ライトスルー対応をするとロールバックしたときの対応などを考慮して<br>トランザクションの一番最後にキャッシュ生成ポイントを仕込まなければならず<br>アプリケーションの実装が複雑になりがちになります。<br>また、デグレでDBとキャッシュのデータ不整合が起きやすくなります。<br>そのため、重いクエリーが発行されれるのでパフォーマンス的にどうしても<br>キャッシュで簡潔したい場合のみに限定して利用しています。</p><h3 id="SQL確認"><a href="#SQL確認" class="headerlink" title="SQL確認"></a>SQL確認</h3><p>アプリケーション側のデバッグツールでもSQLは確認できますが<br>一部クエリーをフィルターして出力してたりするので、DBの生SQLを確認したほうがよかったりします。</p><p>確認するポイントとしては、</p><ul><li>1リクエストで何回クエリーが発行されるかを見ます(30回以上であれば改善します)</li><li>1リクエストで重複してるクエリーを発行していないか確認します</li><li>スロークエリーが発行されていないか確認します。(1秒以上のものがあれば改善します)</li></ul><p>MySQLの設定ファイルに以下を定義するとtmpから生SQLが確認できるようになります。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[mysqld]</span><br><span class="line">general_log=1</span><br><span class="line">general_log_file=/tmp/sql.log</span><br><span class="line">slow_query_log=1</span><br><span class="line">slow_query_log_file=/tmp/slow_sql.log</span><br><span class="line">long_query_time = 1</span><br></pre></td></tr></table></figure><p>自分は開発中、<code>tail -f</code>でこのログを確認しながらパフォーマンス確認をしてます</p><h2 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h2><p>開発する際にパフォーマンスで気にしてることを棚卸し的に書いてみたけど、<br>実際はDBのI&#x2F;Oを減らすことばかりに気に取られないようにしてる。</p><p>どちらかというと、アプリケーションコードの可読性や保守性の方を一番大事にしてて<br>パフォーマンス重視のクエリーのために複雑なSQLを書いたり、<br>複雑なORMを作ると読みとくのに時間を取られるようになるしメンテされなくなる。</p><p>なので、できるだけアプリケーションの実装はシンプルになるよう心掛けて<br>パフォーマンスに問題があればはじめて複雑なことを着手する感じです。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/41qHKrFZi0L._SL160_.jpg" alt="SQLアンチパターン" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">SQLアンチパターン</a></div><div class="amazlet-detail">Bill Karwin <br />オライリージャパン <br />売り上げランキング: 4,987<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873115892/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div><br /><div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank"><img src="https://images-fe.ssl-images-amazon.com/images/I/51pl3HrLCjL._SL160_.jpg" alt="SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)</a></div><div class="amazlet-detail">ミック <br />技術評論社 <br />売り上げランキング: 44,873<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774173010/fujimisakar03-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;開発時にMySQLのパフォーマンスで気を付けてることのまとめました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;メモリ設定&lt;/li&gt;
&lt;li&gt;テーブル定義&lt;/li&gt;
&lt;li&gt;クエリー発行&lt;/li&gt;
&lt;li&gt;アプリケーション側&lt;/li&gt;
&lt;li&gt;まとめ&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;</summary>
      
    
    
    
    
    <category term="DB" scheme="http://blog.fujimisakari.com/tags/DB/"/>
    
  </entry>
  
</feed>
