1. 程式人生 > 資料庫 >redis_note_06_主從複製及高可用

redis_note_06_主從複製及高可用

Node基礎

  1. 陣列的索引使用問題: 沒有語義化, 解決方法: 索引器

  2. 客戶端和服務端的互動模型: 請求 , 業務處理, 響應

  3. 非同步操作的兩大特點:

    (1). 非同步非阻塞: 非同步操作不會阻塞後面程式碼的執行

    (2). 非同步操作的執行順序不依賴非同步操作程式碼的書寫順序

什麼是nodejs

  • node.js官網: https://nodejs.org/en/

  • 中文文件: http://nodejs.cn/api/

  • Node 是一個構建於 Chrome V8引擎之上的一個Javascript 執行環境, 作用是讓js擁有開發服務端的功能

  • 使用事件驅動、非阻塞IO模型(非同步讀寫)使得它非常的輕量級和高效

  • Node中絕大多數API都是非同步(類似於ajax),目的是提高效能

  • npm官網https://www.npmjs.com

nodejs 安裝

win7建議安裝v10版本, https://nodejs.org/zh-cn/download/releases/

node會附帶安裝npm

在cmd中輸入node -v可以獲取版本

服務端js和客戶端js的區別

  • 客戶端JavaScript由 ECMAScript, DOM, BOM 三部分組成
  • 服務端nodejs 由 ECMAScript , 核心模組 , 第三方模組 組成, 其中核心模組在安裝nodejs時已附帶安裝
    • 所以在nodejs中使用dom和bom api會報錯

執行node.js程式

node.js程式實際由node.exe執行, 呼叫方法

  1. 直接打在node.exe, 然後寫程式

  2. 在終端中(如cmd, powershell)中輸入node, 然後寫程式

  3. 在終端中輸入node 程式路徑名

    • tips: 在windows檔案管理器中直接輸入cmd, 即可在當前目錄開啟cmd

    • windows路徑切換cd*

    • nodemon會在js檔案發生變化時重新啟動Node程式, 安裝npm i -g nodemon, 使用nodemon 檔案路徑

node.js核心模組

​ Node應用是由模組組成的,Node遵循了CommonJS的模組規範,來隔離每個模組的作用域,使每個模組在它自身的名稱空間中執行。

CommonJS規範的主要內容:

模組必須通過 module.exports 匯出對外的變數或介面,通過 require() 來匯入其他模組的輸出到當前模組作用域中。

CommonJS模組的特點:

(1)所有程式碼執行在當前模組作用域中,不會汙染全域性作用域
(2)模組同步載入,根據程式碼中出現的順序依次載入
(3)模組可以多次載入,但是隻會在第一次載入時執行一次,然後執行結果就被快取了,以後再載入,就直接讀取快取結果。要想讓模組再次執行,必須清除快取。

require函式用來在一個模組中引入另外一個模組。傳入一個模組名,返回一個模組匯出物件。用法:let cc = require("模組名") ,其中模組名可以用絕對路徑也可以用相對路徑,模組的字尾名.js 可以省略

所以模組使用前要先引入

fs檔案模組

先引入fs模組

const fs = require('fs')

readFile非同步讀取模組

fs.readFile(path[, options], callback)
  • callback <回撥函式>
    • err
    • data <字串|二進位制>

示例:

fs.readFile('檔案路徑', (err, data)=>{
	if(err){
		console.log(err)
		//丟擲異常,throw的作用就是讓node程式終止執行,顯示出現錯誤的行數, 方便除錯
		throw err
	}else{
    	console.log(data)
    }
})

後續nodejs中使用箭頭函式, 為統一程式碼規範, err, data固定寫法

解決中文亂碼:

readFile('檔案路徑','utf-8', (err, data)=>{})
//按utf-8編碼讀取, 解決中文亂碼

writeFile非同步寫入模組

fs.writeFile(file, data[, options], callback)
  • callback <回撥函式>
    • err
  1. 預設寫入會覆蓋
  2. 如果檔名不存在,新建立再寫入
  3. 如果資料夾不存在,報錯

appenFile 非同步追加模組

fs.appendFile(path, data[, options], callback)

callback <回撥函式>

  • err

非同步地追加資料到檔案,如果檔案尚不存在則建立檔案

path路徑模組

node中的絕對路徑和相對路徑

  • node中的相對路徑: ./ 不是相對於當前檔案所在路徑,而是相對於執行node命令的資料夾路徑(當前被執行的檔案所在的資料夾路徑).

    在服務端開發中,一般不要使用相對路徑,而使用絕對路徑

  • 解決方案:在nodejs中,每一個js檔案都有兩個全域性屬性,它可以幫助我們獲取到檔案的絕對路徑

    • __filename:當前js檔案絕對路徑
    • __dirmame:當前js檔案所在目錄的絕對路徑

    因為是js檔案的, 所以寫在js檔案呼叫時才有效, 直接在終端中執行無效

const path = require('path')

join()方法 路徑拼接

path.join([...paths])
  • 使用path.join()的好處: 會自動幫我們新增/轉換分隔符(windows和unix分隔符不同)
  • 當路徑拼寫錯誤的時候, 比如多了個點./page, 會幫我們改正

示例

path.join('/目錄1', '目錄2', '目錄3/目錄4', '目錄5')
//返回'\目錄1\目錄2\目錄3\目錄4\目錄'

let filePath = path.join(__dirname, './page/login.html')

伺服器基本概念

基本的訪問流程

  1. 輸入主機地址
  2. 指定埠(如果沒有指定, 預設是80)
  3. 指定需要訪問的資源路徑
  4. 發起請求
  5. 獲取伺服器返回的結果並處理

http協議

HTTP是一個客戶端終端(使用者)和伺服器端(網站)請求和應答的標準
客戶端和伺服器的通訊必須遵守某種協議,http協議就是最常見的一種

http協議: 超文字傳輸協議(HyperText Transfer Protocol), 是基於TCP/IP協議之上的應用層協議

ip地址

  • 如果以本機做為伺服器,那麼本機伺服器的IP地址預設就為:127.0.0.1

埠是通過埠號來標記的,埠號只有整數,範圍是從0 到65535(2^16-1)

  • 開啟cmd後,用“netstat ”檢視埠狀態,用“netstat -n”命令,以數字格式顯示地址和埠資訊

  • 常見的埠號

    • 80:web伺服器埠
    • 3306:mysql資料伺服器埠

資源url地址(介面地址)

  • 資源url地址由零或多個“/”符號隔開的字串,一般用來表示主機上的一個目錄或檔案地址
  • 一般情況下,我們向伺服器發起請求時,需要準確的告訴伺服器我們想要什麼,這個時候就有必要傳入一個資源url地址了
  • 如http://157.122.54.189:9063/getUserList,這裡面的/getUserList就是資源url地址,這個地址對應著伺服器端的一個業務處理或者檔案地址,例如/getUserList就能夠獲取儲存在伺服器的使用者列表資料

返回資料

返回資料是指客戶端傳送請求之後,從伺服器端返回的資料

返回資料的格式:

  • text/html格式:html程式碼,瀏覽器會以html語法解析

  • text/css:樣式,瀏覽器會以css語法解析

  • application/javascript:js程式碼,瀏覽器會以js語法解析

  • application/json:json格式字串,描述從伺服器返回的資料

返回狀態碼

是用以表示網頁伺服器超文字傳輸協議響應狀態的3位數字程式碼

常見的狀態碼

  • 200請求已成功,請求所希望的響應頭或資料體將隨此響應返回。出現此狀態碼是表示正常狀態
  • 404:請求失敗,請求所希望得到的資源未被在伺服器上發現
  • 500:伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。一般來說,這個問題都會在伺服器端的原始碼出現錯誤時出現

建立簡單的伺服器

流程:

  1. 匯入模組 const ** = require('**')
  2. 建立伺服器 const server = http.creatServer()
  3. 新增伺服器的埠監聽 server.listen(埠號, ()=>{})
  4. 響應使用者的請求, 進行事件處理server.on('request', (req, res)=>{})

注意點:

  • req.url可以獲取當前使用者請求的url

  • res.setHeader('Content-type', 'text/html;charset=UTF-8')
    //res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'})
    //指定res的編碼格式, 兩種方法只需要一種
    //html頁面不需要, 頭部已有
    
  • 客戶端沒有指定url,那麼它預設為/

  • 一切都是為了資料服務, 後臺需要什麼資料, 客戶端發什麼資料. 後臺給什麼資料, 客戶端接收什麼資料

  • code a little, test a little, 從後臺拿到資料, 先打印出來檢查, 多利用列印err或者shrow err 來找到異常

響應不同的url

// 1.引入模組
const http = require('http')
const fs = require('fs')
const path = require('path')

// 2.建立伺服器
const server = http.createServer()

// 3.新增伺服器的埠監聽
server.listen(3000, function () {
    console.log('伺服器開好了: http://127.0.0.1:3000')
})

// 4.新增使用者請求的監聽,只要使用者發起了針對當前伺服器3000埠的請求,就會呼叫回撥函式進行處理, 同時給這個函式傳入兩個引數
//req: request, 客戶端傳遞個伺服器的請求資料(請求報文)
//res: response, 伺服器響應給客戶端的資料
server.on('request', function (req, res) {
    // 想要根據使用者請求返回不同的頁面
    // req.url:可以獲取當前使用者請求的url,如果客戶端沒有指定url,那麼它預設為/
    let url = req.url
    // 這裡需要注意的是,在node伺服器中使用console,會在伺服器端中進行列印,而不是在瀏覽器端列印輸出呢
    console.log(url)
    // 我們可以看到,使用者的不同請求,url是不一樣的,所以我們需要判斷當前的url以決定返回什麼樣的頁面
    // 對於url的判斷,我們在開發的時候一般是在後臺進行約定,前端需要遵守
    // 這裡我們約定    /或/index   就是要請求首頁,   /login   就是要請求登陸頁
    if (url == '/' || url == '/index') {
        fs.readFile(path.join(__dirname, "/views/index.html"), (err, data) => {
            // 細節:這裡不需要設定編碼,因為html頁面已經有預設的編碼了
            
            res.setHeader('Content-type', 'text/html;charset=UTF-8')
            //解決res輸出中文亂碼的問題
            if (err) {
                res.end('404')
            } else {
                res.end(data)
            }
        })
    } else if (url == '/login') {
        fs.readFile(__dirname + "/views/login.html", (err, data) => {
            if (err) {
                res.end('404')
            } else {
                res.end(data)
            }
        })
    } else {
        res.end('404錯誤')
    }
})

跨域請求

// 設定允許跨域請求 --現在不用考慮,只需要知道有這句程式碼就可以
res.setHeader('Access-Control-Allow-Origin', '*');

響應不同的請求

  • 常見的get請求

1.通過超連結發起請求

2.通過瀏覽器位址列發起請求

3.通過ajax發起請求,同時設定type為get

  • 常見的post請求

1.通過表單的預設行為發起請求,同時將method設定為post

2.通過ajax發起請求,同時設定type為post

3.常見的post操作有:新增,編輯,登陸..

獲取請求方式

通過req的method屬性可以獲取當前使用者的請求方式

let method = req.method  //GET  POST

GET請求

const http = require('http')

const server = http.createServer()

server.listen(3003, () => {
    console.log('開始監聽')
})

server.on('request', (req, res) => {
    let url = req.url
    let method = req.method
    if (url == '/register.html' && method == "GET") {
        console.log(`url: ${url}, method: ${method}`)
    } else {
        res.end('404')
    }
})

POST請求

注意點:

  • node支援大容量的引數傳遞, 它會分批接收引數, 接收引數會觸發兩個事件
  • req.on('data', (chunk)=>{}) 每接收一次引數就觸發一次, 接收到的chunk是字串格式
    • 收到的字串是 "鍵值對&鍵值對" 的格式, 比如name=aaa&age=13
    • 等於號前面的就是表單裡面的name, 等於號後面的就是裡面的值
  • req.on('end', ()=>{}) 當所有的引數接收完就會觸發end事件

JSON格式:

  1. 並列的資料之間用逗號(", ")分隔。
  2. 對映用冒號(": ")表示。
  3. 並列資料的集合(陣列)用方括號("[]")表示。
  4. 對映的集合(物件)用大括號("{}")表示。

什麼時候用陣列, 什麼時候用物件

陣列表示有序資料的集合,而物件表示無序資料的集合。如果資料的順序很重要,就用陣列,否則就用物件。

案例: 註冊功能實現

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <!-- method:指定請求方式,由於現在我要做註冊,說明是將客戶端的資料傳遞給伺服器,所以是post請求 -->
  <!-- action:當前表單提交到的目的地址,如果沒有設定,預設就是當前頁面 -->
  <!--  enctype="application/x-www-form-urlencoded":設定提交資料的編碼格式,ajax中使用 -->
  <form method="post" action="http://127.0.0.1:3003/register">
    <!-- name屬性的作用就是生成引數傳遞時的鍵,如果沒有鍵,是不可能生成引數傳遞的 -->
    <!--  -->
    姓名: <input type="text" id='name' name='name'><br>
    年齡: <input type="number" id="age" name='age'><br>
    <!-- type="submit":submit有預設的提交行為,以後避免使用它 -->
    <input type="submit" value="註冊">
  </form>
</body>
</html>
// 建立一個伺服器,能夠實現兩個功能
// 1.響應使用者註冊頁面
// 2.實現使用者註冊功能

const http = require('http')
const fs = require('fs')
const path = require('path')
const querystring = require('querystring')

const server = http.createServer()

server.listen(3003, () => {
    console.log('http://127.0.0.1:3003')
    //和表單的提交埠對應
    //<form method="post" action="http://127.0.0.1:3003/register">
})

server.on('request', (req, res) => {
    // res.writeHead(200, {
    //     'Content-Type': 'text/html;charset=utf-8'
    // })
    res.setHeader('Content-type', 'text/html;charset=UTF-8')

    let url = req.url
    let method = req.method

    if (method == 'GET' && (url == '/register' || url == '/')) {
        //返回頁面
        fs.readFile(path.join(__dirname, '/views/register.html'), (err, data) => {
            if (err) {
                console.log(err)
                throw err
            } else {
                console.log('響應get請求')
                res.end(data)
            }
        })
    } else if (method == 'POST' && url == '/register') {
        //接收資料
        console.log('響應post請求')
        let str = ''
        //node支援大容量的引數傳遞, 它會分批接收引數, 接收引數會觸發兩個事件
        //req.on('data', (chunk)=>{})  每接收一次引數就觸發一次, 接收到的chunk是字串格式
        //req.on('end', ()=>{}) 當所有的引數接收完就會觸發end事件
        req.on('data', (chunk) => {
            str += chunk
        })

        req.on('end', () => {
            console.log(str)
            //收到的字串是 "鍵值對&鍵值對" 的格式, 比如name=aaa&age=13
            //等於號前面的就是表單裡面的name, 等於號後面的就是裡面的值

            let obj = querystring.parse(str)
            //querystring.parse(字串) 這個方法可以把字串轉成物件

            //把物件儲存到json檔案
            //取, 加, 存
            fs.readFile(path.join(__dirname, './data/users.json'), 'utf-8', (err, data) => {
                if (err) {
                    console.log('404')
                } else {
                    let arr = JSON.parse(data)
                    console.log(arr)
                    /* 
                    JSON的格式:
                        1) 並列的資料之間用逗號(", ")分隔。
                        2) 對映用冒號(": ")表示。
                        3) 並列資料的集合(陣列)用方括號("[]")表示。
                        4) 對映的集合(物件)用大括號("{}")表示。
                    */
                    /* 
                     陣列表示有序資料的集合,而物件表示無序資料的集合。如果資料的順序很重要,就用陣列,否則就用物件。
                    */

                    arr.push(obj)

                    fs.writeFile(path.join(__dirname, './data/users.json'), JSON.stringify(arr, null, ' '), (err) => {
                        // JSON.stringify(arr, null, ' ') json格式化, 單引號裡面是空格
                        if (err) {
                            res.end('註冊失敗')
                            console.log(err)
                        } else {
                            res.end('註冊成功')
                        }
                    })
                }
            })
        })
    } else {
        res.end('註冊失敗')
    }
})

客戶端和伺服器互動總結

  1. 使用者開啟瀏覽器

  2. 位址列輸入我們需要訪問的網站網址(URL),包含協議,IP,埠,資源url

  3. 瀏覽器發起一個對這個 地址的 (請求

  4. 服務端監聽指定的 的伺服器軟體接收到這個請求,進行相應的處理(處理

  5. 服務端將處理完的結果返回給客戶端瀏覽器(響應