如何将 firebase 队列用作应用程序的后端

Firebase 提供后端即服务,因为 applciation 开发人员无法选择后端代码。

此示例显示如何使用 firebase 队列创建后端,该后端将在 firebase 数据库的顶部运行,并作为前端应用程序的后端。

在进入代码之前,让我们了解架构,它将如何工作。为简洁起见,我们假设我们将网站用作前端,将 NodeJs 服务器用作后端

先决条件

  1. 使用你的 Google 帐户创建 firebase 应用程序

  2. 将 firebase 添加到你的网页。使用 bower install firebase --save

  3. 使用新创建的 firebase 帐户创建服务帐户(设置 - >权限 - >服务帐户 - >创建服务帐户 - >(指定名称并选中提供新的私钥复选框) - >保存 json 文件,我们需要那个以后。

  4. 配置可以在你喜欢的环境中托管的 NodeJs 服务器

  5. queue/specs 中创建以下端点

    “request_response”:

     {
         "error_state": "request_error_processing",
         "finished_state": "finished_state",
         "in_progress_state": "request_in_progress",
         "start_state": "request_started"
     }
    
  6. 在 NodeJs 服务器内部安装 firebase 服务器端版本 npm install firebase --save,并使用我们从步骤 3 获得的 json 文件初始化你的服务帐户,它看起来像这样

    firebase.initializeApp({serviceAccount:’。/ your_file.json’,databaseURL:‘get_from_firebase_account’});

建筑

以下是整个循环的工作原理。

在前端,你会做这些步骤

  1. 使用 firebase web sdk,你将请求直接写入端点’queue / tasks’中的 firebase 数据库,然后调用你要发送到后端的请求。
  2. 插入任务后,你将在端点 queue/tasks/{taskKey} 上注册侦听器,当后端完成处理你的请求时会调用该侦听器,在上面的任务中写入响应

在后端,你会做这些步骤

  1. 创建无限侦听端点’队列/任务’的服务器
  2. 处理你的任务并在 queue/tasks/response 中写回响应数据
  3. 删除任务

首先创建这个辅助函数,它提供了一种处理回调和承诺的方法

function createPromiseCallback() {
    var cb;
    var promise = new Promise(function (resolve, reject) {
        cb = function (err, data) {
            if (err) return reject(err);
            return resolve(data);
        };
    });
    cb.promise = promise;
    return cb;
}

在前端,你将有这个功能

function sendRequest(kind, params, cb) {

    cb = cb || createPromiseCallback();
    var requestObject = {
        kind: kind,
        params: params
    };
    var tasksRef = firebase.database().ref('queue/tasks');

    var requestKey = tasksRef.push().key;

    var requestRef = tasksRef.child(requestKey);

    function requestHandshake(snap) {
        if (snap && snap.exists() && (snap.val().response || snap.val()._state ===    config.firebase.task.finishState || snap.val()._error_details)) {
            var snapVal = snap.val();
            if (snapVal._error_details) {
                cb(snapVal._error_details.error);
            } else {
            cb(null, snapVal.response);
        }
        requestRef.off('value', requestHandshake);
    }
   }

   var bulkUpdate = {};
   bulkUpdate['queue/tasks/' + requestKey + '/request'] = requestObject;
   bulkUpdate['queue/tasks/' + requestKey + '/_state'] = config.firebase.task.startState;

   firebase.database().ref().update(bulkUpdate)
    .then(function (snap) {
        requestRef.on('value', requestHandshake);
    }).catch(function (err) {
        cb(err);
    });

   return cb.promise;
  }

你可以像 sendRequest('CreateHouseFacade', {houseName:'Test'}) 一样使用这个功能。

Kind 参数用于后端,知道调用处理请求的方法。参数用于传递附加参数信息。

这是后端代码

const database = firebase.database();
const queueRef = database.ref('queue');

const queueOptions = {
    'specId': 'request_response',
    'sanitize': false,
    'suppressStack': false,
    'numWorkers': 3
};

function removeTask(task) {
    var taskRef = queueRef.child(`tasks/${task._id}`);
    return taskRef.remove();
}

function processTask(data, progress, resolve, reject) {
    try {
        requestHandler(data.request).then(response => {
            data.response = response || null;
            return resolve(data);
        }).catch(err => {
            return reject(err);
        }).then(snap => {
            removeTask(data);
        });
    } catch (err) {
        reject(err).then(snap => removeTask(data));
    }
}

function requestHandler(request) {
    if (!request || !request.kind) throw new Error('Absent Request or Kind');
    var deferredResponse = requestHandlerFactory(request.kind, request.params);
    return deferredResponse;
}

function requestHandlerFactory(kind, params) {
    // It includes mapping all your backend services
    switch (kind) {
        case 'CreateHouseFacade': return myService(params)
        default: throw new Error(`Invalid kind ${kind} was specified`);
    }
}

函数 myService 包含你的业务逻辑代码,它将完成 CreateHouseFacade 请求。