Ginco đã sử dụng và tối ưu Cloud Functions như thế nào

Bài viết được sự cho phép của tác giả Ngo Thang

Hôm nay mình chia sẻ đến mọi người 1 case study khá hay mà Ginco đã thực hiện để quản lí giao dịch trong Blockchain bằng cách sử dụng hệ sinh thái Firebase.

Gần đây công ty mình cũng bắt đầu đưa Firebase vào hệ thống. Đúng quả thật dùng Firebase xong thấy yêu nó hẳn. Nó quả thực rất mạnh và cũng khá rẻ. Nên thời gian tới mình sẽ tập trung viết nhiều bài hơn nữa về Firebase để mọi người có cái nhìn tổng quan hơn về nó.

Bây giờ chúng ta cùng nhau tìm hiểu xem Ginco đã sử dụng Firebase để xử lí các bài toán liên quan đến giao dịch Blockchain như thế nào.

  Tối ưu việc load ảnh với RxJava

Mục tiêu bài viết:
・Hiểu được cách dùng Cloud Functions trong hệ thống
・Biết cách tối ưu Cloud Functions để đạt hiệu năng tốt nhất

Đối tượng hướng đến:
・Người có chút hiểu biết về Cloud Functions
・Người đã từng làm việc với Firebase

Ginco là công ty như thế nào?

Ginco là công ty Nhật Bản chuyên cung cấp hệ thống quản lí wallet tiền ảo 1 cách an toàn và bảo mật. Nó có thể gửi, nhận tiền ảo 1 cách đơn giản thông qua hệ thống Blockchain.

Ginco đã sử dụng và tối ưu Cloud Functions như thế nào

Cloud Functions là gì?

Cloud Functions là 1 sản phẩm của Google.

Cloud Functions cho phép bạn chạy code backend tự động trigger các event được kích hoạt bởi tính năng của Firebase (Google).

Ví dụ như khi chúng ta upload 1 ảnh lên Google Storage thì Cloud Functions sẽ tự động trigger sự kiện đó để xử lí 1 tác vụ nào đó. Ví dụ như resize ảnh chẳng hạn …

Các lợi ích khi sử dụng Cloud Functions:

  • Tạo API 1 cách đơn giản. (Xem thêm API là gì)
  • Mỗi 1 hàm được deploy đều được chạy trong 1 môi trường hoàn toàn khác nhau. Do đó chức năng của hàm này sẽ không ảnh hưởng đến hàm khác. Và việc thêm sửa xoá chức năng được thực hiện 1 cách khá đơn giản.
  • Dựa vào số lượng request mà hệ thống sẽ tự động được scale.

Ứng dụng Cloud Functions trong Ginco

Ở Ginco thì Cloud Functions đảm nhiệm 4 chức năng chính:

  • Cung cấp API gọi giao thức RPC của Blockchain.
  • Cung cấp API thu thập thông tin về market (giá cả thị trường, volume …).
  • Push Notification.
  • Thực hiện backup Firestore định kì.

Dưới đây là kiến trúc hệ thống xung quanh Cloud Functions trong Ginco:

Ginco đã sử dụng và tối ưu Cloud Functions như thế nào

Cung cấp API gọi giao thức RPC của Blockchain

Toàn bộ các node blockchain được chạy trên GKE.

Bởi vì các API trong các node blockchain (ví dụ như bitcoin, ethereum…) này hoàn toàn khác nhau. Nên họ đã cung cấp 1 API đã wrapper lại tất cả các API đó thành chung 1 interface để dễ dàng sử dụng.

Giai đoạn đầu thì họ đã dùng HTTP trigger, nhưng cái này có 1 nhược điểm là khi gọi API thì nó không tự động gửi access token của người dùng lên. Nên thành ra mỗi lần gọi API phải tự mình đính kèm access token vào header của request.

Họ đã dùng onCall trigger. Cái này thì nó tự động đính kèm access token của người dùng vào header. Nên khá dễ dàng để chứng thực người dùng.

Cung cấp API thu thập thông tin về market (giá cả thị trường, volume…)

Ginco sử dụng Firestore là cơ sở dữ liệu chính để lưu trữ thông tin về market (giá cả thị trường, volume …)

Các thao tác CRUD liên quan đến dữ liệu sẽ được thực hiện trực tiếp từ phía Client (sử dụng Firebase SDK)

Còn 1 số dữ liệu quan trọng cần tính bảo mật cao hơn thì sẽ không gọi trực tiếp từ SDK Firebase phía Client mà sẽ thông qua Cloud Functions.

Push Notification

1 số event liên quan đến lịch sử gửi nhận coin sẽ thực hiện việc gửi thông báo (push notification) đến người dùng.

Vì Cloud Functions có thể trigger được các sự kiện liên quan đến Firestore nên khi dữ liệu trong Firestore mà bị thay đổi nó sẽ detect được và tiến hành gửi thông báo đến người dùng.

Thực hiện backup Firestore định kì

Việc backup Firestore sẽ được thực hiện thông qua Cloud Functions.

Bằng việc trigger Cloud Pub/Sub, Cloud functions sẽ dễ dàng backup được dữ liệu của Firestore thông qua Firestore command line.

Chúng ta chỉ cần sử dụng cronjob trong App Engine để gửi message đến Pub/Sub. Khi đó Cloud Functions sẽ trigger được event đó và thực hiện backup.

Tối ưu Cloud Functions

Mặc dù Cloud Functions khá là tiện nhưng khi số lượng function tăng lên thì tốc độ response của nó cũng giảm xuống đáng kể (đôi khi mất 13s cho 1 request).

Ở đây mình sẽ nói qua 1 số vấn đề gặp phải và cách khắc phục nó.

Kiến trúc Cloud Functions

Cloud Functions được thực hiện như sau:

  • 1 request sẽ được xử lí bằng 1 instance.
  • Trong trường hợp nhận được request mới:
    • Nếu có instance còn trống thì sẽ gửi request đó đến instance trống đó
    • Nếu không có instance trống thì sẽ tạo instance mới, và gửi request đến instance đó
  • Nếu trong 1 khoảng thời gian nào đó mà instance không xử lí request thì instance đó sẽ bị xoá đi.

Và trường hợp tạo instance mới và xử lí request là tốn thời gian nhất. Trường hợp này người ta gọi là Cold Start. (Mặc dù bản thân việc tạo 1 instance mới chỉ mất tầm mấy trăm ms thôi)

Dưới đây là hình ảnh về hiện tượng Cold Start. Nếu không có instance nào trống thì sẽ tạo ra instance mới để xử lí request.

Ginco đã sử dụng và tối ưu Cloud Functions như thế nào

Cải thiện Cold Start

Cải thiện Cold Start của Cloud Functions

Sau khi tìm hiểu thì họ thấy được, bản thân của việc tạo 1 instance mới hoàn toàn không tốn quá nhiều thời gian (chỉ mất mấy trăm ms). Mà việc tốn nhiều thời gian nhất ở đây là việc load library.

Giả sử như file index.js của chúng ta có nội dung như sau:

 
# index.js
 
exports.Func1 = require('./funcs/func1');
exports.Func2 = require('./funcs/func2');

Nội dung của funcs/func1.js:

 
# funcs/func1.js
 
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
module.exports = functions.https.onRequest((req, res)=>{
    // ......
});

Nội dung của funcs/func2.js:

 
# funcs/func2.js
 
const functions = require('firebase-functions')
const google = require('googleapis');
const rp = require('request-promise');
const projectId = process.env.GCLOUD_PROJECT;
module.exports = functions.storage.bucket(`${projectId}-bucket`)
                     .object()
                     .onFinalize((obj)=>{
    // ......
});

Như ví dụ trên ta có thể thấy được, khi index.js được gọi thì func1, func2 cũng được gọi theo. Khi đó chúng ta sẽ mất thêm thời gian để load các thư viện trong func1, func2.

Và điều này sẽ làm cho thời gian response của chúng ta giảm xuống. Để giải quyết bài toán này thì rất đơn giản. Chỉ cho load những thư viện cần thiết thôi.

 
# index.js
 
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'Func1') {
    exports.Func1 = require('./funcs/func1');
}
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'Func2') {
    exports.Func2 = require('./funcs/func2');
}

Khi Cloud Functions được gọi thì khi đó biến môi trường FUNCTION_NAME sẽ được gán tên của hàm được gọi. Ví dụ như chúng ta muốn gọi func1 thì khi đó giá trị trong FUNCTION_NAME sẽ là func1.

Up version của module thành mới nhất

Cloud Functions sẽ luôn luôn cache module Nodejs lại và chia sẻ đến mọi function trên toàn thế giới. Vì vậy nếu module bạn đang sử dụng có trong cache của Google Cloud Functions thì khi đó sẽ được dùng luôn và không phải mất thời gian load nữa.

Vì vậy để có thể dùng được cache trong Cloud Functions thì chúng ta cố gắng update mọi module đến phiên bản mới nhất.

Thay đổi region trong Cloud Functions

Trước kia thì Cloud Functions chỉ support mỗi region ở Mỹ. Nhưng từ tháng 6 năm ngoái nó support thêm 1 số region khác nữa. Nên các bạn cố gắng chọn region nào gần với hệ thống của các bạn nhất.

Để setting region cho function thì chúng ta làm như sau:

 
const functions = require('firebase-functions');
module.exports = functions.region('asia-northeast1')
 .https
 .onRequest((req, res) => {
    // ……
});

Về danh sách region mà Cloud Functions đang support thì các bạn có thể xem tại đây.

Kết luận

Qua bài này mình đã giới thiệu cho các bạn về 1 case study mà Ginco đã sử dụng và tối ưu Cloud Functions trong hệ thống của họ như thế nào.

Hi vọng nó sẽ giúp ích cho các bạn 1 phần nào đó trong việc cải thiện hiệu năng.

Cảm ơn bạn đã theo dõi bài viết!

Có thể bạn quan tâm:

Xem thêm việc làm Software Developers trên TopDev

Bài viết gốc được đăng tải tại Nghệ thuật coding

  Optimizing Laravel - Tối ưu hoá Laravel