Nginx + Node + Vue 部署初試(2019-02-18修改)

趁着爸媽作年夜飯以前,把以前作的筆記貼出來,新的一年到了,祝你們

Rich overnight

Nginx + Node + Vue 部署初試

知乎javascript

我的博客css

Githubhtml

平常學習筆記vue

Nginx

  1. 定義

    異步框架的 Web服務器,也能夠用做反向代理負載平衡器 , HTTP緩存, 媒體流等的開源軟件。它最初是一個旨在實現最高性能和穩定性的Web服務器。除了HTTP服務器功能外,NGINX還能夠用做電子郵件(IMAPPOP3SMTP)的代理服務器以及HTTPTCPUDP服務器的反向代理和負載平衡器。java

  2. 特色node

    • 更快
    • 高擴展性, Nginx的模塊都是嵌入到二進制文件中執行
    • 高可靠性
    • 低內存消耗
    • 單機支持10萬次的併發鏈接
    • 熱部署, master管理進行與work工做進程分離設計,所以具有熱部署功能
    • 最自由的BSD許可協議
  3. 功能mysql

    • 靜態服務(css , js , html, images, videos
    • SSLTLS SNI 支持
    • HTTP/HTTPS, SMTP, IMAP/POP3 反向代理
    • FastCGI反向代理
    • 負載均衡
    • 頁面緩存(CDN
    • 支持gzipexpirse
    • 支持 keep-alive 和管道鏈接
    • 基於 PCRErewrite 重寫模塊
    • 帶寬限制
    • 基於IP 和名稱的虛擬主機服務
    • 支持訪問速率、併發限制
    • 反向代理(適用2000WPV、併發鏈接1W/秒),簡單的負載均衡和容錯
    • 基於客戶端 IP 地址和 HTTP 基本認證的訪問控制

Mac 安裝Nginx

// 推薦使用`brew`, 安裝`homebrew`
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
// Homebrew 安裝 Nginx 
brew install nginx
// Mac 下 Nginx的目錄
cd /usr/local/etc/nginx
ll -al
vim nginx.conf

homebrew詳見linux

cd-nginx

Nginx 參數列表

配置參數屬性 解釋說明 參數列表
user 設置nginx服務的系統使用用戶 nobody(注意:此處用戶若是比啓動Nginx的用戶權限低,你須要使用當前用戶重啓Nginx)
nginx -s stop 關閉nginx-> nginx 啓動-> `ps aux
grep nginx`查看啓動用戶
worker_processes 開啓的線程數 通常與服務器核數保持一致
error_log 定位全局錯誤日誌文件 錯誤日誌定義等級,[ debug \ info notice \ warn \ error \ crit ],debug輸出最多,crir輸出最少
pid 指定進程id的存儲文件位置
worker_rlimit_nofile 指定一個nginx進程打開的最多文件描述符數目,受系統進程的最大打開文件數量限制
events 包含Nginx中全部處理鏈接的設置
http Nginx http處理的全部核心特性

Event

Nginx是以event(事件)處理模型爲基礎的模塊。它爲了支持跨平臺,抽象出了event模塊。它支持的event處理類型有:AIO(異步IO),/dev/pollSolarisUnix特有),epollLinux特有),eventportSolaris 10特有),kqueueBSD特有),pollrtsig(實時信號),select等。
它的做用是監聽accept後創建的鏈接,對讀寫事件進行添加刪除。事件處理模型和Nginx的非阻塞IO模型結合在一塊兒使用。當IO可讀可寫的時候,相應的讀寫事件就會被喚醒,此時就會去處理事件的回調函數。ios

配置參數屬性 解釋說明 參數列表
worker_connections 定義每一個進程的最大鏈接數,受系統進程的最大打開文件數量限制 單個後臺worker process進程的最大併發連接數 (最大鏈接數= worker_processes worker_connections)
在反向代理環境下:<br/>最大鏈接數 = worker_processes
worker_connections / 4
use 選擇一個可用的事件的模型(能夠在編譯時指定),Nginx會自動選擇事件的模型 [ epoll \ /dev/poll \ poll \ eventport \ kqueue \ select \ rtsig ]
multi_accept 一個新鏈接通知後接受盡量多的鏈接 on / off
accept_mutex 開啓或者禁用使用互斥鎖來打開sockets on / off

Event Use支持的事件模型nginx

events-use

Events詳見

HTTP

能夠嵌套多個server,配置代理,緩存,日誌定義等絕大多數功能和第三方模塊的配置。

配置參數屬性 解釋說明 參數列表
include 主模塊指令,實現對配置文件所包含的文件的設定,能夠減小主配置文件的複雜度,DNS主配置文件中的zonerfc1912,acl基本上都是用include語句
default_type 核心模塊指令 默認設置爲二進制流,也就是當文件類型未定義時使用這種方式
log_format 日誌格式的設定 日誌格式的名稱,可自行設置,後面引用
access_log 引用日誌 引用log_format設置的名稱
keepalive_timeout 設置客戶端鏈接保存活動的超時時間 0是無限制
sendfile 開啓高效文件傳輸模式 on / off
tcp_nopush 開啓防止網絡阻塞 on / off
tcp_nodelay 開啓防止網絡阻塞 on / off
upstream 負載均衡
server Nginx的server虛擬主機配置

Upstream

它的做用是實如今輪詢和客戶端IP之間的後端服務器負荷平衡。

配置參數屬性 解釋說明
輪詢(默認) 當weight不指定時,各服務器weight相同,每一個請求按時間順序逐一分配到不一樣的後端服務器,若是後端服務器down掉,能自動剔除。
weight 指定輪詢概率,weight和訪問比率成正比,用於後端服務器性能不均的狀況
ip_hash 每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,能夠解決session的問題。
fair(第三方) 按後端服務器的響應時間來分配請求,響應時間短的優先分配。
url_hash(第三方) 按訪問url的hash結果來分配請求,使每一個url定向到同一個後端服務器,後端服務器爲緩存時比較有效。

weight 默認爲1.weight越大,負載的權重就越大。

Nginx Upstream 狀態

例如:

upstream news {
    server 127.0.0.1:9527 backup;
    server 127.0.0.1:9527 weight=1 max_fails=2 fail_timeout=3;
    ...
}
配置參數屬性 解釋說明
backup 預留的備份服務器
down 當前的server暫時不參與負載均衡
fail_timeout 通過max_fails 失敗後,服務暫停的時間
max_conns 限制最大的接收的鏈接數
max_fails 容許請求失敗的次數

use location:在server中添加

proxy_pass http://127.0.0.1:9527;
// 由於個人API接口是這個,such as /api/profile
// location 具體匹配規則詳見後面
location ~ /api/ {
    proxy_pass http://127.0.0.1:9527;
}

Server

配置虛擬主機的相關參數,一個http中能夠有多個server

配置參數屬性 解釋說明 參數列表
listen 監聽端口 http -> 80 / https -> 443
server_name 設置主機域名 localhost
charset 設置訪問的語言編碼
access_log 設置虛擬主機訪問日誌的存放路徑及日誌的格式
location 設置虛擬主機的基本信息
Location

配置請求的路由,以及各類頁面的處理狀況。

配置參數屬性 解釋說明 參數列表
root 設置虛擬主機的網站根目錄 Vue項目的根目錄/Users/rainy/Desktop/MyWork/Work/cloudwiz-website/dist
index 設置虛擬主機默認訪問的網頁 index.html index.htm
proxy 經過不一樣協議將請求從NGINX傳遞到代理服務器
  • =: 開頭表示精確匹配,如 api 中只匹配根目錄結尾的請求,後面不能帶任何字符串.
  • ^~ :開頭表示uri以某個常規字符串開頭,不是正則匹配.
  • ~: 開頭表示區分大小寫的正則匹配.
  • ~*: 開頭表示不區分大小寫的正則匹配.
  • / : 通用匹配, 若是沒有其它匹配,任何請求都會匹配到.

匹配優先級(高到低)

  1. location =
  2. location 完整路徑
  3. location ^~ 路徑
  4. location ~,~* 正則順序
  5. location 部分起始路徑
  6. /

詳見Location配置

Reverse Proxy

NGINX代理請求時,它會將請求發送到指定的代理服務器,獲取響應並將其發送回客戶端。可使用指定的協議將請求代理到HTTP服務器(另外一個NGINX服務器或任何其餘服務器)或非HTTP服務器(能夠運行使用特定框架(如PHPPython)開發的應用程序)。

location  / some / path /  {
    proxy_pass  http://www.example.com:8080;
    
    proxy_set_header  Host  $ host ; 
    proxy_set_header  X-Real-IP  $ remote_addr ;
    
    // 禁用特定位置的緩衝
    proxy_buffering  off ;
    proxy_buffers  16  4k ; 
    proxy_buffer_size  2k ;
    
    proxy_bind  127.0.0.2 ; // IP地址也能夠用變量指定
}

將請求傳遞給非HTTP代理服務器,**_pass應使用相應的指令:

配置參數屬性 解釋說明 參數列
proxy_pass 將請求傳遞給HTTP代理服務器
proxy_set_header 傳遞請求標頭 默認狀況下,NGINX在代理請求中從新定義兩個頭字段「Host」和「Connection」,並刪除其值爲空字符串的頭字段。「Host」設置爲$proxy_host變量,「Connection」設置爲close
proxy_buffering 負責啓用和禁用緩衝 on / off
proxy_buffers 請求分配的緩衝區的大小和數量
proxy_buffer_size 代理服務器的響應的第一部分存儲在單獨的緩衝區大小 一般包含一個相對較小的響應頭,而且能夠比其他響應的緩衝區小。
proxy_bind 接受來自特定IP網絡或IP地址範圍的鏈接 指定proxy_bind必要網絡接口的指令和IP地址

詳見Proxy

全局變量Global Variable

變量名 變量含義
$args 請求中的參數
$content_length HTTP請求信息裏的Content-Length
$content_type 請求信息裏的Content-Type
$host 請求信息中的Host,若是請求中沒有Host行,則等於設置的服務器名
$http_cookie cookie 信息
$http_referer 引用地址
$http_user_agent 客戶端代理信息
$remote_addr 客戶端地址
$remote_port 客戶端端口號
$remote_user 客戶端用戶名,認證用
$request_method 請求的方法,好比GETPOST
$request_uri 完整的原始請求URI(帶參數)
$scheme 請求方案,httphttps
$server_addr 接受請求的服務器的地址,若是沒有用listen指明服務器地址,使用這個變量將發起一次系統調用以取得地址(形成資源浪費);
$server_protocol 請求的協議版本,HTTP/1.0HTTP/1.1
$uri 請求中的當前URI, $uri在請求處理期間 ,值可能會發生變化,例如在執行內部重定向或使用索引文件時

全局變量詳見Alphabetical index of variables

修改 http server中的配置

nginx-config

啓動Nginx

nginx
ps -ef | grep nginx

從新加載修改後的Nginx配置文件

nginx -s reload

多謝小夥伴給提出的問題(詳見評論區),reloadresatrt是兩個不一樣的概念。

  1. reload
    從新加載conf文件,不中斷Nginx服務,conf文件有問題則加載上一次的conf.
  2. restart
    會根據配置文件,重啓整個Nginx服務,形成服務器中斷一段時間,固然會由於conf文件問題報錯。

關閉Nginx

nginx -s stop

由於我已經啓動了,因此就重啓一下Nginx便可

nginx-reload

Linux安裝Nginx

Linux安裝

常見的Linux命令

使用pstree查看當前服務器啓動的進程

pstree

pstree

查找Nginx的位置

ps -aux | grep nginx

server-where-nginx

進入nginx的目錄

212-server-nginx

nginx-conf

而後配置nginx.conf文件便可

http {    
    upstream add-news {
        server 127.0.0.1:9527;
    }

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        root   /Users/rainy/Desktop/MyWork/Work/website/dist;
        index index.html index.htm;

        location / {
            # root   html;
            try_files $uri $uri/ @router;
            index  index.html index.htm;
        }

        location @router {
            rewrite ^.*$ /index.html last;
        }

        location ~ /api/ {
            proxy_pass http://127.0.0.1:9527;
        }
                # 個人圖片存放在本地服務器上的路徑👇
        location /news-images/ {
            expires 24h;
            root /Users/rainy/Desktop/MyWork/Work/website/server/;
            autoindex on;
        }
        # 經過轉發某服務器上的圖片 -> https://localhost:9527/*/*.png
        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
            expires 24h;
            proxy_pass https://localhost:9527;
            access_log /root/nginx/logs/images.log;
            proxy_store on;
            proxy_store_access user:rw group:rw all:rw;
            proxy_redirect          off;
            proxy_set_header        Host 127.0.0.1;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            client_max_body_size    10m;
            client_body_buffer_size 1280k;
            proxy_connect_timeout   900;
            proxy_send_timeout      900;
            proxy_read_timeout      900;
            proxy_buffer_size       40k;
            proxy_buffers           40 320k;
            proxy_busy_buffers_size 640k;
            proxy_temp_file_write_size 640k;
        }
    }
}

Docker安裝Nginx

  1. 查找 Docker Hub 上的 nginx鏡像

    docker search nginx

    docker-search

  2. 拉取官方鏡像

    docker pull nginx
  3. 查看當前鏡像

    docker images nginx

    docker-images

Server Tree
tree -C -L 3 -I '*node_modules*'

├── server
│   ├── app.js
│   ├── db
│   │   ├── db.js
│   │   └── newsSql.js
│   ├── package-lock.json
│   ├── package.json
│   └── routers
│       ├── news.js
│       └── router.js

file-tree

Node Server

npm init
npm install express mysql body-parser -S
  1. app.js

    const express = require('express')
    const bodyParser = require('body-parser')
    const app = express()
    const router = require('./routers/router')
    
    const PORT = 9527
    
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: true }))
    
    app.use(router)
    
    app.listen(PORT, () => {
      console.log(`Listen port at ${PORT}`)
    })
  2. db.js -> Mysql配置

    module.exports = {
      mysql: {
        host: 'localhost',
        user: 'root',
        password: 'xxxx',
        database: 'test',
        port: '3306'
      }
    }
  3. router.js

    const express = require('express')
    const router = express.Router()
    const news = require('./news')
    
    router.get('/api/news/queryAll', (req, res, next) => {
      news.queryAll(req, res, next)
    })
    
    router.get('/api/news/query', (req, res, next) => {
      news.queryById(req, res, next)
    })
    
    router.post('/api/news/add', (req, res, next) => {
      news.add(req, res, next)
    })
    
    router.post('/api/news/update', (req, res, next) => {
      news.update(req, res, next)
    })
    
    router.delete('/api/news/deleteNews', (req, res, next) => {
      news.deleteNews(req, res, next)
    })
    
    module.exports = router
  4. newSql.js

    module.exports = {
      createNews: `CREATE TABLE news (
        id int(255) NOT NULL AUTO_INCREMENT,
        type varchar(255) CHARACTER SET utf8 NOT NULL,
        title varchar(255) CHARACTER SET utf8 NOT NULL,
        description varchar(255) CHARACTER SET utf8 NOT NULL,
        occur_time varchar(255) CHARACTER SET utf8 NOT NULL,
        url varchar(255) NOT NULL,
        newsImg varchar(255) NOT NULL,
        PRIMARY KEY (id)
      )`,
      queryAll: 'SELECT * FROM news',
      queryById: 'SELECT * FROM news WHERE id = ?',
      add: 'INSERT INTO news (type, title, description, occur_time, url, newsImg) VALUES (?, ?, ?, ?, ?, ?)',
      update: 'UPDATE news SET type = ?, title = ?, description = ?, occur_time = ?, url = ?, newsImg = ? WHERE id = ?',
      delete: 'DELETE FROM news WHERE id = ?'
    }
  5. news.js

    const mysql = require('mysql')
    const db = require('../db/db')
    const $newsSql = require('../db/newsSql')
    
    let pool = mysql.createPool(db.mysql)
    
    let queryAll = (req, res, next) => {
      pool.getConnection((error, connect) => {
        if (error) {
          throw error
        } else {
          connect.query($newsSql.queryAll, (error, data) => {
            if (error) {
              throw error
            }
            res.json({
              code: '200',
              msg: 'success',
              data
            })
            connect.release()
          })
        }
      })
    }
    
    let queryById = (req, res, next) => {
      let id = +req.query.id
      pool.getConnection((error, connect) => {
        if (error) {
          throw error
        } else {
          connect.query($newsSql.queryById, id, (error, data) => {
            if (error) {
              throw error
            }
            res.json({
              code: '200',
              msg: 'success',
              data
            })
            connect.release()
          })
        }
      })
    }
    
    let add = (req, res, next) => {
      let rb = req.body
      let params = [rb.type, rb.title, rb.description, rb.occur_time, rb.url, rb.newsImg]
      pool.getConnection((error, connect) => {
        if (error) {
          throw error
        } else {
          connect.query($newsSql.add, params, (error, data) => {
            if (error) {
              throw error
            }
            res.json({
              code: '200',
              msg: 'success'
            })
            connect.release()
          })
        }
      })
    }
    
    let update = (req, res, next) => {
      let rb = req.body
      let params = [rb.type, rb.title, rb.description, rb.occur_time, rb.url, rb.newsImg, rb.id]
      pool.getConnection((error, connect) => {
        if (error) {
          throw error
        } else {
          connect.query($newsSql.update, [...params], (error, data) => {
            if (error) {
              throw error
            }
            res.json({
              code: '200',
              msg: 'success'
            })
            connect.release()
          })
        }
      })
    }
    
    let deleteNews = (req, res, next) => {
      let id = +req.query.id
      pool.getConnection((error, connect) => {
        if (error) {
          throw error
        } else {
          connect.query($newsSql.delete, id, (error, data) => {
            if (error) {
              throw error
            }
            res.json({
              code: '200',
              msg: 'success'
            })
            connect.release()
          })
        }
      })
    }
    let mkdirSync = dirname => {
     if (fs.existsSync(dirname)) {
         return true
     } else {
         if (mkdirSync(path.dirname(dirname))) {
             fs.mkdirSync(dirname)
              return true
         }
     }
     return false
     }
    
     let uploadImg = (req, res, next) => {
         const form = new formidable.IncomingForm()
         form.encoding = 'utf-8'
         form.keepExtensions = true
         form.parse(req, (err, fileds, files) => {
             if (err) {
                 console.log(err)
             }
             let imgPath = fs.readFileSync(files.file.path)
             let filename = path.resolve(__dirname, `../${NEWS_IMAGES_PATH}/${files.file.name}`)
             if (mkdirSync(NEWS_IMAGES_PATH)) {
                 fs.writeFile(filename, imgPath, (err) => {
                     if (err) {
                     console.log(err)
                     }
                     res.json({
                         code: '200',
                         msg: 'success',
                         files: files.file,
                     })
                 })
             })
         }
     }
    
    module.exports = {
      queryAll,
      queryById,
      add,
      update,
      deleteNews,
      uploadImg
    }

Vue配置代理以及使用api

  1. config/index.js 修改proxyTable

    module.exports = {
      dev: {
        proxyTable: {
          '/api': {
            target: 'http://127.0.0.1:9527',
            changeOrigin: true,
            pathRewrite: {
              '^/api': '/api'
            }
          }
        }
      }
    }
  2. 使用axios調用接口

    import axios from 'axios'
    export default {
      created () {
        this._getAllNews()
      },
      methods: {
        _getAllNews () {
          axios.get('/api/news/queryAll').then(res => {
                if (+res.data.code === SCC_CODE) {
                  this.news = res.data.data
                }
              })
          }
        }
    }

Vue build打包

npm run build

vue-build

由於個人Server端是Express寫的,啓動Server須要使用Node,因此咱們須要在服務器上安裝Node.

Linux安裝Node 8.x

# Using Ubuntu
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

# Using Debian, as root
curl -sL https://deb.nodesource.com/setup_8.x | bash -
apt-get install -y nodejs

# Using Centos
curl -sL https://rpm.nodesource.com/setup_8.x | bash -
yum install -y nodejs

具體安裝各版本的Node詳見

啓動Node

此處我以前的命令執行錯誤,因此我須要kill這個進程

nohup node website/server/app.js &

node-nohup

nohup:能夠將程序以忽略掛起信號的方式運行起來,被運行的程序的輸出信息將不會顯示到終端。

不管是否將 nohup 命令的輸出重定向到終端,輸出都將附加到當前目錄的 nohup.out文件中。若是當前目錄的 nohup.out 文件不可寫,輸出重定向到$HOME/nohup.out文件中。若是沒有文件能建立或打開以用於追加,那麼 command 參數指定的命令不可調用。若是標準錯誤是一個終端,那麼把指定的命令寫給標準錯誤的全部輸出做爲標準輸出重定向到相同的文件描述符。

nohup-out

到這裏,咱們的WebNode ServerNginx都已經配置並啓動好了,咱們只須要到瀏覽器輸入你的服務器IP:8080便可.

Nginx衆多概念詳見官方詞彙表

nginx-glossary

相關文章
相關標籤/搜索