<?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/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Node.js | みんたく</title>
	<atom:link href="https://mintaku-blog.net/category/develop/node/feed/" rel="self" type="application/rss+xml" />
	<link>https://mintaku-blog.net</link>
	<description>みんたくの技術ブログ</description>
	<lastBuildDate>Sun, 21 Nov 2021 12:42:01 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.11</generator>

<image>
	<url>https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2018/06/cropped-ipad-820272_640.jpg?fit=32%2C32&#038;ssl=1</url>
	<title>Node.js | みんたく</title>
	<link>https://mintaku-blog.net</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">144480658</site>	<item>
		<title>Docker + Node.jsのアプリにPM2を導入する</title>
		<link>https://mintaku-blog.net/node-pm2/</link>
					<comments>https://mintaku-blog.net/node-pm2/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Thu, 11 Nov 2021 03:05:55 +0000</pubDate>
				<category><![CDATA[Node.js]]></category>
		<category><![CDATA[個人開発]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=2079</guid>

					<description><![CDATA[<p>サーバーがダウンを防ぐためDocker + Node.jsのアプリにPM2を導入してみました。 エラーが起きるとサーバーが落ちる Node.jsでAPIを開 …</p>
The post <a href="https://mintaku-blog.net/node-pm2/">Docker + Node.jsのアプリにPM2を導入する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>サーバーがダウンを防ぐためDocker + Node.jsのアプリにPM2を導入してみました。</p>
<h2>エラーが起きるとサーバーが落ちる</h2>
<p>Node.jsでAPIを開発しているのですが、エラーが発生するとサーバーがダウンしてしまいます。一度サーバーがダウンしてしまうと、次リクエストから応答しなくなります。</p>
<p>try/catchでエラーハンドリングしていても、予期せぬところでエラーが発生する可能性があるため、さすがに運用していく上ではサーバーが落ちないようにしたいです。</p>
<p>なんとかサーバーが落ちないようにできないものかと調べてみると、PM2が良さそうな感じでした。</p>
<p>&nbsp;</p>
<h2>PM2とは</h2>
<p>PM2はNode.jsアプリケーションのプロセス管理を行うためのオープンソースです。PM2を使うことで、Node.jsアプリケーションの起動やログ管理、モニタリングができます。</p>
<p>プロセスの状態を常に監視しているため、プロセスが停止した場合などに自動で再起動させる機能を持っています。</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://i0.wp.com/raw.githubusercontent.com/unitech/pm2/master/pres/pm2.20d3ef.png?w=800&#038;ssl=1" alt="" data-recalc-dims="1">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://pm2.keymetrics.io/">PM2 - Home</a>
			</p>
							<div class="ys-blog-card__dscr">
					PM2 is an advanced production process ma&hellip;				</div>
										<div class="ys-blog-card__domain">pm2.keymetrics.io</div>
					</div>
	</div>
</div>

<p>&nbsp;</p>
<h2>PM2を導入する</h2>
<p>PM2を導入してみます。まずpackage.jsonにpm2を追加します。</p>
<p>・package.json</p><pre class="crayon-plain-tag">…

  "dependencies": {

    ...

    "pm2": "latest"
  },

…</pre><p>次にPM2の設定ファイルを作成します。scriptの箇所などは適宜読み替える必要があります。watchをtrueにすることでコードが変更があった場合に自動で再起動してくれます。</p>
<p>・pm2.config.json</p><pre class="crayon-plain-tag">}
  "name": "sample-app",
  "script" : "ts-node src/driver/server.ts",
  "watch": true,
  "env" : {
    "NODE_ENV" : "development"
  },
  "env_production" : {
    "NODE_ENV" : "production"
  }
}</pre><p>&nbsp;</p>
<h2>Dockerfileを修正する</h2>
<p>Dockerfileを修正し、先ほど作成したpm2.config.jsonを読み込ませてpm2を使えるようにします。</p>
<p>&#8211;no-daemonオプションを指定することで、PM2を実行し続けることができます。</p>
<p>・Dockerfile</p><pre class="crayon-plain-tag">FROM node:16
WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install
RUN npm install -g ts-node

COPY . .

EXPOSE 5000

CMD node_modules/pm2/bin/pm2 --no-daemon start pm2.config.json</pre><p>&nbsp;</p>
<h2>PM2で実際に動かしてみる</h2>
<p>Dockerを再度ビルドして動かしみます。</p>
<p><img data-attachment-id="2082" data-permalink="https://mintaku-blog.net/node-pm2/image1-13/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?fit=1308%2C1202&amp;ssl=1" data-orig-size="1308,1202" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image1" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?fit=300%2C276&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?fit=800%2C735&amp;ssl=1" loading="lazy" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?resize=800%2C735&#038;ssl=1" alt="" width="800" height="735" class="aligncenter size-large wp-image-2082" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?resize=1024%2C941&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?resize=300%2C276&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?resize=768%2C706&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image1.png?w=1308&amp;ssl=1 1308w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>無事動きました。</p>
<p>&nbsp;</p>
<h2>挙動の違いを確認する</h2>
<h3>nodemonの場合</h3>
<p><img data-attachment-id="2080" data-permalink="https://mintaku-blog.net/node-pm2/image3-7/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?fit=1170%2C166&amp;ssl=1" data-orig-size="1170,166" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image3" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?fit=300%2C43&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?fit=800%2C113&amp;ssl=1" loading="lazy" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?resize=800%2C113&#038;ssl=1" alt="" width="800" height="113" class="aligncenter size-large wp-image-2080" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?resize=1024%2C145&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?resize=300%2C43&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?resize=768%2C109&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image3.png?w=1170&amp;ssl=1 1170w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>従来のnodemonの場合は、以下のようにエラーが発生した場合「app crashed」になりコードの変更が検知されるまでは再起動されませんでした。</p>
<p>そのため、以降のリクエストはNo Responseとなってしまいます。</p>
<h3>PM2の場合</h3>
<p><img data-attachment-id="2081" data-permalink="https://mintaku-blog.net/node-pm2/image2-10/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?fit=1184%2C318&amp;ssl=1" data-orig-size="1184,318" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image2" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?fit=300%2C81&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?fit=800%2C215&amp;ssl=1" loading="lazy" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?resize=800%2C215&#038;ssl=1" alt="" width="800" height="215" class="aligncenter size-large wp-image-2081" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?resize=1024%2C275&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?resize=300%2C81&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?resize=768%2C206&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2021/11/image2.png?w=1184&amp;ssl=1 1184w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>今回導入したPM2の場合は、以下のようにエラーが発生した場合でもすぐにサーバーが再起動しました。</p>
<p>そのため、以降のリクエストも受け付けることができます。</p>The post <a href="https://mintaku-blog.net/node-pm2/">Docker + Node.jsのアプリにPM2を導入する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/node-pm2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2079</post-id>	</item>
		<item>
		<title>Node.js + MySQLのアプリケーションをDocker上で動かせるようにする</title>
		<link>https://mintaku-blog.net/docker-nodejs/</link>
					<comments>https://mintaku-blog.net/docker-nodejs/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Thu, 04 Nov 2021 04:07:59 +0000</pubDate>
				<category><![CDATA[Node.js]]></category>
		<category><![CDATA[アーキテクチャ]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=2066</guid>

					<description><![CDATA[<p>ローカルで動かしていたNode.js + MySQLのアプリケーションをDocker上で動かせるようにしました。またSequelizeを使ってマイグレーショ …</p>
The post <a href="https://mintaku-blog.net/docker-nodejs/">Node.js + MySQLのアプリケーションをDocker上で動かせるようにする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>ローカルで動かしていたNode.js + MySQLのアプリケーションをDocker上で動かせるようにしました。またSequelizeを使ってマイグレーションを実行するところまでメモしています。</p>
<h2>docker-compose.yml、Dockerfileの作成</h2>
<p>・docker-compose.yml</p><pre class="crayon-plain-tag">version: '3.4'

services:
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample_db
    ports:
      - '3306:3306'
    volumes:
      - data_volume:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password

  api:
    image: api
    env_file: ./app.env
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - 5000:5000
    volumes:
      - .:/usr/src/app
    depends_on:
      - db

volumes:
  data_volume:
</pre><p>・Dockerfile</p><pre class="crayon-plain-tag">FROM node:16

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 5000

CMD sleep 1; npm run dev
</pre><p>&nbsp;</p>
<p>docker-compose.ymlではMySQLの「db」とNode.jsの「api」の2つのサービスを定義しています。</p>
<h3>「db」サービスの定義</h3>
<p>公式からMySQL 8.0のバージョンのイメージを使用します。</p>
<p>environmentでMySQLに接続する際のパスワードと生成するDBを指定します。portsでポートフォワーディングの設定をしており、今回はホスト側のポート3306からコンテナ側のポート3306に繋がります。</p>
<p>volumesで「data_volume」というボリュームをコンテナの/var/lib/mysql/ディレクトリにマウントすることで、コンテナが削除されてもデータが保持されます。</p>
<p>また、MySQL8.0からはログイン認証方式のデフォルトがcaching_sha2_passwordになったことにより、接続時にエラーが発生するため、旧来の認証方法にするためのコマンドを追加しています。</p>
<h3>「api」サービスの定義</h3>
<p>apiというimageを指定しています。env_fileでapiコンテナからdbコンテナに接続する際の環境変数が定義されているapp.envファイルを読み込むようにしています。</p>
<p>buildでapiを起動するためのビルドを実行します。今回はDockerfileを指定し、そこでNode.jsのアプリケーションをDocker化しています。</p>
<p>参考：https://nodejs.org/ja/docs/guides/nodejs-docker-webapp/</p>
<h3>depends_onは起動順を担保しているだけで、稼働順を担保していない</h3>
<p>少しハマったのは、docker-compose upでコンテナの作成・起動を成功するが、APIからDBに接続する際にエラーになったことです。</p>
<p>depend_onでDBが起動が終わったタイミングで接続すると思っていたのですが、よくよく調べてみるとdepends_onは起動順を担保しているだけで、稼働順を担保していないことがわかりました。</p>
<p>つまり、DBから起動していたのですが、DBの起動が完了する前にAPIがDBに接続しようとしていたのです。</p>
<p>そのため、確実にDBが起動完了してからAPIが接続するようにするために「CMD sleep 1; npm run dev」で1秒するスリープさせるようにしました。色々調べてみると公式とかですとシェルを使ってみるとかかいてあったのですが、とりあえずこの方法でやっています。</p>
<p>&nbsp;</p>
<h2>Sequelizeでマイグレーションを実行する</h2>
<p>docker-compose upで問題なくコンテナ作成・起動・接続できたらSequelizeを導入していきます。</p>
<p>SequelizeはNode.jsのORMでマイグレーション機能があり、これを使ってマイグレーションしていきます。以下のコマンドでSequelizeとその依存パッケージをインストールと初期化を行います。</p><pre class="crayon-plain-tag">$ docker-compose run --rm api npm i mysql2 sequelize sequelize-cli
$ docker-compose run --rm api npx sequelize-cli init</pre><p>次に以下のコマンドでモデルとマイグレーションファイルを作成します。今回はToDoアプリを想定しており、tasksというテーブルをつくります。</p><pre class="crayon-plain-tag">$ docker-compose run --rm app npx sequelize-cli model:generate --name Task --attributes content:string,status:integer</pre><p>実行するとモデルとマイグレーションファイルが作成されます。マイグレーションファイルを少し修正しておきます。具体的にstatusにデフォルト値を追加したりcreated_atをCURRENT_TIMESTAMP、updated_atをCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPにしました。</p>
<p>・xxxxxxxx-create-task.js</p><pre class="crayon-plain-tag">'use strict'
module.exports = {
  up: async (queryInterface, Sequelize) =&gt; {
    await queryInterface.createTable('tasks', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      content: {
        type: Sequelize.STRING
      },
      status: {
        type: Sequelize.INTEGER,
        defaultValue: 1
      },
      created_at: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      },
      updated_at: {
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
      }
    })
  },
  down: async (queryInterface, Sequelize) =&gt; {
    await queryInterface.dropTable('tasks')
  }
}
</pre><p>先ほど起動したMySQLのコンテナの接続情報をconfigファイルに設定して、マイグレーションを実行していきます。正常に実行されれば完了です。</p><pre class="crayon-plain-tag">$ docker-compose run --rm app npx sequelize-cli db:migrate</pre><p>あとはseedersフォルダで初期データを投入することができます。シードファイルを作成して投入したいデータをセットし、以下のコマンドを実行することでシードできます。</p><pre class="crayon-plain-tag">$ docker-compose run --rm app npx sequelize-cli db:seed:all</pre><p></p>The post <a href="https://mintaku-blog.net/docker-nodejs/">Node.js + MySQLのアプリケーションをDocker上で動かせるようにする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/docker-nodejs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2066</post-id>	</item>
		<item>
		<title>Node.jsの特徴や使い方について整理する</title>
		<link>https://mintaku-blog.net/about-nodejs/</link>
					<comments>https://mintaku-blog.net/about-nodejs/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Tue, 02 Feb 2021 06:08:58 +0000</pubDate>
				<category><![CDATA[Node.js]]></category>
		<category><![CDATA[まとめ系]]></category>
		<category><![CDATA[技術本]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1839</guid>

					<description><![CDATA[<p>普段何気なく使っている技術や言葉について、表面的な知識にせず、しっかりとイメージできるまで自分なりに調べて整理し、理解するシリーズ。 Node.jsとは N …</p>
The post <a href="https://mintaku-blog.net/about-nodejs/">Node.jsの特徴や使い方について整理する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>普段何気なく使っている技術や言葉について、表面的な知識にせず、しっかりとイメージできるまで自分なりに調べて整理し、理解するシリーズ。</p>
<h2>Node.jsとは</h2>
<p>Node.jsはサーバサイドで動くJavaScript実行環境で、軽量で効率よく多くのリクエストを処理するネットワークアプリケーションの構築が可能です。</p>
<p>&nbsp;</p>
<h2>Node.jsの特徴</h2>
<h3>JavaScriptでサーバサイドの処理を実装できる</h3>
<p>JavaScriptによってサーバサイドの処理を実装できることで、フロントエンドからサーバサイドまでをJavaScriptのみで記述することができます。</p>
<p>これまでサーバサイドに手を出せなかった人が、フロントからサーバサイドまで通貫して行えるようになるというのは、大きな特徴であり利点になります。</p>
<h3>ノンブロッキングI/O</h3>
<p>ノンブロッキングI/Oによって、シングルスレッドで複数の処理を非同期に行うことができます。例えばDBにアクセスしたら、呼び出し元はその結果を待たずに次の処理に移り、DBの結果が出たというコールバックが返ってきたら、その結果を受け取ります。シングルスレッドなので、アクセスが増えてもメモリの消費量は少なく済みます。</p>
<p>このようにノンブロッキングI/Oによって、少ないメモリ消費で多くの処理を行うことができます。</p>
<p>ちなみに対義語としてブロッキングI/Oがあり、こちらはノンブロッキングI/Oとは違い呼び出し元はその結果を待ってから次の処理に進みます。</p>
<p>ノンブロッキングI/OとブロッキングI/Oのわかりやすい説明は以下が参考になります。</p>
<div class="ys-blog-card__text-link"><a href="https://wa3.i-3-i.info/word1617.html" >https://wa3.i-3-i.info/word1617.html</a></div>
<h4>シングルスレッド</h4>
<p>シングルスレッドとマルチスレッドについて整理しておきます。</p>
<p>Apachなど一般的なサーバーは「マルチスレッド」に対応しており、複数のアクセスが来た場合にはそれぞれメモリを割り当てて処理を行います。しかしこの方法は「C10K問題(詳しくは<a href="https://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C" target="_blank" rel="noopener">https://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C</a>)」と言われるように、1万規模の大量アクセスが来た場合にメモリがパンクして効率が悪くなります。</p>
<p>「シングルスレッド」の場合は、1つのメモリでアクセスを1つずつ処理するのでメモリ効率は良いが、1つずつしか処理できないので大量のアクセスを制御しにくくなります。</p>
<p>その点Node.jsは、JavaScriptの特徴であるシングルスレッド基盤の言語であり、非同期処理ができるため、少ないメモリ消費で大量のアクセスを高速に処理が可能になります。</p>
<h3>イベントループ</h3>
<p>シングルスレッドはスレッドが1つなのに多くの作業が同時に処理できるのは、同時実行をサポートしているイベントループという仕組みがあるからです。</p>
<p>イベントループは、リクエストやコールバックなどをイベントとして扱い、そのイベントに関する処理が終わった後に次のイベントを処理するように動作します。</p>
<p>イベントループの対義語はスレッドになります。スレッドはリクエストに対し、スレッドを立ち上げて対応します。リクエストが大量にくると、処理待ちが発生します。</p>
<p>例えば、Apacheはスレッドモデル、Nginxはイベントループモデルで動作します。スレッドモデルの場合はスレッドが増えるほどメモリの使用率が上昇します。イベントループモデルではシングルスレッドなので、メモリの使用率は少なく済みます。</p>
<p>イベントループモデルにもデメリットがあり、イベントが終わった後に次のイベント、またその次のイベントというように進むため、どこかで処理がブロックされると全体のイベントが止まってしまいます。</p>
<p>そこでNode.jsでは、このイベントループに加えてノンブロッキングI/Oがあることで、少ないメモリ消費で複数の処理を非同期に行うことができます。</p>
<h3>「V8」エンジンによる高速な実行環境</h3>
<p>Node.jsはJavaScriptを高速に実行させるため、Googleが開発した実行エンジン「V8」を採用しています。「V8」はChromeブラウザに搭載されており、これをサーバーサイドのNode.jsでも使えるようにしています。</p>
<p>従来の実行エンジンと違い、JavaScriptを即座にコンピュータが理解できる機械語に変換して処理を行うため非常に高速です。</p>
<p>このような実行エンジンを利用することで、Node.jsのさらなる高速な処理の実現を可能にしています。</p>
<p>&nbsp;</p>
<h2>Node.jsでHello Worldをやってみる</h2>
<p>まずは基本のHello Worldを試してみます。</p><pre class="crayon-plain-tag">var http = require('http');

var server = http.createServer(function(req, res){
  res.writeHead(200, {'ContentType': 'text/plain'});
  res.write('Hello World');
  res.end();
});

server.listen(1234);
console.log('サーバーを起動しました');</pre><p>一つずつ処理をみていきます。</p>
<p>まずrequire関数でhttpオブジェクトを生成しています。</p>
<p>次に、httpオブジェクトのcreateServerメソッドでサーバーオブジェクトを生成しています。これがnode.jsでつくるサーバーとなります。</p>
<p>コールバック関数の引数にhttpリクエストオブジェクト(第一引数)、httpレスポンスオブジェクト(第二引数)を受け取っています。</p>
<p>サーバーへリクエストが送られた際の動作を定義しています。</p>
<ul>
<li>writeHeadメソッドでhttpヘッダを書き出す(デフォルトは200, {&#8216;ContentType&#8217;: &#8216;text/plain&#8217;})</li>
<li>writeメソッドでhtmlコンテンツを書き出す</li>
<li>endメソッドでhtmlコンテンツの出力を完了する</li>
</ul>
<p>生成したサーバーオブジェクトをlistenメソッドで待ち受け状態にします。引数ではポート番号を指定しています。</p>
<p>&nbsp;</p>
<h2>ExpressでHello Worldをやってみる</h2>
<p>ExpressはNode.jsで利用できるWebアプリケーションフレームワークで、広く使われているフレームワークの1つですです。</p>
<p>npmを初期化します。以下のコマンドを実行したらカレントディレクトリにpackage.jsonが生成されていることを確認しましょう。</p><pre class="crayon-plain-tag">$ npm init</pre><p>&nbsp;</p>
<p>Expressをインストールするには、npmを使用します。以下のコマンドでインストールできます。</p>
<p>saveオプションを使うとインストール対象のバージョン情報がpackage.json記録されます。</p><pre class="crayon-plain-tag">$ npm install express --save</pre><p>&nbsp;</p>
<p>ExpressでHello Worldを試してみます。</p><pre class="crayon-plain-tag">var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.send('Hello World');
});

var server = app.listen(1234, function(){
  console.log('サーバーを起動しました');
});</pre><p>一つずつ処理をみていきます。</p>
<p>まずrequire関数でExpressのオブジェクトを生成しています。</p>
<p>Expressオブジェクトのexpressメソッドで「アプリケーション」としての役割を担うオブジェクトを生成します。</p>
<p>次にアプリケーションオブジェクトのgetメソッドでGETリクエストの登録します。</p>
<p>この例ではルートにアクセスされた際の処理のみを登録していますが、リクエストの登録は必要なだけ何回でも行えます。sendメソッドは、様々なタイプの応答を送信するためのメソッドです。</p>
<p>最後にアプリケーションオブジェクトのlistenメソッドでポート番号とコールバック関数を指定して待受状態にしています。</p>
<p>&nbsp;</p>
<h2>Expressプロジェクトの雛形を作成</h2>
<p>Expressアプリケーションの雛形は「expressgenerator」と言うライブラリを使用して自動生成します。</p>
<p>「-g」はグローバルインストールを行うためのオプションで、ライブラリをNode.jsのグローバルな領域へインストールします。expressgeneratorはプログラム中で利用するものではないため、グローバルな領域にインストールします。</p><pre class="crayon-plain-tag">$ npm install express-generator -g</pre><p>&nbsp;</p>
<p>雛形の作成は以下のexpressコマンドで生成します。</p><pre class="crayon-plain-tag">$ express --view=ejs</pre><p>ここで使用している「&#8211;view=ejs」と言うオプションは、「テンプレートエンジンはEJSを使う」という意味です。このオプションを指定しないと「jade」と言うテンプレートエンジンを使う設定になってしまいます。</p>
<p>このコマンドを実行すると実行したディレクトリ内にいくつかのフォルダやファイルが生成されます。</p>
<p>雛形を生成したら、そのままディレクトリを移動せず（D:\node\expgeneのまま）次のコマンドを実行します。npmはpackage.jsonの「dependencies」に指定されているライブラリを一括でインストールする機能を持っています。</p><pre class="crayon-plain-tag">$ npm install</pre><p>&nbsp;</p>
<p>以下のコマンドでサーバーが起動します。デフォルトで用意されているindex.jsにアクセスします。</p>
<p><div class="ys-blog-card__text-link"><a href="http://localhost:3000" >http://localhost:3000</a></div></p><pre class="crayon-plain-tag">$ npm start</pre><p>&nbsp;</p>
<h2>Express雛形のリソースの説明</h2>
<h3>binディレクトリ</h3>
<p>binはサーバを起動するためのwwwファイルを格納しているディレクトリです。</p>
<p>通常、サーバを起動すること以外はあまり意識する必要はありません。このwwwファイルにはapp.jsを呼び出してサーバを実行するためのスクリプトが記述されています。</p>
<h3>publicディレクトリ</h3>
<p>画像やCSS、JSライブラリなどの静的ファイルを配置するためのディレクトリです。</p>
<p>images、stylesheets、javascriptsの3つのサブディレクトリも用意されています。</p>
<h3>routesディレクトリ</h3>
<p>画面ごとの処理を記述したJSファイルを配置するためのディレクトリです。デフォルトでindex.jsとusers.jsが配置されています。このディレクトリ配下に各画面用のJSファイルを用意して処理を実装します。</p>
<h3>viewsディレクトリ</h3>
<p>画面のテンプレートファイルを配置するためのディレクトリです。</p>
<p>テンプレートエンジンにEJSを指定した場合、デフォルトでindex.ejsとerror.ejsが配置されます。</p>
<h3>app.js</h3>
<p>プログラムのメインとなるファイルで、wwwから呼び出される前提の内容になっています。このファイルには最初から主に次の処理が実装されています。</p>
<ul>
<li>各種オブジェクトの生成</li>
<li>オブジェクトの利用宣言</li>
<li>リクエスト毎の遷移設定</li>
<li>エラー時の処理</li>
</ul>
<p>これらのファイルには各種「宣言」に関する処理を実装します。処理はroutes配下に用意する画面ごとのJSファイルに実装するからです。</p>
<p>開発を進めるにあたり、画面を増やしたり新たに利用するライブラリを増やしたりする場合、このファイルにはそれらの「宣言」を加えていくことになります。</p>
<p>&nbsp;</p>
<h2>参考</h2>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://i0.wp.com/www.sejuku.net/blog/wp-content/uploads/2024/06/howto-03-2024-06-29T004437.456.png?w=800&#038;ssl=1" alt="" data-recalc-dims="1">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://www.sejuku.net/blog/45745">Node.jsとは？特徴や活用する利点を初心者向けにわかりやすく解説 | 侍エンジニアブログ</a>
			</p>
							<div class="ys-blog-card__dscr">
					Node.jsとは何なのか、その特徴をできることや使い道も交えて紹介します。No&hellip;				</div>
										<div class="ys-blog-card__domain">www.sejuku.net</div>
					</div>
	</div>
</div>


<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFtYXpvbmF3cy5jb20lMkYwJTJGNDQwMDMlMkZwcm9maWxlLWltYWdlcyUyRjE1MzcxNTA4NTI_aXhsaWI9cmItNC4wLjAmYXI9MSUzQTEmZml0PWNyb3AmbWFzaz1lbGxpcHNlJmJnPUZGRkZGRiZmbT1wbmczMiZzPWFlNzMwMWM0Mzc0ODhiZDRiODg5NzhjMjQ0N2JhZjc4%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D741e2118fa0f0b854f7ac872c7e3d896?ixlib=rb-4.0.0&amp;w=1200&amp;fm=jpg&amp;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9Tm9kZS5qcyVFMyU4MSVBQiVFMyU4MSVBNCVFMyU4MSU4NCVFMyU4MSVBNiVFOCVBQSVCRiVFMyU4MSVCOSVFMyU4MSVBNiVFMyU4MSVCRiVFMyU4MSU5RiZ0eHQtYWxpZ249bGVmdCUyQ3RvcCZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTU2JnR4dC1wYWQ9MCZzPTUyOGY2Y2NhM2NhMjQ5MDhiZmRhZDQ5MjQzYWNmN2Vl&amp;mark-x=120&amp;mark-y=112&amp;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBreXJpZWxlaXNvbiZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTM2JnR4dC1wYWQ9MCZzPTk3YTU4ZjJmMWE2NzBkMjU3ZDkxMDUzNzdhOTllMDkx&amp;blend-x=242&amp;blend-y=480&amp;blend-w=838&amp;blend-h=46&amp;blend-fit=crop&amp;blend-crop=left%2Cbottom&amp;blend-mode=normal&amp;s=ef65c774cd9e15f7e6b82a30786f9f30" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://qiita.com/kyrieleison/items/f726061803a0e536c3e1">Node.jsについて調べてみた #Node.js - Qiita</a>
			</p>
							<div class="ys-blog-card__dscr">
					この雑な文章は、2015年5月中旬頃に調べていた内容をほじくり返してきたものです&hellip;				</div>
										<div class="ys-blog-card__domain">qiita.com</div>
					</div>
	</div>
</div>

<div class="ys-blog-card__text-link"><a href="https://wa3.i-3-i.info/word1617.html" >https://wa3.i-3-i.info/word1617.html</a></div>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://i0.wp.com/coliss.com/wp-content/uploads-201904/2019122101.png?w=800&#038;ssl=1" alt="" data-recalc-dims="1">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://coliss.com/articles/build-websites/operation/javascript/javascript-visualized-event-loop.html">  JavaScript イベントループの仕組みをGIFアニメで分かりやすく解説 | コリス</a>
			</p>
							<div class="ys-blog-card__dscr">
					JavaScript イベントループの仕組みについてGIFアニメーションで分かり&hellip;				</div>
										<div class="ys-blog-card__domain">coliss.com</div>
					</div>
	</div>
</div>


<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://nodejs.org/en/next-data/og/announcement/Node.js%20%E2%80%94%20%E3%81%A9%E3%81%93%E3%81%A7%E3%82%82JavaScript%E3%82%92%E4%BD%BF%E3%81%8A%E3%81%86" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://nodejs.org/ja/">Node.js — どこでもJavaScriptを使おう</a>
			</p>
							<div class="ys-blog-card__dscr">
					Node.js® is a free, open-source, cross-p&hellip;				</div>
										<div class="ys-blog-card__domain">nodejs.org</div>
					</div>
	</div>
</div>

<div class="ys-blog-card__text-link"><a href="https://meetup-jp.toast.com/896" >https://meetup-jp.toast.com/896</a></div>
<p><iframe loading="lazy" title="JavaScriptエンジニアのためのNode.js入門" type="text/html" width="800" height="550" frameborder="0" allowfullscreen style="max-width:100%" src="https://read.amazon.com.au/kp/card?preview=inline&#038;linkCode=kpd&#038;ref_=k4w_oembed_uUDpkXSWJSbxKS&#038;asin=B01N2Y16BQ&#038;tag=kpembed-20"></iframe></p>The post <a href="https://mintaku-blog.net/about-nodejs/">Node.jsの特徴や使い方について整理する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/about-nodejs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1839</post-id>	</item>
		<item>
		<title>Node.jsでgRPC-Webのチュートリアルを試してみた</title>
		<link>https://mintaku-blog.net/grpc-node/</link>
					<comments>https://mintaku-blog.net/grpc-node/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Sun, 21 Jun 2020 14:07:34 +0000</pubDate>
				<category><![CDATA[Node.js]]></category>
		<category><![CDATA[RPC]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1523</guid>

					<description><![CDATA[<p>こちらを参考にgRPC-Webを使ってHello! Worldを表示するチュートリアルをやってみました。 参考：https://github.com/grp …</p>
The post <a href="https://mintaku-blog.net/grpc-node/">Node.jsでgRPC-Webのチュートリアルを試してみた</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>こちらを参考にgRPC-Webを使ってHello! Worldを表示するチュートリアルをやってみました。</p>
<p>参考：<a href="https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld" target="_blank" rel="noopener noreferrer">https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld</a></p>
<h2>全体構成</h2>
<p><img data-attachment-id="1524" data-permalink="https://mintaku-blog.net/grpc-node/img_2809/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?fit=2560%2C940&amp;ssl=1" data-orig-size="2560,940" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;}" data-image-title="IMG_2809" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?fit=300%2C110&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?fit=800%2C294&amp;ssl=1" loading="lazy" class="aligncenter wp-image-1524 size-large" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645-1024x376.jpg?resize=800%2C294&#038;ssl=1" alt="" width="800" height="294" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?resize=1024%2C376&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?resize=300%2C110&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?resize=768%2C282&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?resize=1536%2C564&amp;ssl=1 1536w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?resize=2048%2C752&amp;ssl=1 2048w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?w=1600&amp;ssl=1 1600w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/IMG_2809-scaled-e1591579474645.jpg?w=2400&amp;ssl=1 2400w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>今回のgRPC-Webを使ったHello! Worldを表示するための全体構成はこののようになります。</p>
<p>gRPC-Webでは、gRPCの呼び出しに変換する必要があり、Envoyプロキシを通して実現しています。</p>
<p><span style="font-size: 8px;">参考：<a href="https://qiita.com/betchi/items/0e9040f21b1f8ecbf321" target="_blank" rel="noopener noreferrer">https://qiita.com/betchi/items/0e9040f21b1f8ecbf321</a></span></p>
<p><span style="font-size: 8px;">参考：<a href="https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880" target="_blank" rel="noopener noreferrer">https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880</a></span></p>
<p><span style="font-size: 8px;">参考：<a href="https://qiita.com/namusyaka/items/71cf27fd3242adbf348c" target="_blank" rel="noopener noreferrer">https://qiita.com/namusyaka/items/71cf27fd3242adbf348c</a></span></p>
<p>&nbsp;</p>
<h2>Protocol BuffersでgRPCサービスを定義する</h2>
<p>ここではRPCメソッド、リクエスト、レスポンスを持つサービスを定義します。</p>
<p>・helloworld.proto</p><pre class="crayon-plain-tag">syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}</pre><p>&nbsp;</p>
<h2>Node.jsでgRPCサービスを実装</h2>
<p>次に、Node.jsでgRPCサービスを実装します。ここでは、クライアントからリクエストを受け取り、call.request.nameを介してメッセージフィールドにアクセスできます。その後、レスポンスを作成し、クライアントに送り返します。</p>
<p>・server.js</p><pre class="crayon-plain-tag">const PROTO_PATH = __dirname + '/helloworld.proto';

const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
  PROTO_PATH,
  {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
  }
);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const helloworld = protoDescriptor.helloworld;

function doSayHello(call, callback) {
  callback(null, {
    message: 'Hello! ' + call.request.name
  });
}

function getServer() {
  const server = new grpc.Server();
  server.addService(helloworld.Greeter.service, {
    sayHello: doSayHello,
  });
  return server;
}

if (require.main === module) {
  const server = getServer();
  server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
  server.start();
}

exports.getServer = getServer;</pre><p>&nbsp;</p>
<h2>Envoyプロキシの設定</h2>
<p>次に、ブラウザのgRPC-Webリクエストをバックエンドに転送するようにEnvoyプロキシを構成する必要があります。ここでは、ポート:8080でリッスンするようにEnvoyを構成し、ポート:9090でクラスターにgRPC-Webリクエストを転送します。</p>
<p>・envoy.yaml</p><pre class="crayon-plain-tag">admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  cluster: greeter_service
                  max_grpc_timeout: 0s
              cors:
                allow_origin_string_match:
                - prefix: "*"
                allow_methods: GET, PUT, DELETE, POST, OPTIONS
                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                max_age: "1728000"
                expose_headers: custom-header-1,grpc-status,grpc-message
          http_filters:
          - name: envoy.grpc_web
          - name: envoy.cors
          - name: envoy.router
  clusters:
  - name: greeter_service
    connect_timeout: 0.25s
    type: logical_dns
    http2_protocol_options: {}
    lb_policy: round_robin
    hosts: [{ socket_address: { address: localhost, port_value: 9090 }}]</pre><p>&nbsp;</p>
<h2>Envoy用Dockerfileの作成</h2>
<p>Envoyを起動させるためにDockerfileを作成します。</p>
<p>・envoy.Dockerfile</p><pre class="crayon-plain-tag">FROM envoyproxy/envoy:v1.14.1
COPY ./envoy.yaml /etc/envoy/envoy.yaml
CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml</pre><p>&nbsp;</p>
<h2>クライアントコードの作成</h2>
<p>HelloRequest、HelloReplyおよびGreeterClientクラスをインポートするファイルは、前に定義したprotocジェネレータによって生成されます(後で生成します)。</p>
<p>GreeterClientクラスをインスタンス化し、HelloRequestprotobufオブジェクトにフィールドを設定します。ファイルで定義したのと同じように、client.sayHello()を介してgRPC呼び出しを行うことができます。</p>
<p>・client.js</p><pre class="crayon-plain-tag">const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

var client = new GreeterClient('http://localhost:8080');

var request = new HelloRequest();
request.setName('World');

client.sayHello(request, {}, (err, response) =&gt; {
  console.log(response.getMessage());
});</pre><p>&nbsp;</p>
<h2>package.jsonの作成</h2>
<p>package.jsonを作成します。</p>
<p>・package.json</p><pre class="crayon-plain-tag">{
  "name": "grpc-web-simple-example",
  "version": "0.1.0",
  "description": "gRPC-Web simple example",
  "devDependencies": {
    "@grpc/proto-loader": "~0.5.4",
    "async": "~1.5.2",
    "google-protobuf": "~3.12.0",
    "grpc": "~1.24.2",
    "grpc-web": "~1.1.0",
    "lodash": "~4.17.0",
    "webpack": "~4.43.0",
    "webpack-cli": "~3.3.11"
  }
}</pre><p>&nbsp;</p>
<h2>index.htmlの作成</h2>
<p>最後にindex.htmlを作成します。</p>
<p>・index.html</p><pre class="crayon-plain-tag">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;gRPC-Web Example&lt;/title&gt;
    &lt;script src="./dist/main.js"&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;Open up the developer console and see the logs for the output.&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre><p>&nbsp;</p>
<p>最終的に作成・生成したファイルは以下のようになりました。</p>
<p><img data-attachment-id="1525" data-permalink="https://mintaku-blog.net/grpc-node/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-06-07-17-40-16/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?fit=526%2C508&amp;ssl=1" data-orig-size="526,508" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-06-07 17.40.16" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?fit=300%2C290&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?fit=526%2C508&amp;ssl=1" loading="lazy" class="aligncenter size-full wp-image-1525" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?resize=526%2C508&#038;ssl=1" alt="" width="526" height="508" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?w=526&amp;ssl=1 526w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/356d8ae48c95ad220206dfe449ac8c6f.png?resize=300%2C290&amp;ssl=1 300w" sizes="(max-width: 526px) 100vw, 526px" data-recalc-dims="1" /></p>
<p>&nbsp;</p>
<h2>Protobufメッセージとクライアントサービススタブの生成</h2>
<h3>protocのインストール</h3>
<p>こちらを参考にprotocをインストールします。</p>
<p><a href="https://github.com/protocolbuffers/protobuf/releases" target="_blank" rel="noopener noreferrer">https://github.com/protocolbuffers/protobuf/releases</a></p>
<p>以下でインストールされたか確認します。</p><pre class="crayon-plain-tag">$ which protoc
/usr/local/bin/protoc</pre><p></p>
<h3>protoc-gen-grpc-webのインストール</h3>
<p>こちらからprotoc-gen-grpc-web-1.1.0-darwin-x86_64をダウンロードし、パスを通します。</p>
<p><a href="https://github.com/grpc/grpc-web/releases" target="_blank" rel="noopener noreferrer">https://github.com/grpc/grpc-web/releases</a></p><pre class="crayon-plain-tag">$ sudo mv ~/Downloads/protoc-gen-grpc-web-1.1.0-darwin-x86_64 \
/usr/local/bin/protoc-gen-grpc-web</pre><p></p><pre class="crayon-plain-tag">$ chmod +x /usr/local/bin/protoc-gen-grpc-web</pre><p>protocとprotoc-gen-grpc-webのインストールが完了したら、以下のコマンドでjsファイルを生成します。</p><pre class="crayon-plain-tag">$ protoc -I=. helloworld.proto \
--js_out=import_style=commonjs:. \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:.</pre><p>以下の2つのファイルが生成されたことを確認します。</p>
<ul>
<li>helloworld_pb.js: HelloRequestとHelloReplyクラスが含まれたファイル</li>
<li>helloworld_grpc_web_pb.js: GreeterClientクラスが含まれたファイル</li>
</ul>
<p>protoc-gen-grpc-web: program not found or is not executableのエラーが出た場合は、こちらを参考にしてください。</p>
<p><a href="https://hsuzuki.hatenablog.com/entry/2018/10/30/184544" target="_blank" rel="noopener noreferrer">https://hsuzuki.hatenablog.com/entry/2018/10/30/184544</a></p>
<h3>クライアントのjsコードをコンパイル</h3>
<p>クラインとのjsコードをブラウザが認識できるようにコンパイルします。webpackでclient.jsがビルドされdist/main.jsが生成されます。</p><pre class="crayon-plain-tag">$ npm install</pre><p></p><pre class="crayon-plain-tag">$ npx webpack client.js --mode development</pre><p>&nbsp;</p>
<h2>実際に動かしてみる</h2>
<p>以上でサンプルを動かす準備は整いました。gRPCサービス、Envoyプロキシ、クライアントをそれぞれ立ち上げます。</p>
<p>gRPCサービス(ポート:9090リッスン)を立ち上げます。</p><pre class="crayon-plain-tag">$ node server.js</pre><p>次にEnvoyプロキシを実行します。このenvoy.yamlファイルは、ポート:8080でリッスンし、転送するようにEnvoyを構成します。</p><pre class="crayon-plain-tag">$ docker build -t helloworld / envoy -f ./envoy.Dockerfile .</pre><p></p><pre class="crayon-plain-tag">$ docker run -d -p 8080：8080 -p 9901：9901 helloworld / envoy</pre><p>最後にwebサーバを立ち上げます。</p><pre class="crayon-plain-tag">$ python3 -m http.server 8081</pre><p>全て正常に立ち上げたらlocalhost:8081にアクセスします。</p>
<p>デベロッパーツールからコンソールを立ち上げるとHello! Worldと表示されていることが確認できます。</p>
<p><img data-attachment-id="1527" data-permalink="https://mintaku-blog.net/grpc-node/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-06-07-17-32-47/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?fit=2036%2C212&amp;ssl=1" data-orig-size="2036,212" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-06-07 17.32.47" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?fit=300%2C31&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?fit=800%2C84&amp;ssl=1" loading="lazy" class="aligncenter size-large wp-image-1527" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?resize=800%2C84&#038;ssl=1" alt="" width="800" height="84" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?resize=1024%2C107&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?resize=300%2C31&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?resize=768%2C80&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?resize=1536%2C160&amp;ssl=1 1536w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?w=2036&amp;ssl=1 2036w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/24eb438664b042c469988fcdc1aef6f3.png?w=1600&amp;ssl=1 1600w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>&nbsp;</p>
<p>client.jsのsetNameをTestにして再度ビルドするとちゃんとHello! Testに変わっていることが確認できます。</p><pre class="crayon-plain-tag">request.setName('Test');</pre><p><img data-attachment-id="1530" data-permalink="https://mintaku-blog.net/grpc-node/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-06-07-17-34-47/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?fit=2344%2C192&amp;ssl=1" data-orig-size="2344,192" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-06-07 17.34.47" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?fit=300%2C25&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?fit=800%2C66&amp;ssl=1" loading="lazy" class="aligncenter size-large wp-image-1530" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=800%2C66&#038;ssl=1" alt="" width="800" height="66" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=1024%2C84&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=300%2C25&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=768%2C63&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=1536%2C126&amp;ssl=1 1536w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?resize=2048%2C168&amp;ssl=1 2048w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/f5e23bae93919fbea256b7cc9e822237.png?w=1600&amp;ssl=1 1600w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>&nbsp;</p>
<h2>便利な拡張機能</h2>
<p><img data-attachment-id="1526" data-permalink="https://mintaku-blog.net/grpc-node/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-06-07-17-38-09/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?fit=1162%2C612&amp;ssl=1" data-orig-size="1162,612" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-06-07 17.38.09" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?fit=300%2C158&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?fit=800%2C421&amp;ssl=1" loading="lazy" class="aligncenter size-large wp-image-1526" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?resize=800%2C421&#038;ssl=1" alt="" width="800" height="421" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?resize=1024%2C539&amp;ssl=1 1024w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?resize=300%2C158&amp;ssl=1 300w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?resize=768%2C404&amp;ssl=1 768w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/06/5557207d762900d69fea40824623644a.png?w=1162&amp;ssl=1 1162w" sizes="(max-width: 800px) 100vw, 800px" data-recalc-dims="1" /></p>
<p>こちらのChrome拡張機能を使うことでgRPCサーバーから返されたリクエスト、レスポンス、エラーオブジェクトのデシリアライズされたJSONが表示されるようになります。</p>
<p><a href="https://chrome.google.com/webstore/detail/grpc-web-developer-tools/ddamlpimmiapbcopeoifjfmoabdbfbjj/related?hl=en" target="_blank" rel="noopener noreferrer">https://chrome.google.com/webstore/detail/grpc-web-developer-tools/ddamlpimmiapbcopeoifjfmoabdbfbjj/related?hl=en</a></p>The post <a href="https://mintaku-blog.net/grpc-node/">Node.jsでgRPC-Webのチュートリアルを試してみた</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/grpc-node/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1523</post-id>	</item>
	</channel>
</rss>
