nodeでバッチスクリプトを書くときの道具箱
お久しぶりです。
業務をしているとちょっとしたバッチスクリプトを書くことがあると思います。
最近たまたまnodeでCSVを使ったバッチ処理を書くことがあったので、その際のtipsをまとめてみます。
nodeは最近標準ライブラリが少しずつ充実してきましたが、普段からゴリゴリnode書いてない身からするとGoやRubyといった標準ライブラリやノウハウが浸透している言語の方がやりやすい部分はあるなと感じた今日この頃
紹介するtips
- CSVから行を逐次読み込みする
- コマンドライン引数を取得する
- タイムスタンプのついた構造化ログを吐く
- 非同期処理の並列数を制限しつつ並列で実行する
- jest, vitestを使わずにテストコードを書く
CSVから行を逐次読み込みする
バッチ処理などでは巨大なファイルを扱うことも多いでしょう。
ファイルを扱う場合は、ファイル全体を読み込むのではなく1行ずつ読み込みながら処理を進めていくことで少ないメモリ使用量でサイズの大きなファイルも扱うことができます。
そのため、あらかじめファイルサイズが限定されている場合を除いて出来るだけ逐次行を読み込んで処理する書き方をすることが一般的でしょう。
nodeではcsvというライブラリを使うことでCSVの生成・変換・読み込み・書き出しをすることができます。
CSVをパースする方法は同期的にパースする方法やcallback関数を使う方法もありますが、今回はstreamを使う方法を紹介します。
fs.createReadStrem
を使ってファイルのストリームを取得しpipeでparserを渡してあげることで、CSVの一行ずつイテレートする AsyncIterator
を取得することができます。
import * as fs from 'node:fs'; import { parse } from 'csv'; const fileStream = fs.createReadStream('items.csv'); const rows = fileStream.pipe(parse({ trim: true, skip_empty_lines: true })); for await (const row of rows) { console.log(row); // ['xxxx', 'yyyy', 'zzzz'] }
CSV Parse - Async iterator API
コマンドライン引数を取得する
これまでも process.argv
を使うことでコマンドライン引数を扱うことができましたが、最近のバージョンでより便利なutil関数が標準ライブラリに追加されました。
util.parseArgs
はv18.3でexperimentalとして追加されv20.0から正式なAPIになりました。
import * as util from 'node:util'; const { values, positionals } = util.parseArgs({ allowPositionals: true, // デフォルトでは、オプション指定のない引数は指定できない options: { env: { type: 'string', short: 'e', }, target: { type: 'string', short: 't', multiple: true, }, }, }); console.log(values) console.log(positionals)
$ node parse-args.mjs items.csv -e local -t foo --target bar > [Object: null prototype] { env: 'local', target: [ 'foo', 'bar' ] } > [ 'items.csv' ]
Util | Node.js v20.0.0 Documentation
タイムスタンプのついた構造化ログを吐く
バッチ処理ではいつどこまで処理が進んだか・いつどんなエラーが出たかを記録しておくことが重要です。
その際には、ログにタイムスタンプが必要になりますし、またログが構造化されていると後々解析するのに便利です。
また、スクリプトを実行中ログをターミナルに出力しつつもファイルにも保存しておきたいということもあるでしょう。今回は、pinoというライブラリを使ってこの辺りを実現してみます。
import { pino, stdTimeFunctions } from 'pino'; const transport = pino.transport({ dedupe: true, targets: [ { level: 'info', target: 'pino/file', options: { destination: '1' }, // stdout }, { level: 'error', target: 'pino/file', options: { destination: '2' }, // stderr }, { level: 'info', target: 'pino/file', options: { destination: './logs/info.log', mkdir: true }, }, { level: 'error', target: 'pino/file', options: { destination: './logs/error.log', mkdir: true }, } ] }); const logger = pino({ timestamp: stdTimeFunctions.isoTime, }, transport); logger.info('info'); // written to stdout and info.log logger.warn('warn'); // written to stdout nad info.log logger.error('error'); // written to stderr and error.log logger.fatal('fatal'); // written to stderr and error.log
上記のようにするとターミナルとそれぞれのファイルに以下のようなログを出力することができます。
// info.log {"level":30,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":1,"msg":"info"} {"level":50,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":3,"msg":"error"} // error.log {"level":40,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":2,"msg":"warn"} {"level":60,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":4,"msg":"fatal"}
また、構造化されたログは人間には読みづらいため開発環境では pino-pretty
を使うとヒューマンリーダブルなログをターミナルに表示することができます。
GitHub - pinojs/pino: 🌲 super fast, all natural json logger
非同期処理の並列数を制限しつつ並列で実行する
バッチスクリプトでは、多くのケースでhttpリクエストやDBアクセスなどのIO処理を利用するでしょう。
その際、出来るだけ効率的に処理するために処理をasync関数としてIOを並列化して処理することが考えられます。
しかし、単に全ての処理を非同期実行して Promise.all
するようなやり方ではリクエストの並列数をコントロールすることができません。
この解決には複数の方法が考えられ、タスクをチャンクしチャンクごとに Promise.all
するようなやりかたなども考えられますが、今回のような AsyncIterator
を使うような場合はchunkしづらいため別の方法を取ります。
今回は、iteratorを消費するワーカーを複数作ることで、並列に実行しつつ並列数を制御します。
このやり方の場合、 for await ... of
を使うことで簡単に AsyncIterator
に対応することができますね。
const iterator = Array.from('abcdefghi').values(); async function doWork(iterator) { for await (const value of iterator) { await setTimeout(1000); console.log(value); } } // イテレータを消費するワーカーを3つ作る const workers = new Array(3).fill(iterator).map(doWork); // 全てのワーカーの処理完了を待つ await Promise.allSettled(workers);
a b c # 1秒後 d e f # ...
Run Concurrent Tasks With a Limit Using Pure JavaScript - Maxim Orlov
jest, vitestを使わずにテストコードを書く
書き捨てのスクリプトだがテストは書いておきたいという場合にわざわざTesting Libraryを導入して設定するのは面倒ですよね。
v18からexperimentalで導入されていたnode標準のTest Runnerがv20で無事stableになりました。
サクッとテストを書きたい場合にライブラリをインストールする必要がないのでありがたいですね。
非同期処理を含むテストも書けますし、テストの構造化もできます。describe
,it
を使って書き方や before
, afterEach
といったフックもできるみたいなので、ちょっとテスト書いて動作確認したいといった場合にライブラリが必要になる場面がかなり減りそうです。
import { test, describe, it } from 'node:test' import * as assert from 'node:assert'; test('first test', (t) => { assert.strictEqual(1, 1); }); test('asynchronous passing test', async (t) => { assert.strictEqual(1, 1); }); test('top level test', async (t) => { await t.test('subtest', (t) => { assert.strictEqual(1, 1); }); }); describe('A thing', () => { it('should work', () => { assert.strictEqual(1, 1); }); });
実行結果もそこそこ見やすそうでいい感じです。
Test runner | Node.js v20.0.0 Documentation
感想
node自体は慣れているつもりでしたが、普段書かないような処理を書こうと思うと意外に標準ライブラリの使い方やライブラリの選定が必要になりますね。
近年nodeの標準ライブラリが便利になってきているのでさらに今後に期待です。
intellijでGPGが使えなくなった件の備忘
yubikeyにGPGの鍵を取り込んで運用してます。 コミットの署名やGitHubへのSSHはyubikeyに取り込んでいる鍵を使うようになっているのですが、一時期からintellijでうまくコミットやfetchができなくなってしまいました。
恐らくこの問題が発生したのはIDEのインストールを個別に行わずToolbox経由に切り替えたあたりからだった気がします。 ターミナルでの運用は問題なくintellijの問題かと思ってしばらくアップデートを待っていましたが一向に直らない。
intellijのSHELLが /bin/sh
になっていた
Mac使っているのでデフォルトのシェルは zsh
ですが、気づいたらintellijの内部で使われているシェルが sh
になってました。
intellij上で使うターミナルエミュレーターには、明示的に zsh
を指定しているので気がつきませんでしたが、IDEが内部でgitなどのCLIを動かすときに使われているシェルが sh
になってました。
ただ、これは社用Macで起きていたのですが、私用のMacでToolbox経由でintellijインストールしてもプロジェクトに設定されている SHELL
は /bin/zsh
だったのでなぜそうなってしまったのか。。
intellijで有効になっているシェルはTools > Terminal > Project settings > Environment Variables
のダイアログを開くと確認できるっぽいです。
.profile
でもGPGのパスと環境変数を設定してあげることでintellijで問題なくGPGを使えるようになりました。
React Routerのルーティング可能なモーダルのアイデア
ようやくReact Router v6が正式リリースされましたね。
1年以上のベータ期間では、GitHub上のドキュメントを読むしかありませんでしたが、公式のページもリニューアルされました。
今回は、SPAで一度は悩むであろうルーティング可能なモーダルの実装のアイデアを紹介します。
公式ドキュメントでもパスに対応したモーダルを表示するexampleが紹介されていますが、公式の実装は直接コンテンツにアクセスした場合にはモーダルではないページを表示する実装になっています。 特定のパスにアクセスした際にモーダルを表示したいという場合にはこのexampleは参考になりません。
コンポーネントベースのルーティングとオブジェクトベースのルーティングを組み合わせる
v6では、バンドルサイズが大幅に改善されたのに加えhooksの充実やインターフェースの変更が行われました。
その中で、useRoutes
というオブジェクトスタイルでルーティングを定義できるhooksが追加されています。
v5では、恐らくパスがモーダルのパスかどうかをどうにかして判別してハンドリングする必要がありましたが、v6ではuseRoutes
とRoutes
コンポーネントを組み合わせることでシンプルにルーティング可能なモーダルを実装することができます。
この実装では、特定のパスでモーダルを直接開くことができると同時に、任意のページを表示している際にリンクから任意のモーダルを表示することもできます。
ただし、モーダルを直接開く場合のバックグラウンドのページはルートになります。
reference
rechartsを使ってちょっと複雑なグラフを描いてみる
今回は、rechartsというReact向けのグラフ描画ライブラリを使って少し複雑なグラフを作ってみます。
rechartsはexampleやAPIドキュメントが整備されており、比較的はじめやすいライブラリではありますが、ドキュメントに記載されていない機能もちょこちょこあります。
今回やってみる内容
- グリッドをストライプにする
- グラフ内に複数のデータソースから複数の種類のグラフを表示する
作ってみたグラフは、生後0 ~ 24ヶ月の乳幼児の体重、身長の平均値から算出したガイドとその上に実際の子供のデータをプロットするというようなことをイメージしています。
グラフのグリッドをストライプにする
まずは、簡単な方から
グラフに表示されるグリッドの帯に互い違いに色をつけてストライプにすることができます。
<CartesianGrid horizontalFill={['#C0C0C0', '#FFFFFF']} fillOpacity={0.2} stroke="lightgrey" />
グラフにグリッドを表示させるには、CartesianGrid
を用います。
このコンポーネントには、 horizontalFill
, verticalFill
という属性が用意されており、ここに配列で希望する色を配列で指定することでグリッドをストライプにすることができます。
ただ、その際何もしないとグリッドのストロークが消えてしまうので fillOpacity
を使って調整する必要があります。
複数のデータソースから複数の種類のグラフを表示する
ということでここで具体的に実現する内容は以下になります。
- y軸をふたつ表示する
- AreaChartとLineChartを両方表示する
- AreaChartとLineChartで別々のデータソースを使用する
y軸をふたつ表示する
y軸をふたつ表示するには、まさに YAxis
をふたつ用意すれば良いのですが、その際 orientation
属性で表示位置を指定した上で yAxisId
属性でそれぞれの軸の識別子を定義します。
その後、 それぞれのコンポーネントでも明示的に yAxisId
を指定することで軸とデータを紐付けます。
<YAxis yAxisId="weight" orientation="left" /> <YAxis yAxisId="height" orientation="right" /> <Area dataKey="weight" yAxisId="weight" /> <Area dataKey="height" yAxisId="height" />
AreaChartとLineChartを両方表示する
ComposedChart
を使うと複数の種類のチャートを同時に表示することができます。
ただ、今回の場合ガイドである AreaChart
はツールチップや凡例の表示から除外したいですね。
その際、 tooltipType="none"
, legendType="none"
を指定することでツールチップ、凡例を非表示にすることができます。
<ComposedChart data={guides} > <Tooltip /> <Legend /> ... <Area type="monotone" dataKey="weight" yAxisId="weight" tooltipType="none" legendType="none" /> <Area type="monotone" dataKey="height" yAxisId="height" tooltipType="none" legendType="none" /> ... </ComposedChart>
AreaChartとLineChartで別々のデータソースを使用する
グラフのガイドは事前に定義したものを使い、実際のデータと別々の状態で扱いたいですよね。
データソースがひとつに限定される場合、ふたつのデータをうまくマージしなければいけません。
recharts では、基本的に AraaChart
などの基本的なラッパーコンポーネントの方にデータを指定して各コンポーネントで参照するキーを指定する形なので複数のデータソースが利用できないように見えますが、 data
属性を使って Line
などのコンポーネントに直接データを入れることもできます。
今回の例では、ガイドのデータは Chart
コンポーネントに指定し実際のデータを Line
コンポーネントに直接入れています。
<Line type="linear" data={records} dataKey="weight" />
終わりに
今回は、rechartsを使って公式のexamplesにもないようなグラフを作ってみました。 rechartsは、高機能で使いやすくドキュメントもある程度整備されていますが、凝った機能はドキュメントにも載っていなかったりするのでstackoverfllowや実際のソースから自分の要求する機能が存在するかどうか確認する必要がありますね。
2020年の学習と2021年やりたいこと
年末年始あたりに投稿しようと思っていましたが、大分遅れてしまいました、供養。
2020年に取り組んだもの
React - The Complete Guide (incl Hooks, React Router, Redux) - Udemy
主にサーバーサイド、インフラをメインにしていますが、フロントもある程度できるようになっておきたいというのと、近い将来業務でもフロントの開発をすることになりそうということでやってみました。
Vueに関しても少々触ったことがあるのですが、Reactの方がTypeScriptとの相性がよい点、Vueと比べてプリミティブなためJSやライブラリの仕組みを理解する必要があり結果的に勉強になる点からReactを集中的に勉強することにしました。
ただ、当初自分にとってReactの仕組みやReduxの考え方にはハードルが高くQiitaの記事などをいくつ読んでもわかったようなわからないような感じでした。
そのため、Udemyの動画を見ながら写経することにしたのですが、日本語だと選択肢があまり多くないため英語のコースも含めて検討したところ、このMaximilianさんのコースは評価が高く、Reactを網羅的に学習できそうだったので選びました。
モダンJSの記法からReactの仕組み、Reduxの考え方を解説しながら最終的にReact Hooks, React Sagaまでカバーしてアプリケーションを開発することで網羅的な学習をすることができました。
ビデオ自体が全部で約40時間ほどあり、写経・デバック等含めて100時間くらいは使った気がしますが、かなりReactに慣れました。Understanding TypeScript - Udemy
上記と同様でUdemyのMaxilimianさんのコースです。 こちらもわかりやすかったですが、ボリュームはReactほどなく、TypeScriptを使いこなすにはやはり実践で型システムをガンガン使っていくしかないのかなという印象りあクト! TypeScriptで始めるつらくないReact開発
Udemyのコースでは、valina jsでのコースだったため、TSとReactの感触をつかむために読みました。 ただ、React, Redux, JS/TS自体の話はUdemyでほとんど網羅できていたと思う。DNSをはじめよう ~基礎からトラブルシューティングまで
DNSの基本的な仕組みから、代表的なレコードの役割を学ぶことが出来た。 雰囲気でRoute 53を触っていたが、これを読んだおかげで大分自信をもってRoute 53をいじれるようになった。SSLをはじめよう ~「なんとなく」から「ちゃんとわかる!」へ~
httpsの重要性やSSL証明書の仕組みを学ぶことができた。 重要な技術だがDNSほど普段細々とした作業もないので、実作業などではまだ活きる場面に出会ってない。Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る
社内の一部でGoを採用するため、こちらで勉強させていただきました。 シンプルな文法と標準ライブラリの機能が充実していること、非同期処理が手軽にかけること、コンパイルが早くテスト、ベンチが外部依存なしにできることなどなどかなり魅力的な言語でした。 RubyでRailsがデファクトになっていることとは対照的にGoでは、あまり厚いフレームワークを使わない傾向があると思います。 こちらの書籍では基本的に標準ライブラリを利用してWeb アプリケーションの構築工程が解説されおり、標準ライブラリの仕組みやインターフェースになれることが出来ます。 外部のライブラリを使う場合も標準ライブラリでのインターフェースが踏襲されていることも多いので、標準ライブラリになれておくことは重要なようです。Pragmatic Terraform on AWS
実務でインフラを0から組むことがあり、IaSを一気に推し進めるためterraformの導入を決めました。 使い方やディレクトリ構成の指針として活用させていただきました。 AWSのサンプルコードが豊富でかなり助かりました。低レイヤを知りたい人のためのCコンパイラ作成入門
これはまだ「電卓レベルの言語の作成」までしか終わってませんが、写経して実際に手元で動かしながら取り組んでいます。 この教材に出会うまでコンパイラがどうゆう仕組みで動いているか、ほとんど想像できない状態でしたが、構文解析やアセンブリの出力がどうゆうものなのかある程度理解することが出来ました。 これまで、アセンブリを触ることは全くなかったのですが、低レイヤへのとっかかり、Cやmakeに慣れる機会としてかなり良かったです。 今後も時間があれば続きをやりたいと思います。HTML5&CSS3デザイン 現場の新標準ガイド【第2版】
上述のUdemyのReactの教材では、HTML/CSSに関しては突っ込んだ話はないので、現状を把握するために読みました。 どちらかというと辞書的な使い方が想定されるような書籍ですが、読み切れないボリュームではないのです。 これまでなんとなく使っていたHTMLタグの使用や把握していなかったCSSプロパティを頭にいれることで、フロントエンドの実務でキャッチアップがかなりスムーズになったと思います。Web API: The Good Parts
大量のAPIを開発する機会があり、体系的な教材が欲しかったので読みました。 Web APIを設計する上での、エンドポイントの考え方、レスポンスのスキーマの考え方などいくつかの指針を得ることが出来た。 また、HTTPにおいてのセキュリティについても記載があり、特にHTTPのセキュリティヘッダに関してはこれまで意識する機会がなかったので役に立ちました。ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
これまでDDDについては、Evans本を読んだり(途中まで)、記事などで断片的に情報を仕入れていましたが、こちらの書籍は読みやすく具体的なサンプルコードが豊富で DDDのとっかかりとしてかなりよいと思いました。 ただ、DDDの本質的な部分は深く説明されているわけではないので、やはり概念的な部分を深く理解するにはEvans本やその他の本を参照しながら実際にトライアンドエラーを繰り返していかないといけないという印象
2021年やりたいこと
今年は、今のところ「オブジェクト指向のこころ」、「体系的に学ぶ 安全なWebアプリケーションの作り方」を読みました。 あと前々から興味のあったRustに入門するため「実践Rustプログラミング入門」を読み進めてます。 公式のドキュメントある程度読んだことがあるのですが、こうゆう書き方・ライブラリがよく使われますみたいなのが書かれていて結構よいです。 今後は「Linuxの仕組み」、「マスタリングTCP/IP」、「Real Wold HTTP」あたりを読みたい、普段使っている技術の中身を知るのが面白いというのと、ブラックボックスにしておきたくないというのがある。 競プロやってみないのでその勉強もしたいなーという感じです。
オブジェクト指向のこころを読んだ
TD;DR
- ドメイン駆動設計に興味があったが、オブジェクト指向を体系的に学ぶ必要性を感じた
- デザインパターンを絡めながらオブジェクト指向の原則を説明しており、デザインパターンの有効性とオブジェクト指向の原則を理解できた
- 関数型言語への興味
雰囲気でオブジェクト指向言語を使っていた
今まで、オブジェクト指向言語を日常的に利用し、プログラミング言語の書籍に書いてある「オブジェクト指向」の使い方やインターネット上の断片的な情報を元に『オブジェクト指向』を使ってきました。
エンジニアにとってアプリケーションの設計は永遠のテーマであり、私もドメイン駆動設計関連の記事や書籍をいくつか読んでは完全に消化仕切れずにいました。 段々わかってきたのがドメイン駆動設計はオブジェクト指向の延長線上にあるっぽいということ。
そもそも自分は現状もっとも広く利用されているオブジェクト指向というパラダイムをを中途半端にしか理解できていないのだからドメイン駆動設計を学ぶ段階になかったのです。
ということで、オブジェクト指向の書籍を探したのですが、複数の知人がおすすめしていたためこちらの書籍を選びました。 電子版がなく大型本なので物理的に読みやすくはなかったのですが、内容は非常に良かったので満足しています。
本書のながれ
まず、とあるシステムを題材に継承を多用した従来のオブジェクト指向の手法で設計をし、要求の変更があった際の保守性や柔軟性といった継承の問題を示します。
その後、いくつかのデザインパターンを紹介し、先ほどのシステムをデザインパターンを利用した形で再度設計します。
これによって、特殊化と再利用目的とした継承ではなく、デザインパターンを利用することでより堅牢で柔軟な設計が可能であることが説明されています。
また、既存のデザインパターンはそれぞれ特定の状況で適用するものですが、インターフェースを用いて設計し、集約を用いて流動的要素をカプセル化する
というデザインパターンの教訓はどのような設計でも適用可能であり、それに従うことで柔軟な設計が可能なことが語られ、オブジェクト指向の原則をより深く解説されています。
プログラミング言語入門本では、継承などの機能的な側面は説明されてもなかなかクラス・オブジェクトの責務や実装、設計のカプセル化といったトピックは深く説明されないことが多いと思います、本書では実際にパターンを利用して設計するプロセスを見せることでオブジェクト指向の本質を実感できるつくりになっています。
本書を読んだことで、オブジェクト指向の階段を確実に進んだ実感がありますし、本書で紹介されていないデザインパターンについてもひと通り理解しておきたいというモチベーションも生まれました。
関数型言語への興味
オブジェクト指向をある程度「うまくやる」方法は理解したものの、そもそもオブジェクト指向で語られる適切な抽象化
というものが非常に難しいことは日々実感しているところである。
そんな中ふと、オブジェクト指向と関数型プログラミングを検討した記事を読んだことでオブジェクト指向以外のパラダイムも勉強してみたいなーと思ってしまった。
Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。
近年おおくのOOP言語は関数型言語から機能を輸入はしているものの、やはり純粋関数型言語をやらないと関数型プログラミングのなんたるかはわからないだろうなーと思い とりあえずすごいHな本を買ってしまったので、地道に読んでいきます。。
Laravel-Swooleでunix domain socketを使う方法
最近PHPで新しくサーバーを立てる機会があったのですが、出来るだけパフォーマンスのオーバーヘッドを意識したくない && シンプルな処理しかしない小規模なサーバーということもありポピュラーなnginx + fpmの構成ではなく、Laravel-Swooleを採用しました。
Laravel-Swooleは、swooleというPHPでイベント駆動な非同期サーバーを実現するextensionをLaravel/Lumenで簡単に利用可能にするライブラリです。
ドキュメントをひと通りチェックしたところ、The support of swoole_http_server for Http is not complete. So, you should configure your swoole server with nginx proxy in your production environment.
と記載があり、単体での利用は推奨されていないようでした。
9. Nginx Configuration · swooletw/laravel-swoole Wiki · GitHub
ただ、サンプルで用意されているnginxの設定はリバースプロキシをTCP/IPで行っており、Laravel-Swooleのドキュメントにはunix domain socketに関する具体的な設定例は見当たりませんでした。 一応、swoole自体もunix socketに対応していますし、Laravel-Swooleの設定項目にもunix socketが存在します。
swoole_http.php
<?php return [ 'server' => [ 'host' => env('SWOOLE_HTTP_HOST', '/var/run/swoole.sock'), // socketを置くパスを指定する 'port' => env('SWOOLE_HTTP_PORT', '0'), // unix socketを使う場合0にする 'socket_type' => SWOOLE_UNIX_STREAM, // unix socktを使う場合 SWOOLE_UNIX_STREAM / SWOOLE_UNIX_DGRAM を指定する // .... ], // ... ];
以上でswoole自体の設定は出来たのです、Laravel-Swooleとnginxを別ユーザーで実行する場合socketのパーミッションをいじってやる必要があります。
取り急ぎpermissionを変更したいのですが、php-fpmでいうところのlisten.mode
に相当する設定項目がLaravel-Swooleにはないようです。
dockerで利用するので、swoole起動時にフックしたいのですが、どうしようかと思ったところswoole
のexampleに参考になりそうな記述がありました。
どうやら、swooleサーバーの起動時に発行されるイベントにフックしてchmod
を叩いてやれば良いようです。
Laravel-Swooleでは、swoole.start
というイベントが発行されるので、それにフックしてsocketのパーミッションを変更します。
laravel-swoole/Manager.php at master · swooletw/laravel-swoole · GitHub
EventServiceProvider.php
<?php namespace App\Providers; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; use SwooleTW\Http\Server\Facades\Server; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], ]; /** * Register any events for your application. * * @return void */ public function boot() { parent::boot(); // swoole 起動時にsocketのpermissionを変更する Event::listen('swoole.start', function () { $server = $this->app->make(Server::class); chmod($server->host, 0666); }); } }
ただ、unix domain socketを使う場合のpermissionは666
でベストなのか、dockerで複数コンテナ使う場合のユーザーの取り回しなどは未解決、、
FPM opcache + preload / swoole / PHPP-PMあたりの比較してみたい。