node.js框架設計搭建和部署

主題

主要介紹node.js的框架的技術選型和框架搭建,以及如何在服務器上部署發佈。這裏客戶端使用小程序來介紹。小程序主要功能涉及到用戶受權登陸,地圖定位,webSocket發送定位,二維碼掃描添加設備。javascript

技術選型

  • Koa2,http的框架
  • Mongodb,非關係型數據庫,配合typegoose的庫來編寫
  • ws,封裝webSocket的一個模塊
  • jsonwebtoken,生成token的庫
  • TypeScript 使用ts-node的庫來啓動
  • pm2,進程管理,日誌查看,服務器部署的工具

Koa+Typescript,除了自己類型系統對數據庫的數據模型定義有好處外。Koa中間件的context有不少庫或者用戶自定義數據結構掛載在上面,若是沒有類型聲明,開發者可能不知道context上有哪些類型,致使工程維護性增長。html

Mongodb(www.mongodb.com/cn)Mongodb自己查詢語法是JS寫的,很符合前端的使用。基於文檔處理,自己查詢速度也比較快。關聯表的結構設計,也至關簡單,學習成本低。對於前端來講對錶的設計經驗不足,若是使用mysql之類的關係型數據庫,若是設計不當,極可能致使性能問題。前端

pm2是node社區比較成熟的進程管理框架,自己自帶日誌查看,錯誤重啓,服務器部署的能力。java

工程結構

總體工程的設計使用mvc的結構,Koa中間件,使用TS類型輔助。node

目錄

-src  --api  接口的api好比微信小程序登陸受權相關的api  --config  配置項  --const  常量  --controllers 控制層  --interfaces 接口  --middleware 自定義中間層  --modules 模型層,主要放數據庫相關的模型數據  --types ts 拓展類型  --utils 工具包,放請求,logger,配置,jwt相關的工具  --server.ts 服務啓動入口 -.env 全局常量配置(用戶自定義的,不提交git) -.env.example 全局常量配置 -ecosystem.config.js pm2的配置文件,部署的時候自動執行mysql

開發環境配置和啓動

數據庫

安裝

數據庫:docs.mongodb.com/manual/inst… 可視化工具:www.mongodb.com/download-ce…   選擇社區免費版本linux

配置

輸入mongo就能夠鏈接。最好配置用戶和密碼,雖然本地未必要配置密碼,可是服務端確定要配置的,因此就先介紹下如何配置。nginx

  1. 命令行輸入mongo,進入數據庫shell
  2. 輸入use admin 進入數據庫
  3. 建立管理員帳戶
db.createUser({
  user: "useradmin",
  pwd: "adminpassword", 
  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})
複製代碼
  1. 驗證是否添加成功 db.auth("useradmin", "adminpassword"),成功返回1,而後輸入exit退出
  2. 修改配置文件 mac在/usr/local/etc/mongod.conf下,輸入code /usr/local/etc/mongod.conf 最後一行添加(注意縮進)
security:
  authorization: enabled
複製代碼

6.重啓服務git

brew services restart
複製代碼

7.建立一個應用的數據庫 use app_db 8.對這個應用的數據庫建立權限web

// 擁有者
db.createUser({
  user: "youruser",
  pwd: "yourpassword", 
  roles: [{
    role: "dbOwner", db:"app_db" 
  }]
})

// 可讀寫
db.createUser({ 
  user: "youruser2", 
  pwd: "yourpassword2", 
  roles: 
  [{ 
    role: "readWrite",db: "app_db" 
  }] 
})
複製代碼

9.最後能夠輸入鏈接來啓動

mongodb://youruser2:yourpassword2@localhost/app_db
複製代碼

十、可使用可視化工具compass鏈接

項目配置

重命名.env.example爲.env,基本的.env代碼以下

# 這個是ENV全局變量,其中變量NODE_ENV在開始執行建立, development: 是開發環境,production:是生產環境。

# mongodb的地址 production生產環境依賴
MONGODB_URI=mongodb://<mlab_user>:<mlab_password>@<mlab_connection_url>

# mongodb的地址 developmet開發環境依賴
MONGODB_URI_LOCAL=mongodb://localhost:27017/go_app

# token的祕鑰
TOKEN_KEY=ting.ting@huang_GO!GO!GO!!!

# 微信AppId
WX_APP_ID=wx0840e0b26c481112

# 微信的AppSecret
WX_APP_SECRET=34c4824eb1115281d249e85dba4f8157
複製代碼

項目啓動

安裝ts-node和typescript到開發環境

使用npm start命令啓動,安裝nodemon到開發環境,而後以下輸入命令

nodemon --watch 'src/**/*' -e ts,tsx --exec 'ts-node' ./src/server.ts
複製代碼

使用vscode,這裏是我推薦的方式。而後配置launch.json,使用F5啓動、調試。

{
  // 使用 IntelliSense 瞭解相關屬性。
  // 懸停以查看現有屬性的描述。
  // 欲瞭解更多信息,請訪問: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "runtimeArgs": ["-r", "ts-node/register"],
      "args": ["${workspaceFolder}/src/server.ts"],
      "env": {
        "NODE_ENV": "development"
      },
      "sourceMaps": true,
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

複製代碼

服務器配置和部署

服務器使用CentOS,之因此不使用ubuntu,是由於不必使用到界面,也是大多數服務器採用到的方案。固然使用ubuntu也沒什麼問題,內核都同樣。而後使用yum安裝一些經常使用的工具庫。

鏈接服務器

首先在本機上建立ssh公鑰和祕鑰,而後去服務器添加公鑰,而後鏈接服務器。 命令以下 ssh -q -l root -p 22 111.230.28.25

安裝數據庫

和開發環境配置相似,只是都是linux下的,建立密碼和本地使用compass鏈接驗證。這裏注意記得在要給服務開端口。

Nginx安裝和配置

網上教程不少,我這裏主要介紹使用yum來安裝Nginx,使用yum安裝很是簡單。使用systemctl來啓動(固然也有使用service,可是不能開機啓動了),有興趣想了解systemctl相關命令能夠看看這個www.ruanyifeng.com/blog/2016/0…

安裝

  1. 由於Nginx不在yum的默認源中,因此要手動添加源
$ sudo rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
複製代碼
  1. 安裝
$ sudo yum install nginx
複製代碼

配置

安裝完Nginx,會生成一個配置文件,路徑是:/etc/nginx/nginx.conf。輸入命令 vim nginx.conf 來編輯

user  nginx;
worker_processes 1;
events {
   worker_connections 1024;
}
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
http {
   server {
       listen  80;
       server_name localhost;
       location / {
           proxy_pass  http://127.0.0.1:8088;
           proxy_set_header   Host             $host;
           proxy_set_header   X-Real-IP        $remote_addr;
           proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
       }
       location ^~ /websocket/ {
           proxy_pass  http://127.0.0.1:8088;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "Upgrade";
       }
    }
     server {
        listen          443 ssl;
        server_name     localhost
        ssl             on;
        ssl_certificate cert/2760618_www.dayuan.tech.pem;
        ssl_certificate_key  cert/2760618_www.dayuan.tech.key;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDH:AESGCM:HIGH:!RC4:!DH:!MD5:!3DES:!aNULL:!eNULL;
        ssl_prefer_server_ciphers  on;
        location / {
            proxy_pass http://127.0.0.1:8088;
            proxy_set_header        Host    $http_host;
            proxy_set_header        X-Real-IP       $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location ^~ /websocket/ {
            proxy_pass http://127.0.0.1:8088;
            proxy_set_header        Host    $http_host;
            proxy_set_header        X-Real-IP       $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    #tcp_nopush on;

    keepalive_timeout  65;

    gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
複製代碼

啓動

設置開機啓動

systemctl enable nginx
複製代碼

啓動服務

systemctl start nginx
複製代碼

中止服務

systemctl stop nginx
複製代碼

從新啓動服務

systemctl restart nginx
複製代碼

熱更新,也就是不想從新啓動整個服務,只想從新加載配置文件

systemctl reload nginx
複製代碼

小程序登陸模塊

image.png

和這裏圖有點不一樣的是,登陸憑證校驗完後,我是使用一個會過時token來驗證。驗證成功後客戶端會發送一次獲取頭像和暱稱等用戶基本信息的請求,獲取後,再把這些傳回給開發者服務器。之後用戶基本信息都從開發者服務器獲取就好。 還有一個主意點微信的auth.getAccessToken,須要按期獲取,具體以下developers.weixin.qq.com/miniprogram…

Koa控制層統一處理

// 路由模塊使用前須要先安裝和實例化
import Router from 'koa-router'
import fs from 'fs'
import checkToken from '@/middleware/checkToken'

const router = new Router()

/** * 將全部controller下的文件都加載到router */
let urls = fs.readdirSync(__dirname + '/../controllers')

// 全部請求api路由前都要通過這個控制層
router.use('/api', checkToken)

urls.forEach((element: string) => {
  let module = require(__dirname + '/../controllers/' + element)
  router.use('/api', module.default.routes(), module.default.allowedMethods())
})

export default router

複製代碼

模型層使用Typegoose

若是不用Typegoose

interface Car {
  model?: string;
}

interface Job {
  title?: string;
  position?: string;
}

interface User {
  name?: string;
  age: number;
  job?: Job;
  car: Car | string;
}

mongoose.model('User', {
  name: String,
  age: { type: Number, required: true },
  job: {
    title: String;
    position: String;
  },
  car: { type: Schema.Types.ObjectId, ref: 'Car' }
});

mongoose.model('Car', {
  model: string,
});
複製代碼

用了之後

class Job {
  @prop()
  title?: string;

  @prop()
  position?: string;
}

class Car extends Typegoose {
  @prop()
  model?: string;
}

class User extends Typegoose {
  @prop()
  name?: string;

  @prop({ required: true })
  age!: number;

  @prop()
  job?: Job;

  @prop({ ref: Car })
  car?: Ref<Car>;
}
複製代碼

WebSocket配置

不管是kos還webSocket都是調用Node標準的http模塊建立的http.Server監聽的,koa只是把響應的函數註冊到http.Server,同理WebSocket也能夠把響應的函數註冊到http.Server,因此可使用同一個端口。我是對webSokcet作了一層封裝,其中鏈接的代碼以下

function connectSocket(server: Server) {
  wss = new WebSocketServer({ server })
  wss.on('connection', onConnection)
  server.on('upgrade', (request, socket, head) => {
    const pathname = url.parse(request.url).pathname
    if (pathname === '/websocket/location') {
      wss.handleUpgrade(request, socket, head, ws => {
        wss.emit('connection', ws, request)
      })
    }
  })
}
複製代碼
相關文章
相關標籤/搜索