準備
1、域名
2、註冊開發者賬號
3、開發者後臺配置伺服器資訊
配置服務
搭建HTTP服務
安裝NodeJS和NPM
執行下面的命令安裝:
1 2 | curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - yum install nodejs -y |
編寫 HTTP Server 原始碼
先建立一個工作目錄
1 2 | root@172.17.0.9:~ # mkdir -p /data/release/weapp |
然後進入這個目錄
1 2 | root@172.17.0.9:~ # cd /data/release/weapp/ |
建立package.json
檔案
1 2 | root@172.17.0.9:/data/release/weapp # vi package.json |
拷貝下面的內容進去
1 2 3 4 | { "name": "weapp", "version": "1.0.0" } |
接下來建立app.js
檔案,拷貝下面內容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 引用 express 來支援 HTTP Server 的實現 const express = require('express'); // 建立一個 express 例項 const app = express(); // 實現唯一的一箇中介軟體,對於所有請求,都輸出 "Response from express" app.use((request, response, next) => { response.write('Response from express'); response.end(); }); // 監聽埠,等待連線 const port = 8765; app.listen(port); // 輸出伺服器啟動日誌 console.log(`Server listening at http://127.0.0.1:${port}`); |
這裡呼叫了Express.js
監聽8765埠。
執行HTTP服務
這裡我們用pm2來守護HTTP服務,首先安裝pm2
1 2 | root@172.17.0.9:/data/release/weapp # npm install pm2 --global |
上一步配置HTTP服務的時候,用到了Express.js
,我們要安裝相應的依賴
1 2 | root@172.17.0.9:/data/release/weapp # npm install express --save |
然後用pm2來啟動HTTP服務
1 2 | root@172.17.0.9:/data/release/weapp # pm2 start app.js --watch |
檢視當前服務資訊可通過
1 2 | root@172.17.0.9:/data/release/weapp # pm2 show app |
搭建HTTPS服務
安裝Nginx
可以直接通過yum來安裝
1 2 | root@172.17.0.9:/data/release/weapp # yum install nginx -y |
如果這裡報錯沒有nginx包,需要新建配置檔案
1 | vim /etc/yum.repos.d/nginx.repo |
將下面的內容貼上到配置檔案中
1 2 3 4 5 | [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1 |
然後再執行第一步的安裝命令即可。
配置HTTPS反向代理
首先配置nginx的配置檔案
1 2 | root@172.17.0.9:/etc/nginx/conf.d # vim ssl.conf |
把下面的內容拷進去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | server { listen 443; server_name www.leonlei.top; # 改為繫結證書的域名 # ssl 配置 ssl on; ssl_certificate 1_www.leonlei.top_bundle.crt; # 改為自己申請得到的 crt 檔案的名稱 ssl_certificate_key 2_www.leonlei.top.key; # 改為自己申請得到的 key 檔案的名稱 ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:8765; } } |
然後將再騰訊雲申請的免費SSL證書上傳到/etc/nginx
目錄下
1 2 | LeonLeiMBP15-110:gaoshilei$ scp -p /Users/gaoshilei/Downloads/www/Nginx/1_www.leonlei.top_bundle.crt root@115.159.87.164:/etc/nginx/1_www.leonlei.top_bundle.crt LeonLeiMBP15-110:gaoshilei$ scp -p /Users/gaoshilei/Downloads/www/Nginx/2_www.leonlei.top.key root@115.159.87.164:/etc/nginx/2_www.leonlei.top.key |
然後重啟nginx
1 2 | root@172.17.0.9:/etc/nginx # nginx -s reload |
配置小程式會話
由於小程式不支援cookies和瀏覽器的快取機制,需要建立獨立的會話層,這裡我們用另外一臺伺服器用來做會話
安裝MongoDB
1 | [root@VM_0_13_centos ~]# yum install mongodb-server mongodb -y |
安裝完成之後檢視是否安裝成功
1 2 3 4 5 6 | [root@VM_0_13_centos ~]# mongod --version db version v2.6.12 2017-11-27T18:11:21.773+0800 git version: nogitversion 2017-11-27T18:11:21.773+0800 OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013 [root@VM_0_13_centos ~]# mongo --version MongoDB shell version: 2.6.12 |
啟動MongoDB
建立資料儲存和日誌的儲存目錄
1 2 | [root@VM_0_13_centos ~]# mkdir -p /data/mongodb [root@VM_0_13_centos ~]# mkdir -p /data/logs/mongodb |
然後使用命令啟動MongoDB
1 2 3 4 | [root@VM_0_13_centos ~]# mongod --fork --dbpath /data/mongodb --logpath /data/logs/mongodb/weapp.log about to fork child process, waiting until server is ready for connections. forked process: 6660 child process started successfully, parent exiting |
使用命令檢視是否啟動成功
1 2 | [root@VM_0_13_centos ~]# netstat -ltp | grep 27017 tcp 0 0 0.0.0.0:27017 0.0.0.0:* LISTEN 6660/mongod |
新增 MongoDB 使用者
登入資料庫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [root@VM_0_13_centos ~]# mongo MongoDB shell version: 2.6.12 connecting to: test Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user Server has startup warnings: 2017-11-27T18:13:28.129+0800 [initandlisten] 2017-11-27T18:13:28.129+0800 [initandlisten] ** WARNING: Readahead for /data/mongodb is set to 4096KB 2017-11-27T18:13:28.129+0800 [initandlisten] ** We suggest setting it to 256KB (512 sectors) or less 2017-11-27T18:13:28.129+0800 [initandlisten] ** http://dochub.mongodb.org/core/readahead > |
然後建立一個使用者
1 2 3 4 5 | > use weapp switched to db weapp > db.createUser({ user: 'weapp', pwd: 'weapp-dev', roles: ['dbAdmin', 'readWrite']}); Successfully added user: { "user" : "wechatapp", "roles" : [ "dbAdmin", "readWrite" ] } > |
安裝Node模組
實現小程式的會話功能,需要安裝 connect-mongo 和 wafer-node-session,
1 2 3 | [root@VM_0_13_centos ~]# cd /data/release/weapp [root@VM_0_13_centos /data/release/weapp]# npm install connect-mongo wafer-node-session --save |
實現小程式會話
在目錄/data/release/weapp
中新建檔案config.js
,然後將下面的程式碼拷入(注意appID和appSecret)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | module.exports = { serverPort: '8765', // 小程式 appId 和 appSecret // 請到 https://mp.weixin.qq.com 獲取 AppID 和 AppSecret appId: 'wx9fd292461a087d38', appSecret: 'b05144b0d40a08337bb2c476dc543f8a', // mongodb 連線配置,生產環境請使用更復雜的使用者名稱密碼 mongoHost: '127.0.0.1', mongoPort: '27017', mongoUser: 'weapp', mongoPass: 'weapp-dev', mongoDb: 'weapp' }; |
然後修改app.js
,新增會話邏輯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // 引用 express 來支援 HTTP Server 的實現 const express = require('express'); // 引用 wafer-session 支援小程式會話 const waferSession = require('wafer-node-session'); // 使用 MongoDB 作為會話的儲存 const MongoStore = require('connect-mongo')(waferSession); // 引入配置檔案 const config = require('./config'); // 建立一個 express 例項 const app = express(); // 新增會話中介軟體,登入地址是 /login app.use(waferSession({ appId: config.appId, appSecret: config.appSecret, loginPath: '/login', store: new MongoStore({ url: `mongodb://${config.mongoUser}:${config.mongoPass}@${config.mongoHost}:${config.mongoPort}/${config.mongoDb}` }) })); // 在路由 /me 下,輸出會話裡包含的使用者資訊 app.use('/me', (request, response, next) => { response.json(request.session ? request.session.userInfo : { noBody: true }); if (request.session) { console.log(`Wafer session success with openId=${request.session.userInfo.openId}`); } }); // 實現一箇中介軟體,對於未處理的請求,都輸出 "Response from express" app.use((request, response, next) => { response.write('Response from express'); response.end(); }); // 監聽埠,等待連線 app.listen(config.serverPort); // 輸出伺服器啟動日誌 console.log(`Server listening at http://127.0.0.1:${config.serverPort}`); |
新增完成重啟服務
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | root@172.17.0.9:/data/release/weapp # pm2 restart app Use --update-env to update environment variables >>>> In-memory PM2 is out-of-date, do: >>>> $ pm2 update In memory PM2 version: 2.0.18 Local PM2 version: 2.8.0 [PM2] Applying action restartProcessId on app [app](ids: 1) [PM2] [app](1) ✓ ┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤ │ app │ 1 │ fork │ 10193 │ online │ 7 │ 0s │ 0% │ 10.2 MB │ root │ enabled │ │ weapp │ 0 │ fork │ 2067 │ online │ 0 │ 19h │ 0% │ 45.5 MB │ root │ disabled │ └──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘ |
WebSocket 服務
安裝Node模組
小程式使用ws
模組與伺服器實現WebSocket通訊,所以先安裝node依賴
1 2 | root@172.17.0.9:/data/release/weapp # npm install ws --save |
實現WebSocket服務
建立websocket.js
,寫入一下內容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | // 引入 ws 支援 WebSocket 的實現 const ws = require('ws'); // 匯出處理方法 exports.listen = listen; /** * 在 HTTP Server 上處理 WebSocket 請求 * @param {http.Server} server * @param {wafer.SessionMiddleware} sessionMiddleware */ function listen(server, sessionMiddleware) { // 使用 HTTP Server 建立 WebSocket 服務,使用 path 引數指定需要升級為 WebSocket 的路徑 const wss = new ws.Server({ server, path: '/ws' }); // 監聽 WebSocket 連線建立 wss.on('connection', (ws,request) => {// 要升級到 WebSocket 協議的 HTTP 連線 // 被升級到 WebSocket 的請求不會被 express 處理, // 需要使用會話中間節獲取會話 sessionMiddleware(request, null, () => { const session = request.session; if (!session) { // 沒有獲取到會話,強制斷開 WebSocket 連線 ws.send(JSON.stringify(request.sessionError) || "No session avaliable"); ws.close(); return; } // 保留這個日誌的輸出可讓實驗室能檢查到當前步驟是否完成 console.log(`WebSocket client connected with openId=${session.userInfo.openId}`); serveMessage(ws, session.userInfo); }); }); // 監聽 WebSocket 服務的錯誤 wss.on('error', (err) => { console.log(err); }); } /** * 進行簡單的 WebSocket 服務,對於客戶端發來的所有訊息都回復回去 */ function serveMessage(ws, userInfo) { // 監聽客戶端發來的訊息 ws.on('message', (message) => { console.log(`WebSocket received: ${message}`); ws.send(`Server: Received(${message})`); }); // 監聽關閉事件 ws.on('close', (code, message) => { console.log(`WebSocket client closed (code: ${code}, message: ${message || 'none'})`); }); // 連線後馬上傳送 hello 訊息給會話對應的使用者 ws.send(`Server: 恭喜,${userInfo.nickName}`); } |
修改app.js
呼叫WebSocket服務
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | // HTTP 模組同時支援 Express 和 WebSocket const http = require('http'); // 引用 express 來支援 HTTP Server 的實現 const express = require('express'); // 引用 wafer-session 支援小程式會話 const waferSession = require('wafer-node-session'); // 使用 MongoDB 作為會話的儲存 const MongoStore = require('connect-mongo')(waferSession); // 引入配置檔案 const config = require('./config'); // 引入 WebSocket 服務實現 const websocket = require('./websocket'); // 建立一個 express 例項 const app = express(); // 獨立出會話中介軟體給 express 和 ws 使用 const sessionMiddleware = waferSession({ appId: config.appId, appSecret: config.appSecret, loginPath: '/login', store: new MongoStore({ url: `mongodb://${config.mongoUser}:${config.mongoPass}@${config.mongoHost}:${config.mongoPort}/${config.mongoDb}` }) }); app.use(sessionMiddleware); // 在路由 /me 下,輸出會話裡包含的使用者資訊 app.use('/me', (request, response, next) => { response.json(request.session ? request.session.userInfo : { noBody: true }); if (request.session) { console.log(`Wafer session success with openId=${request.session.userInfo.openId}`); } }); // 實現一箇中介軟體,對於未處理的請求,都輸出 "Response from express" app.use((request, response, next) => { response.write('Response from express'); response.end(); }); // 建立 HTTP Server 而不是直接使用 express 監聽 const server = http.createServer(app); // 讓 WebSocket 服務在建立的 HTTP 伺服器上監聽 websocket.listen(server, sessionMiddleware); // 啟動 HTTP 服務 server.listen(config.serverPort); // 輸出伺服器啟動日誌 console.log(`Server listening at http://127.0.0.1:${config.serverPort}`); |
完成之後用`pm2重啟服務。
更新Nginx代理
需要向配置檔案中新增WebSocket支援,修改之前配置好的/etc/nginx/conf.d/ssl.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # WebSocket 配置 map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 443; server_name www.leonlei.top; # 改為繫結證書的域名 # ssl 配置 ssl on; ssl_certificate 1_www.leonlei.top.crt; # 改為自己申請得到的 crt 檔案的名稱 ssl_certificate_key 2_www.leonlei.top.key; # 改為自己申請得到的 key 檔案的名稱 ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; # WebSocket 配置 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; location / { proxy_pass http://127.0.0.1:8765; } } |
配置完成重啟nginx
1 2 | root@172.17.0.9:/etc/nginx/conf.d # nginx -s reload |