ໃຊ້ SDK ແລະ API

SDK ສຳລັບ Node.js, Python ແລະ Go ເທິງ REST API ທຳມະດາທີ່ເຊັນທຸກ Request ຕາມມາດຕະຖານ IETF

SDK

SDK ສຳລັບ Node.js, Python ແລະ Go ເຊັນ Request ໃຫ້ ລອງໃໝ່ເອງເມື່ອລະບົບບໍ່ຫວ່າງຊົ່ວຄາວ ແລະ ມີ worker ສຳເລັດຮູບທີ່ລໍຮັບຂໍ້ຄວາມ ແລ່ນໂຄດຂອງທ່ານ ແລ້ວລຶບຂໍ້ຄວາມທີ່ເຮັດສຳເລັດໃຫ້ ຕິດຕັ້ງຕາມພາສາທີ່ໃຊ້:

ພາສາຕ້ອງໃຊ້ຕິດຕັ້ງ
Node.js18.17+npm install @thailandhosting/queue-service
Python3.9+pip install thailandhosting-queue-service
Go1.23+go get github.com/mrzack99s/thailandhosting-queue-service-sdk-go

ໃຫ້ client ຮູ້ Region ຂອງ Queue ແລະ Access key ຈາກ Console (ເກັບໄວ້ໃນ environment variable ບໍ່ແມ່ນໃນໂຄດ) ແລະ ໃຊ້ client ໂຕດຽວທັງໂປຣແກຣມ

Node.js
import { Client } from '@thailandhosting/queue-service';

const client = new Client({
  region: '<region>',
  accessKeyId: process.env.TH_QUEUE_ACCESS_KEY_ID,
  secretAccessKey: process.env.TH_QUEUE_SECRET_ACCESS_KEY,
});
const orders = client.queue('orders');

// Send
await orders.sendJSON({ orderId: 42 });
await orders.send('plain text', { attributes: { kind: 'email' }, delay: 30 });

// A worker: waits for messages, runs the handler, deletes what it finished
const stop = new AbortController();
process.on('SIGTERM', () => stop.abort());
await orders.process(async (m) => {
  const { orderId } = m.json();
  await ship(orderId); // throwing leaves the message to be tried again
}, { concurrency: 8, retryDelay: 30, signal: stop.signal });
Python
import os
from thailandhosting_queueservice import Client

client = Client(
    access_key_id=os.environ["TH_QUEUE_ACCESS_KEY_ID"],
    secret_access_key=os.environ["TH_QUEUE_SECRET_ACCESS_KEY"],
    region="<region>",
)
orders = client.queue("orders")

# Send
orders.send_json({"order_id": 42})
orders.send("plain text", attributes={"kind": "email"}, delay=30)

# A worker: waits for messages, runs the handler, deletes what it finished
def handle(m):
    ship(m.json()["order_id"])  # raising leaves the message to be tried again

orders.process(handle, concurrency=8, retry_delay=30)
Go
client, err := queueservice.New(queueservice.Config{
	Region:          "<region>",
	AccessKeyID:     os.Getenv("TH_QUEUE_ACCESS_KEY_ID"),
	SecretAccessKey: os.Getenv("TH_QUEUE_SECRET_ACCESS_KEY"),
})
if err != nil {
	log.Fatal(err)
}
orders := client.Queue("orders")

// Send
_, err = orders.SendJSON(ctx, map[string]any{"orderId": 42}, nil)

// A worker: waits for messages, runs the handler, deletes what it finished.
// It runs until ctx ends.
err = orders.Process(ctx, func(ctx context.Context, m queueservice.Message) error {
	var order struct{ OrderID int }
	if err := m.JSON(&order); err != nil {
		return err
	}
	return ship(ctx, order.OrderID) // an error leaves the message to be tried again
}, &queueservice.ProcessOptions{Concurrency: 8, RetryDelay: 30 * time.Second})

ສ້າງ ແລະ ແກ້ Queue ລວມທັງຮັບ ແລະ ລຶບຂໍ້ຄວາມເອງກໍໄດ້:

Node.js
// Create a queue or change its settings (the key must manage queues)
await client.putQueue('orders', { visibilityTimeout: 120, maxReceives: 5, deadLetterQueue: 'orders-dlq' });

// Receive by hand
for (const m of await orders.receive({ max: 10, wait: 20 })) {
  await handle(m.body);
  await orders.delete(m.receipt);
}

const info = await client.getQueue('orders'); // info.messages: visible, inFlight, delayed

ຂໍ້ຄວາມໜຶ່ງອາດມາຮອດຫຼາຍກວ່າໜຶ່ງຄັ້ງ (ເຊັ່ນ worker ຢຸດກ່ອນລຶບ) ຂຽນໂຄດໃຫ້ແລ່ນຊ້ຳໄດ້ໂດຍບໍ່ເສຍຫາຍ ເຊັ່ນ ກວດກ່ອນວ່າອໍເດີນີ້ຕັດເງິນໄປແລ້ວຫຼືຍັງ

REST API

Endpoint ຂອງແຕ່ລະ Region ຄື https://queues.thailandhosting.com/<region> ແລະ API ຢູ່ທີ່ …/<region>/v1/queues body ແລະ ຄຳຕອບເປັນ JSON ແລະ ໄລຍະເວລາທຸກຄ່າເປັນວິນາທີ

Requestໃຊ້ເຮັດຫຍັງ
GET /v1/queuesລາຍຊື່ Queue ທີ່ Key ເຂົ້າເຖິງໄດ້
PUT /v1/queues/{name}ສ້າງ Queue ຫຼື ແກ້ການຕັ້ງຄ່າ (Key ຕ້ອງຈັດການ Queue ໄດ້) ເຊັ່ນ {"visibilityTimeout":60,"maxReceives":5,"deadLetterQueue":"orders-dlq"}
GET /v1/queues/{name}ການຕັ້ງຄ່າ ແລະ ຈຳນວນຂໍ້ຄວາມ (ລໍຮັບ ກຳລັງປະມວນຜົນ ລໍ Delay)
POST /v1/queues/{name}/messagesສົ່ງ 1 ຂໍ້ຄວາມ {"body":"…","attributes":{…},"delay":0} ຫຼື ສູງສຸດ 10 ຂໍ້ຄວາມ {"messages":[…]}
POST /v1/queues/{name}/receiveຮັບສູງສຸດ 10 ຂໍ້ຄວາມ {"max":10,"wait":20,"visibilityTimeout":120} ໄດ້ id, body, receipt, receives
POST /v1/queues/{name}/deleteລຶບຂໍ້ຄວາມທີ່ເຮັດສຳເລັດ {"receipts":["…"]} (ສູງສຸດ 10)
POST /v1/queues/{name}/visibilityຂໍເວລາເພີ່ມ ຫຼື ຄືນຂໍ້ຄວາມໃຫ້ຄົນອື່ນຮັບທັນທີ {"receipt":"…","timeout":0}
POST /v1/queues/{name}/purgeລຶບຂໍ້ຄວາມທັງໝົດໃນ Queue
DELETE /v1/queues/{name}ລຶບ Queue ພ້ອມຂໍ້ຄວາມ

ການເຊັນ Request

ທຸກ Request ເຊັນດ້ວຍ HTTP Message Signatures (RFC 9421) ໂດຍໃຊ້ Secret ຂອງທ່ານ (HMAC-SHA256) ຄອບຄຸມ method, host, path, query ແລະ Content-Digest (RFC 9530) ຂອງ body Secret ຈຶ່ງບໍ່ຖືກສົ່ງໄປກັບ Request ເວລາຂອງເຄື່ອງຕ້ອງຫ່າງຈາກຂອງເຮົາບໍ່ເກີນ 5 ນາທີ Error ເປັນ Problem Details (RFC 9457) ທີ່ມີ code

ບໍ່ໃຊ້ SDK: Node.js

Node.js 18+
import crypto from 'node:crypto';
const KEY_ID = 'YOUR_KEY_ID', SECRET = 'YOUR_SECRET';
const ENDPOINT = 'https://queues.thailandhosting.com/<region>';

async function call(method, path, body) {
  const url = new URL(ENDPOINT + path);
  const data = body ? JSON.stringify(body) : '';
  const headers = { 'Content-Type': 'application/json' };
  let covered = '"@method" "@authority" "@path" "@query"';
  let base = `"@method": ${method}\n"@authority": ${url.host}\n"@path": ${url.pathname}\n"@query": ?${url.search.slice(1)}\n`;
  if (data) {
    headers['Content-Digest'] = `sha-256=:${crypto.createHash('sha256').update(data).digest('base64')}:`;
    covered += ' "content-digest"';
    base += `"content-digest": ${headers['Content-Digest']}\n`;
  }
  const params = `(${covered});created=${Math.floor(Date.now() / 1000)};keyid="${KEY_ID}";alg="hmac-sha256"`;
  base += `"@signature-params": ${params}`;
  headers['Signature-Input'] = `th=${params}`;
  headers['Signature'] = `th=:${crypto.createHmac('sha256', SECRET).update(base).digest('base64')}:`;
  const res = await fetch(url, { method, headers, body: data || undefined });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return res.status === 204 ? null : res.json();
}

await call('POST', '/v1/queues/orders/messages', { body: JSON.stringify({ orderId: 42 }) });
const { messages } = await call('POST', '/v1/queues/orders/receive', { max: 10, wait: 20 });
for (const m of messages) {
  // ... do the work ...
  await call('POST', '/v1/queues/orders/delete', { receipts: [m.receipt] });
}

ບໍ່ໃຊ້ SDK: Python

Python 3, standard library only
import base64, hashlib, hmac, json, time, urllib.request, urllib.parse

KEY_ID, SECRET = "YOUR_KEY_ID", "YOUR_SECRET"
ENDPOINT = "https://queues.thailandhosting.com/<region>"

def call(method, path, body=None):
    url = urllib.parse.urlsplit(ENDPOINT + path)
    data = json.dumps(body).encode() if body is not None else b""
    headers = {"Content-Type": "application/json"}
    covered = '"@method" "@authority" "@path" "@query"'
    base = f'"@method": {method}\n"@authority": {url.netloc}\n"@path": {url.path}\n"@query": ?{url.query}\n'
    if data:
        headers["Content-Digest"] = "sha-256=:" + base64.b64encode(hashlib.sha256(data).digest()).decode() + ":"
        covered += ' "content-digest"'
        base += f'"content-digest": {headers["Content-Digest"]}\n'
    params = f'({covered});created={int(time.time())};keyid="{KEY_ID}";alg="hmac-sha256"'
    base += f'"@signature-params": {params}'
    headers["Signature-Input"] = "th=" + params
    headers["Signature"] = "th=:" + base64.b64encode(hmac.new(SECRET.encode(), base.encode(), hashlib.sha256).digest()).decode() + ":"
    req = urllib.request.Request(url.geturl(), data=data or None, method=method, headers=headers)
    with urllib.request.urlopen(req) as res:
        return json.loads(res.read() or "null")

call("POST", "/v1/queues/orders/messages", {"body": json.dumps({"orderId": 42})})
for m in call("POST", "/v1/queues/orders/receive", {"max": 10, "wait": 20})["messages"]:
    # ... do the work ...
    call("POST", "/v1/queues/orders/delete", {"receipts": [m["receipt"]]})

ຂໍ້ຄວາມໜຶ່ງ (body ລວມ attributes) ບໍ່ເກີນ 256 KiB ສົ່ງຄັ້ງລະບໍ່ເກີນ 10 ຂໍ້ຄວາມລວມ 1 MiB ຮັບຄັ້ງລະບໍ່ເກີນ 10 ຂໍ້ຄວາມ ຂໍ້ຄວາມທີ່ຮັບແລ້ວຍັງບໍ່ລຶບໄດ້ບໍ່ເກີນ 120,000 ຂໍ້ຄວາມຕໍ່ Queue Project ໜຶ່ງມີໄດ້ສູງສຸດ 1,000 Queue ແລະ 50 Access key

ຖ້າພົບບັນຫາ

ຂຶ້ນ 401 “invalid_signature”

ກວດ Key ID ແລະ Secret ວ່າເຊັນຈາກ path ເຕັມລວມ /<region> ແລະ ໂມງຂອງເຄື່ອງຕົງ (ເປີດ NTP)

ຂຶ້ນ 409 “invalid_receipt” ຕອນລຶບ

ຂໍ້ຄວາມເລີຍ Visibility timeout ໄປແລ້ວ ແລະ ຖືກສົ່ງໃຫ້ Worker ອື່ນ receipt ເກົ່າຈຶ່ງໃຊ້ບໍ່ໄດ້ ໃຫ້ເພີ່ມ Visibility timeout ແລະ ເຮັດວຽກໃຫ້ເຮັດຊ້ຳໄດ້

ຂຶ້ນ 429 “queue_full” ຫຼື “too_many_in_flight”

Queue ມີຂໍ້ຄວາມຫຼາຍເກີນໄປ ຫຼື ຮັບໄປແລ້ວຍັງບໍ່ລຶບເກີນ 120,000 ຂໍ້ຄວາມ ໃຫ້ເພີ່ມ Worker ແລະ ລຶບຂໍ້ຄວາມທີ່ເຮັດສຳເລັດ

ຂຶ້ນ 421 “wrong_region”

ເອີ້ນຜິດ Region ໃຊ້ Endpoint ທີ່ສະແດງໃນຄຳຕອບ ຫຼື ເອີ້ນໂດຍບໍ່ໃສ່ Region ໃນ path

ຍັງແກ້ບໍ່ໄດ້? ໃຫ້ທີມງານຊ່ວຍ

ເປີດ ticket ຈາກໜ້າຈັດການ ບອກຊື່ເວັບ ຫຼື ບໍລິການ ສິ່ງທີ່ເຮັດ ແລະ ຂໍ້ຄວາມທີ່ເຫັນເທິງຈໍ ທີມງານຈະຕອບກັບໃນເລື່ອງດຽວກັນ ຕິດຕາມໄດ້ໃນບ່ອນດຽວ