Tối ưu hệ thống để xử lý dữ liệu với workers và D1 database
Hệ thống của mình tối ưu với các bước workflows : nhập thông tin xem view từ web laravel lên D1 database và frontend sẽ lấy dữ liệu đó từ workers mục đích giảm truy vấn đến backend . đối với web ít traffic thì việc này đơn giản nếu nhiều thì nó rất đáng kể . Code workers của mình :
Đầu tiên về workers : src\Service\CacheService.js
const CacheService = {
async get(cacheKey) {
const cache = caches.default;
const request = new Request(`https://cache.local/${cacheKey}`);
const response = await cache.match(request);
if (!response) return null;
return await response.json();
},
// Mặc định là 5 phút (300 giây)
async set(cacheKey, data, maxAge = 300) {
const request = new Request(`https://cache.local/${cacheKey}`);
const response = new Response(JSON.stringify(data), {
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": `s-maxage=${maxAge}`
}
});
const cache = caches.default;
await cache.put(request, response.clone());
return response;
},
async delete(cacheKey) {
const cache = caches.default;
const request = new Request(`https://cache.local/${cacheKey}`);
return await cache.delete(request);
}
}
export default CacheService;
Tiếp theo đến file : src\index.js
import CacheService from './Service/CacheService';
function getCorsHeaders(request) {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization", // Thêm header cụ thể hơn nếu cần
};
}
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const pathnames = url.pathname.split('/').filter(Boolean); // Tách phần đường dẫn thành mảng không có phần tử rỗng
const method = request.method;
const key = url.searchParams.get('key'); // Lấy giá trị 'key' từ query string
// Kiểm tra nếu key không hợp lệ (nếu cần)
// if (key !== env.KEY) {
// return new Response('Yêu cầu cần key ', { status: 400 });
// }
// Xử lý yêu cầu có 'api' trong đường dẫn
if (pathnames[0] === 'post') {
const post = pathnames[1] || null; // Lấy phần thứ hai trong đường dẫn là 'post'
if (!post) {
return new Response('yeu cau khong hop le', { status: 400 });
}
const cacheKey = `views_post_${post}`; // Tạo khóa cache từ post ID
const cacheData = await CacheService.get(cacheKey); // Kiểm tra cache
// Nếu dữ liệu đã có trong cache
if (cacheData) {
return new Response(JSON.stringify({ ...cacheData, cache: "hit" }), {
status: 200,
headers: {
...getCorsHeaders(request),
"Cache-Control": "public, max-age=50", // Cài đặt cache cho response
}
});
}
// Nếu không có trong cache, truy vấn cơ sở dữ liệu
const result = await env.DB.prepare("SELECT count FROM views WHERE postid = ?").bind(post).first();
// Dữ liệu trả về từ DB
let responseData = {
...result || { status: "error", message: "Error" },
cache: "miss"
};
const statusCode = result ? 200 : 404;
// Nếu có dữ liệu, lưu vào cache
if (result) {
responseData = {
...responseData,
status: "success",
message: "OK"
};
await CacheService.set(cacheKey, responseData, 20); // Lưu vào cache với thời gian sống là 20 giây
}
return new Response(JSON.stringify(responseData), {
status: statusCode,
headers: {
...getCorsHeaders(request),
"Cache-Control": "public, max-age=20", // Cache response
}
});
}
// Trả về mặc định nếu không phải API
return new Response(' /post/{postid} để lấy dữ liệu view của post ');
},
};
Tiếp theo trong file : wrangler.jsonc
/**
* For more details on how to configure Wrangler, refer to:
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "nhatnhat",
"main": "src/index.js",
"compatibility_date": "2025-04-10",
"observability": {
"enabled": true
},
"d1_databases": [
{
"binding": "DB",
"database_name": "xxxxxx",
"database_id": "588d8a1a-xxxxxx"
}
],
/**
* Smart Placement
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
*/
// "placement": { "mode": "smart" },
/**
* Bindings
* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
* databases, object storage, AI inference, real-time communication and more.
* https://developers.cloudflare.com/workers/runtime-apis/bindings/
*/
/**
* Environment Variables
* https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
*/
"vars": {
"KEY": "hien123"
},
/**
* Note: Use secrets to store sensitive data.
* https://developers.cloudflare.com/workers/configuration/secrets/
*/
/**
* Static Assets
* https://developers.cloudflare.com/workers/static-assets/binding/
*/
// "assets": { "directory": "./public/", "binding": "ASSETS" },
/**
* Service Bindings (communicate between multiple Workers)
* https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
*/
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
}
Sau đó bạn chỉ cần deploy dữ liệu với lệnh : npm run deploy
2 Code trên laravel :
mình cài đặt laravel với lệnh :
composer create-project laravel/laravel la12connectd1cloudfare
Ở đây mình dùng gói
composer require ntanduy/cloudflare-d1-database
Tiếp theo mình vào config : config/database.php thêm dữ liệu database vào :
'd1' => [
'driver' => 'd1',
'prefix' => '',
'database' => env('CLOUDFLARE_D1_DATABASE_ID', ''),
'api' => 'https://api.cloudflare.com/client/v4',
'auth' => [
'token' => env('CLOUDFLARE_TOKEN', ''),
'account_id' => env('CLOUDFLARE_ACCOUNT_ID', ''),
],
],
thông tin env lấy ở đây :
CLOUDFLARE_D1_DATABASE_ID=xxx-89cc-44af-b0d1-bf03942e7b2b
CLOUDFLARE_TOKEN=xxxx
CLOUDFLARE_ACCOUNT_ID=xxx
giờ đến đoạn gửi dữ liệu lên D1 trong laravel : app\Jobs\IncrementJob.php
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class IncrementJob implements ShouldQueue
{
use Queueable;
public int $post;
/**
* Create a new job instance.
*/
public function __construct($post)
{
$this->post = $post;
}
/**
* Execute the job.
*/
public function handle(): void
{
$increment = 1;
// Kiểm tra bản ghi đã tồn tại hay chưa
$check = DB::connection('d1')->table('views')->where('postid', $this->post)->first();
if (!$check) {
// Nếu không có bản ghi nào thì tạo mới
DB::connection('d1')->table('views')->insert([
'postid' => $this->post,
'count' => 1, // Tạo count ban đầu là 1
]);
// Bạn có thể log hoặc xử lý ở đây
Log::info("Created new record for postid: " . $this->post);
} else {
// Nếu có bản ghi thì cập nhật
DB::connection('d1')->table('views')->where('postid', $this->post)->update([
'count' => DB::raw('IFNULL(count, 0) + 1'), // Tăng count lên 1
]);
Log::info("Cập nhật view postid: " . $this->post);
}
}
}
Vậy là xong hehe