Node 初探
Nginx
+ Node
+ Vue
部署初试(2019-03-08更新)
Nginx
异步框架的 Web服务器,也可以用作反向代理,负载平衡器 , HTTP缓存, 媒体流等的开源软件。它最初是一个旨在实现最高性能和稳定性的
Web
服务器。除了HTTP
服务器功能外,NGINX
还可以用作电子邮件(IMAP
,POP3
和SMTP
)的代理服务器以及HTTP
,TCP
和UDP
服务器的反向代理和负载平衡器。特点
- 更快
- 高扩展性,
Nginx
的模块都是嵌入到二进制文件中执行 - 高可靠性
- 低内存消耗
- 单机支持10万次的并发连接
- 热部署,
master
管理进行与work
工作进程分离设计,因此具备热部署功能 - 最自由的
BSD
许可协议
功能
- 静态服务(
css
,js
,html
,images
,videos
) SSL
和TLS SNI
支持HTTP/HTTPS
,SMTP
,IMAP/POP3
反向代理FastCGI
反向代理- 负载均衡
- 页面缓存(
CDN
) - 支持
gzip
、expirse
- 支持
keep-alive
和管道连接 - 基于
PCRE
的rewrite
重写模块 - 带宽限制
- 基于
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
Nginx 参数列表
配置参数属性 | 解释说明 | 参数列表 |
---|---|---|
user | 设置nginx服务的系统使用用户 | nobody (注意:此处用户如果比启动Nginx 的用户权限低,你需要使用当前用户重启Nginx )nginx -s stop 关闭nginx -> nginx 启动-> `ps aux |
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/poll
(Solaris
和Unix
特有),epoll
(Linux
特有),eventport
(Solaris
10特有),kqueue
(BSD
特有),poll
,rtsig
(实时信号),select
等。
它的作用是监听accept
后建立的连接,对读写事件进行添加删除。事件处理模型和Nginx
的非阻塞IO模型结合在一起使用。当IO
可读可写的时候,相应的读写事件就会被唤醒,此时就会去处理事件的回调函数。
配置参数属性 | 解释说明 | 参数列表 |
---|---|---|
worker_connections | 定义每个进程的最大连接数,受系统进程的最大打开文件数量限制 | 单个后台worker process进程的最大并发链接数 (最大连接数= worker_processes worker_connections) 在反向代理环境下: 最大连接数 = 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
支持的事件模型
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
以某个常规字符串开头,不是正则匹配.~
: 开头表示区分大小写的正则匹配.~*
: 开头表示不区分大小写的正则匹配./
: 通用匹配, 如果没有其它匹配,任何请求都会匹配到.
匹配优先级(高到低)
location =
location 完整路径
location ^~ 路径
location ~,~* 正则顺序
location 部分起始路径
/
Reverse Proxy
当NGINX
代理请求时,它会将请求发送到指定的代理服务器,获取响应并将其发送回客户端。可以使用指定的协议将请求代理到HTTP
服务器(另一个NGINX
服务器或任何其他服务器)或非HTTP
服务器(可以运行使用特定框架(如PHP
或Python
)开发的应用程序)。
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
应使用相应的指令:
fastcgi_pass
将请求传递给FastCGI服务器uwsgi_pass
将请求传递给uwsgi服务器scgi_pass
将请求传递给SCGI服务器memcached_pass
将请求传递给memcached服务器
配置参数属性 | 解释说明 | 参数列 |
---|---|---|
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地址 |
全局变量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 | 请求的方法,比如GET 、POST 等 |
$request_uri | 完整的原始请求URI (带参数) |
$scheme | 请求方案,http 或https |
$server_addr | 接受请求的服务器的地址,如果没有用listen 指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费); |
$server_protocol | 请求的协议版本,HTTP/1.0 或HTTP/1.1 |
$uri | 请求中的当前URI , $uri 在请求处理期间 ,值可能会发生变化,例如在执行内部重定向或使用索引文件时 |
全局变量详见Alphabetical index of variables
修改 http
server
中的配置
启动Nginx
nginx
ps -ef | grep nginx
新加载修改后的Nginx
配置文件
nginx -s reload
reload
和resatrt
是两个不同的概念。
reload
重新加载conf
文件,不中断Nginx
服务,conf
文件有问题则加载上一次的conf
.restart
会根据配置文件,重启整个Nginx
服务,造成服务器中断一段时间,当然会因为conf
文件问题报错。
关闭Nginx
nginx -s stop
因为我已经启动了,所以就重启一下Nginx
即可
Linux
安装Nginx
使用pstree
查看当前服务器启动的进程
pstree
查找Nginx
的位置
ps -aux | grep nginx
进入nginx
的目录
然后配置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
查找 Docker Hub 上的
nginx
镜像docker search nginx
拉取官方镜像
docker pull nginx
查看当前镜像
docker images nginx
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
Node Server
npm init
npm install express mysql body-parser -S
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}`)
})db.js
->Mysql
配置module.exports = {
mysql: {
host: 'localhost',
user: 'root',
password: 'xxxx',
database: 'test',
port: '3306'
}
}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 = routernewSql.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 = ?'
}news.js
const mysql = require('mysql')
const formidable = require('formidable')
const fs = require('fs')
const path = require('path')
const db = require('../db/db')
const $newsSql = require('../db/newsSql')
let pool = mysql.createPool(db.mysql)
let NEWS_IMAGES_PATH = 'news-images'
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
config/index.js
修改proxyTable
module.exports = {
dev: {
proxyTable: {
'/api': {
target: 'http://127.0.0.1:9527',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
}
}
}
}使用
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
因为我的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 &
nohup
:可以将程序以忽略挂起信号的方式运行起来,被运行的程序的输出信息将不会显示到终端。
无论是否将 nohup
命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out
文件中。如果当前目录的 nohup.out
文件不可写,输出重定向到$HOME/nohup.out
文件中。如果没有文件能创建或打开以用于追加,那么 command
参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。
到这里,我们的Web
,Node Server
,Nginx
都已经配置并启动好了,我们只需要到浏览器输入你的服务器IP:8080
即可.
- 什么是应用交付?
- 什么是应用交付控制器(ADC)?
- 什么是应用程序服务器与Web服务器?
- 什么是缓存?
- 什么是云负载平衡?
- 什么是聚类?
- 什么是DevOps?
- 什么是分布式拒绝服务(DDoS)?
- 什么是DNS负载平衡?
- 什么是全局服务器负载平衡?
- 什么是高可用性?
- 什么是HTTP?
- 什么是HTTP / 2?
- 什么是混合负载均衡?
- 什么是第4层负载均衡?
- 什么是第7层负载平衡?
- 什么是负载平衡?
- 什么是媒体服务器?
- 什么是微服务?
- 什么是网络负载均衡器?
- 什么是NGINX?
- 什么是渐进式下载?
- 什么是反向代理服务器?
- 什么是反向代理与负载均衡器?
- 什么是循环负载平衡?
- 什么是面向服务的体系结构(SOA)?
- 什么是会话持久性?
- 什么是SSL负载均衡器?
- 什么是Web加速?
- 什么是Web服务器?