Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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でちゃんと動いたので
開発が可能になった

YukiMiyatake
C++が喋れる ゲームプログラマ インフラ、サーバ、UNITY、ゲームエンジンが最近多いな・・ MONA: MPpuEnmqDYBCxSZyG5cBDt6UWtXczmRmkn BTC: 13JpgsF3n6K2WhjEeUuUUqS7V71gWdFx56 BCH: 18q6rfi9ynyTgynrB8tJ2eSDLPQM32RZk5
http://murasame-labo.hatenablog.com/
murasame
ゲーム、エンタメ、サーバインフラ等 少人数で技術力の高い仕事をする会社
http://murasame-lab.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away