午夜91福利视频,午夜成人在线观看,午夜在线视频免费观看,午夜福利短视频,精品午夜成人免费视频APP

小程序模板網

小程序·云開發的云函數路由高級玩法

發布時間(jian):2018-09-26 08:39 所屬欄目:小程序開發教程
李成熙,騰訊云高級工程師。2014年度畢業加入騰訊AlloyTeam,先后負責過QQ群、花樣直播、騰訊文檔等項目。2018年加入騰訊云云開發團隊。專注于性能優化、工程化和小程序服務。微博 | 知乎 | Github

概念回顧

在(zai)掘金(jin)開發者大會上,在(zai)推薦實踐那里,我有提到一(yi)種云(yun)函數(shu)的用法,我們可以(yi)將(jiang)相同的一(yi)些操作,比如(ru)用戶管理(li)、支付邏(luo)(luo)輯,按照業(ye)務(wu)的相似(si)性,歸類到一(yi)個云(yun)函數(shu)里,這樣比較(jiao)方便(bian)管理(li)、排查問題(ti)以(yi)及邏(luo)(luo)輯的共(gong)享。甚(shen)至如(ru)果你(ni)的小程序的后臺邏(luo)(luo)輯不(bu)復雜,請求量不(bu)是特(te)別(bie)大,完全(quan)可以(yi)在(zai)云(yun)函數(shu)里面做一(yi)個單一(yi)的微服務(wu),根據路由(you)來處理(li)任務(wu)。

用下面三幅圖可(ke)以概括,我們來回顧一(yi)下:

比如(ru)這里(li)就是傳統的云函(han)數用法,一個云函(han)數處理一個任務,高度解(jie)耦。

第二(er)幅架構圖就是嘗試將請(qing)求歸(gui)類(lei),一個云函數處(chu)理某(mou)一類(lei)的(de)請(qing)求,比如有(you)專(zhuan)(zhuan)門負(fu)責處(chu)理用戶的(de),或者專(zhuan)(zhuan)門處(chu)理支(zhi)付的(de)云函數。

最后一(yi)幅圖顯示這里只有(you)一(yi)個云(yun)函數(shu),云(yun)函數(shu)里有(you)一(yi)個分派任務的(de)(de)路由管理,將不(bu)(bu)同的(de)(de)任務分配(pei)給不(bu)(bu)同的(de)(de)本地函數(shu)處理。

tcb-router 介紹及用法

為了(le)方便大家(jia)試用,咱們(men)騰訊云(yun) Tencent Cloud Base 團隊開發了(le) tcb-router,云(yun)函數路由(you)管理庫方便大家(jia)使用。

那具(ju)體怎(zen)么使(shi)用 tcb-router 去實現上面提到的架構呢(ni)?下面我會逐(zhu)一舉例子。

架構一:一個云函數處理一個任務
這種(zhong)架構下,其實不(bu)需要(yao)用到 tcb-router,像普(pu)通(tong)那樣寫好(hao)云函(han)數,然(ran)后在小程序端調用就可(ke)以了。

  • 云函數
// 函數 router
exports.main = (event, context) => {
    return {
        code: 0,
        message: 'success'
    };
};
小程序端
wx.cloud.callFunction({
      name: 'router',
      data: {
        name: 'tcb',
        company: 'Tencent'
      }
    }).then((res) => {
      console.log(res);
    }).catch((e) => {
      console.log(e);
});

架構二: 按請求給云函數歸類
此類(lei)架構就是將相似的請求歸類(lei)到同一(yi)個云(yun)函數(shu)處理(li),比如(ru)可以分為(wei)用戶管理(li)、支付(fu)等(deng)等(deng)的云(yun)函數(shu)。

  • 云函數
// 函數 pay
const TcbRouter = require('tcb-router');

exports.main = async (event, context) => {
    const app = new TcbRouter({ event });
    
    app.router('makeOrder', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'make order success'
        }
    });

    app.router('pay', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'pay success'
        }
    });

    return app.serve();
};
  • 小程序端
// 注冊用戶
wx.cloud.callFunction({
      name: 'user',
      data: {
        $url: 'register',
        name: 'tcb',
        password: '09876'
      }
    }).then((res) => {
      console.log(res);
    }).catch((e) => {
      console.log(e);
});

// 下單商品
wx.cloud.callFunction({
      name: 'pay',
      data: {
        $url: 'makeOrder',
        id: 'xxxx',
        amount: '3'
      }
    }).then((res) => {
      console.log(res);
    }).catch((e) => {
      console.log(e);
});

架構(gou)三: 由一個云(yun)函數(shu)處理所有服務(wu)

  • 云函數
// 函數 router
const TcbRouter = require('tcb-router');

exports.main = async (event, context) => {
    const app = new TcbRouter({ event });
    
    app.router('user/register', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'register success'
        }
    });

    app.router('user/login', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'login success'
        }
    });

    app.router('pay/makeOrder', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'make order success'
        }
    });

    app.router('pay/pay', async (ctx, next) => {
        await next();
    }, async (ctx, next) => {
        await next();
    }, async (ctx) => {
        ctx.body = {
            code: 0,
            message: 'pay success'
        }
    });

    return app.serve();
};
  • 小程序端
// 注冊用戶

wx.cloud.callFunction({
      name: 'router',
      data: {
        $url: 'user/register',
        name: 'tcb',
        password: '09876'
      }
    }).then((res) => {
      console.log(res);
    }).catch((e) => {
      console.log(e);
});

// 下單商品
wx.cloud.callFunction({
      name: 'router',
      data: {
        $url: 'pay/makeOrder',
        id: 'xxxx',
        amount: '3'
      }
    }).then((res) => {
      console.log(res);
    }).catch((e) => {
      console.log(e);
});

借鑒 Koa2 的中間件機制實現云函數的路由管理

小程序·云(yun)開發的云(yun)函數目(mu)前更推薦(jian) async/await 的玩(wan)法來處理異(yi)步操作,因此這里也(ye)參考了同(tong)樣是基于 async/await 的 Koa2 的中間(jian)件實現(xian)機制。

從(cong)上面的(de)一些(xie)例(li)子我(wo)們可(ke)以看出,主要是通過 use 和 router 兩種(zhong)方法(fa)傳入(ru)路由以及(ji)相關(guan)處理的(de)中間件(jian)。

use 只能(neng)(neng)傳入一個中間件(jian),路由也只能(neng)(neng)是(shi)字(zi)符串,通常用于 use 一些所有路由都得(de)使用的中間件(jian)

// 不寫路由表示該中間件應用于所有的路由

app.use(async (ctx, next) => {

});

app.use('router', async (ctx, next) => {

});
router 可以傳一個或多個中間件,路由也可以傳入一個或者多個。

app.router('router', async (ctx, next) => {

});

app.router(['router', 'timer'], async (ctx, next) => {
    await next();
}, async (ctx, next) => {
    await next();
}, async (ctx, next) => {

});

不過,無論是 use 還是 router,都只(zhi)是將路由和(he)中間件信息,通過 _addMiddleware 和(he) _addRoute 兩(liang)個方法,錄入到 _routerMiddlewares 該對象(xiang)中,用(yong)于后續(xu)調用(yong) serve 的時(shi)候(hou),層(ceng)層(ceng)去執行中間件。

最(zui)重要的運行中間件邏(luo)輯,則是(shi)在 serve 和 compose 兩個方法里。

serve 里主(zhu)要(yao)的(de)作用是做路(lu)由(you)的(de)匹配(pei)以及將中(zhong)間(jian)件(jian)組合好之后(hou)(hou),通(tong)(tong)過(guo) compose 進行下(xia)(xia)一步的(de)操(cao)作。比如以下(xia)(xia)這段(duan)節(jie)選(xuan)的(de)代碼,其實是將匹配(pei)到(dao)的(de)路(lu)由(you)的(de)中(zhong)間(jian)件(jian),以及 * 這個通(tong)(tong)配(pei)路(lu)由(you)的(de)中(zhong)間(jian)件(jian)合并到(dao)一起(qi),最(zui)后(hou)(hou)依次執(zhi)行。

let middlewares = (_routerMiddlewares[url]) ? _routerMiddlewares[url].middlewares : [];
// put * path middlewares on the queue head
if (_routerMiddlewares['*']) {
    middlewares = [].concat(_routerMiddlewares['*'].middlewares, middlewares);
}

組合(he)好(hao)中間(jian)件后,執行這一(yi)(yi)段,將(jiang)中間(jian)件 compose 后并返(fan)回(hui)一(yi)(yi)個函數,傳(chuan)入(ru)上下文 this 后,最(zui)后將(jiang) this.body 的值(zhi) resolve,即一(yi)(yi)般(ban)在最(zui)后一(yi)(yi)個中間(jian)件里,通過對 ctx.body 的賦值(zhi),實現云函數的對小程(cheng)序端的返(fan)回(hui):

const fn = compose(middlewares);

return new Promise((resolve, reject) => {
    fn(this).then((res) => {
        resolve(this.body);
    }).catch(reject);
});

那(nei)么 compose 是怎么組合(he)好這(zhe)些中(zhong)間件的呢?這(zhe)里截(jie)取(qu)部份代碼進行分析(xi)

function compose(middleware) {
    /**
     * ... 其它代碼 
     */
    return function (context, next) {
        // 這里的 next,如果是在主流程里,一般 next 都是空。
        let index = -1;

        // 在這里開始處理處理第一個中間件
        return dispatch(0);

        // dispatch 是核心的方法,通過不斷地調用 dispatch 來處理所有的中間件
        function dispatch(i) {
            if (i <= index) {
                return Promise.reject(new Error('next() called multiple times'));
            }

            index = i;

            // 獲取中間件函數
            let handler = middleware[i];

            // 處理完最后一個中間件,返回 Proimse.resolve
            if (i === middleware.length) {
                handler = next;
            }

            if (!handler) {
                return Promise.resolve();
            }

            try {
                // 在這里不斷地調用 dispatch, 同時增加 i 的數值處理中間件
                return Promise.resolve(handler(context, dispatch.bind(null, i + 1)));
            }
            catch (err) {
                return Promise.reject(err);
            }
        }
    }
}

看完(wan)這(zhe)里的代碼,其實有點疑惑,怎么通過 Promise.resolve(handler(xxxx)) 這(zhe)樣的代碼邏(luo)輯可(ke)以推進(jin)中間件的調用呢(ni)?

首(shou)先(xian),我(wo)們(men)知道,handler 其實就(jiu)(jiu)是(shi)一個 async function,next,就(jiu)(jiu)是(shi) dispatch.bind(null, i + 1) 比如這(zhe)個:

async (ctx, next) => {
    await next();
}

而我(wo)們知道(dao),dispatch 是返(fan)回一(yi)個(ge) Promise.resolve 或者(zhe)一(yi)個(ge) Promise.reject,因(yin)此在(zai) async function 里執行(xing) await next(),就相當(dang)于(yu)觸發下一(yi)個(ge)中(zhong)間件的(de)調用。

當 compose 完(wan)成(cheng)后(hou),還是會(hui)返回一個 function (context, next),于(yu)是就走到下面這個邏輯,執行 fn 并傳入上下文 this 后(hou),再將在中(zhong)間件中(zhong)賦值的 this.body resolve 出來,最(zui)終(zhong)就成(cheng)為云(yun)函數數要(yao)返回的值。

const fn = compose(middlewares);

return new Promise((resolve, reject) => {
    fn(this).then((res) => {
        resolve(this.body);
    }).catch(reject);
});

看(kan)到 Promise.resolve 一個 async function,許多人(ren)都會很困(kun)惑。其實撇除 next 這(zhe)個往下調用中間件(jian)的(de)邏輯,我們(men)可以(yi)很好地(di)將邏輯簡化成下面這(zhe)段示例:

let a = async () => {
    console.log(1);
};

let b = async () => {
    console.log(2);

    return 3;
};


let fn = async () => {
    await a();
    return b();
};

Promise.resolve(fn()).then((res) => {
    console.log(res);
});

// 輸出
// 1
// 2
// 3


易優小程序(企業版)+靈活api+前后代(dai)碼開(kai)源(yuan) 碼(ma)云倉庫:
本文地址://www.jinyoudianli.com/wxmini/doc/course/24839.html 復制鏈接 如需定制請聯系易(yi)優客(ke)服咨詢:

工作日 8:30-12:00 14:30-18:00
周六及部(bu)分節假日(ri)提供值(zhi)班服務(wu)

易小優
轉(zhuan)人工 ×