某日,心血來(lái)潮,想將hexo搭建的站點(diǎn)移除,從零開(kāi)始搭建自己的站點(diǎn)。
涉及的內(nèi)容包含??:
購(gòu)買服務(wù)器
站點(diǎn)前后端分析
服務(wù)器環(huán)境搭建
發(fā)布前端項(xiàng)目
發(fā)布后端項(xiàng)目
關(guān)聯(lián)域名
站點(diǎn)提前體驗(yàn)地址:
戳地址:jimmyarea.com
購(gòu)買服務(wù)器
這里選擇了騰訊云的服務(wù)器,你也可以選擇阿里云之類的。
相關(guān)的云服務(wù)器的配置如下:
# 操作系統(tǒng)
CentOS 7.5 64位
# CPU
1核
# 內(nèi)存
2GB
# 公網(wǎng)帶寬
1Mbps
基礎(chǔ)配置(1核2GB)的機(jī)型適合有一定的訪問(wèn)量的網(wǎng)站;當(dāng)然,也可以選擇入門配置(1核1GB)的機(jī)型,它適用起步階段的個(gè)人網(wǎng)站。
PS:本次的搭建沒(méi)有特殊說(shuō)明均在騰訊云上完成?,并基于CentOS 7.5 64位
站點(diǎn)前后端分析
站點(diǎn)采用前后端分離思想,前后端獨(dú)立上線,方便開(kāi)發(fā)和維護(hù)等~
站點(diǎn)前端
目前的功能有
? 登陸
? 注冊(cè)
? 重置密碼
? 社交列表
? 個(gè)人簡(jiǎn)介
? 文章列表
? 留言功能
? 技能圖 COMING SOON
? 發(fā)表文章功能【管理端】COMING SOON
ETC
前端使用了業(yè)界成熟的UI框架 -- ANT DESING。
彈性布局,能兼容移動(dòng)端設(shè)備~
項(xiàng)目使用ANT DESIGN PRO V5進(jìn)行初始化,直接對(duì)請(qǐng)求的request進(jìn)行抽離改造,利于維護(hù)。
# src/utils/request.ts
// 默認(rèn)的框架請(qǐng)求
import { getStore } from '@/utils/storage';
import { extend } from 'umi-request';
import type { ResponseError } from 'umi-request';
import { notification } from 'antd';
// @see https://beta-pro.ant.design/docs/request-cn
const codeMessage = {
200: '服務(wù)器成功返回請(qǐng)求的數(shù)據(jù)。',
201: '新建或修改數(shù)據(jù)成功。',
202: '一個(gè)請(qǐng)求已經(jīng)進(jìn)入后臺(tái)排隊(duì)(異步任務(wù))。',
204: '刪除數(shù)據(jù)成功。',
400: '發(fā)出的請(qǐng)求有錯(cuò)誤,服務(wù)器沒(méi)有進(jìn)行新建或修改數(shù)據(jù)的操作。',
401: '用戶沒(méi)有權(quán)限(令牌、郵箱、密碼錯(cuò)誤)。',
403: '用戶得到授權(quán),但是訪問(wèn)是被禁止的。',
404: '發(fā)出的請(qǐng)求針對(duì)的是不存在的記錄,服務(wù)器沒(méi)有進(jìn)行操作。',
405: '請(qǐng)求方法不被允許。',
406: '請(qǐng)求的格式不可得。',
410: '請(qǐng)求的資源被永久刪除,且不會(huì)再得到的。',
422: '當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),發(fā)生一個(gè)驗(yàn)證錯(cuò)誤。',
500: '服務(wù)器發(fā)生錯(cuò)誤,請(qǐng)檢查服務(wù)器。',
502: '網(wǎng)關(guān)錯(cuò)誤。',
503: '服務(wù)不可用,服務(wù)器暫時(shí)過(guò)載或維護(hù)。',
504: '網(wǎng)關(guān)超時(shí)。',
};
/**
* 異常處理程序
*/
const errorHandler = (error: ResponseError) => {
const { response, data } = error;
if (response && response.status) {
const errorText = data.msg || codeMessage[response.status] || response.statusText;
// const { status, url } = response;
const { status } = response;
notification.error({
message: `請(qǐng)求錯(cuò)誤 ${status}`,
description: errorText,
});
}
if (!response) {
notification.error({
description: '您的網(wǎng)絡(luò)發(fā)生異常,無(wú)法連接服務(wù)器',
message: '網(wǎng)絡(luò)異常',
});
}
throw error;
};
/**
* 配置request請(qǐng)求時(shí)的默認(rèn)參數(shù)
*/
const request = extend({
errorHandler, // 默認(rèn)錯(cuò)誤處理
credentials: 'include', // 默認(rèn)請(qǐng)求是否帶上cookie
timeout: 5000,
prefix: '',
});
// 請(qǐng)求攔截器
request.interceptors.request.use((url, options) => {
const token = getStore('token') || '';
const theOptions = options || {};
if (token) {
theOptions.headers = {
// 處理header中的token
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${token}`,
};
}
return {
url,
options: { ...theOptions },
};
});
request.interceptors.response.use(async (response: any) => response);
export default request;
對(duì)授權(quán)登陸進(jìn)行移除等,形成一個(gè)純凈的,符合設(shè)計(jì)的jimmyarea站點(diǎn)的功能需求。
前端開(kāi)發(fā)完,運(yùn)行腳手架自帶的命令行yarn run build 或者 npm run build 生成一個(gè)dist文件。這個(gè)文件就是你要發(fā)布到服務(wù)器上的文件了~
當(dāng)然,完成前端的開(kāi)發(fā),如果你對(duì)性能有所要求,可以進(jìn)行相關(guān)的優(yōu)化。這里,我對(duì)moment.js進(jìn)行了優(yōu)化,剔除不必要的local,壓縮了相關(guān)的圖片等等...具體的你可以通過(guò)運(yùn)行yarn run analyze來(lái)觀察優(yōu)化
Nginx上開(kāi)啟了gzip 別忘了哦
站點(diǎn)后端
后端使用的是KOA2框架去開(kāi)發(fā),本地使用nodemon運(yùn)行服務(wù)調(diào)試,線上采用pm2來(lái)啟動(dòng)服務(wù)。
目前的接口有:
? 登陸
? 注冊(cè)
? 加密鹽
? 重置密碼
? 社交列表
? 文章列表
? 留言功能
? 郵件發(fā)送
? 發(fā)表文章功能【管理端】COMING SOON
ETC
我們從零開(kāi)始搭建一個(gè)NODE的服務(wù)端項(xiàng)目,目錄結(jié)構(gòu)如下:
backend
├── config
| ├── default.json
| ├── production.json # 生產(chǎn)環(huán)境的配置
| └── test.json # 測(cè)試環(huán)境配置
├── src # 主要代碼存放地方
| ├── controllers
| | ├── user.js
| | └── ...
| ├── middlewares
| | ├── auth.js
| | └── ...
| ├── models
| | └── mongo
| | ├── schema
| | | ├── User.js
| | | └── ...
| | └── index.js
| ├── router
| | └── index.js
| ├── service # 第三方服務(wù)
| | └── index.js
| ├── utils
| | ├── bcrypt.js
| | └── ...
| ├── app.js
| └── index.js # 入口文件
├── .babelrc # 處理babel
├── apidoc.json # 生成接口文檔的配置
├── package.json # 依賴
├── pm2.json # pm2 線上運(yùn)行的配置
└── README.md 項(xiàng)目說(shuō)明
其中, package.json的內(nèi)容如下:
{
"version": "1.0.0",
"author": "Jimmy",
"scripts": {
"start": "NODE_ENV=development node src/index.js",
"dev": "NODE_ENV=development nodemon src/index.js",
"nodepro": "yarn run build && NODE_ENV=production node app/index.js",
"pm2pro": "yarn run build && NODE_ENV=production pm2 start pm2.json",
"clean": "rm -rf app/",
"compile": "babel src/ --out-dir app/ --retain-lines",
"build": "yarn run clean && yarn run compile",
"docs": "apidoc -i src/controllers -o apidoc/"
},
"dependencies": {
"bcrypt": "^5.0.1",
"bluebird": "^3.7.2",
"config": "^3.3.6",
"jsonwebtoken": "^8.5.1",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-convert": "^2.0.0",
"koa-json": "^2.0.2",
"koa-jwt": "^4.0.1",
"koa-logger": "^3.2.1",
"koa-router": "^10.0.0",
"koa-session": "^6.2.0",
"mongodb": "^3.6.7",
"mongoose": "^5.12.7",
"nodemailer": "^6.6.0",
"uuid": "^8.3.2",
"xss": "^1.0.8"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-plugin-add-module-exports": "^0.1.4",
"babel-plugin-transform-runtime": "^6.8.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2015-loose": "^7.0.0",
"babel-preset-stage-3": "^6.5.0",
"babel-register": "^6.26.0",
"nodemon": "^2.0.7"
}
}
當(dāng)然,你也可以使用成熟的框架EGGJS。
數(shù)據(jù)庫(kù)是MongoDB,對(duì)象模型工具Mongoose。
下面是一個(gè)連接數(shù)據(jù)庫(kù)查詢文章列表的接口:
# src/models/mongo/schema/Article.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
// 定義文章字段
let ArticleSchema = new Schema({
title: String, // 文章標(biāo)題
url: String, // 文章url
count: String, // 文章閱讀量
thumb: String, // 文章點(diǎn)贊量
meta: {
createAt: {
type: Date,
default: Date.now()
},
updateAt: {
type: Date,
default: Date.now()
}
}
})
mongoose.model('Article', ArticleSchema)
# src/router/index.js
// 文章部分
router.get('/public/article', Article.getArticleData)
# src/controllers/article.js
let mongoose = require('mongoose')
let Article = mongoose.model('Article')
/**
* @apiGroup Article
* @api {get} /public/article 查看社交信息
* @apiVersion 1.0.0
*
*
* @apiSuccessExample {json} 返回成功
* HTTP/1.1 200
* {
* "code": 0,
* "msg": "ok",
* "data": [{
* "key": "value"
* }]
* }
*
* @apiSampleRequest off
*/
exports.getArticleData = async (ctx, next) => {
let data = await Article.find({})
ctx.body = {
results: data,
current: 1
}
}
上面示例,線上的接口地址是https://jimmyarea.com/api/public/article ,感興趣的話可以點(diǎn)擊此鏈接感受下。
嗯~到這里,我們服務(wù)器也買了,本地上前端項(xiàng)目和后端項(xiàng)目都跑起來(lái)了,那么,我們可以考慮將我們的項(xiàng)目上線了。
服務(wù)器環(huán)境搭建
在項(xiàng)目上線前,我們得做好準(zhǔn)備,這涉及Nginx代理,數(shù)據(jù)庫(kù),后端node等環(huán)境。
安裝數(shù)據(jù)庫(kù)MongoDB
基于成本的考慮,我們將數(shù)據(jù)庫(kù)和應(yīng)用都放在同一臺(tái)云服務(wù)器上面。
如果是公司企業(yè)的話,建議購(gòu)買云數(shù)據(jù)庫(kù)等
演示的是:服務(wù)器操作系統(tǒng)為 CentOS 7.5 64位
mongodb的安裝教程: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/
當(dāng)然,你也可以在文章Linux Centos 7安裝MongoDB(簡(jiǎn)單!詳細(xì)!) 按照指引完成數(shù)據(jù)庫(kù)的安裝。
安裝完數(shù)據(jù)庫(kù)之后,建議開(kāi)啟權(quán)限驗(yàn)證及設(shè)置用戶名密碼,不然什么人都可以訪問(wèn)你的數(shù)據(jù)庫(kù),那很是尷尬??
比如:
db.createUser(
{
user: "simpleUser",
pwd: “123456”,
roles: [“readWrite”,”dbAdmin”,”userAdmin”]
}
)
安裝Nginx
基于centos的yum源操作
# 安裝nginx 卸載[yum -y remove nginx]
yum -y install nginx
# 查看版本
nginx -v
# 啟動(dòng)nginx
systemctl start nginx.service
# 訪問(wèn) 默認(rèn)端口是80
your_ip:80
# 配置
cd /etc/nginx/conf.d
... 有待補(bǔ)充
# 以下是nginx常用命令
# 啟動(dòng)nginx服務(wù)
systemctl start nginx.service
# 停止nginx服務(wù)
systemctl stop nginx.service
# 重啟nginx服務(wù)
systemctl restart nginx.service
# 重新讀取nginx配置(這個(gè)最常用, 不用停止nginx服務(wù)就能使修改的配置生效)
systemctl reload nginx.service
- 相關(guān)啟動(dòng)命令也可如下
nginx -t #測(cè)試配置文件是否有語(yǔ)法錯(cuò)誤
nginx -s reopen #重啟Nginx
nginx -s reload #重新加載Nginx配置文件,然后以優(yōu)雅的方式重啟Nginx
nginx -s stop #強(qiáng)制停止Nginx服務(wù)
nginx -s quit #優(yōu)雅地停止Nginx服務(wù)(即處理完所有請(qǐng)求后再停止服務(wù))
nginx -c [配置文件路徑] #為 Nginx 指定配置文件
配置Nginx
查看Nginx配置文件nginx.conf
vi /etc/nginx/nginx.conf
檢查是否有如下語(yǔ)句:
include /etc/nginx/conf.d/*.conf
如果存在,說(shuō)明nginx的配置文件均設(shè)置在conf.d文件夾下,我們進(jìn)入到conf.d文件夾
cd /etc/nginx/conf.d/
在 conf.d 目錄下新建一個(gè)站點(diǎn)的配置文件,例如:test.com.conf:
vi test.com.conf
配置內(nèi)容這樣設(shè)置(按Insert鍵進(jìn)入編輯模式):
server {
# 監(jiān)聽(tīng)80端口
listen 80;
# 服務(wù)器名稱(隨意)
server_name www.test.com test.com;
# 路由匹配
location / {
# 文件目錄,即你存放靜態(tài)資源的地方
root /usr/share/nginx/test.com;
# 設(shè)置首頁(yè)為根目錄下index.html文件
index index.html;
}
}
寫好后,按ESC鍵退出編輯模式,輸入 :wq 保存并退出編輯
:wq
檢查配置文件是否有誤
nginx -t
重啟Nginx服務(wù)
systemctl start nginx
或者
nginx -s reload
接著我們將靜態(tài)資源存放在定義的 /usr/share/nginx/test.com 目錄下。
這里我們僅做測(cè)試,于是在該目錄下創(chuàng)建一個(gè)測(cè)試網(wǎng)頁(yè)index.html文件,你可以嘗試下,看瀏覽器頁(yè)面地址有沒(méi)有訪問(wèn)到這個(gè)index.html文件。
Nginx處理CORS
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
發(fā)布前端項(xiàng)目
第一種方式
將前端項(xiàng)目打包,生成dist文件夾,之后放在/usr/share/nginx指定的目錄中即可。
第一種方式比較繁瑣,我們用第二種方式。
第二種方式
方式二:直接拉取代碼倉(cāng)庫(kù),打包即可
現(xiàn)在,我們用github為進(jìn)行項(xiàng)目托管,那么我們可以在自己都買的服務(wù)環(huán)境中將Git和Github進(jìn)行SSH連接。
首先在服務(wù)器上面安裝版本控制系統(tǒng)GIT。
yum install git
之后配置git與github關(guān)聯(lián):
git config --global user.name "your_name"
git config --global user.email "your_email"
然后用ssh生成公鑰:
ssh-keygen -t rsa -C "your_email"
之后一直回車即可(如果你要更改文件命名,可按提示修改)。
最后,將公鑰添加到github中
在
.ssh文件夾中找到id_rsa.pub這個(gè)文件,復(fù)制里面的所有內(nèi)容。登陸
github賬號(hào),點(diǎn)擊頭像旁的小三角展開(kāi),點(diǎn)擊Settings - SSH and GPG keys - New SSH key,在Title中取一個(gè)名字(任意),Key中粘貼你剛剛復(fù)制的內(nèi)容。然后點(diǎn)擊Add SSH key即可。
詳細(xì)內(nèi)容可以直接戳官方教程使用 SSH 連接到 GitHub
完成之后,我們可以在/usr/share/nginx下指定的目錄進(jìn)行操作:
# clone
git clone your_github_repository_url
# 安裝依賴
yarn install || npm install
# 打包
yarn run build
后期如果有新文件加入,重新拉取打包即可。
發(fā)布后端項(xiàng)目
這里是NODE項(xiàng)目,那么我們得先準(zhǔn)備自己相關(guān)的NODE環(huán)境。
安裝包文件nodejs
# 使用EPEL安裝
yum install epel-release
# 安裝node js
sudo yum install nodejs
# 查看安裝版本
node -v
# v6.13.3
# 版本過(guò)低,升級(jí)到最新穩(wěn)定版
# 安裝n,n是nodejs管理工具
npm install -g n
# 安裝nodejs最新版本
n latest
# 穩(wěn)定版 n node的穩(wěn)定版版本號(hào)
# 之后切換nodejs版本即可
n
上面是一種安裝方式,讀者可以嘗試其他的安裝方式,這里不展開(kāi)了
其他安裝
安裝yarn包管理器,比npm性能優(yōu)越
npm install -g yarn安裝pm2的node進(jìn)程管理工具
npm install pm2 -g或yarn global add pm2pm2 讓 Nodejs 服務(wù)常駐
配置nginx反向代理
比如:
upstream api {
server 127.0.0.1:6000;
keepalive 2000;
}
server {
location /api {
proxy_pass http://api;
}
}
發(fā)布
發(fā)布的操作也是通過(guò)git拉區(qū)在github上的后端項(xiàng)目,然后運(yùn)行本項(xiàng)目自定義的yarn run pm2pro啟動(dòng)項(xiàng)目即可。
你可以通過(guò)命令行pm2 list查看服務(wù)運(yùn)行的情況。
pm2 list
| id | name | mode | ? | status | cpu | memory |
|---|---|---|---|---|---|---|
| 0 | jimmy-server | cluster | 880 | online | 0% | 56.5mb |
關(guān)聯(lián)域名
我們成功發(fā)布了前后端項(xiàng)目,配置好nginx代理之后,項(xiàng)目就能夠通過(guò) IP 地址進(jìn)行訪問(wèn)了,本項(xiàng)目的服務(wù)器的ip地址是111.230.192.27。
為了用戶友好體驗(yàn),我們是不是考慮買個(gè)域名了呢?
這里我購(gòu)買的域名是jimmyarea.com, 還順帶買了證書(shū) - 用于開(kāi)啟https。
購(gòu)買域名
這里使用的是騰訊云提供的服務(wù)。
購(gòu)買完域名后,根據(jù)指引去實(shí)名進(jìn)行認(rèn)證。運(yùn)營(yíng)商有很詳細(xì)的操作指南,畢竟要用戶,這里不詳細(xì)說(shuō)明。
域名解析
這里采用的是騰訊云服務(wù)器和騰訊域名,你可以參考這個(gè) - 快速添加域名解析 來(lái)完成?
Nginx并配置SSL證書(shū)
https證書(shū)申請(qǐng)
將證書(shū)上傳到服務(wù)器的指定目錄
# 用命令行上傳
scp -r local_dir username@servername:remote_dir
# 例子
scp -r test root@192.168.0.101:/var/www/ 把當(dāng)前目錄下的test目錄上傳到服務(wù)器的/var/www/ 目錄
具體可以參考官方- Nginx 服務(wù)器 SSL 證書(shū)安裝部署 進(jìn)行操作,這里不贅述。
- 域名備案,以騰訊為例,域名申請(qǐng)n個(gè)自然日后可以提交申請(qǐng)備案了。域名不備案,通過(guò)域名訪問(wèn)不穩(wěn)定~囧 ??
親測(cè)域名備案前后需要兩個(gè)星期 審核-修改-審核
后話
至此,可以美滋滋地對(duì)項(xiàng)目進(jìn)行迭代開(kāi)發(fā)--上線。當(dāng)然,如果你想更加工程化,方便點(diǎn),你可以引入jenkins啥的 從零開(kāi)始搭建JENKINS+GITHUB持續(xù)集成環(huán)境【多圖】,docker啥的。
成品地址: jimmyarea.com,感興趣可以體驗(yàn)下。
倉(cāng)庫(kù)代碼地址不公開(kāi),畢竟里面涉及到數(shù)據(jù)庫(kù)賬號(hào)密碼等個(gè)人信息,我全部設(shè)置為privacy了。