欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS如何實(shí)現(xiàn)基于websocket的多端橋接平臺(tái)

 更新時(shí)間:2021年05月14日 15:31:18   作者:蚊子博客  
我們?cè)谡{(diào)試過程使用的工具有:modheader,postman等,但這些工具都會(huì)存在的問題:缺少客戶端里相應(yīng)的設(shè)備信息;即使將cookie信息復(fù)制出來,也是存在過期的問題;多個(gè)設(shè)備之間切換時(shí)不方便;針對(duì)這些存在的問題,我基于websocket雙向通信的特點(diǎn),實(shí)現(xiàn)了多端橋接管理平臺(tái)

1. 要調(diào)試什么

我們主要要知道調(diào)試什么,最終回去到什么樣子的結(jié)果:

1.調(diào)試接口,傳入接口地址,即可獲取對(duì)應(yīng)的結(jié)果;并且可以同時(shí)調(diào)試多個(gè)設(shè)備;

2.調(diào)試 jsapi,輸入對(duì)應(yīng)的方法,則即可在新聞客戶端中展示出效果。

在調(diào)試接口方面,其實(shí)我們有一種方法可以方便地進(jìn)行調(diào)試,但有兩個(gè)限制條件:Android系統(tǒng)和測(cè)試版的客戶端,這樣通過 Chrome 瀏覽器進(jìn)行橋接。但這種方式,在 iOS 系統(tǒng)和正式版的客戶端中,就失效了。

2. websocket 的特性

WebSocket 協(xié)議的最大特點(diǎn)就是,服務(wù)器可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是真正的雙向平等對(duì)話,屬于服務(wù)器推送技術(shù)的一種。

其他特點(diǎn)包括:

1.建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。

2.與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是 80 和 443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器。

3.數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。

4.可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。

5.沒有同源限制,客戶端可以與任意服務(wù)器通信。

6.協(xié)議標(biāo)識(shí)符是 ws(如果加密,則為 wss),服務(wù)器網(wǎng)址就是 URL。

3. 建立 socket 連接

為了滿足我們?cè)诘?1 部分設(shè)置的調(diào)試目標(biāo),我們這里要實(shí)現(xiàn)的功能有:

1.PC 端相當(dāng)于房主,建立房間后,其他設(shè)備可以進(jìn)入到該房間,一個(gè)設(shè)備只能進(jìn)入到一個(gè)房間中;

2.客戶端有斷線重連的機(jī)制,當(dāng)客戶端斷開連接后,可以嘗試重連;

3.服務(wù)端維護(hù)一個(gè)心跳檢測(cè)的機(jī)制,當(dāng)有新設(shè)備進(jìn)入或者之前的設(shè)備退出時(shí),要及時(shí)地更新當(dāng)前房間中的設(shè)備列表;

3.1 如何創(chuàng)建房間

在瀏覽器上輸入房間的標(biāo)識(shí),若瀏覽器與服務(wù)端成功建立起 websocket 連接后,則在瀏覽器端創(chuàng)建對(duì)應(yīng)的二維碼。用微信/手 Q 或者其他掃描二維碼的設(shè)備進(jìn)行掃描,即可通過提前設(shè)定的 scheme 協(xié)議,跳轉(zhuǎn)到新聞客戶端里對(duì)應(yīng)的調(diào)試頁面。

若客戶端里也與服務(wù)端成功建立 websocket 連接后,則相當(dāng)于進(jìn)入房間成功,PC 端會(huì)出現(xiàn)一個(gè)對(duì)應(yīng)的圖標(biāo)。

ws.open(serverId)
    .then(() => {
        // PC 端成功建立連接后

        setStatus("linked"); // 更新頁面的狀態(tài)
        // 生成二維碼
        qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
            setCodeUrl(url);
        });
    })
    .catch(e => {
        // 建立連接失敗
        console.error(e);
        Modal.error({ title: "當(dāng)前服務(wù)器出現(xiàn)問題啦,正在搶修中" });
        setStatus("unlink");
    });

3.2 客戶端的斷線重現(xiàn)機(jī)制

在移動(dòng)端中的頁面有個(gè)特點(diǎn),當(dāng)屏幕黑屏后,或者因?yàn)槠渌脑?,客戶端?huì)自動(dòng)斷開 socket 連接。

為了方便進(jìn)行調(diào)試,而不是每次在斷開連接后,需要手動(dòng)點(diǎn)擊,或者重新進(jìn)入頁面。我在這里實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的斷線重連機(jī)制。websocket 連接斷開時(shí),會(huì)執(zhí)行onclose的回調(diào),因此,我們可以在 onclose 事件中進(jìn)行再次重連的機(jī)制。

同時(shí),為了防止無限制的重連嘗試,我在這里也進(jìn)行了下限制,最多重連 3 次,3 次后還沒有重新連接上,則停止連接;若重連成功,則將重連次數(shù)重置為 3。

斷開連接時(shí):

// 斷開連接時(shí)
ws.onclose(() => {
    timer = setTimeout(() => {
        setStatus("unlink");
        setCodeUrl("");
    }, 500);

    reconnectNum--;
    // 限制重連的次數(shù)
    if (reconnectNum >= 0) {
        _open(); // 嘗試重新連接
    }
});

連接成功時(shí):

ws.open(serverId).then(() => {
    // PC 端成功建立連接后
    +reconnectNum = 3;
    +timer && clearTimeout(timer);

    setStatus("linked"); // 更新頁面的狀態(tài)
    // 生成二維碼
    qrcode(`/tools/index.html#/newslist?serverId=${serverId}`).then(url => {
        setCodeUrl(url);
    });
});

3.3 心跳檢測(cè)

就像我們?cè)?QQ 群里聊天一樣,哪個(gè)人在線要一目了然,若有人進(jìn)入到聊天群,或者有人退出了,都要通知房主,并及時(shí)地更新群列表。

心跳檢測(cè)主要有 2 種方式:客戶端發(fā)起的心跳檢測(cè)和服務(wù)端維護(hù)的心跳檢測(cè)。我們稍微講解下這兩種:

1.客戶端發(fā)起的心跳:每隔一段固定的時(shí)間,向服務(wù)器端發(fā)送一個(gè) ping 數(shù)據(jù),如果在正常的情況下,服務(wù)器會(huì)返回一個(gè) pong 給客戶端,如果客戶端通過 onmessage 事件能監(jiān)聽到的話,說明請(qǐng)求正常。

2.服務(wù)端維護(hù)的心跳:每隔一段時(shí)間,檢測(cè)所有連接的狀態(tài),若狀態(tài)為斷開時(shí),則將其從列表中剔除。

我在這里使用的是服務(wù)端維護(hù)的心跳檢測(cè),當(dāng)房間里的設(shè)備數(shù)量發(fā)生變化時(shí),則服務(wù)端向客戶端推送最新的設(shè)備列表:

// 持續(xù)監(jiān)測(cè)客戶端的連接狀態(tài)
// 若已斷開連接,則將客戶端清除
let aliveClients = new Map();
let lastAliveLength = new Map();
setInterval(() => {
    let clients = {};
    wss.clients.forEach(function each(ws) {
        if (ws.isAlive === false) {
            return ws.terminate();
        }
        const serverId = ws.serverId;
        if (clients[serverId]) {
            clients[serverId].push(ws);
        } else {
            clients[serverId] = [ws];
        }

        ws.isAlive = false;
        ws.ping(() => {});
    });
    for (let serverId in clients) {
        aliveClients.set(serverId, clients[serverId]);
        const length = clients[serverId].length;

        // 若當(dāng)前serverId連接的設(shè)備數(shù)量發(fā)生變化,則發(fā)送消息
        if (length !== lastAliveLength.get(serverId)) {
            // 想當(dāng)前所有serverId的設(shè)備發(fā)送消息
            sendAll("devices", clients[serverId], serverId);

            // 存儲(chǔ)上次當(dāng)前serverId的連接數(shù)
            lastAliveLength.set(serverId, length);
        }
    }

    const size = wss.clients.size;

    console.log("connection num: ", size, new Date().toTimeString());
}, 2000);

4. 進(jìn)行接口的調(diào)試

我們?cè)诘?3 節(jié)已經(jīng)成功把 PC 端和新聞客戶端連接起來了,那么怎么進(jìn)行雙端數(shù)據(jù)的通信?

4.1 接口的調(diào)試

我們?cè)谶@里要傳入 3 個(gè)字段:

1.serverId: 即房間號(hào),服務(wù)端要將信息廣播給所有帶有 serverId 的成員;

2.type: 類型,這條指令是要做什么的;

3.msg: 傳入的參數(shù);

在接口調(diào)試的過程中,則傳入的參數(shù)是:

const params = {
    type: "post", // 類型
    msg: {
        // 參數(shù)
        url: "https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336"
    }
};

當(dāng)客戶端正常完成接口的請(qǐng)求后,則將接口結(jié)果、cookie 和設(shè)備信息等返回到 PC 端:

// 請(qǐng)求的方法
const post = url => {
    if (window.TencentNews && window.TencentNews.post) {
        window.TencentNews.post(url, {}, window[id], { loginType: "qqorweixin" }, {});
    } else if (window.TencentNews && window.TencentNews.postData) {
        window.TencentNews.postData(url, '{"a":"b"}', id, "requestErrorCallback");
    }
};

// 移動(dòng)端向服務(wù)端發(fā)起的數(shù)據(jù)
ws.send({
    type: "postCb", // 執(zhí)行的結(jié)果
    msg: {
        method: "post",
        result,
        cookie: document.cookie,
        appInfo
    }
});

這樣就能在前端展示出結(jié)果了,而且是真實(shí)的數(shù)據(jù)請(qǐng)求。

4.2 歷史記錄的存儲(chǔ)

歷史記錄這塊,我們周邊的同學(xué)在試用的過程中,還是非常迫切需要的需求。要不然每次要測(cè)試之前的接口地址時(shí),都需要重新輸入或者粘貼,非常不方便。

我們把用戶請(qǐng)求的 URL、返回的結(jié)果、cookie、設(shè)備信息等比較完整的信息存儲(chǔ)到 boss 中,而本地只存儲(chǔ)歷史的 URL,當(dāng)用戶需要再次測(cè)試之前的接口時(shí),點(diǎn)擊一下即可。若需要查看之前調(diào)試的接口,可以去鷹眼上進(jìn)行查看。

本地采用的是localStorage的方式進(jìn)行存儲(chǔ)。還有更重要的是,我們也使用mobx的響應(yīng)式工具,能夠在用戶完成這次請(qǐng)求后,馬上在側(cè)邊的歷史記錄里看到結(jié)果。

5. 新聞客戶端內(nèi) jsapi 的調(diào)試

除了可以調(diào)試接口外,還可以進(jìn)行一些新聞客戶端內(nèi)的 jsapi 調(diào)試。我們新聞客戶端的 jsapi 有兩種調(diào)用的方式:

// 直接調(diào)用
window.TencentNews.login("qqorweixin", isLogined => console.log(isLogined));

// invoke方式調(diào)用
window.TencentNews.invoke("login", "qqorweixin", isLogined => console.log(isLogined));

這里我選擇了使用invoke的方式來調(diào)用 jsapi。

PC 端發(fā)起 jsapi 的調(diào)用:

ws.send({
    type: "call",
    msg: {
        method: method,
        params: slice.call(arguments)
    }
});

移動(dòng)端在收到服務(wù)端發(fā)過來的請(qǐng)求后,進(jìn)行 jsapi 的調(diào)用,并將執(zhí)行的結(jié)果返回到 PC 端即可:

const handleNewsApi = async (msg: any): Promise<any> => {
    await tencentReady();

    const { method, params } = msg;
    return new Promise(resolve => {
        window.TencentNews.invoke(method, ...params, (result: any) => {
            resolve({ method, result });
        });
    });
};

6. 總結(jié)

到這里,我的“基于 websocket 的多端橋接平臺(tái)”基本上已經(jīng)構(gòu)建完畢了。不過還是有 2 個(gè)問題要簡(jiǎn)要的說明下。

6.1 為什么要手動(dòng)輸入 serverId

最開始想著用戶創(chuàng)建房間時(shí),由系統(tǒng)隨機(jī)產(chǎn)生一個(gè) uuid,但后來想,如果用戶刷新頁面了,這個(gè) uuid 就會(huì)發(fā)生變化,導(dǎo)致無法連接到之前的 uuid,所以這里就換成了手動(dòng)輸入。

6.2 如何保證一個(gè)客戶端的 socket 請(qǐng)求都進(jìn)入到同一個(gè)進(jìn)程中

當(dāng)我們后臺(tái)采用多個(gè)進(jìn)程時(shí),若用戶的請(qǐng)求我們不做干預(yù),會(huì)造成請(qǐng)求的隨機(jī)訪問,產(chǎn)生 400 的請(qǐng)求,畢竟最開始連接在 A 進(jìn)程中,現(xiàn)在發(fā)起的請(qǐng)求到 B 進(jìn)程中,B 進(jìn)程不知道怎么處理了。

這里有多種方式可以進(jìn)行處理:

方法 介紹 優(yōu)點(diǎn) 缺點(diǎn)
一致性 hash 算法 所有的主機(jī)和連接都分配到 0 ~ 2^32-1 的虛擬圓中 1. 適用在大規(guī)模的應(yīng)用;
2. 某個(gè)主機(jī)或者進(jìn)程掛掉后,影響小
實(shí)現(xiàn)比較復(fù)雜
nginx 分配 自帶的 ip_hash 可實(shí)現(xiàn)負(fù)載均衡;
同一 ip 會(huì)被分配給固定的后端服務(wù)器
配置方便 可能會(huì)集中到某個(gè)進(jìn)程中

我這里的平臺(tái)是內(nèi)部的調(diào)試平臺(tái),用戶量不大,殺雞焉用牛刀,而且我們只有一臺(tái)機(jī)器,因此我們考慮的是同一個(gè) IP 進(jìn)入到同一個(gè)進(jìn)程中。這里我借用里 nginx 中的 ip_hash 思想:當(dāng)請(qǐng)求來到主進(jìn)程后,我這里對(duì) IP 進(jìn)行加權(quán)計(jì)算后,然后按照進(jìn)程的個(gè)數(shù)進(jìn)行取模。

顯然這種方式也有可能存在一個(gè)進(jìn)程中 socket 連接過多的問題,不過在用戶量不多的時(shí)候完全可以接受(針對(duì)這個(gè)問題我也考慮了別的方法,例如瀑布流的方式,每次給子進(jìn)程分配連接的時(shí)候,都首先獲取到連接數(shù)最少的那個(gè)進(jìn)程,然后連接分配給這個(gè)進(jìn)程,不過還要維護(hù)一個(gè)表,每次都要計(jì)算)。

6.3 多進(jìn)程之間的通信

同一個(gè)房間里,當(dāng) PC 端的 socket 連接和多個(gè)移動(dòng)端的連接不在同一個(gè)進(jìn)程中時(shí),就會(huì)存在跨進(jìn)程的問題。一個(gè)極端的例子,每個(gè) socket 連接都在不同的進(jìn)程中,那么就要考慮如何通知其他的進(jìn)程,需要給客戶端發(fā)送請(qǐng)求了。

比較簡(jiǎn)單的方式利用我們的機(jī)制,每個(gè) PC 端的用戶就是房主,可以創(chuàng)建一個(gè)房間,移動(dòng)設(shè)備就是房間中的成員,每個(gè)房間都是獨(dú)立的,互不干擾。這樣我們把房間里所有的 socket 連接,通過房間的標(biāo)識(shí),都放到同一個(gè)進(jìn)程中,這樣就沒有跨進(jìn)程的問題了。但這種方式存在的一個(gè)問題是:一個(gè)房間里的連接過多時(shí),都需要這同一個(gè)進(jìn)程來承擔(dān),而別的進(jìn)程卻閑著的。

還有可以使用 redis:利用 redis 的發(fā)布/訂閱者模式,將當(dāng)前進(jìn)程中的房間標(biāo)識(shí)和信息廣播到其他的進(jìn)程中,其他進(jìn)程中有相同房間標(biāo)識(shí)的 socket 連接,進(jìn)行相應(yīng)的操作。

以上就是JS如何實(shí)現(xiàn)基于websocket的多端橋接平臺(tái)的詳細(xì)內(nèi)容,更多關(guān)于JS基于websocket的多端橋接平臺(tái)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • js 距離某一時(shí)間點(diǎn)時(shí)間是多少實(shí)現(xiàn)代碼

    js 距離某一時(shí)間點(diǎn)時(shí)間是多少實(shí)現(xiàn)代碼

    距離某一時(shí)間點(diǎn)時(shí)間是多少,在本文將為大家介紹下js中是如何實(shí)現(xiàn)的,感興趣的朋友不要錯(cuò)過
    2013-10-10
  • JS實(shí)現(xiàn)漂亮的淡藍(lán)色滑動(dòng)門效果代碼

    JS實(shí)現(xiàn)漂亮的淡藍(lán)色滑動(dòng)門效果代碼

    這篇文章主要介紹了JS實(shí)現(xiàn)漂亮的淡藍(lán)色滑動(dòng)門效果代碼,涉及JavaScript通過自定義函數(shù)遍歷頁面元素及動(dòng)態(tài)設(shè)置元素屬性的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • JS實(shí)現(xiàn)點(diǎn)擊鏈接切換顯示隱藏內(nèi)容的方法

    JS實(shí)現(xiàn)點(diǎn)擊鏈接切換顯示隱藏內(nèi)容的方法

    這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊鏈接切換顯示隱藏內(nèi)容的方法,涉及javascript鼠標(biāo)事件響應(yīng)及頁面元素屬性動(dòng)態(tài)變換相關(guān)操作技巧,需要的朋友可以參考下
    2017-10-10
  • 基于JavaScript實(shí)現(xiàn)頁面輪播圖漸變效果的示例代碼

    基于JavaScript實(shí)現(xiàn)頁面輪播圖漸變效果的示例代碼

    這篇文章主要給大家分享如何使用JavaScript實(shí)現(xiàn)一個(gè)頁面輪播圖漸變效果,輪播圖是網(wǎng)頁開發(fā)中常見的功能之一,它能夠展示多個(gè)圖片或內(nèi)容,并以一定的時(shí)間間隔進(jìn)行自動(dòng)切換,而通過添加漸變效果,可以讓切換過程更加平滑流暢,感興趣的小伙伴可以自己動(dòng)手嘗試一下
    2023-10-10
  • webpack常用構(gòu)建優(yōu)化策略小結(jié)

    webpack常用構(gòu)建優(yōu)化策略小結(jié)

    這篇文章主要介紹了webpack常用構(gòu)建優(yōu)化策略小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 在JavaScript中使用for循環(huán)的方法

    在JavaScript中使用for循環(huán)的方法

    這篇文章主要介紹了如何在JavaScript中使用for循環(huán),通過使用JavaScript?for...in循環(huán),我們可以循環(huán)對(duì)象的鍵或?qū)傩?,在迭代?duì)象屬性或進(jìn)行調(diào)試時(shí),它可能很有用,但在迭代數(shù)組或?qū)?duì)象進(jìn)行修改時(shí),應(yīng)該避免使用for...in循環(huán),需要的朋友可以參考下
    2022-11-11
  • JavaScript DOMContentLoaded事件案例詳解

    JavaScript DOMContentLoaded事件案例詳解

    這篇文章主要介紹了JavaScript DOMContentLoaded事件案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • JavaScript性能優(yōu)化 創(chuàng)建文檔碎片(document.createDocumentFragment)

    JavaScript性能優(yōu)化 創(chuàng)建文檔碎片(document.createDocumentFragment)

    講這個(gè)方法之前,我們應(yīng)該先了解下插入節(jié)點(diǎn)時(shí)瀏覽器會(huì)做什么。
    2010-07-07
  • js實(shí)現(xiàn)導(dǎo)航欄上下動(dòng)畫效果

    js實(shí)現(xiàn)導(dǎo)航欄上下動(dòng)畫效果

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)導(dǎo)航欄上下動(dòng)畫效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Web?Components入門教程示例詳解

    Web?Components入門教程示例詳解

    這篇文章主要為大家介紹了Web?Components入門教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05

最新評(píng)論