برای هر چیز، یک نسخهٔ زنده با وضعیت خودش.
اکتور نمونهای از یک class در فانکشن شماست که با نام صدا زده میشود. از هر نام در هر لحظه فقط یک نسخه اجرا میشود و فضای ذخیرهسازی خودش را دارد. اتاق گفتگو، ویرایش همزمان، شمارندهٔ دقیق و محدودیت درخواست برای هر کاربر بدون دیتابیس جدا درست کار میکنند.
آنچه میگیرید
فقط یک نسخهٔ زنده
env.ROOMS.get("room-42") همیشه به همان یک اکتور میرسد، روی هر سروری که باشد. درخواستها به ترتیب به آن میرسند و دو نسخه از یک اکتور همزمان اجرا نمیشوند.
فضای ذخیرهسازی خودش
هر اکتور کلیدها و مقدارهای خودش را با get، put، delete و list دارد. هر نوشتن پیش از آنکه اکتور پاسخ بدهد ماندگار شده است.
همه یا هیچ
همهٔ نوشتنهای یک درخواست با هم ثبت میشوند یا هیچکدام. با storage.transaction بخشی از کار را جداگانه برگردانید.
بیدار شدن در زمان معین
با storage.setAlarm زمانی تعیین کنید تا اکتور در آن زمان با alarm() بیدار شود، حتی اگر کسی صدایش نزند. برای پاک کردن وضعیت کهنه، یادآوری یا کار دورهای.
وبسوکتهایی که میخوابند
اکتور اتصال وبسوکت را با acceptWebSocket نگه میدارد و هر پیام را در webSocketMessage میگیرد. اتاقی که ساکت است میخوابد و اتصالهایش باز میمانند، پس هزینهٔ کمی دارد.
گروههای اتصال
به هر اتصال تگ بزنید و با getWebSockets به همهٔ یک گروه پیام بفرستید، مثل همهٔ کسانی که یک سند را باز کردهاند.
namespace و اتصال
هر namespace اکتورهای یک class از یک فانکشن پروژه است. خود فانکشن آن را با env.ROOMS دارد؛ فانکشن یا برنامهٔ دیگری از پروژه آن را با چند خط در فایل پروژه متصل میکند.
آماده پیش از اولین درخواست
در سازنده، وضعیت را با blockConcurrencyWhile از فضای ذخیرهسازی بخوانید؛ هیچ درخواستی پیش از تمام شدن آن به اکتور نمیرسد.
بدون از دست رفتن داده
اگر سروری از کار بیفتد، اکتورهایش روی سرور دیگری با همان دادهها پاسخ میدهند. هیچ نوشتنی که تایید شده گم نمیشود.
تغییر کد، داده سر جایش
class یا فانکشن یک namespace را عوض کنید؛ اکتورها دادههایشان را نگه میدارند و درخواست بعدی با کد تازه اجرا میشود. دادههای namespace حذفشده حدود یک روز نگه داشته میشود و با ایجاد دوباره با همان نام برمیگردد.
مصرف و گزارش تغییرات
درخواستها، زمان اجرا، خواندن و نوشتن هر namespace، به تفکیک روز. و اینکه چه کسی، کی، چه چیزی را در آن تغییر داد.
یک اتاق گفتگو در چند خط
هر اتاق یک اکتور است: اتصالها را نگه میدارد، پیام را به همه میفرستد و شمارهٔ پیامها را در فضای ذخیرهسازی خودش نگه میدارد.
export class Room {
constructor(state, env) {
this.state = state
}
async fetch(request) {
const [client, server] = Object.values(new WebSocketPair())
this.state.acceptWebSocket(server, ["lobby"])
return new Response(null, { status: 101, webSocket: client })
}
async webSocketMessage(ws, message) {
const n = ((await this.state.storage.get("count")) ?? 0) + 1
await this.state.storage.put("count", n)
for (const s of this.state.getWebSockets("lobby")) s.send(message)
await this.state.storage.setAlarm(Date.now() + 60_000)
}
async alarm() {
await this.state.storage.delete("typing")
}
}به ازای مصرف
درخواستها، زمان اجرا، خواندن و نوشتن، دادهای که نگه میدارید، و برای وبسوکتها دقیقههای اتصال و پیامها، با همان قیمت دقیقهٔ اتصال ریلتایم. اکتوری که میخوابد زمان اجرا ندارد. مصرف اول از اعتبار همان ماه و سپس از کیف پول کم میشود.
پرسشهای رایج
از یک فانکشن ممکن است همزمان چند نسخه اجرا شود و هیچکدام چیزی را بین درخواستها به خاطر نمیسپارد. از هر اکتور فقط یک نسخه هست و وضعیتش را در فضای ذخیرهسازی خودش نگه میدارد.
درخواستها یکی پس از دیگری به همان نسخه میرسند، پس شمارنده دقیق میماند و دو نوشتن روی هم نمیافتند.
داخل ایران، روی سرورهای خود پلتفرم. هر نوشتن پیش از پاسخ اکتور ماندگار شده است و با از کار افتادن یک سرور گم نمیشود.
سقفی ندارد، چون اکتور در سکوت میخوابد و اتصال باز میماند. در استقرار یا جابهجایی، اتصال با کد ۱۰۱۲ بسته میشود تا مرورگر دوباره متصل شود.
اگر فقط باید پیامی به همهٔ کاربران یک کانال برسد، ریلتایم بدون نوشتن کد سمت سرور این کار را میکند. اگر سرور باید هر پیام را بررسی کند، وضعیتی نگه دارد یا منطق خودش را اجرا کند، اکتور بنویسید.
اکتور بیکار میخوابد و زمان اجرا ندارد. فقط دادهای که نگه میدارد، و اگر اتصالی باز مانده باشد دقیقههای اتصال، حساب میشود.
اولین اکتور را بسازید
یک class در فانکشن، یک دستور برای namespace.
فعلاً ثبتنام با کد دعوت است. اگر کد دارید، همین حالا حساب ایجاد کنید.
ثبتنام باکد ایمیلکد پیامکی