Posted at

年末休みにnodeでAPIサーバを勉強した復習〜part1 設計編


はじめに

https://qiita.com/YukiMiyatake/items/9cef90e2a18573e7bca3

ここで 年末休みにnodeを勉強した内容をまとめたが

その中身を細かく切り出す


目的

いま新人教育をしているが、その新人教育をオープンにする

私自身 出し惜しみはしない。むしろ もし私の知識で少しでも世の中が便利になるのならラッキーだ

また 間違った事は 公開すれば教えてくれるはず


検討

まずは新人教育のための題材を作ることからはじめた


制作物

スマホのソーシャルゲーム用のAPIサーバ

自分がゲームエンタメ業界が多いので、ソーシャルゲームのサーバは持っていて損はない

以前 JavaPlayframeworkで作っていたが そろそろ作り直したいと思っていた


言語

ソーシャルゲームのAPIサーバはほとんどがDBからReadしたりそのままWriteしたりCPU処理は少ない

マイクロサービス化も検討しているため、重い処理があればその部分は別の言語で動かすことも出来る

そのため 言語自体の速度はそこまで気にするシーンは無いはずである

最も重要なのは、クライアントからのコネクション時の起動やI/Oである


Perl、PHP、Python、Rubyなど

これらのスクリプト言語は基本的に非同期処理を行なう事が難しい

(非同期ライブラリもあるが 一般的ではないしパフォーマンスも出ない)

そのため、コネクションごとにプロセスを起動する事になるが 上記の理由で

プロセス起動コスト、タスク上限問題(C10K問題)のため、私の中では選択肢に入らない

却下


Haskell、Erlang、Elixir・・・

マイナー言語というと怒られるかもしれないがマイナー言語である

非同期処理は得意であるが、お客さんに納品しにくいので却下した

個人的には 嫌いじゃない言語だけどね


C#、Kotlin、Swift

C#は クロスプラットフォームになってきてるけど、まだベンダーロックインに分類した

ベンダーロックイン言語は使いたくない

C#はIL2CPPとか使えると面白くなるんだけどね

C#は言語的に優れてると思うんだけどね・・

今回とは別で C#サーバは勉強したいが 今回は却下


Scala

コンパイルの遅さが嫌い それ以外はとってもいい

いずれScalaサーバは勉強しますが今回は却下で


C++、Java、Go、node

非同期が得意でメジャー言語、特に欠点のない言語で この選択肢になった

C++は新人にはまだ難しく 言語の習得に時間がかかるので今回は却下した

Goは以前プロダクトに使ったが、文法が20年ほど古く これ以上使う気がおこらない

Javaは速度も速く、システム系では最も使われていて 仕事的に何も不自由ないので教えたいと思った

が、nodeを選んだ。

この2択は かなりどちらでも問題なかったが あえていうなら、AWS LambdaのようなFaaSサービスに使いやすいからだ

もちろんnodeには弱点がある

Javaに比べると遅いし、同時接続も非同期にしては弱い。

だが フロントエンドでもJavaScriptは使うので この際覚えてもらうには良いかと。


FW

nodeに決めたからにはフレームワークは node、Express、MongoDB に決めた

また APIキャッシュとしてRedisを使い、課金など重要なDBは MySQLの併用を検討している

Typescriptを必須にした。

このあたりは nodeではよくある構成と思う


実行環境

Docker(DockerCompose)を前提で作る

AWSを考えているが他のサービスでも可能

マイクロサービス化も当然考えている

開発はWindowsでもMacでもDocker使えば問題ない


ソース管理、タスク管理、スケジュール管理

ソース管理はGithubかGitlabか悩んだが 機能が多く全部つかいこなしてないGitlabの採用を決めた

理由は特にないが、機能豊富でサイトも軽いので不自由がないからだ

タスク管理も GitlabのIssueで管理することにした。かんばんがデフォルトでついてるのは良い

スケジュールは 建てないことにした。それよりは確実に勉強すること


DevOps

とりあえずSlackBotを導入して コミットやマージリクエスト、Issueを通知させることにした


設計

設計もGitlabのWikiで行った


DB,API

DBは基本的には user_idをキーとした1テーブルでほとんどカバー出来るが

フィールドが多いと R/Wが遅くなる、マイクロサービス化したときに同じテーブルだとコンフリクトする

ため、機能ごとにテーブルをわけた

細かいフィールドはゲームの内容で変わるのであとで考えるとして

Account、ダウンロードデータ、ステータス、カード、デッキ、アーチーブメント、ログボ、フレンド、ブロック、課金・・・

と詳細に分割した

また APIも使うDBに紐づけた


環境構築


Docker

AlpainLinuxをベースに、node、Mongo、Redis、MySQLのイメージを使う

dockerfile、docker-composeを設定して 難なく動かす

これで環境構築が終わる 素晴らしい世の中だ

version: '3'

services:
node:
build:
context: ./server/docker/construct_node # Dockerfile保存場所
image: sss-node # イメージ名
container_name: sss-node # コンテナ名
tty: true
ports: # ローカルとホストのポートを接続
- 3000:3000
volumes: # 現在のディレクトリをworkdir(/src)にマウント
- ./:/src

mysql:
build:
context: ./server/docker/construct_mysql # Dockerfile保存場所
image: sss-mysql # イメージ名
container_name: sss-mysql # コンテナ名
ports: # ローカルとホストのポートを接続
- 33060:3306
volumes: # databaseをホストと共有
- ./server/docker/construct_mysql/data:/var/lib/mysql
environment:
- "MYSQL_ROOT_PASSWORD=root" # ルートのパスワードは必須

mongo:
build:
context: ./server/docker/construct_mongo # Dockerfile保存場所
image: sss-mongo # イメージ名
container_name: sss-mongo # コンテナ名
ports: # ローカルとホストのポートを接続
- 27018:27017
volumes: # databaseをホストと共有
- ./server/docker/construct_mongo/data:/data/db

redis:
build:
context: ./server/docker/construct_redis # Dockerfile保存場所
image: sss-redis # イメージ名
container_name: sss-redis # コンテナ名
command: redis-server --appendonly yes # データをファイルに保存する形式に指定
ports: # ローカルとホストのポートを接続
- 16379:6379
volumes: # databaseをホストと共有
- ./server/docker/construct_redis/data:/data

さすが新人コメント多い


モジュール

npmでは色々インストールした

express、mongooseなど


Typescript

最初はWebpackを導入したが やはり消耗した・・

が最終的には package.jsonだけでなんとかなった!

concurrentlyを導入したが

  "main": "src/app.ts",

"scripts": {
"start": "npm run serve",
"build": "npm run build-ts",
"serve": "node dist/app.js",
"watch-node": "nodemon dist/app.js",
"watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-ts\" \"npm run watch-node\"",
"test": "jest --forceExit --coverage --verbose",
"watch-test": "npm run test -- --watchAll",
"build-ts": "tsc",
"watch-ts": "tsc -w",
"tslint": "tslint -c tslint.json -p tsconfig.json",
"copy-static-assets": "ts-node copyStaticAssets.ts",
"debug": "npm run build && npm run watch-debug",
"serve-debug": "nodemon --inspect dist/server.js",
"watch-debug": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run serve-debug\""
},

nodemonでファイルの変更を監視しJSファイルが変更されたらnode再起動

これで快適だ


HelloWorld

まずはJavaScriptでテストをしていく

var express = require('express');

var app = express();

app.get('/', function(req, res, next) {
res.send("Welcom to SSS");
});

app.listen(3000);

docker-composeでちゃんと動いたので

開発が可能になった