Ли Чэнси, старший инженер Tencent Cloud. После выпуска в 2014 году он присоединился к Tencent AlloyTeam и отвечал за такие проекты, как QQ group, Fantasia Live и Tencent Documents. Присоединился к команде разработчиков Tencent Cloud в 2018 году. Сосредоточьтесь на оптимизации производительности, инжиниринге и небольших программах.Вейбо | Знай почти | Github
Обзор концепции
На конференции разработчиков Nuggets в рекомендуемой практике я упомянул об использовании облачной функции.Мы можем классифицировать одни и те же операции, такие как управление пользователями и платежная логика, в облаке в зависимости от сходства бизнеса.В функции, удобнее управлять, устранять неполадки и делиться логикой. Даже если фоновая логика вашего апплета несложная и объем запросов не особо большой, вы можете сделать единый микросервис в облаке функционирующим и обрабатывать задачи по маршруту.
Это можно резюмировать на следующих трех картинках, давайте рассмотрим:
Например, вот традиционное использование облачных функций: одна облачная функция выполняет одну задачу и сильно отделена друг от друга.
Вторая архитектурная схема — попытаться классифицировать запросы.Облачная функция обрабатывает запросы определенного типа.Например, есть облачные функции, которые специально отвечают за обработку пользователей или специализируются на обработке платежей.
На последнем рисунке показано, что существует только одна облачная функция, и облачная функция имеет управление маршрутизацией, которое назначает задачи и назначает разные задачи разным локальным функциям для обработки.
tcb-router
Введение и использование
Чтобы облегчить каждому пробную версию, наша команда Tencent Cloud Tencent Cloud Base разработалаtcb-router, библиотека управления маршрутизацией облачных функций удобна для всех.
как использоватьtcb-router
Чтобы реализовать упомянутую выше архитектуру? Ниже я приведу примеры один за другим.
Архитектураtcb-router
- облачная функция
// 函数 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);
});
: классифицировать облачные функции по запросу. Этот тип архитектуры предназначен для классификации похожих запросов к одной и той же облачной функции для обработки, таких как облачные функции, которые можно разделить на управление пользователями, оплату и так далее.
- облачная функция
// 函数 user
const TcbRouter = require('tcb-router');
exports.main = async (event, context) => {
const app = new TcbRouter({ event });
app.router('register', async (ctx, next) => {
await next();
}, async (ctx, next) => {
await next();
}, async (ctx) => {
ctx.body = {
code: 0,
message: 'register success'
}
});
app.router('login', async (ctx, next) => {
await next();
}, async (ctx, next) => {
await next();
}, async (ctx) => {
ctx.body = {
code: 0,
message: 'login success'
}
});
return app.serve();
};
// 函数 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);
});
Архитектура третья:Все услуги обрабатываются одной облачной функцией
- облачная функция
// 函数 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.
Облачная функция, разработанная облаком апплета, в настоящее время более рекомендуетсяasync/await
Способ игры для обработки асинхронных операций, поэтому здесь также ссылка, основанная на том жеasync/await
Механизм реализации промежуточного программного обеспечения Koa2.
Из некоторых приведенных выше примеров мы можем видеть, что в основномuse
а такжеrouter
Два метода передаются в маршрутах и промежуточном программном обеспечении для соответствующей обработки.
use
Можно передать только одно промежуточное ПО, а маршрут может быть только строкой, обычно используемой для использования некоторого промежуточного ПО, которое могут использовать все маршруты.
// 不写路由表示该中间件应用于所有的路由
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
все просто передают информацию о маршрутизации и промежуточной программе через_addMiddleware
а также_addRoute
Два метода, введенные в_routerMiddlewares
Этот объект используется для последующих вызововserve
Когда слой промежуточного программного обеспечения для выполнения.
Наиболее важная рабочая логика промежуточного ПО находится вserve
а такжеcompose
Двумя способами.
serve
Основная функция здесь — выполнить сопоставление маршрутизации и после объединения промежуточного ПО передатьcompose
Перейдите к следующему шагу. Например, следующий фрагмент кода на самом деле является промежуточным программным обеспечением, которое будет соответствовать маршруту, и*
Промежуточное ПО этого подстановочного маршрута объединяется и выполняется последовательно.
let middlewares = (_routerMiddlewares[url]) ? _routerMiddlewares[url].middlewares : [];
// put * path middlewares on the queue head
if (_routerMiddlewares['*']) {
middlewares = [].concat(_routerMiddlewares['*'].middlewares, middlewares);
}
После объединения промежуточного программного обеспечения выполните этот раздел, чтобы положить промежуточное программное обеспечениеcompose
и вернуть функцию, передав в контекстеthis
, и наконецthis.body
значениеresolve
, то есть вообще в последнем middleware, черезctx.body
, чтобы реализовать возврат облачной функции в апплет:
const fn = compose(middlewares);
return new Promise((resolve, reject) => {
fn(this).then((res) => {
resolve(this.body);
}).catch(reject);
});
Такcompose
Как объединить эти промежуточные программы? Часть кода перехвачена здесь для анализа
function compose(middleware) {
/**
* ... 其它代码
*/
return function (context, next) {
// 这里的 next,如果是在主流程里,一般 next 都是空。
let index = -1;
// 在这里开始处理处理第一个中间件
return dispatch(0);
// dispath 是核心的方法,通过不断地调用 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);
}
}
}
}
Прочитав код здесь, я на самом деле немного запутался, как пройтиPromise.resolve(handler(xxxx))
Может ли такая логика кода ускорить вызов промежуточного программного обеспечения?
Во-первых, мы знаем, чтоhandler
На самом делеasync function
,next
,то естьdispatch.bind(null, i + 1)
Например это:
async (ctx, next) => {
await next();
}
И мы знаем,dispath
ВозвращениеPromise.resolve
ИлиPromise.reject
async function
выполнятьawait next()
, что эквивалентно запуску следующего вызова ПО промежуточного слоя.
когдаcompose
После завершения он все равно вернетfunction (context, next)
, так что переходим к следующей логике, выполняемfn
и пройти в контекстеthis
После этого присвойте значение, назначенное в промежуточном программном обеспеченииthis.body
resolve
В конечном итоге оно станет значением, которое будет возвращено номером облачной функции.
const fn = compose(middlewares);
return new Promise((resolve, reject) => {
fn(this).then((res) => {
resolve(this.body);
}).catch(reject);
});
ВидетьPromise.resolve
Одинasync function
, многие люди будут сбиты с толку. На самом деле удалитьnext
Логика вызова поперечного программного обеспечения, мы можем упростить логику к следующему примеру:
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