<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>go - shungo blog</title>
	<atom:link href="https://shungoblog.com/tag/go/feed" rel="self" type="application/rss+xml" />
	<link>https://shungoblog.com</link>
	<description>しゅんごブログ</description>
	<lastBuildDate>Fri, 02 May 2025 11:19:44 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shungoblog.com/wp-content/uploads/2022/04/cropped-IMG_0718-32x32.jpg</url>
	<title>go - shungo blog</title>
	<link>https://shungoblog.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/>
<atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<atom:link rel="hub" href="https://websubhub.com/hub"/>
<atom:link rel="self" href="https://shungoblog.com/tag/go/feed"/>
	<item>
		<title>【Go】 import cycle not allowed (循環参照)について</title>
		<link>https://shungoblog.com/programming/go-import-cycle-not-allowed.html</link>
					<comments>https://shungoblog.com/programming/go-import-cycle-not-allowed.html#respond</comments>
		
		<dc:creator><![CDATA[しゅんご]]></dc:creator>
		<pubDate>Thu, 23 Feb 2023 13:57:43 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[go]]></category>
		<category><![CDATA[エンジニア]]></category>
		<guid isPermaLink="false">https://shungoblog.com/?p=1985</guid>

					<description><![CDATA[<p>目次 import cycle not allowed (循環参照とは？)循環参照が起こるコード例ファイル構成go.modmain.goa.gob.gomain.goを実行してみる循環参照の対策方法 import cyc [&#8230;]</p>
<p>The post <a href="https://shungoblog.com/programming/go-import-cycle-not-allowed.html">【Go】 import cycle not allowed (循環参照)について</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">import cycle not allowed (循環参照とは？)</a></li><li><a href="#toc2" tabindex="0">循環参照が起こるコード例</a><ol><li><a href="#toc3" tabindex="0">ファイル構成</a></li><li><a href="#toc4" tabindex="0">go.mod</a></li><li><a href="#toc5" tabindex="0">main.go</a></li><li><a href="#toc6" tabindex="0">a.go</a></li><li><a href="#toc7" tabindex="0">b.go</a></li></ol></li><li><a href="#toc8" tabindex="0">main.goを実行してみる</a></li><li><a href="#toc9" tabindex="0">循環参照の対策方法</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">import cycle not allowed (循環参照とは？)</span></h2>
<p><span>他のpackageにimportしたときに、こんなエラーが出た人はいるんではないでしょうか？</span></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>import cycle not allowed</code></pre>
</div>
<p>このエラーは循環参照が起きているときの吐かれるエラーです。</p>
<p>&nbsp;</p>
<p>たとえばpackage aとbがあった場合に、</p>
<ul>
<li><strong>package aでbの関数やメソッドを参照している</strong></li>
<li><strong>package bでもaの関数やメソッドを参照している</strong></li>
</ul>
<p>と循環参照していることになり、<code>import cycle not allowed</code>というエラーが出てしまいます。</p>
<p>&nbsp;</p>
<h2><span id="toc2">循環参照が起こるコード例</span></h2>
<p>循環参照が発生する具体的なコードを書いてみます。</p>
<h3><span id="toc3">ファイル構成</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang=""><code>./app
├─ a/a.go
├─ b/b.go
├─ main.go
└─ go.mod</code></pre>
</div>
<h3><span id="toc4">go.mod</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="go.mod"><code>module app

go 1.19</code></pre>
</div>
<h3><span id="toc5">main.go</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="main.go"><code>package main

import (
  "app/a"
)

func main() {
  a.CallB()
}
</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc6">a.go</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="a.go"><code>package a

import (
  "fmt"
  "app/b"
)

func PrintA() {
  fmt.Println("A")
}

func CallB() {
  b.PrintB()
}</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc7">b.go</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="b.go"><code>package b

import (
  "fmt"
  "app/a"
)

func PrintB() {
  fmt.Println("B")
}

func CallA() {
  a.PrintA()
}</code></pre>
</div>
<h2><span id="toc8">main.goを実行してみる</span></h2>
<p>この状態で<code>main.go</code>を実行すると<code>import cycle not allowed</code>のエラーが出ます。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% go run main.go
package command-line-arguments
        imports app/a
        imports app/b
        imports app/a: import cycle not allowed</code></pre>
</div>
<h2><span id="toc9">循環参照の対策方法</span></h2>
<p>&nbsp;</p>
<p>循環参照が起きないようにするには主に下記の方法があるようです。</p>
<ul>
<li><strong>ファイル構成を変更する</strong></li>
<li><strong>interfaceを使う</strong></li>
<li><strong>同じpackageにまとめる</strong></li>
</ul>
<p>それぞれの具体的な実装例はまた別の記事で紹介しようと思います。</p><p>The post <a href="https://shungoblog.com/programming/go-import-cycle-not-allowed.html">【Go】 import cycle not allowed (循環参照)について</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://shungoblog.com/programming/go-import-cycle-not-allowed.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Docker】 Go × Mailhog × net/smtpでメール送信する</title>
		<link>https://shungoblog.com/programming/docker-go-mailhog-net-smtp.html</link>
					<comments>https://shungoblog.com/programming/docker-go-mailhog-net-smtp.html#respond</comments>
		
		<dc:creator><![CDATA[しゅんご]]></dc:creator>
		<pubDate>Wed, 22 Feb 2023 14:11:45 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[go]]></category>
		<category><![CDATA[mailhog]]></category>
		<guid isPermaLink="false">https://shungoblog.com/?p=1967</guid>

					<description><![CDATA[<p>目次 はじめにコードファイル構成Dockerfiledocker-compose.ymlmain.goメール送信コンテナに入りmain.goを実行mailhogで送信されたメールを確認 はじめに Docker環境で構築し [&#8230;]</p>
<p>The post <a href="https://shungoblog.com/programming/docker-go-mailhog-net-smtp.html">【Docker】 Go × Mailhog × net/smtpでメール送信する</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">コード</a><ol><li><a href="#toc3" tabindex="0">ファイル構成</a></li><li><a href="#toc4" tabindex="0">Dockerfile</a></li><li><a href="#toc5" tabindex="0">docker-compose.yml</a></li><li><a href="#toc6" tabindex="0">main.go</a></li></ol></li><li><a href="#toc7" tabindex="0">メール送信</a><ol><li><a href="#toc8" tabindex="0">コンテナに入りmain.goを実行</a></li><li><a href="#toc9" tabindex="0">mailhogで送信されたメールを確認</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>Docker環境で構築したGoからメール送信する方法をご紹介します。</p>
<p>メールサーバーには、開発環境でメール送信を確認できる<a rel="nofollow noopener external" target="_blank" href="https://github.com/mailhog/MailHog">mailhog</a>を使用します。</p>
<p>メール送信には標準パッケージの<a rel="nofollow noopener external" target="_blank" href="https://pkg.go.dev/net/smtp">net/smtp</a>を用います。</p>
<p>&nbsp;</p>
<p>今回の実装以外にもいろいろ追加していますが、</p>
<p>下記でメール送信を実装しているので参考にしてみてください！</p>

<a rel="nofollow noopener external" target="_blank" href="https://github.com/shungo0525/doker_go" title="GitHub - shungo0525/doker_go" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://shungoblog.com/wp-content/uploads/cocoon-resources/blog-card-cache/803e1b3a18b5f12c742e15f875c82fcb." alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">GitHub - shungo0525/doker_go</div><div class="blogcard-snippet external-blogcard-snippet">Contribute to shungo0525/doker_go development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/shungo0525/doker_go" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
<p>&nbsp;</p>
<h2><span id="toc2">コード</span></h2>
<h3><span id="toc3">ファイル構成</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang=""><code>./root_dir
├─ app
│   ├─ main.go
│   ├─ go.mod
│   └─ go.sum
├─ docker-compose.yml
└─ Dockerfile</code></pre>
</div>
<h3><span id="toc4">Dockerfile</span></h3>
<p>とりあえずGoが使える環境を構築します。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang="Dockerfile"><code>FROM golang:1.19-alpine

RUN apk update &amp;&amp; apk add git

# appディレクトリの作成
RUN mkdir /go/src/app
# ワーキングディレクトリの設定
WORKDIR /go/src/app</code></pre>
</div>
<h3><span id="toc5">docker-compose.yml</span></h3>
<p>開発環境用のメールサーバーであるmailhogを使用します。</p>
<p>service名の<strong>mailhog</strong>とSMPTポートである<strong>1025</strong>はメール送信するときに指定します。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-yaml" data-lang="docker-compose.yml"><code>version: '3'
services:
  app:
    build: .
    tty: true
    volumes:
      - ./app:/go/src/app
    depends_on:
      - mysql
    ports:
      - 8000:8000

  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "8025:8025" # MailhogのWebとAPI用のポート
      - "1025:1025" # SMTPポート</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc6">main.go</span></h3>
<p><code>docker-compose.yml</code>で指定したservice名の<strong>mailhog</strong>をhostnameに、portを<strong>1025</strong>に指定します。</p>
<p><code>docker-compose.yml</code>の値とここで指定する値が異なるとメール送信できないので注意しましょう。</p>
<p>送信元、送信先、メッセージ内容は必要に応じて変更してください。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="main.go"><code>package main

import (
  "fmt"
  "net/smtp"
  "os"
  "strings"
)

var (
  hostname = "mailhog" // docker-composeで指定したサービス名
  port     = 1025 // mailhogのSMPTポート
  username = "user@example.com"
  password = "password"
  from     = "from@example.net"
  subject  = "hello"
  body     = "Hello World!"
  receiver = []string{"receiver@example.com"}
)

func main() {
  SendEmail()
}

func SendEmail() {
  smtpServer := fmt.Sprintf("%s:%d", hostname, port)
  auth := smtp.CRAMMD5Auth(username, password)
  msg := []byte(fmt.Sprintf("To: %s\nSubject: %s\n\n%s", strings.Join(receiver, ","), subject, body))

  if err := smtp.SendMail(smtpServer, auth, from, receiver, msg); err != nil {
    fmt.Fprintln(os.Stderr, err)
  }
}</code></pre>
</div>
<h2><span id="toc7">メール送信</span></h2>
<h3><span id="toc8">コンテナに入りmain.goを実行</span></h3>
<p>コンテナに入って<code>main.go</code>を実行することで、<code>SendEmail()</code>が実行されメール送信されます。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% dc exec app sh
# go run main.go</code></pre>
</div>
<h3><span id="toc9">mailhogで送信されたメールを確認</span></h3>
<p><a rel="nofollow noopener external" target="_blank" href="http://localhost:8025/">http://localhost:8025/</a>を開くとMailhogでローカル環境で送信したメールを確認することができます。</p>
<p><img fetchpriority="high" decoding="async" src="https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37-300x88.jpg" alt="go-mail-1" width="955" height="280" class="alignnone wp-image-1976" srcset="https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37-300x88.jpg 300w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37-1024x300.jpg 1024w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37-768x225.jpg 768w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37-1536x450.jpg 1536w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.37.jpg 1920w" sizes="(max-width: 955px) 100vw, 955px" /></p>
<p>クリックすると送信内容の詳細を確認することができます。</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" src="https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41-300x88.jpg" alt="go-mail-2" width="1285" height="377" class="alignnone wp-image-1977" srcset="https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41-300x88.jpg 300w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41-1024x300.jpg 1024w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41-768x225.jpg 768w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41-1536x450.jpg 1536w, https://shungoblog.com/wp-content/uploads/2023/02/スクリーンショット-2023-02-22-23.07.41.jpg 1920w" sizes="(max-width: 1285px) 100vw, 1285px" /></p><p>The post <a href="https://shungoblog.com/programming/docker-go-mailhog-net-smtp.html">【Docker】 Go × Mailhog × net/smtpでメール送信する</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://shungoblog.com/programming/docker-go-mailhog-net-smtp.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Docker × Go で gRPC を動かしてみた</title>
		<link>https://shungoblog.com/programming/docker-go-grpc.html</link>
					<comments>https://shungoblog.com/programming/docker-go-grpc.html#respond</comments>
		
		<dc:creator><![CDATA[しゅんご]]></dc:creator>
		<pubDate>Mon, 13 Feb 2023 13:05:14 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[go]]></category>
		<category><![CDATA[grpc]]></category>
		<guid isPermaLink="false">https://shungoblog.com/?p=1781</guid>

					<description><![CDATA[<p>目次 はじめにローカル環境で実行する場合パッケージインストールディレクトリ構成protoファイル作成protocコマンドでコードを自動生成gRPCサーバーを作成gRPCurlをインストールgRPCサーバーを起動gRPCク [&#8230;]</p>
<p>The post <a href="https://shungoblog.com/programming/docker-go-grpc.html">Docker × Go で gRPC を動かしてみた</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">ローカル環境で実行する場合</a><ol><li><a href="#toc3" tabindex="0">パッケージインストール</a></li><li><a href="#toc4" tabindex="0">ディレクトリ構成</a></li><li><a href="#toc5" tabindex="0">protoファイル作成</a></li><li><a href="#toc6" tabindex="0">protocコマンドでコードを自動生成</a></li><li><a href="#toc7" tabindex="0">gRPCサーバーを作成</a></li><li><a href="#toc8" tabindex="0">gRPCurlをインストール</a></li><li><a href="#toc9" tabindex="0">gRPCサーバーを起動</a></li><li><a href="#toc10" tabindex="0">gRPCクライアントを作成</a></li><li><a href="#toc11" tabindex="0">クライアントを実行して動作確認</a></li></ol></li><li><a href="#toc12" tabindex="0">Docker環境で実行する場合</a><ol><li><a href="#toc13" tabindex="0">Dockerfile</a></li><li><a href="#toc14" tabindex="0">docker-compose.yml</a></li><li><a href="#toc15" tabindex="0">Docker環境で実行する</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>この記事では<strong>go × gRPC</strong>で簡単なリクエスト送信・レスポンスの表示を説明していきます。</p>
<p>大きく分けて、下記の3つを順番に実行していきます。</p>
<ul>
<li>protoファイルからコードを自動生成</li>
<li>goでgRPCサーバーを起動</li>
<li>goでクライアントを実行してリクエスト送信・レスポンス表示</li>
</ul>
<p>最初はローカルで実行して、次に同じことをDocker環境でも実行します。</p>
<p>&nbsp;</p>
<p>この記事を書くにあたり、下記を参考にさせていただきました。</p>
<p>参考: <a rel="nofollow noopener external" target="_blank" href="https://zenn.dev/hsaki/books/golang-grpc-starting/viewer">作ってわかる！ はじめてのgRPC</a></p>
<p>&nbsp;</p>
<p>今回実装したリポジトリです。</p>
<p>全体のコードはこちらをご覧くだしさい。</p>

<a rel="nofollow noopener external" target="_blank" href="https://github.com/shungo0525/grpc" title="GitHub - shungo0525/grpc" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://shungoblog.com/wp-content/uploads/cocoon-resources/blog-card-cache/5f8b70b73a9dc0d396e83d28d911d91d." alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">GitHub - shungo0525/grpc</div><div class="blogcard-snippet external-blogcard-snippet">Contribute to shungo0525/grpc development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/shungo0525/grpc" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
<h2><span id="toc2">ローカル環境で実行する場合</span></h2>
<h3><span id="toc3">パッケージインストール</span></h3>
<p>まずは必要なパッケージをインストールします。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ brew install protobuf
$ go get -u google.golang.org/grpc
$ go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc</code></pre>
</div>
<p>パッケージの依存関係を管理するためのgo.modファイルを生成します。</p>
<p>&nbsp;</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ cd s<span>tudy_grpc # 作業ディレクトリ
</span>$ go mod init mygrpc</code></pre>
</div>
<h3><span id="toc4">ディレクトリ構成</span></h3>
<p>今回は下記のようなディレクトリ構成にしました。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang=""><code><span>./study_grpc # 作業ルートディレクトリ
├─ api
│   └─ hello.proto # protoファイル
<span class="token unchanged">├─ cmd
│   ├─ server
│   │ └─ main.<span class="hljs-keyword">go</span>
</span><span class="token inserted-sign inserted">│   └─ client
│    <span class="token unchanged">    </span>└─ main.<span class="hljs-keyword">go</span></span>
├─ pkg
│   └─ grpc # ここにコードを自動生成させる
│         ├─ hello.pb.<span class="hljs-keyword">go</span>
│         └─ hello_grpc.pb.<span class="hljs-keyword">go</span>
├─ <span class="hljs-keyword">go</span>.mod
└─ <span class="hljs-keyword">go</span>.sum</span></code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc5">protoファイル作成</span></h3>
<p><code><strong>api/hello.proto</strong></code></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang=""><code><span><span class="hljs-comment">// protoのバージョンの宣言</span>
</span><span>syntax</span>= <span><span class="hljs-string">"proto3"</span></span><span>;
</span><span>
</span><span><span class="hljs-comment">// protoファイルから自動生成させるGoのコードの置き先</span>
</span><span><span class="hljs-comment">// (詳細は4章にて)</span>
</span><span>option </span>go_package = <span><span class="hljs-string">"pkg/grpc"</span></span><span>;
</span><span>
</span><span><span class="hljs-comment">// packageの宣言</span>
</span><span><span class="hljs-keyword">package</span> </span>myapp<span>;
</span><span>
</span><span><span class="hljs-comment">// サービスの定義</span>
</span><span>service </span>GreetingService {
   <span><span class="hljs-comment">// サービスが持つメソッドの定義</span>
</span><span>  </span><span><span class="hljs-function">rpc </span></span><span class="hljs-function"><span class="hljs-title">Hello</span> <span class="hljs-params">(HelloRequest)</span> </span><span><span class="hljs-function"><span class="hljs-title">returns</span> </span></span><span class="hljs-function"><span class="hljs-params">(HelloResponse)</span></span><span>;
</span>}

<span><span class="hljs-comment">// 型の定義</span>
</span><span>message </span>HelloRequest {
   <span>string</span>name = <span><span class="hljs-number">1</span></span><span>;
</span>}

<span>message </span>HelloResponse {
   <span>string</span>message = <span><span class="hljs-number">1</span></span><span>;
</span>}</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc6">protocコマンドでコードを自動生成</span></h3>
<p>下記のコマンドを実行すると<code>pkg/grpc</code><span>ディレクトリ直下に、2つのファイルが生成されます。</span></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code><span><span class="hljs-meta">$  </span></span><span class="token builtin class-name"><span class="bash"><span class="hljs-built_in">cd</span></span></span><span><span class="bash"> api
</span><span class="hljs-meta">$</span><span class="bash"> protoc </span></span><span class="token parameter variable"><span class="bash">--go_out</span></span><span class="token operator"><span class="bash">=</span></span><span class="token punctuation"><span class="bash">..</span></span><span><span class="bash">/pkg/grpc </span></span><span class="token parameter variable"><span class="bash">--go_opt</span></span><span class="token operator"><span class="bash">=</span></span><span><span class="bash">paths</span></span><span class="token operator"><span class="bash">=</span></span><span><span class="bash">source_relative </span></span><span class="token punctuation"><span class="bash">\
</span></span><span>         --go-grpc_out</span><span class="token operator">=</span><span class="token punctuation">..</span><span>/pkg/grpc --go-grpc_opt</span><span class="token operator">=</span><span>paths</span><span class="token operator">=</span><span>source_relative </span><span class="token punctuation">\
</span><span>         hello.proto</span></code></pre>
</div>
<p>&nbsp;</p>
<p><strong><code><span class="token parameter variable">--go_opt</span><span class="token operator">=</span>paths<span class="token operator">=</span>source_relative</code></strong>は相対パスを指定するオプションです。</p>
<p><strong><code>./study_grpc/api</code></strong>でコマンドを実行したため、相対パスでファイルの保存先を指定しています。</p>
<p>&nbsp;</p>
<p>ちなみに<strong><code><span class="token parameter variable">--go_opt</span><span class="token operator">=</span>paths<span class="token operator">=</span>source_relative</code></strong>を使わず、<strong><code>./study_grpc/</code></strong>から実行する場合は、</p>
<p>下記のコマンドになります。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code><span>$ protoc --go_out=./ --go-grpc_out=./ api/hello.proto</span></code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc7">gRPCサーバーを作成</span></h3>
<p><code><strong>cmd/server/main.go</strong></code></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="Go"><code>package main

import (
  "fmt"
  "net"
  "log"
  "os"
  "context"
  "os/signal"
  "google.golang.org/grpc"
  "google.golang.org/grpc/reflection"
  hellopb "mygrpc/pkg/grpc"
)

func NewMyServer() *myServer {
  return &amp;myServer{}
}

func main() {
  // 1. 8080番portのLisnterを作成
  port := 8080
  listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
  if err != nil {
    panic(err)
  }

  // 2. gRPCサーバーを作成
  s := grpc.NewServer()

  // 3. gRPCサーバーにGreetingServiceを登録
  hellopb.RegisterGreetingServiceServer(s, NewMyServer())

  // 4. サーバーリフレクションの設定
  reflection.Register(s)

  // 5. 作成したgRPCサーバーを、8080番ポートで稼働させる
  go func() {
    log.Printf("start gRPC server port: %v", port)
    s.Serve(listener)
  }()

  // 6.Ctrl+Cが入力されたらGraceful shutdownされるようにする
  quit := make(chan os.Signal, 1)
  signal.Notify(quit, os.Interrupt)
  &lt;-quit
  log.Println("stopping gRPC server...")
  s.GracefulStop()
}

type myServer struct {
  hellopb.UnimplementedGreetingServiceServer
}

func (s *myServer) Hello(ctx context.Context, req *hellopb.HelloRequest) (*hellopb.HelloResponse, error) {
  // リクエストからnameフィールドを取り出して
  // "Hello, [名前]!"というレスポンスを返す
  return &amp;hellopb.HelloResponse{
    Message: fmt.Sprintf("Hello, %s!", req.GetName()),
  }, nil
}</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc8">gRPCurlをインストール</span></h3>
<p>gPRCはcurlで動作確認できませんが、gPRCurlを使うことで,</p>
<p>ローカル環境でも<span>動作確認することができます。</span></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code><span>$ brew  </span><span class="token function">install</span><span> grpcurl</span></code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc9">gRPCサーバーを起動</span></h3>
<p>これでgRPCサーバーを起動する準備ができました！</p>
<p>下記コマンドを実行してgRPCが起動すれば成功です！</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code><span>$ go cmd/server/run main.go
</span>2023/02/13 10:01:38 start gRPC server port: 8080</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc10">gRPCクライアントを作成</span></h3>
<p><strong>cmd/server/main.go</strong></p>
<p>&nbsp;</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-go" data-lang="Go"><code>package main

import (
  "bufio"
  "fmt"
  "os"
  "log"
  "context"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials/insecure"
  hellopb "mygrpc/pkg/grpc"
)

var (
  scanner *bufio.Scanner
  client  hellopb.GreetingServiceClient
)

func main() {
  fmt.Println("start gRPC Client.")

  // 1. 標準入力から文字列を受け取るスキャナを用意
  scanner = bufio.NewScanner(os.Stdin)

  // 2. gRPCサーバーとのコネクションを確立
  address := "localhost:8080"
  conn, err := grpc.Dial(
    address,

    grpc.WithTransportCredentials(insecure.NewCredentials()),
    grpc.WithBlock(),
  )
  if err != nil {
    log.Fatal("Connection failed.")
    return
  }
  defer conn.Close()

  // 3. gRPCクライアントを生成
  client = hellopb.NewGreetingServiceClient(conn)

  for {
    fmt.Println("1: send Request")
    fmt.Println("2: exit")
    fmt.Print("please enter &gt;")

    scanner.Scan()
    in := scanner.Text()

    switch in {
    case "1":
      Hello()

    case "2":
      fmt.Println("bye.")
      goto M
    }
  }
M:
}

func Hello() {
  fmt.Println("Please enter your name.")
  scanner.Scan()
  name := scanner.Text()

  req := &amp;hellopb.HelloRequest{
    Name: name,
  }
  res, err := client.Hello(context.Background(), req)
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println(res.GetMessage())
  }
}</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc11">クライアントを実行して動作確認</span></h3>
<p>gRPCサーバーとは別のターミナルを開いて、下記を実行することで、</p>
<p>gPRCサーバーに対して、リクエスト送信・レスポンス表示をすることができます。<span style="background-color: #e9ebec; color: inherit; font-family: inherit; font-size: var(--hcb-font-size,14px); white-space: pre;"></span></p>
<div class="hcb_wrap">
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code><span>$ go run cmd/client/main.go
start gRPC Client.

</span><span class="token number">1</span><span>: Hello
</span><span class="token number">2</span><span>: </span><span class="token builtin class-name">exit </span><span>please enter </span><span class="token operator">&gt;</span><span class="token number">1

</span><span>Please enter your name.
shungo
Hello, shungo</span><span class="token operator">!</span><span class="token number">

1</span><span>: Hello
</span><span class="token number">2</span><span>: </span><span class="token builtin class-name">exit </span><span>please enter</span><span> </span><span class="token operator">&gt;</span><span class="token number">2
</span><span>bye.</span></code></pre>
</div>
</div>
<p><span>このように、リクエスト送信・レスポンスの表示ができれば成功です！</span></p>
<p>&nbsp;</p>
<h2><span id="toc12">Docker環境で実行する場合</span></h2>
<p>これまではローカル環境にパッケージをインストールして、ローカル環境でgRPC用のコードを生成していました。</p>
<p>gRPC入門の記事を見るとローカルで実行するものが多いですが、バージョン管理などを考慮したらやはりDocker環境で実行したいですよね。</p>
<p>そこで、これまでやってきたことを今後はDocker環境で実行してみようと思います。</p>
<p>&nbsp;</p>
<h3><span id="toc13">Dockerfile</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-docker" data-lang="docker"><code>FROM golang:1.19.5-buster

ENV PROTOBUF_VERSION3.17.3

RUN apt-getupdate \
    &amp;&amp; apt-getinstall -y protobuf-compiler unzip --no-install-recommends \
    &amp;&amp; apt-getclean \
    &amp;&amp; rm-rf /var/lib/apt/lists/*

WORKDIR /grpc
COPY. /grpc

## build時のみ使いたいので、runで実行。
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
RUN go get google.golang.org/grpc/internal/channelz@v1.53.0</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc14">docker-compose.yml</span></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-yaml" data-lang="docker-compose.yml"><code>version:'3'

services:
  grpc:
    build: .
    volumes:
      - .:/grpc
    ports:
      - "8080:8080"
   tty: true</code></pre>
</div>
<p>&nbsp;</p>
<h3><span id="toc15">Docker環境で実行する</span></h3>
<p>1つ目のターミナルでdocker-composeを起動します。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ docker-compose up</code></pre>
</div>
<p>2つ目のターミナルでコンテナに入り、gRPCサーバーを起動します。</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ docker-compose exec grpc bash
&gt; go run cmd/server/main.go
2023/02/13 10:01:38 start gRPC server port: 8080
</code></pre>
</div>
<p>3つ目のターミナルでコンテナに入り、クライアントを動かします。</p>
<p>&nbsp;</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ docker-compose exec grpc bash
&gt; go run cmd/client/main.go
<span class="token number"><span class="hljs-attr">1</span></span><span>: <span class="hljs-string">Hello
</span></span><span class="token number"><span class="hljs-attr">2</span></span><span>:</span><span class="token builtin class-name"><span class="hljs-string">exit
</span></span><span><span class="hljs-attr">please</span> <span class="hljs-string">enter</span></span><span class="token operator"><span class="hljs-string">&gt;</span></span><span class="token number"><span class="hljs-string">1

</span></span><span><span class="hljs-attr">Please</span> <span class="hljs-string">enter your name.
</span><span class="hljs-attr">shungo
</span><span class="hljs-meta">Hello,</span> <span class="hljs-string">shungo!

</span></span><span class="token number"><span class="hljs-attr">1</span></span><span>: <span class="hljs-string">Hello
</span></span><span class="token number"><span class="hljs-attr">2</span></span><span>:</span><span class="token builtin class-name"><span class="hljs-string">exit
</span></span><span><span class="hljs-attr">please</span> <span class="hljs-string">enter</span></span><span class="token operator"><span class="hljs-string">&gt;</span></span><span class="token number"><span class="hljs-string">2

</span></span><span><span class="hljs-attr">bye.</span></span></code></pre>
</div>
<p>これでDocker環境でもgRPCを動かせることを確認できました！</p>
<p>上記では実行していませんが、コンテナ内で同じようにprotocコマンドでコードを生成することも可能です。</p><p>The post <a href="https://shungoblog.com/programming/docker-go-grpc.html">Docker × Go で gRPC を動かしてみた</a> first appeared on <a href="https://shungoblog.com">shungo blog</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://shungoblog.com/programming/docker-go-grpc.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
