diff --git a/public/_images/swoole_class_id.svg b/public/_images/swoole_class_id.svg new file mode 100644 index 0000000..7c2458e --- /dev/null +++ b/public/_images/swoole_class_id.svg @@ -0,0 +1 @@ +SwooleServer Gaya AsinkronServer (Layanan TCP/UDP)1Http\Server (Turunan Server)2Websocket\Server (Turunan Http\Server)3Redis\Server (Protokol Redis, Turunan Server)4Server\Port (Multi-port, Turunan Server)5Server Coroutine MurniCo\Server (Layanan TCP/UDP Coroutine)1Co\Http\Server2Co\Websocket\Server3Co\run (Container Coroutine)4KlienClient (Klien I/O Sinkron, dapat digunakan di FPM)1Co\Client (Klien Coroutine, API identik dengan Client)2Co\Http\Client & Co\Websocket\Client3Co\Http2\Client4Co\System (Klien Coroutine Sistem)5Co\Socket (Klien Coroutine Socket)6Co\PostgreSQL (Klien Coroutine pgSQL)7Coroutine Sekali Klik (Hook fungsi native PHP)8Redis, PDO, Mysqlistream_socketOperasi Filestream_selectCURLHook Fungsi Blocking (sleep, exec, gethostbyname, dll)CoroutineAPI Coroutine1Connection Pool Bawaan2Channel (Komunikasi antar Coroutine)3WaitGroup4Konkurensi Coroutine5Debug Coroutine6Utilitas LainnyaManajemen Proses1Process (Single Process)Process\Pool (Process Pool)Event (Event Epoll)2Lock (Lock antar Proses)3Atomic (Atomic Counter tanpa Lock)4Table (Shared Memory Performa Tinggi)5Timer (Timer Milidetik)6 \ No newline at end of file diff --git a/public/de/_navbar.md b/public/de/_navbar.md index e930b97..7da409b 100644 --- a/public/de/_navbar.md +++ b/public/de/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/en/_navbar.md b/public/en/_navbar.md index e930b97..7da409b 100644 --- a/public/en/_navbar.md +++ b/public/en/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/fr/_navbar.md b/public/fr/_navbar.md index e930b97..7da409b 100644 --- a/public/fr/_navbar.md +++ b/public/fr/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/id/CONTRIBUTING.md b/public/id/CONTRIBUTING.md new file mode 100644 index 0000000..295990d --- /dev/null +++ b/public/id/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Kontributor Dokumen + +Terima kasih kepada para kontributor berikut yang telah membuat dokumentasi `Swoole` menjadi lebih baik. + + + + + + + + + + + + + + + + + + + + + + + + + +

sdfjklmin

📖

baly2000

📖

zhmm

📖

吴亲库里

📖

✨小透明・宸✨

📖

Lingjie Lin

📖

Arun Fung

📖

jie295053415

📖

huanghui

📖

nhzex

📖

Success

📖

yuntian001

📖

SETSESSION

📖

baicai

📖
+ + + + diff --git a/public/id/README.md b/public/id/README.md new file mode 100644 index 0000000..eeadaff --- /dev/null +++ b/public/id/README.md @@ -0,0 +1,60 @@ +# Swoole + +?> `Swoole` adalah mesin komunikasi jaringan paralel berbasis event-driven asynchronous dan coroutine, ditulis dalam `C++`. Swoole menyediakan dukungan [coroutine](/coroutine) dan [high-performance](/question/use?id=how-is-the-performance-of-swoole) network programming untuk `PHP`. Dilengkapi dengan berbagai modul server dan klien untuk berbagai protokol komunikasi, kamu bisa dengan cepat membuat `TCP/UDP services`, `Web berperforma tinggi`, `WebSocket services`, `IoT`, `real-time communication`, `game`, `microservices`, dan lain-lain. `PHP` nggak lagi terbatas di web tradisional. + +## Diagram Class Swoole + +!>Klik link untuk langsung menuju halaman dokumentasi terkait + +[//]: # (https://naotu.baidu.com/file/bd9d2ba7dfae326e6976f0c53f88b18c) + + + +## Situs Resmi + +* [Situs Resmi Swoole](//www.swoole.com) +* [Produk Komersial & Dukungan](//business.swoole.com) +* [Tanya Jawab Swoole](//wenda.swoole.com) + +## Repositori + +* [GitHub](//github.com/swoole/swoole-src) **(Kasih Star ya)** +* [Gitee](//gitee.com/swoole/swoole) +* [PECL](//pecl.php.net/package/swoole) + +## Alat Pengembangan + +* [IDE Helper](https://github.com/swoole/ide-helper) +* [Yasd](https://github.com/swoole/yasd) +* [debugger](https://github.com/swoole/debugger) +* [sdebug](https://github.com/swoole/sdebug) + +## Hak Cipta + +Konten asli dokumen ini berasal dari [dokumentasi Swoole versi lama](https://wiki.swoole.com/wiki/index/prid-1), yang dibuat untuk menyelesaikan masalah dokumentasi yang sudah lama dikeluhkan. Dokumentasi ini menggunakan format modern, hanya mencakup konten `Swoole4`, sudah memperbaiki banyak kesalahan dari dokumen lama, mengoptimalkan detail, dan menambahkan contoh kode serta materi pembelajaran biar lebih ramah buat pemula `Swoole`. + +Seluruh konten dalam dokumen ini, termasuk teks, gambar, dan materi audio-visual, adalah hak cipta dari **Shanghai SWO Network Technology Co., Ltd**. Media, situs web, atau individu boleh mengutip dalam bentuk tautan eksternal, tapi dilarang menyalin atau mempublikasikan dalam bentuk apa pun tanpa izin. + +## Penggagas Dokumen + +* Yang Cai [GitHub](https://github.com/TTSimple) +* Guo Xinhua [Weibo](https://www.weibo.com/u/2661945152) +* [Lu Fei](https://github.com/sy-records) [Akun Resmi Weixin](http://go.qq52o.me/a/mp) + +## Laporan Masalah + +Kalau nemu masalah dengan konten dokumen ini (typo, kesalahan contoh kode, konten kurang, dll) atau punya saran, silakan buat [issue](https://github.com/swoole/docs/issues/new). Bisa juga langsung klik tombol [Edit](/?id=main) di pojok kanan atas buat kirim `Pull request`. + +Kalau kontribusinya diterima, nama kamu akan ditambahkan ke daftar [Kontributor Dokumen](/CONTRIBUTING) sebagai tanda terima kasih. + +## Prinsip Dokumentasi + +Pakai bahasa yang lugas, **usahakan** seminimal mungkin menjelaskan detail teknis dan konsep internal `Swoole`. Konsep internalnya nanti bisa diurus di bagian `hack` terpisah; + +Kalau ada konsep yang nggak bisa dihindari, **wajib** ada satu tempat khusus yang menjelaskan konsep itu, dan tempat lain tinggal nge-link ke sana. Contohnya: [Event Loop](/learn?id=what-is-eventloop); + +Nulis dokumentasi harus dari sudut pandang pengguna, pastikan gampang dipahami; + +Kalau ada perubahan fitur, **wajib** ubah semua bagian yang terkait, jangan cuma sebagian; + +Setiap modul fitur **wajib** punya contoh kode yang lengkap. diff --git a/public/id/_navbar.md b/public/id/_navbar.md new file mode 100644 index 0000000..de33a75 --- /dev/null +++ b/public/id/_navbar.md @@ -0,0 +1,10 @@ +* Bahasa / Language + * [Chinese Sederhana](https://wiki.swoole.com/zh-cn/) + * [Chinese Tradisional](https://wiki.swoole.com/zh-Hant/) + * [English](https://wiki.swoole.com/en/) + * [Japanese](https://wiki.swoole.com/ja/) + * [Korean](https://wiki.swoole.com/ko/) + * [French](https://wiki.swoole.com/fr/) + * [German](https://wiki.swoole.com/de/) + * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/id/_sidebar.md b/public/id/_sidebar.md new file mode 100644 index 0000000..607b1b1 --- /dev/null +++ b/public/id/_sidebar.md @@ -0,0 +1,122 @@ + +* Instalasi + * [Instalasi Swoole](environment.md) + * [Konflik Ekstensi](getting_started/extension.md) + +* [Contoh Sederhana](start/start_server.md) + * [Server TCP](start/start_tcp_server.md) + * [Server UDP](start/start_udp_server.md) + * [Server HTTP](start/start_http_server.md) + * [Server WebSocket](start/start_ws_server.md) + * [Server MQTT (IoT)](start/start_mqtt.md) + * [Jalankan Task Async (Task)](start/start_task.md) + * [Pengenalan Coroutine](start/coroutine.md) + +* [Server (Async Style)](server/init.md) + * [Server TCP/UDP](server/tcp_init.md) + * [Method](server/methods.md) + * [Properti](server/properties.md) + * [Konfigurasi](server/setting.md) + * [Callback Event](server/events.md) + * [Server HTTP](http_server.md) + * [Server WebSocket](websocket_server.md) + * [Server Redis](redis_server.md) + * [Multi-port Listening](server/port.md) + +* [Server (Coroutine Style)](server/co_init.md) + * [Server TCP](coroutine/server.md) + * [Server HTTP](coroutine/http_server.md) + * [Server WebSocket](coroutine/ws_server.md) + +* [Klien](client_init.md) + * [Klien Sync Blocking](client.md) + * [Klien Async Callback](client_async.md) + * [Klien Coroutine](coroutine_client/init.md) + * [Klien TCP/UDP](coroutine_client/client.md) + * [Klien Socket](coroutine_client/socket.md) + * [Klien HTTP/WebSocket](coroutine_client/http_client.md) + * [Klien HTTP2](coroutine_client/http2_client.md) + * [Klien PostgreSQL](coroutine_client/postgresql.md) + * [Klien FastCGI](coroutine_client/fastcgi.md) + * [Klien MySQL](coroutine_client/mysql.md) + * [Klien Redis](coroutine_client/redis.md) + +* [Coroutine](coroutine.md) + * [Satu Klik Coroutine](runtime.md) + * [API Inti](coroutine/coroutine.md) + * [Coroutine Container](coroutine/scheduler.md) + * [API Sistem](coroutine/system.md) + * [API Proses](coroutine/proc_open.md) + * [Channel](coroutine/channel.md) + * [WaitGroup](coroutine/wait_group.md) + * [Barrier](coroutine/barrier.md) + * [Panggilan Konkuren](coroutine/multi_call.md) + * [IO-Uring](coroutine/iouring.md) + * [Connection Pool](coroutine/conn_pool.md) + * [Library](library.md) + * [Debug Coroutine](coroutine/gdb.md) + * [Panduan Pemrograman](coroutine/notice.md) + +* Operasi File Async + * [Implementasi](file/engine.md) + * [Konfigurasi](file/setting.md) + +* Manajemen Thread + * [Buat Thread](thread/thread.md) + * [Thread Pool](thread/pool.md) + * [Method & Properti](thread/info) + * [Concurrent Map](thread/map.md) + * [Concurrent List](thread/arraylist.md) + * [Concurrent Queue](thread/queue.md) + * [Sync Barrier](thread/barrier.md) + * [Tipe Data](thread/transfer.md) + +* Manajemen Proses + * [Buat Proses](process/process.md) + * [Process Pool](process/process_pool.md) + * [Process Manager](process/process_manager.md) + * [Shared Memory High-Performance (Table)](memory/table.md) + +* Manajemen Konkurensi + * [Lock](memory/lock.md) + * [Coroutine Lock](memory/coroutine_lock.md) + * [Atomic Counter](memory/atomic.md) + +* [Event Loop](event.md) + +* [Timer](timer.md) +* [Ekstensi PHP Standard Library](stdext/index.md) + * [String](stdext/string.md) + * [Array](stdext/array.md) + * [Stream](stdext/stream.md) + +* Lainnya + * [Konstanta](consts.md) + * [Kode Error](other/errno.md) + * [Konfigurasi ini](other/config.md) + * [Fungsi Lain](functions.md) + * [Alat](other/tools.md) + * [Alias Fungsi](other/alias.md) + * [Lapor Bug](other/issue.md) + * [Parameter Kernel](other/sysctl.md) + * [Daftar Sinyal Linux](other/signal.md) + * [Diskusi Online](other/discussion.md) + * [Kontributor](CONTRIBUTING.md) + * [Donasi](other/donate.md) + * [Pengguna & Studi Kasus](case.md) + +* Tanya Jawab + * [Masalah Instalasi](question/install.md) + * [Masalah Penggunaan](question/use.md) + * [Tentang Swoole](question/swoole.md) + +* Manajemen Versi + * [Rencana Dukungan](version/supported.md) + * [Perubahan Tidak Kompatibel](version/bc.md) + * [Catatan Rilis](version/log.md) + +* Belajar Swoole + * [Dasar-dasar](learn.md) + * [Panduan Pemrograman](getting_started/notice.md) + * [Pengetahuan Lain](learn_other.md) + * [Artikel Swoole](blog_list.md) diff --git a/public/id/blog_list.md b/public/id/blog_list.md new file mode 100644 index 0000000..f3976d5 --- /dev/null +++ b/public/id/blog_list.md @@ -0,0 +1,30 @@ +# Artikel Seri Swoole + +Untuk memudahkan pembelajaran Swoole, bagian ini mengumpulkan dan merangkum artikel-artikel seri Swoole. + +## PHP & Swoole + +* [Cara meneruskan pesan yang disubscribe dari Redis ke client WebSocket](https://segmentfault.com/a/1190000010986855) +* [Menggunakan fitur task Swoole di PHP-FPM atau Apache](https://segmentfault.com/a/1190000007993744) +* [Pembahasan tentang Swoole Coroutine dan Asynchronous IO](https://mp.weixin.qq.com/s/pymXJoSHIj3Cvtmi6lzcVw) + +## Swoole Kernel + +* [Optimasi kinerja pengiriman data dari master process ke worker process di Swoole Server](https://segmentfault.com/a/1190000021641401) +* [【Swoole Bagian 2】Sekilas tentang Swoole Server](https://mp.weixin.qq.com/s/Y1_1nJzh6XmPA9dCMzzdTA) +* [【Studi Kode Sumber Swoole】Memahami implementasi coroutine Swoole secara mendalam](https://mp.weixin.qq.com/s/xNXeLSqus4htHpbrVfSHdQ) +* [Perjalanan Swoole Coroutine](https://mp.weixin.qq.com/s/ETRlQQYmRb-IE4Ci5o_p0w) +* [Perjalanan Swoole Coroutine - Bagian Awal](https://segmentfault.com/a/1190000018889185) +* [Panduan Pengembangan Ekstensi C/C++ Coroutine (1): Keamanan Memori](https://segmentfault.com/a/1190000018533664) +* [Analisis prinsip implementasi swoole_table](https://segmentfault.com/a/1190000010853095) + +## Lainnya + +* [PHP & Swoole vs Java, Go, dan pemilihan teknologi lainnya](https://segmentfault.com/a/1190000020180096) +* [Diskusi tentang tren teknologi backend setelah 2018](https://segmentfault.com/a/1190000012975028) +* [Kemana arah masa depan programmer PHP di tahun 2017](https://segmentfault.com/a/1190000008888700) + +## Kolom + +* [Swoole - Zhihu](https://zhuanlan.zhihu.com/swoole) +* [Swoole - SegmentFault](https://segmentfault.com/blog/swoole) diff --git a/public/id/case.md b/public/id/case.md new file mode 100644 index 0000000..9d87b81 --- /dev/null +++ b/public/id/case.md @@ -0,0 +1,34 @@ +# Pengguna dan Studi Kasus + +Jika Anda menggunakan Swoole, silakan kirim informasi Anda ke daftar ini agar lebih banyak pengguna memahami skenario penggunaan Swoole yang sebenarnya, dan membangun ekosistem Swoole yang lebih baik. + +## Kami Menantikan Informasi Anda + +Buat `issue` di [swoole-inc/report](https://github.com/swoole-inc/report/issues) dengan informasi berikut: + +* Perusahaan, sekolah, atau organisasi Anda +* Kota dan negara Anda +* Kontak Anda: Weibo, email, WeChat +* Skenario bisnis tempat Anda menggunakan Swoole + +!> Jika tidak nyaman membagikan kontak, perlu mengubah informasi terkait, atau tidak ingin muncul di daftar ini, kirim email terpisah ke: team@swoole.com + +## Daftar Studi Kasus Pengguna + +Nama | Lokasi | Kontak | Skenario Bisnis +-|-|-|- +[Departemen Layanan Nilai Tambah Tencent](https://www.tencent.com) | Caohejing, Shanghai, China | - | Microservices, koneksi panjang +Xindong Network Co., Ltd. | Gedung A2, Jalan Wanrong, Distrik Jing'an, Shanghai, China | jonwang@xindong.com | Backend manajemen game dan aplikasi App, sistem manajemen microservices, sistem toko dalam game +[100tal Xueersi Online Education of Tomorrow Group](https://www.xueersi.com) | Beijing, China | hantianfeng@100tal.com | WebAPI, microservices, middleware, infrastruktur +JD.com | Shanghai, China | rsshuai74269@163.com | WebAPI, WebSocket +Shenzhoushunliban | Beijing, China | yydick@sohu.com | RPC microservices +LovePet Doctor | Shanghai, China | zhangwj@5ichong.com | WebAPI +[KK Group](https://www.kkguan.com) | Dongguan, Shenzhen, China | h@hyperf.io | Web API, microservices, infrastruktur, middleware +[Knowyourself](https://www.knowyourself.cc) | Shanghai, China | l@hyperf.io | Web API, microservices +[Mengtui](http://www.mengtuiapp.com) | Shanghai, China | d@hyperf.io | E-commerce, microservices +Glu Mobile | 875 Howard St #100, San Francisco, CA, AS | demin.yin@glu.com | Microservices untuk game mobile, backend pemrosesan data, dll. +[Departemen Infrastruktur Baidu](https://xray.baidu.com) | Beijing, China | Wang Xinhua | Membangun layanan pengumpulan data performa aplikasi berkinerja tinggi menggunakan Swoole. Saat ini mendukung lini produk utama Baidu seperti Mobile Baidu, Haokan Video, Baidu Tieba, Baidu Netdisk, dll. Puncak qps: 500.000 +[2345 Net](http://2345.net) | Shanghai, China | gaox@2345.com | Client HTTP dan TCP asinkron berbasis Swoole untuk push data real-time ke bisnis PC; pengguna online melebihi puluhan juta, kecepatan push mencapai 100.000 pesan/detik +[Legend of Sword](https://github.com/caohao-php/ycsocket) | Beijing, China | WeChat: 649947921 | Game mini WeChat "Legend of Sword", server game RPG role-playing skala besar +Jiayibingding (Beijing) E-commerce Co., Ltd. | Beijing, China | sibowen@jybdshop.cn | Mall microservices, CRM web api +Jiancaomao | Foshan, Guangdong, China | 381345509@qq.com | Layanan push, NLP word segmentation, API video pendek dan live streaming, pencarian produk Swoole-ElasticSearch-Sql diff --git a/public/id/client.md b/public/id/client.md new file mode 100644 index 0000000..9d2e97a --- /dev/null +++ b/public/id/client.md @@ -0,0 +1,872 @@ +# Swoole\Client + +`Swoole\Client`, selanjutnya disebut `Client`, menyediakan kode enkapsulasi untuk klien `TCP/UDP/UnixSocket`. Cukup gunakan `new Swoole\Client` untuk menggunakannya. Dapat digunakan di lingkungan `FPM/Apache`. +Dibandingkan dengan fungsi [streams](https://www.php.net/streams) tradisional, ada beberapa keunggulan: + +* Fungsi `stream` memiliki waktu tunggu default yang lama, jika respons lawan lama, dapat menyebabkan pemblokiran berkepanjangan +* `fread` pada fungsi `stream` memiliki batas panjang buffer default `8192`, tidak dapat mendukung paket besar `UDP` +* `Client` mendukung `waitall`, bisa mengambil data sekaligus jika panjang paket sudah pasti, tidak perlu membaca berulang kali +* `Client` mendukung `UDP Connect`, mengatasi masalah penggabungan paket `UDP` +* `Client` adalah kode `C` murni yang khusus menangani `socket`, fungsi `stream` sangat kompleks. `Client` memiliki performa lebih baik +* Dapat menggunakan fungsi [swoole_client_select](/client?id=swoole_client_select) untuk mengontrol konkurensi beberapa `Client` + +### Contoh Lengkap + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP); +if (!$client->connect('127.0.0.1', 9501, -1)) { + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); +``` + +## Method + +### __construct() + +Method konstruktor + +```php +Swoole\Client::__construct(int $sock_type, bool $is_sync = false, string $key); +``` + +* **Parameter** + + * **`int $sock_type`** + * **Fungsi**: Menentukan jenis `socket` [mendukung `SWOOLE_SOCK_TCP`, `SWOOLE_SOCK_TCP6`, `SWOOLE_SOCK_UDP`, `SWOOLE_SOCK_UDP6`]. Lihat [bagian ini](/server/methods?id=__construct) untuk penjelasan lebih lanjut + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`bool $is_sync`** + * **Fungsi**: Mode sinkron blocking, hanya bisa diisi `false`. Untuk mode asynchronous callback, gunakan `Swoole\Async\Client` + * **Default**: `false` + * **Nilai Lain**: Tidak ada + + * **`string $key`** + * **Fungsi**: `Key` untuk koneksi panjang [secara default menggunakan `IP:PORT` sebagai `key`. `key` yang sama, meskipun new dua kali, tetap hanya menggunakan satu koneksi TCP] + * **Default**: `IP:PORT` + * **Nilai Lain**: Tidak ada + +!> Gunakan makro yang disediakan oleh level bawah untuk menentukan jenis, lihat [definisi konstanta](/consts) + +#### Membuat Koneksi Panjang di PHP-FPM/Apache + +```php +$cli = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP); +``` + +Setelah menambahkan flag [SWOOLE_KEEP](/client?id=swoole_keep), koneksi `TCP` yang dibuat tidak akan ditutup saat request PHP selesai atau saat memanggil `$cli->close()`. Saat berikutnya `connect` dipanggil, akan menggunakan koneksi yang dibuat sebelumnya. Cara penyimpanan koneksi panjang secara default menggunakan `ServerHost:ServerPort` sebagai `key`. Bisa menentukan `key` di parameter ke-`3`. + +Destruktor objek `Client` otomatis memanggil method [close](/client?id=close) untuk menutup `socket` + +#### Menggunakan Client di Server + +* `Client` harus digunakan di [fungsi callback](/server/events) event. +* `Server` dapat terhubung dengan `socket client` dari bahasa pemrograman apa pun. Begitu pula `Client` dapat terhubung ke `socket server` dari bahasa pemrograman apa pun. + +!> Menggunakan `Client` ini di lingkungan korutin `Swoole4+` akan menurun ke [model sinkron](/learn?id=同步io异步io). + +### set() + +Mengatur parameter klien, harus dijalankan sebelum [connect](/client?id=connect). + +```php +Swoole\Client->set(array $settings); +``` + +Lihat [opsi konfigurasi](/client?id=配置) Client untuk opsi yang tersedia. + +### connect() + +Terhubung ke server jarak jauh. + +```php +Swoole\Client->connect(string $host, int $port, float $timeout = 0.5, int $sock_flag = 0): bool +``` + +* **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat server [mendukung resolusi domain asinkron otomatis, `$host` bisa langsung diisi domain] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu tunggu + * **Satuan**: Detik [mendukung float, misal `1.5` berarti `1s` + `500ms`] + * **Default**: `0.5` + * **Nilai Lain**: Tidak ada + + * **`int $sock_flag`** + * Untuk tipe `UDP`, menandakan apakah mengaktifkan `udp_connect`. Setelah diaktifkan, `$host` dan `$port` akan diikat, paket dari `host/port` yang tidak ditentukan akan dibuang. + * Untuk tipe `TCP`, `$sock_flag=1` berarti mengatur `socket` non-blocking, setelah itu fd ini akan menjadi [IO asinkron](/learn?id=同步io异步io), `connect` akan segera kembali. Jika `$sock_flag` diatur ke `1`, maka sebelum `send/recv` harus menggunakan [swoole_client_select](/client?id=swoole_client_select) untuk memeriksa apakah koneksi selesai. + +* **Return Value** + + * Mengembalikan `true` + * Mengembalikan `false`, periksa properti `errCode` untuk penyebab kegagalan + +* **Mode Sinkron** + +Method `connect` akan memblokir hingga koneksi berhasil dan mengembalikan `true`. Setelah itu bisa mengirim atau menerima data dari server. + +```php +if ($cli->connect('127.0.0.1', 9501)) { + $cli->send("data"); +} else { + echo "connect failed."; +} +``` + +Jika koneksi gagal, akan mengembalikan `false` + +> Klien `TCP` sinkron setelah menjalankan `close`, dapat melakukan `Connect` lagi untuk membuat koneksi baru ke server + +* **Koneksi Ulang Saat Gagal** + +Jika `connect` gagal dan ingin mencoba koneksi ulang, harus melakukan `close` terlebih dahulu untuk menutup `socket` lama, jika tidak akan mengembalikan error `EINPROCESS` karena `socket` saat ini sedang terhubung ke server, klien tidak tahu apakah koneksi berhasil, sehingga tidak dapat menjalankan `connect` lagi. Memanggil `close` akan menutup `socket` saat ini, dan level bawah akan membuat `socket` baru untuk koneksi. + +!> Saat mengaktifkan [SWOOLE_KEEP](/client?id=swoole_keep), parameter pertama dari `close` harus diatur ke `true` untuk memaksa menghancurkan koneksi panjang `socket` + +```php +if ($socket->connect('127.0.0.1', 9502) === false) { + $socket->close(true); + $socket->connect('127.0.0.1', 9502); +} +``` + +* **UDP Connect** + +Secara default level bawah tidak mengaktifkan `udp connect`. Saat klien `UDP` menjalankan `connect`, level bawah akan segera mengembalikan sukses setelah membuat `socket`. Pada saat ini `socket` terikat ke alamat `0.0.0.0`, lawan mana pun dapat mengirim paket ke port ini. + +Misal `$client->connect('192.168.1.100', 9502)`, sistem operasi akan memberikan port acak `58232` untuk klien `socket`. Mesin lain seperti `192.168.1.101` juga dapat mengirim paket ke port ini. + +?> Tanpa `udp connect`, `getsockname` akan mengembalikan `host` `0.0.0.0` + +Atur parameter ke-`4` menjadi `1` untuk mengaktifkan `udp connect`, `$client->connect('192.168.1.100', 9502, 1, 1)`. Maka klien dan server akan terikat, level bawah akan mengikat `socket` berdasarkan alamat server. Misalnya terhubung ke `192.168.1.100`, `socket` akan terikat ke alamat lokal `192.168.1.*`. Setelah mengaktifkan `udp connect`, klien tidak akan lagi menerima paket dari host lain ke port ini. + +### recv() + +Menerima data dari server. + +```php +Swoole\Client->recv(int $size = 65535, int $flags = 0): string | false +``` + +* **Parameter** + + * **`int $size`** + * **Fungsi**: Panjang maksimum buffer data yang diterima [jangan diatur terlalu besar, karena akan memakan banyak memori] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $flags`** + * **Fungsi**: Parameter tambahan [seperti [Client::MSG_WAITALL](/client?id=clientmsg_waitall)], lihat [bagian ini](/client?id=常量) untuk parameter spesifik + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil menerima data mengembalikan string + * Koneksi ditutup mengembalikan string kosong + * Mengembalikan `false`, dan mengatur properti `$client->errCode` + +* **Protokol EOF/Length** + + * Jika klien mengaktifkan deteksi `EOF/Length`, tidak perlu mengatur `$size` dan `$waitall`. Lapisan ekstensi akan mengembalikan paket lengkap atau `false`, lihat bab [Protokol Analisis](/client?id=协议解析). + * Saat menerima header paket yang salah atau nilai panjang melebihi [package_max_length](/server/setting?id=package_max_length), `recv` akan mengembalikan string kosong, kode PHP harus menutup koneksi ini. + +### send() + +Mengirim data ke server jarak jauh, harus setelah koneksi dibuat. + +```php +Swoole\Client->send(string $data): int|false +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Konten yang dikirim [mendukung data biner] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil mengirim, mengembalikan panjang data terkirim + * Mengembalikan `false`, dan mengatur properti `errCode` + +* **Catatan** + + * Jika belum menjalankan `connect`, memanggil `send` akan memicu peringatan + * Data yang dikirim tidak memiliki batasan panjang + * Jika data terlalu besar dan buffer Socket penuh, program akan memblokir menunggu dapat menulis + +### sendfile() + +Mengirim file ke server, fungsi ini berdasarkan panggilan sistem operasi `sendfile` + +```php +Swoole\Client->sendfile(string $filename, int $offset = 0, int $length = 0): bool +``` + +!> sendfile tidak bisa digunakan untuk klien UDP dan koneksi terenkripsi SSL tunnel + +* **Parameter** + + * **`string $filename`** + * **Fungsi**: Menentukan path file yang akan dikirim + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $offset`** + * **Fungsi**: Offset upload file [dapat menentukan mulai transmisi data dari bagian tengah file. Fitur ini dapat digunakan untuk mendukung resumable upload] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $length`** + * **Fungsi**: Ukuran data yang dikirim [default ukuran seluruh file] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Jika file yang dimasukkan tidak ada, akan mengembalikan `false` + * Eksekusi berhasil mengembalikan `true` + +* **Catatan** + + * `sendfile` akan memblokir hingga seluruh file terkirim atau terjadi error fatal + +### sendto() + +Mengirim paket `UDP` ke host `IP:PORT` mana pun, hanya mendukung tipe `SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6` + +```php +Swoole\Client->sendto(string $ip, int $port, string $data): bool +``` + +* **Parameter** + + * **`string $ip`** + * **Fungsi**: Alamat `IP` host tujuan, mendukung `IPv4/IPv6` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port host tujuan + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $data`** + * **Fungsi**: Data yang akan dikirim [tidak boleh melebihi `64K`] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +### enableSSL() + +Mengaktifkan enkripsi tunnel SSL secara dinamis, hanya dapat digunakan jika mengompilasi `swoole` dengan `--enable-openssl`. + +```php +Swoole\Client->enableSSL(): bool +``` + +Klien menggunakan komunikasi teks biasa saat membuat koneksi, dan ingin beralih ke enkripsi `SSL` di tengah jalan, dapat menggunakan method `enableSSL`. Jika sejak awal sudah SSL, lihat [konfigurasi SSL](/client?id=ssl相关). Menggunakan `enableSSL` untuk mengaktifkan enkripsi tunnel `SSL` secara dinamis, perlu memenuhi dua syarat: + +* Tipe klien saat dibuat harus non-`SSL` +* Klien sudah terhubung dengan server + +Memanggil `enableSSL` akan memblokir menunggu jabat tangan `SSL` selesai. + +* **Contoh** + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +// Aktifkan enkripsi tunnel SSL +if ($client->enableSSL()) +{ + // Jabat tangan selesai, data yang dikirim dan diterima sudah terenkripsi + $client->send("hello world\n"); + echo $client->recv(); +} +$client->close(); +``` + +### getPeerCert() + +Mendapatkan informasi sertifikat server, hanya dapat digunakan jika mengompilasi `swoole` dengan `--enable-openssl`. + +```php +Swoole\Client->getPeerCert(): string|false +``` + +* **Return Value** + + * Mengembalikan string informasi sertifikat `X509` + * Mengembalikan `false` + +!> Method ini hanya bisa dipanggil setelah jabat tangan SSL selesai. + +Dapat menggunakan fungsi `openssl_x509_parse` dari ekstensi `openssl` untuk mengurai informasi sertifikat. + +!> Perlu mengaktifkan [--enable-openssl](/environment?id=编译选项) saat mengompilasi swoole + +### verifyPeerCert() + +Memverifikasi sertifikat server, hanya dapat digunakan jika mengompilasi `swoole` dengan `--enable-openssl`. + +```php +Swoole\Client->verifyPeerCert() +``` + +### isConnected() + +Mengembalikan status koneksi Client + +* Mengembalikan false, berarti saat ini tidak terhubung ke server +* Mengembalikan true, berarti saat ini terhubung ke server + +```php +Swoole\Client->isConnected(): bool +``` + +!> Method `isConnected` mengembalikan status lapisan aplikasi, hanya menunjukkan bahwa `Client` telah menjalankan `connect` dan berhasil terhubung ke `Server`, dan belum menjalankan `close` untuk menutup koneksi. +`Client` dapat menjalankan `send`, `recv`, `close` dll, tetapi tidak dapat menjalankan `connect` lagi. Ini tidak berarti koneksi pasti tersedia, saat menjalankan `send` atau `recv` masih mungkin mengembalikan error, karena lapisan aplikasi tidak bisa mendapatkan status koneksi `TCP` level bawah, hanya saat `send` atau `recv` terjadi interaksi dengan kernel, barulah status ketersediaan koneksi yang sebenarnya didapatkan. + +### getSockName() + +Digunakan untuk mendapatkan host:port lokal socket klien. + +!> Hanya dapat digunakan setelah koneksi + +```php +Swoole\Client->getsockname(): array|false +``` + +* **Return Value** + +```php +array('host' => '127.0.0.1', 'port' => 53652); +``` + +### getPeerName() + +Mendapatkan alamat IP dan port socket lawan. + +!> Hanya mendukung tipe `SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6/SWOOLE_SOCK_UNIX_DGRAM` + +```php +Swoole\Client->getpeername(): array|false +``` + +Setelah klien protokol `UDP` mengirim paket ke server, mungkin bukan server tersebut yang merespons. Dapat menggunakan method `getpeername` untuk mendapatkan `IP:PORT` server yang sebenarnya merespons. + +!> Fungsi ini harus dipanggil setelah `$client->recv()` + +### close() + +Menutup koneksi. + +```php +Swoole\Client->close(bool $force = false): bool +``` + +* **Parameter** + + * **`bool $force`** + * **Fungsi**: Memaksa menutup koneksi [dapat digunakan untuk menutup koneksi panjang [SWOOLE_KEEP](/client?id=swoole_keep)] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +Setelah koneksi `swoole_client` di-`close`, jangan melakukan `connect` lagi. Cara yang benar adalah menghancurkan `Client` saat ini, membuat `Client` baru, dan membuat koneksi baru. + +Objek `Client` akan otomatis `close` saat di-destruksi. + +### shutdown() + +Menutup klien + +```php +Swoole\Client->shutdown(int $how): bool +``` + +* **Parameter** + + * **`int $how`** + * **Fungsi**: Mengatur cara menutup klien + * **Default**: Tidak ada + * **Nilai Lain**: Swoole\Client::SHUT_RDWR (tutup baca dan tulis), SHUT_RD (tutup baca), Swoole\Client::SHUT_WR (tutup tulis) + +### getSocket() + +Mendapatkan handle `socket` level bawah, objek yang dikembalikan adalah handle resource `sockets`. + +!> Method ini membutuhkan ekstensi `sockets`, dan perlu mengaktifkan opsi [--enable-sockets](/environment?id=编译选项) saat kompilasi + +```php +Swoole\Client->getSocket() +``` + +Dapat menggunakan fungsi `socket_set_option` untuk mengatur parameter `socket` yang lebih rendah. + +```php +$socket = $client->getSocket(); +if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; +} +``` + +### swoole_client_select + +Swoole\Client menggunakan panggilan sistem `select` untuk [event loop IO](/learn?id=什么是eventloop) dalam pemrosesan paralel, bukan epoll_wait. Berbeda dengan [Event module](/event), fungsi ini digunakan di lingkungan IO sinkron (jika dipanggil di proses Worker Swoole, akan menyebabkan epoll [event loop IO](/learn?id=什么是eventloop) Swoole sendiri tidak mendapat kesempatan untuk dijalankan). + +Prototipe fungsi: + +```php +int swoole_client_select(array &$read, array &$write, array &$error, float $timeout); +``` + +* `swoole_client_select` menerima 4 parameter, `$read`, `$write`, `$error` masing-masing adalah deskriptor file yang dapat dibaca/ditulis/error. +* 3 parameter ini harus berupa referensi variabel array. Elemen array harus berupa objek `swoole_client`. +* Method ini berdasarkan panggilan sistem `select`, maksimal mendukung `1024` `socket` +* Parameter `$timeout` adalah waktu tunggu panggilan sistem `select`, satuan detik, menerima float +* Fungsinya mirip dengan `stream_select()` asli PHP, bedanya stream_select hanya mendukung tipe variabel stream PHP, dan performanya buruk. + +Setelah berhasil dipanggil, akan mengembalikan jumlah event, dan memodifikasi array `$read`/`$write`/`$error`. Gunakan foreach untuk melintasi array, lalu jalankan `$item->recv`/`$item->send` untuk mengirim/menerima data. Atau panggil `$item->close()` atau `unset($item)` untuk menutup `socket`. + +`swoole_client_select` mengembalikan `0` berarti dalam waktu yang ditentukan, tidak ada IO yang tersedia, panggilan `select` telah timeout. + +!> Fungsi ini dapat digunakan di lingkungan `Apache/PHP-FPM` + +```php +$clients = array(); + +for($i=0; $i< 20; $i++) +{ + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); // sinkron blocking + $ret = $client->connect('127.0.0.1', 9501, 0.5, 0); + if(!$ret) + { + echo "Connect Server fail.errCode=".$client->errCode; + } + else + { + $client->send("HELLO WORLD\n"); + $clients[$client->sock] = $client; + } +} + +while (!empty($clients)) +{ + $write = $error = array(); + $read = array_values($clients); + $n = swoole_client_select($read, $write, $error, 0.6); + if ($n > 0) + { + foreach ($read as $index => $c) + { + echo "Recv #{$c->sock}: " . $c->recv() . "\n"; + unset($clients[$c->sock]); + } + } +} +``` + +## Properti + +### errCode + +Kode error + +```php +Swoole\Client->errCode: int +``` + +Saat `connect/send/recv/close` gagal, nilai `$swoole_client->errCode` akan diatur secara otomatis. + +Nilai `errCode` sama dengan `Linux errno`. Dapat menggunakan `socket_strerror` untuk mengubah kode error menjadi pesan error. + +```php +echo socket_strerror($client->errCode); +``` + +Referensi: [Daftar Kode Error Linux](/other/errno?id=linux) + +### sock + +Deskriptor file koneksi socket. + +```php +Swoole\Client->sock; +``` + +Dalam kode PHP dapat menggunakan + +```php +$sock = fopen("php://fd/".$swoole_client->sock); +``` + +* Mengubah `socket` dari `Swoole\Client` menjadi `stream socket`. Dapat memanggil fungsi `fread/fwrite/fclose` dll. + +* `$fd` di [Swoole\Server](/server/methods?id=__construct) tidak bisa menggunakan method ini, karena `$fd` hanya angka, deskriptor file `$fd` milik proses utama, lihat mode [SWOOLE_PROCESS](/learn?id=swoole_process). + +* `$swoole_client->sock` dapat diubah menjadi int sebagai `key` array. + +!> Perlu diperhatikan: nilai properti `$swoole_client->sock` hanya bisa didapatkan setelah `$swoole_client->connect`. Sebelum terhubung ke server, nilai properti ini adalah `null`. + +### reuse + +Menunjukkan apakah koneksi ini baru dibuat atau menggunakan kembali yang sudah ada. Digunakan bersama [SWOOLE_KEEP](/client?id=swoole_keep). + +#### Skenario Penggunaan + +Setelah klien `WebSocket` terhubung ke server, perlu melakukan jabat tangan. Jika koneksi menggunakan kembali, tidak perlu jabat tangan lagi, langsung kirim frame data `WebSocket`. + +```php +if ($client->reuse) { + $client->send($data); +} else { + $client->doHandShake(); + $client->send($data); +} +``` + +### reuseCount + +Menunjukkan berapa kali koneksi ini digunakan kembali. Digunakan bersama [SWOOLE_KEEP](/client?id=swoole_keep). + +```php +Swoole\Client->reuseCount; +``` + +### type + +Menunjukkan jenis `socket`, akan mengembalikan nilai `$sock_type` dari `Swoole\Client::__construct()` + +```php +Swoole\Client->type; +``` + +### id + +Mengembalikan nilai `$key` dari `Swoole\Client::__construct()`, digunakan bersama [SWOOLE_KEEP](/client?id=swoole_keep) + +```php +Swoole\Client->id; +``` + +### setting + +Mengembalikan konfigurasi yang diatur oleh `Swoole\Client::set()` + +```php +Swoole\Client->setting; +``` + +## Konstanta + +### SWOOLE_KEEP + +Swoole\Client mendukung pembuatan koneksi TCP panjang ke server di `PHP-FPM/Apache`. Cara penggunaan: + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP); +$client->connect('127.0.0.1', 9501); +``` + +Setelah mengaktifkan opsi `SWOOLE_KEEP`, request selesai tidak akan menutup `socket`, saat `connect` berikutnya akan otomatis menggunakan koneksi yang dibuat sebelumnya. Jika `connect` mendeteksi koneksi sudah ditutup server, maka `connect` akan membuat koneksi baru. + +?> Keunggulan SWOOLE_KEEP + +* Koneksi panjang `TCP` dapat mengurangi konsumsi IO tambahan dari `connect` 3-way handshake / `close` 4-way handshake +* Mengurangi jumlah `close`/`connect` di sisi server + +### Swoole\Client::MSG_WAITALL + +* Jika menetapkan parameter Client::MSG_WAITALL, harus menentukan `$size` yang akurat, jika tidak akan menunggu terus sampai panjang data yang diterima mencapai $size +* Tanpa Client::MSG_WAITALL, `$size` maksimal `64K` +* Jika `$size` salah, akan menyebabkan `recv` timeout, mengembalikan `false` + +### Swoole\Client::MSG_DONTWAIT + +Menerima data secara non-blocking, akan segera kembali baik ada data maupun tidak. + +### Swoole\Client::MSG_PEEK + +Mengintip data di buffer `socket`. Dengan parameter `MSG_PEEK`, `recv` membaca data tidak akan mengubah pointer, sehingga panggilan `recv` berikutnya masih akan mengembalikan data dari posisi sebelumnya. + +### Swoole\Client::MSG_OOB + +Membaca data out-of-band, silakan cari "`TCP out-of-band data`". + +### Swoole\Client::SHUT_RDWR + +Menutup sisi baca dan tulis klien. + +### Swoole\Client::SHUT_RD + +Menutup sisi baca klien. + +### Swoole\Client::SHUT_WR + +Menutup sisi tulis klien. + +## Konfigurasi + +`Client` dapat menggunakan method `set` untuk mengatur beberapa opsi, mengaktifkan fitur tertentu. + +### Analisis Protokol + +?> Analisis protokol untuk mengatasi [masalah batas paket TCP](/learn?id=tcp数据包边界问题), arti konfigurasi terkait sama dengan `Swoole\Server`. Detail lihat bab konfigurasi [Protokol Swoole\Server](/server/setting?id=open_eof_check). + +* **Deteksi EOF** + +```php +$client->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n\r\n", + 'package_max_length' => 1024 * 1024 * 2, +)); +``` + +* **Deteksi Panjang** + +```php +$client->set(array( + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, // Byte ke-N adalah nilai panjang paket + 'package_body_offset' => 4, // Mulai hitung panjang dari byte ke berapa + 'package_max_length' => 2000000, // Panjang maksimum protokol +)); +``` + +!> Saat ini mendukung [open_length_check](/server/setting?id=open_length_check) dan [open_eof_check](/server/setting?id=open_eof_check) 2 fungsi pemrosesan protokol otomatis; +Setelah mengonfigurasi analisis protokol, method `recv()` klien tidak akan menerima parameter panjang, dan akan selalu mengembalikan paket data lengkap. + +* **Protokol MQTT** + +!> Mengaktifkan analisis protokol `MQTT`, callback [onReceive](/server/events?id=onreceive) akan menerima paket `MQTT` lengkap. + +```php +$client->set(array( + 'open_mqtt_protocol' => true, +)); +``` + +* **Ukuran Buffer Socket** + +!> Termasuk buffer OS level bawah `socket`, buffer memori penerima data lapisan aplikasi, buffer memori pengirim data lapisan aplikasi. + +```php +$client->set(array( + 'socket_buffer_size' => 1024 * 1024 * 2, // buffer 2M +)); +``` + +* **Nonaktifkan Algoritma Nagle** + +```php +$client->set(array( + 'open_tcp_nodelay' => true, +)); +``` + +### SSL + +* **Konfigurasi Sertifikat SSL/TLS** + +```php +$client->set(array( + 'ssl_cert_file' => $your_ssl_cert_file_path, + 'ssl_key_file' => $your_ssl_key_file_path, +)); +``` + +* **ssl_verify_peer** + +Memverifikasi sertifikat server. + +```php +$client->set([ + 'ssl_verify_peer' => true, +]); +``` + +Setelah diaktifkan akan memverifikasi apakah sertifikat cocok dengan domain host, jika tidak, koneksi akan otomatis ditutup + +* **Sertifikat Self-Signed** + +Atur `ssl_allow_self_signed` ke `true` untuk mengizinkan sertifikat self-signed. + +```php +$client->set([ + 'ssl_verify_peer' => true, + 'ssl_allow_self_signed' => true, +]); +``` + +* **ssl_host_name** + +Mengatur nama host server, digunakan bersama konfigurasi `ssl_verify_peer` atau [Client::verifyPeerCert](/client?id=verifypeercert). + +```php +$client->set([ + 'ssl_host_name' => 'www.google.com', +]); +``` + +* **ssl_cafile** + +Saat `ssl_verify_peer` diatur ke `true`, digunakan untuk memverifikasi sertifikat `CA` yang digunakan untuk sertifikat jarak jauh. Nilai opsi ini adalah path lengkap dan nama file sertifikat `CA` di sistem file lokal. + +```php +$client->set([ + 'ssl_cafile' => '/etc/CA', +]); +``` + +* **ssl_capath** + +Jika `ssl_cafile` tidak diatur, atau file yang ditunjuk `ssl_cafile` tidak ada, maka akan mencari sertifikat yang sesuai di direktori yang ditentukan `ssl_capath`. Direktori ini harus merupakan direktori sertifikat yang sudah di-hash. + +```php +$client->set([ + 'ssl_capath' => '/etc/capath/', +]) +``` + +* **ssl_passphrase** + +Password file sertifikat lokal [ssl_cert_file](/server/setting?id=ssl_cert_file). + +* **Contoh** + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL); + +$client->set(array( + 'ssl_cert_file' => __DIR__.'/ca/client-cert.pem', + 'ssl_key_file' => __DIR__.'/ca/client-key.pem', + 'ssl_allow_self_signed' => true, + 'ssl_verify_peer' => true, + 'ssl_cafile' => __DIR__.'/ca/ca-cert.pem', +)); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +echo "connect ok\n"; +$client->send("hello world-" . str_repeat('A', $i) . "\n"); +echo $client->recv(); +``` + +### package_length_func + +Mengatur fungsi kalkulasi panjang, cara penggunaan sama persis dengan [package_length_func](/server/setting?id=package_length_func) di `Swoole\Server`. Digunakan bersama [open_length_check](/server/setting?id=open_length_check). Fungsi panjang harus mengembalikan integer. + +* Mengembalikan `0`, data tidak cukup, perlu menerima lebih banyak data +* Mengembalikan `-1`, data error, level bawah akan otomatis menutup koneksi +* Mengembalikan nilai panjang total paket (termasuk panjang total header dan body paket), level bawah akan otomatis menggabungkan paket dan mengembalikannya ke fungsi callback + +Secara default level bawah maksimal membaca data `8K`, jika panjang header kecil mungkin ada konsumsi salinan memori. Atur parameter `package_body_offset`, level bawah hanya membaca header untuk analisis panjang. + +* **Contoh** + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP); +$client->set(array( + 'open_length_check' => true, + 'package_length_func' => function ($data) { + if (strlen($data) < 8) { + return 0; + } + $length = intval(trim(substr($data, 0, 8))); + if ($length <= 0) { + return -1; + } + return $length + 8; + }, +)); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); +``` + +### socks5_proxy + +Konfigurasi proxy socks5. + +!> Mengatur hanya satu opsi tidak valid, setiap kali harus mengatur `host` dan `port`; `socks5_username`, `socks5_password` adalah parameter opsional. `socks5_port`, `socks5_password` tidak boleh `null`. + +```php +$client->set(array( + 'socks5_host' => '192.168.1.100', + 'socks5_port' => 1080, + 'socks5_username' => 'username', + 'socks5_password' => 'password', +)); +``` + +### http_proxy + +Konfigurasi proxy HTTP. + +!> `http_proxy_port`, `http_proxy_password` tidak boleh `null`. + +* **Pengaturan Dasar** + +```php +$client->set(array( + 'http_proxy_host' => '192.168.1.100', + 'http_proxy_port' => 1080, +)); +``` + +* **Pengaturan Autentikasi** + +```php +$client->set(array( + 'http_proxy_user' => 'test', + 'http_proxy_password' => 'test_123456', +)); +``` + +### bind + +!> Hanya mengatur bind_port tidak valid, atur bind_port dan bind_address secara bersamaan + +?> Jika mesin memiliki beberapa kartu jaringan, mengatur parameter `bind_address` dapat memaksa `Socket` klien terikat ke alamat jaringan tertentu. +Mengatur `bind_port` memungkinkan `Socket` klien menggunakan port tetap untuk terhubung ke server luar. + +```php +$client->set(array( + 'bind_address' => '192.168.1.100', + 'bind_port' => 36002, +)); +``` + +### Lingkup + +Item konfigurasi `Client` di atas juga berlaku untuk klien berikut: + +* [Swoole\Coroutine\Client](/coroutine_client/client) +* [Swoole\Coroutine\Http\Client](/coroutine_client/http_client) +* [Swoole\Coroutine\Http2\Client](/coroutine_client/http2_client) diff --git a/public/id/client_async.md b/public/id/client_async.md new file mode 100644 index 0000000..5c53e1d --- /dev/null +++ b/public/id/client_async.md @@ -0,0 +1,166 @@ +# Swoole\Async\Client + +`Swoole\Async\Client`, selanjutnya disebut `Client`, adalah klien jaringan asinkron non-blocking `TCP/UDP/UnixSocket`. Klien asinkron perlu mengatur fungsi callback event, bukan menunggu secara sinkron. + +- Klien asinkron adalah subclass dari `Swoole\Client`, dapat memanggil beberapa method dari klien blocking sinkron +- Hanya tersedia di versi `6.0` atau lebih + +## Contoh Lengkap + +```php +$cli = new Swoole\Async\Client(SWOOLE_SOCK_TCP); + +$client->on("connect", function(Swoole\Async\Client $client) { + Assert::true($client->isConnected()); + $client->send(RandStr::gen(1024, RandStr::ALL)); +}); + +$client->on("receive", function(Swoole\Async\Client $client, string $data){ + $recv_len = strlen($data); + $client->send(RandStr::gen(1024, RandStr::ALL)); + $client->close(); + Assert::false($client->isConnected()); +}); + +$client->on("error", function(Swoole\Async\Client $client) { + echo "error"; +}); + +$client->on("close", function(Swoole\Async\Client $client) { + echo "close"; +}); + +$client->connect("127.0.0.1", 9501, 0.2); +``` + +## Method + +Halaman ini hanya mencantumkan method yang berbeda dari `Swoole\Client`. Untuk method yang tidak dimodifikasi oleh subclass, lihat [klien blocking sinkron](client.md). + +### __construct() + +Method konstruktor, lihat konstruktor kelas induk + +```php +Swoole\Async\Client::__construct(int $sock_type, bool $async = true, string $key); +``` + +> Parameter kedua klien asinkron harus `true` + +### on() + +Mendaftarkan fungsi callback event `Client`. + +```php +Swoole\Async\Client->on(string $event, callable $callback): bool +``` + +!> Memanggil method `on` berulang kali akan menimpa pengaturan sebelumnya + +* **Parameter** + + * `string $event` + * Fungsi: Nama event callback, tidak sensitif huruf besar/kecil + * Default: Tidak ada + * Nilai Lain: Tidak ada + + * `callable $callback` + * Fungsi: Fungsi callback + * Default: Tidak ada + * Nilai Lain: Tidak ada + + !> Dapat berupa string nama fungsi, method statis kelas, array method objek, fungsi anonim. Lihat [bagian ini](/learn?id=几种设置回调函数的方式). + +* **Return Value** + + * Mengembalikan `true` jika operasi berhasil, `false` jika gagal. + +### isConnected() +Menentukan apakah klien saat ini sudah terhubung dengan server. + +```php +Swoole\Async\Client->isConnected(): bool +``` + +* Mengembalikan `true` jika terhubung, `false` jika tidak terhubung + +### sleep() + +Menghentikan sementara penerimaan data. Setelah dipanggil, akan dihapus dari event loop dan tidak lagi memicu event penerimaan data, kecuali method `wakeup()` dipanggil untuk melanjutkan. + +```php +Swoole\Async\Client->sleep(): bool +``` + +* Mengembalikan `true` jika operasi berhasil, `false` jika gagal + +### wakeup() + +Melanjutkan penerimaan data. Setelah dipanggil, akan ditambahkan ke event loop. + +```php +Swoole\Async\Client->wakeup(): bool +``` + +* Mengembalikan `true` jika operasi berhasil, `false` jika gagal + +### enableSSL() + +Mengaktifkan enkripsi `SSL/TLS` secara dinamis, biasanya digunakan untuk klien `startTLS`. Kirim data teks biasa dulu setelah koneksi dibuat, lalu mulai transmisi terenkripsi. + +```php +Swoole\Async\Client->enableSSL(callable $callback): bool +``` + +* Fungsi ini hanya bisa dipanggil setelah `connect` berhasil +* Klien asinkron harus mengatur `$callback`, yang akan dipanggil setelah jabat tangan `SSL` selesai +* Mengembalikan `true` jika operasi berhasil, `false` jika gagal + +## Event Callback + +### connect +Terpicu setelah koneksi dibuat. Jika proxy `HTTP` atau `Socks5` dan enkripsi tunnel `SSL` diatur, akan terpicu setelah jabat tangan proxy selesai dan jabat tangan enkripsi `SSL` selesai. + +```php +$client->on("connect", function(Swoole\Async\Client $client) { + Assert::true($client->isConnected()); +}); +``` + +Setelah event callback ini, menggunakan `isConnected()` akan mengembalikan `true` + +### error +Terpicu saat koneksi gagal dibuat. Dapat membaca `$client->errCode` untuk mendapatkan informasi error. + +```php +$client->on("error", function(Swoole\Async\Client $client) { + var_dump($client->errCode); +}); +``` + +- Perhatikan bahwa `connect` dan `error` hanya akan terpicu salah satu, koneksi berhasil atau gagal, hanya ada satu hasil +- `Client::connect()` mungkin langsung mengembalikan `false`, menandakan koneksi gagal, saat itu callback `error` tidak akan dijalankan, pastikan memeriksa nilai kembali panggilan `connect` +- Event `error` adalah hasil asinkron, ada waktu tunggu `IO` antara inisiasi koneksi hingga event `error` terpicu +- `connect` gagal langsung adalah kegagalan seketika, dipicu langsung oleh sistem operasi, tidak ada waktu tunggu `IO` + +### receive +Terpicu setelah data diterima + +```php +$client->on("receive", function(Swoole\Async\Client $client, string $data){ + var_dump(strlen($data)); +}); +``` + +- Jika tidak ada protokol yang diatur, seperti `EOF` atau `LENGTH`, panjang data maksimum yang dikembalikan adalah `64K` +- Jika parameter pemrosesan protokol diatur, panjang data maksimum ditentukan oleh parameter `package_max_length`, default `2M` +- `$data` pasti tidak kosong. Jika ada error sistem atau koneksi ditutup, event `close` akan terpicu + +### close +Terpicu saat koneksi ditutup + +```php +$client->on("close", function(Swoole\Async\Client $client) { + echo "close"; +}); +``` diff --git a/public/id/client_init.md b/public/id/client_init.md new file mode 100644 index 0000000..594069f --- /dev/null +++ b/public/id/client_init.md @@ -0,0 +1,3 @@ +# Swoole Client + +Bagian ini mencakup semua client yang disediakan Swoole, termasuk synchronous blocking client dan coroutine client. Swoole 4 gak lagi mendukung asynchronous client, kebutuhan yang sama bisa diganti sepenuhnya dengan coroutine client, [referensi](/learn?id=同步io异步io). diff --git a/public/id/consts.md b/public/id/consts.md new file mode 100644 index 0000000..6080d6f --- /dev/null +++ b/public/id/consts.md @@ -0,0 +1,114 @@ +# Konstanta + +!> Ini tidak mencakup semua konstanta. Untuk melihat semua konstanta, silakan kunjungi atau instal: [ide-helper](https://github.com/swoole/ide-helper/blob/master/output/swoole/constants.php) + +## Swoole + +Konstanta | Keterangan +---|--- +SWOOLE_VERSION | Nomor versi Swoole saat ini, dalam format string, misalnya 1.6.0 + +## Parameter Konstruktor + +Konstanta | Keterangan +---|--- +[SWOOLE_BASE](/learn?id=swoole_base) | Menggunakan mode Base, kode bisnis berjalan langsung di proses Reactor +[SWOOLE_PROCESS](/learn?id=swoole_process) | Menggunakan mode Process, kode bisnis berjalan di proses Worker + +## Tipe Socket + +Konstanta | Keterangan +---|--- +SWOOLE_SOCK_TCP | Membuat socket tcp +SWOOLE_SOCK_TCP6 | Membuat socket tcp ipv6 +SWOOLE_SOCK_UDP | Membuat socket udp +SWOOLE_SOCK_UDP6 | Membuat socket udp ipv6 +SWOOLE_SOCK_UNIX_DGRAM | Membuat socket unix dgram +SWOOLE_SOCK_UNIX_STREAM | Membuat socket unix stream +SWOOLE_SOCK_SYNC | Klien sinkron + +## Metode Enkripsi SSL + +Konstanta | Keterangan +---|--- +SWOOLE_SSLv3_METHOD | - +SWOOLE_SSLv3_SERVER_METHOD | - +SWOOLE_SSLv3_CLIENT_METHOD | - +SWOOLE_SSLv23_METHOD (Metode enkripsi default) | - +SWOOLE_SSLv23_SERVER_METHOD | - +SWOOLE_SSLv23_CLIENT_METHOD | - +SWOOLE_TLSv1_METHOD | - +SWOOLE_TLSv1_SERVER_METHOD | - +SWOOLE_TLSv1_CLIENT_METHOD | - +SWOOLE_TLSv1_1_METHOD | - +SWOOLE_TLSv1_1_SERVER_METHOD | - +SWOOLE_TLSv1_1_CLIENT_METHOD | - +SWOOLE_TLSv1_2_METHOD | - +SWOOLE_TLSv1_2_SERVER_METHOD | - +SWOOLE_TLSv1_2_CLIENT_METHOD | - +SWOOLE_DTLSv1_METHOD | - +SWOOLE_DTLSv1_SERVER_METHOD | - +SWOOLE_DTLSv1_CLIENT_METHOD | - +SWOOLE_DTLS_SERVER_METHOD | - +SWOOLE_DTLS_CLIENT_METHOD | - + +!> `SWOOLE_DTLSv1_METHOD`, `SWOOLE_DTLSv1_SERVER_METHOD`, `SWOOLE_DTLSv1_CLIENT_METHOD` telah dihapus di Swoole versi >= `v4.5.0`. + +## Protokol SSL + +Konstanta | Keterangan +---|--- +SWOOLE_SSL_TLSv1 | - +SWOOLE_SSL_TLSv1_1 | - +SWOOLE_SSL_TLSv1_2 | - +SWOOLE_SSL_TLSv1_3 | - +SWOOLE_SSL_SSLv2 | - +SWOOLE_SSL_SSLv3 | - + +!> Tersedia di Swoole versi >= `v4.5.4` + +## Level Log + +Konstanta | Keterangan +---|--- +SWOOLE_LOG_DEBUG | Log debug, hanya untuk pengembangan dan debugging kernel +SWOOLE_LOG_TRACE | Log trace, dapat digunakan untuk melacak masalah sistem, log debug diatur secara hati-hati untuk membawa informasi penting +SWOOLE_LOG_INFO | Informasi umum, hanya untuk tampilan informasi +SWOOLE_LOG_NOTICE | Informasi pemberitahuan, sistem mungkin memiliki beberapa perilaku seperti restart, shutdown +SWOOLE_LOG_WARNING | Informasi peringatan, sistem mungkin memiliki beberapa masalah +SWOOLE_LOG_ERROR | Informasi kesalahan, terjadi kesalahan kritis pada sistem, perlu segera ditangani +SWOOLE_LOG_NONE | Setara dengan menonaktifkan informasi log, informasi log tidak akan dikeluarkan + +!> Log `SWOOLE_LOG_DEBUG` dan `SWOOLE_LOG_TRACE` harus digunakan setelah mengompilasi ekstensi Swoole dengan [--enable-debug-log](/environment?id=debug参数) atau [--enable-trace-log](/environment?id=debug参数). Dalam versi normal, meskipun `log_level = SWOOLE_LOG_TRACE` diatur, log tersebut tidak dapat dicetak. + +## Label Lacak + +Layanan yang berjalan di produksi selalu memproses banyak permintaan setiap saat, jumlah log yang dikeluarkan di tingkat bawah sangat besar. Anda dapat menggunakan `trace_flags` untuk mengatur label log trace, hanya mencetak sebagian log trace. `trace_flags` mendukung pengaturan beberapa item trace menggunakan operator `|` OR. + +```php +$serv->set([ + 'log_level' => SWOOLE_LOG_TRACE, + 'trace_flags' => SWOOLE_TRACE_SERVER | SWOOLE_TRACE_HTTP2, +]); +``` + +Item trace berikut didukung di tingkat bawah, Anda dapat menggunakan `SWOOLE_TRACE_ALL` untuk melacak semua item: + +* `SWOOLE_TRACE_SERVER` +* `SWOOLE_TRACE_CLIENT` +* `SWOOLE_TRACE_BUFFER` +* `SWOOLE_TRACE_CONN` +* `SWOOLE_TRACE_EVENT` +* `SWOOLE_TRACE_WORKER` +* `SWOOLE_TRACE_REACTOR` +* `SWOOLE_TRACE_PHP` +* `SWOOLE_TRACE_HTTP2` +* `SWOOLE_TRACE_EOF_PROTOCOL` +* `SWOOLE_TRACE_LENGTH_PROTOCOL` +* `SWOOLE_TRACE_CLOSE` +* `SWOOLE_TRACE_HTTP_CLIENT` +* `SWOOLE_TRACE_COROUTINE` +* `SWOOLE_TRACE_REDIS_CLIENT` +* `SWOOLE_TRACE_MYSQL_CLIENT` +* `SWOOLE_TRACE_AIO` +* `SWOOLE_TRACE_ALL` diff --git a/public/id/coroutine.md b/public/id/coroutine.md new file mode 100644 index 0000000..f89973e --- /dev/null +++ b/public/id/coroutine.md @@ -0,0 +1,84 @@ +# Coroutine + +Bagian ini memperkenalkan beberapa konsep dasar korutin dan pertanyaan umum. + +Mulai versi 4.0, `Swoole` menyediakan fitur lengkap `Coroutine` + `Channel`, menghadirkan model pemrograman `CSP` yang baru. + +1. Developer dapat menulis kode sinkron tanpa sadar mencapai efek dan performa [IO asinkron](/learn?id=同步io异步io), menghindari logika kode yang tersebar akibat callback asinkron tradisional dan terjebak dalam banyak lapisan callback yang membuat kode tidak terawat +2. Karena level bawah mengenkapsulasi korutin, dibandingkan dengan framework korutin tradisional di lapisan `PHP`, developer tidak perlu menggunakan keyword [yield](https://www.php.net/manual/zh/language.generators.syntax.php) untuk menandai operasi korutin `IO`, sehingga tidak perlu memahami secara mendalam semantik `yield` atau mengubah setiap level panggilan menjadi `yield`, ini sangat meningkatkan efisiensi pengembangan +3. Menyediakan berbagai [klien korutin](/coroutine_client/init) lengkap yang dapat memenuhi kebutuhan sebagian besar developer. + +## Apa itu Coroutine + +Korutin bisa dipahami sederhana sebagai thread, hanya saja thread ini berada di ruang pengguna, tidak memerlukan partisipasi sistem operasi, biaya pembuatan, penghancuran, dan peralihan sangat rendah. Berbeda dengan thread, korutin tidak dapat memanfaatkan CPU multi-inti. Untuk memanfaatkan CPU multi-inti, perlu bergantung pada model multi-proses `Swoole`. + +## Apa itu Channel + +`Channel` dapat dipahami sebagai antrian pesan, khusus untuk korutin. Banyak korutin berkomunikasi melalui operasi `push` dan `pop` untuk memproduksi dan mengonsumsi pesan dalam antrian, mengirim atau menerima data untuk komunikasi antar korutin. Perlu dicatat bahwa `Channel` tidak bisa lintas proses, hanya dapat berkomunikasi antar korutin dalam satu proses `Swoole`. Aplikasi paling tipikal adalah [connection pool](/coroutine/conn_pool) dan [panggilan konkuren](/coroutine/multi_call). + +## Apa itu Wadah Korutin + +Gunakan `Coroutine::create` atau `go()` untuk membuat korutin (lihat [bagian alias](/other/alias?id=协程短名称)), di korutin yang dibuat barulah bisa menggunakan `API` korutin, dan korutin harus dibuat di dalam wadah korutin, lihat [wadah korutin](/coroutine/scheduler). + +## Penjadwalan Korutin + +Di sini akan dijelaskan secara sederhana apa itu penjadwalan korutin. Pertama, setiap korutin bisa dipahami sebagai thread. Kita tahu multi-threading untuk meningkatkan konkurensi program, begitu pula multi-korutin. + +Setiap request pengguna akan membuat korutin, request selesai maka korutin berakhir. Jika ada ribuan request konkuren secara bersamaan, pada suatu saat di dalam proses bisa ada ribuan korutin, karena sumber daya CPU terbatas, kode korutin mana yang harus dijalankan? + +Proses keputusan kode korutin mana yang akan dijalankan CPU adalah `penjadwalan korutin`. Bagaimana strategi penjadwalan `Swoole`? + +- Pertama, saat menjalankan kode korutin, jika menemui `Co::sleep()` atau menghasilkan `IO` jaringan, misalnya `MySQL->query()`, ini pasti proses yang memakan waktu, `Swoole` akan menempatkan fd koneksi MySQL ini ke [EventLoop](/learn?id=什么是eventloop). + * Kemudian CPU korutin ini diberikan ke korutin lain: **yaitu `yield` (ditangguhkan)** + * Menunggu data MySQL kembali lalu melanjutkan eksekusi korutin ini: **yaitu `resume` (dilanjutkan)** + +- Kedua, jika kode korutin memiliki kode intensif CPU, dapat mengaktifkan [enable_preemptive_scheduler](/other/config), Swoole akan memaksa korutin ini melepaskan CPU. + +## Prioritas Korutin Induk-Anak + +Korutin anak (yaitu logika di dalam `go()`) dijalankan terlebih dahulu sampai terjadi `yield` korutin (di `Co::sleep()`), lalu [penjadwalan korutin](/coroutine?id=协程调度) ke korutin luar. + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +echo "main start\n"; +run(function () { + echo "coro " . Coroutine::getcid() . " start\n"; + Coroutine::create(function () { + echo "coro " . Coroutine::getcid() . " start\n"; + Coroutine::sleep(.2); + echo "coro " . Coroutine::getcid() . " end\n"; + }); + echo "coro " . Coroutine::getcid() . " do not wait children coroutine\n"; + Coroutine::sleep(.1); + echo "coro " . Coroutine::getcid() . " end\n"; +}); +echo "end\n"; + +/* +main start +coro 1 start +coro 2 start +coro 1 do not wait children coroutine +coro 1 end +coro 2 end +end +*/ +``` + +## Hal yang Perlu Diperhatikan + +Hal-hal yang perlu diperhatikan sebelum menggunakan Swoole: + +### Variabel Global + +Korutin membuat logika asinkron menjadi sinkron, tetapi peralihan antar korutin terjadi secara implisit, sehingga konsistensi variabel global dan variabel `static` tidak dapat dijamin sebelum dan sesudah peralihan korutin. + +Di `PHP-FPM`, parameter request, parameter server dll dapat diperoleh melalui variabel global. Di dalam `Swoole`, **tidak dapat** memperoleh parameter atribut apa pun melalui variabel `$_GET/$_POST/$_REQUEST/$_SESSION/$_COOKIE/$_SERVER` dll yang diawali dengan `$_`. + +Dapat menggunakan [context](/coroutine/coroutine?id=getcontext) dengan id korutin untuk isolasi, mencapai isolasi variabel global. + +### Berbagi Koneksi TCP Multi-Korutin + +[Lihat](/question/use?id=client-has-already-been-bound-to-another-coroutine) diff --git a/public/id/coroutine/barrier.md b/public/id/coroutine/barrier.md new file mode 100644 index 0000000..df7d970 --- /dev/null +++ b/public/id/coroutine/barrier.md @@ -0,0 +1,44 @@ +# Coroutine\Barrier + +Di [Swoole Library](https://github.com/swoole/library), infrastruktur menyediakan alat manajemen konkurensi coroutine yang lebih praktis: `Coroutine\Barrier`, atau penghalang coroutine. Diimplementasikan berdasarkan PHP reference counting dan Coroutine API. + +Dibandingkan dengan [Coroutine\WaitGroup](/coroutine/wait_group), `Coroutine\Barrier` lebih sederhana penggunaannya. Cukup dengan melewatkan parameter atau sintaks `use` pada closure, lalu gunakan di fungsi coroutine anak. + +!> Tersedia sejak Swoole versi >= v4.5.5. + +## Contoh Penggunaan + +```php +use Swoole\Coroutine\Barrier; +use Swoole\Coroutine\System; +use function Swoole\Coroutine\run; +use Swoole\Coroutine; + +run(function () { + $barrier = Barrier::make(); + + $count = 0; + $N = 4; + + foreach (range(1, $N) as $i) { + Coroutine::create(function () use ($barrier, &$count) { + System::sleep(0.5); + $count++; + }); + } + + Barrier::wait($barrier); + + assert($count == $N); +}); +``` + +## Alur Eksekusi + +* Pertama, gunakan `Barrier::make()` untuk membuat barrier coroutine baru +* Di coroutine anak, gunakan sintaks `use` untuk melewatkan barrier, menambah reference count +* Di posisi yang perlu menunggu, tambahkan `Barrier::wait($barrier)`, ini akan otomatis menangguhkan coroutine saat ini, menunggu coroutine anak yang mereferensi barrier ini keluar +* Saat coroutine anak keluar, reference count objek `$barrier` akan berkurang, hingga mencapai `0` +* Saat semua coroutine anak selesai memproses tugas dan keluar, reference count objek `$barrier` menjadi `0`, di fungsi destruktor objek `$barrier`, infrastruktur akan otomatis melanjutkan coroutine yang ditangguhkan, kembali dari fungsi `Barrier::wait($barrier)` + +`Coroutine\Barrier` adalah pengontrol konkurensi yang lebih mudah digunakan dibandingkan [WaitGroup](/coroutine/wait_group) dan [Channel](/coroutine/channel), secara signifikan meningkatkan pengalaman pengguna dalam pemrograman konkuren PHP. diff --git a/public/id/coroutine/channel.md b/public/id/coroutine/channel.md new file mode 100644 index 0000000..1473fb2 --- /dev/null +++ b/public/id/coroutine/channel.md @@ -0,0 +1,227 @@ +# Coroutine\Channel + +> Sebaiknya baca [ikhtisar](/coroutine) terlebih dahulu untuk memahami konsep dasar coroutine sebelum membaca bagian ini. + +Channel, digunakan untuk komunikasi antar coroutine, mendukung banyak coroutine produsen dan banyak coroutine konsumen. Infrastruktur otomatis menangani perpindahan dan penjadwalan coroutine. + +## Prinsip Implementasi + + * Channel mirip dengan `Array` di `PHP`, hanya menggunakan memori, tanpa alokasi resource tambahan. Semua operasi adalah operasi memori, tanpa konsumsi `IO`. + * Infrastruktur menggunakan PHP reference counting, tanpa copy memori. Bahkan mengirim string atau array besar tidak menghasilkan overhead performa tambahan. + * `channel` berdasarkan reference counting, zero-copy. + +## Contoh Penggunaan + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Channel; +use function Swoole\Coroutine\run; + +run(function(){ + $channel = new Channel(1); + Coroutine::create(function () use ($channel) { + for($i = 0; $i < 10; $i++) { + Coroutine::sleep(1.0); + $channel->push(['rand' => rand(1000, 9999), 'index' => $i]); + echo "{$i}\n"; + } + }); + Coroutine::create(function () use ($channel) { + while(1) { + $data = $channel->pop(2.0); + if ($data) { + var_dump($data); + } else { + assert($channel->errCode === SWOOLE_CHANNEL_TIMEOUT); + break; + } + } + }); +}); +``` + +## Method + +### __construct() + +Method konstruktor channel. + +```php +Swoole\Coroutine\Channel::__construct(int $capacity = 1) +``` + + * **Parameter** + + * **`int $capacity`** + * **Fungsi**: Mengatur kapasitas [harus bilangan bulat >= `1`] + * **Bawaan**: `1` + * **Nilai Lain**: Tidak ada + +!> Infrastruktur menggunakan PHP reference counting untuk menyimpan variabel, buffer hanya perlu memori sebesar `$capacity * sizeof(zval)`, di `PHP7` ukuran `zval` adalah `16` byte. Contoh: `$capacity = 1024`, maka `Channel` maksimal akan memakan `16K` memori. + +!> Saat digunakan di `Server`, harus dibuat setelah [onWorkerStart](/server/events?id=onworkerstart). + +### push() + +Menulis data ke channel. + +```php +Swoole\Coroutine\Channel->push(mixed $data, float $timeout = -1): bool +``` + + * **Parameter** + + * **`mixed $data`** + * **Fungsi**: Data push [bisa berupa variabel PHP apa pun, termasuk anonymous function dan resource] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Untuk menghindari ambiguitas, jangan menulis `null` atau `false` ke channel. + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: Detik [mendukung float, seperti `1.5` berarti `1s` + `500ms`] + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + * **Versi**: Swoole versi >= v4.2.12 + + !> Saat channel penuh, `push` akan menangguhkan coroutine saat ini. Jika dalam waktu yang ditentukan tidak ada konsumen yang mengonsumsi data, akan terjadi timeout, infrastruktur akan melanjutkan coroutine saat ini, panggilan `push` akan segera mengembalikan `false`, gagal menulis. + + * **Return Value** + + * Mengembalikan `true` jika berhasil + * Mengembalikan `false` jika channel ditutup, bisa gunakan `$channel->errCode` untuk kode error + + * **Ekstensi** + + * **Channel Penuh** + + * Otomatis `yield` coroutine saat ini. Setelah coroutine konsumen lain `pop` mengonsumsi data, channel bisa ditulis, akan kembali `resume` coroutine saat ini. + * Saat banyak coroutine produsen `push` bersamaan, infrastruktur otomatis mengantre dan `resume` coroutine produsen tersebut secara berurutan. + + * **Channel Kosong** + + * Otomatis membangunkan salah satu coroutine konsumen + * Saat banyak coroutine konsumen `pop` bersamaan, infrastruktur otomatis mengantre dan `resume` coroutine konsumen tersebut secara berurutan. + +!> `Coroutine\Channel` menggunakan memori lokal, memori antar proses berbeda terisolasi. Hanya bisa melakukan `push` dan `pop` di coroutine berbeda dalam proses yang sama. + +### pop() + +Membaca data dari channel. + +```php +Swoole\Coroutine\Channel->pop(float $timeout = -1): mixed +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: Detik [mendukung float, seperti `1.5` berarti `1s` + `500ms`] + * **Bawaan**: `-1` [berarti tidak pernah timeout] + * **Nilai Lain**: Tidak ada + * **Versi**: Swoole versi >= v4.0.3 + + * **Return Value** + + * Nilai kembali bisa berupa variabel PHP apa pun, termasuk anonymous function dan resource + * Mengembalikan `false` jika channel ditutup + + * **Ekstensi** + + * **Channel Penuh** + + * Setelah `pop` mengonsumsi data, akan otomatis membangunkan salah satu coroutine produsen untuk menulis data baru + * Saat banyak coroutine produsen `push` bersamaan, infrastruktur otomatis mengantre dan `resume` secara berurutan. + + * **Channel Kosong** + + * Otomatis `yield` coroutine saat ini. Setelah coroutine produsen lain `push` memproduksi data, channel bisa dibaca, akan kembali `resume` coroutine saat ini. + * Saat banyak coroutine konsumen `pop` bersamaan, infrastruktur otomatis mengantre dan `resume` secara berurutan. + +### stats() + +Mendapatkan status channel. + +```php +Swoole\Coroutine\Channel->stats(): array +``` + + * **Return Value** + + Mengembalikan array, channel buffer akan mencakup `4` item informasi, channel non-buffer mengembalikan `2` item informasi. + + - `consumer_num` Jumlah konsumen, menandakan channel saat ini kosong, ada `N` coroutine yang menunggu coroutine lain memanggil `push` untuk memproduksi data + - `producer_num` Jumlah produsen, menandakan channel saat ini penuh, ada `N` coroutine yang menunggu coroutine lain memanggil `pop` untuk mengonsumsi data + - `queue_num` Jumlah elemen di dalam channel + +```php +array( + "consumer_num" => 0, + "producer_num" => 1, + "queue_num" => 10 +); +``` + +### close() + +Menutup channel. Dan membangunkan semua coroutine yang menunggu baca/tulis. + +```php +Swoole\Coroutine\Channel->close(): bool +``` + +!> Membangunkan semua coroutine produsen, method `push` mengembalikan `false`; membangunkan semua coroutine konsumen, method `pop` mengembalikan `false`. + +### length() + +Mendapatkan jumlah elemen di dalam channel. + +```php +Swoole\Coroutine\Channel->length(): int +``` + +### isEmpty() + +Menentukan apakah channel saat ini kosong. + +```php +Swoole\Coroutine\Channel->isEmpty(): bool +``` + +### isFull() + +Menentukan apakah channel saat ini penuh. + +```php +Swoole\Coroutine\Channel->isFull(): bool +``` + +## Properti + +### capacity + +Kapasitas buffer channel. + +Kapasitas yang diatur di [konstruktor](/coroutine/channel?id=__construct) akan disimpan di sini, namun **jika kapasitas yang diatur kurang dari 1**, variabel ini akan sama dengan 1. + +```php +Swoole\Coroutine\Channel->capacity: int +``` + +### errCode + +Mendapatkan kode error. + +```php +Swoole\Coroutine\Channel->errCode: int +``` + + * **Return Value** + +Nilai | Konstanta | Fungsi +---|---|--- +0 | SWOOLE_CHANNEL_OK | Bawaan, berhasil +-1 | SWOOLE_CHANNEL_TIMEOUT | Timeout, pop gagal (timeout) +-2 | SWOOLE_CHANNEL_CLOSED | Channel sudah ditutup, melanjutkan operasi channel diff --git a/public/id/coroutine/conn_pool.md b/public/id/coroutine/conn_pool.md new file mode 100644 index 0000000..d4e96f4 --- /dev/null +++ b/public/id/coroutine/conn_pool.md @@ -0,0 +1,204 @@ +# Koneksi Pool + +Swoole sejak versi `v4.4.13` menyediakan connection pool coroutine bawaan. Bab ini akan menjelaskan cara menggunakan connection pool yang sesuai. + +## ConnectionPool + +[ConnectionPool](https://github.com/swoole/library/blob/master/src/core/ConnectionPool.php), connection pool dasar, berdasarkan Channel dengan penjadwalan otomatis, mendukung constructor apa pun (`callable`), constructor harus mengembalikan objek koneksi. + +* Method `get` mendapatkan koneksi (akan membuat koneksi baru jika pool belum penuh) +* Method `put` mengembalikan koneksi +* Method `fill` mengisi connection pool (membuat koneksi terlebih dahulu) +* Method `close` menutup connection pool + +!> [Komponen DB](https://github.com/simple-swoole/db) dari [framework Simps](https://simps.io) mengenkapsulasi Database, mengimplementasikan fungsi seperti pengembalian koneksi otomatis dan transaksi. Bisa dijadikan referensi atau langsung digunakan. Lihat [dokumentasi Simps](https://simps.io/#/zh-cn/database/mysql). + +## Database + +Enkapsulasi tingkat tinggi untuk berbagai connection pool database dan proxy objek, mendukung reconnect otomatis saat koneksi putus. Saat ini mendukung tiga tipe database: PDO, Mysqli, Redis. + +* `PDOConfig`, `PDOProxy`, `PDOPool` +* `MysqliConfig`, `MysqliProxy`, `MysqliPool` +* `RedisConfig`, `RedisProxy`, `RedisPool` + +!> 1. Reconnect MySQL dapat memulihkan sebagian besar konteks koneksi (fetch mode, attribute yang sudah diatur, Statement yang sudah dikompilasi, dll.), namun konteks seperti transaksi tidak bisa dipulihkan. Jika koneksi yang sedang dalam transaksi terputus, exception akan dilempar. Harap evaluasi sendiri keandalan reconnect; +2. Mengembalikan koneksi yang sedang dalam transaksi ke connection pool adalah perilaku tidak terdefinisi, developer harus memastikan koneksi yang dikembalikan bisa digunakan kembali; +3. Jika ada objek koneksi yang mengalami exception dan tidak bisa digunakan kembali, developer perlu memanggil `$pool->put(null);` untuk mengembalikan koneksi kosong demi menjaga keseimbangan jumlah pool. + +### PDOPool/MysqliPool/RedisPool :id=pool + +Digunakan untuk membuat objek connection pool, memiliki dua parameter, yaitu objek Config dan ukuran connection pool. + +```php +$pool = new \Swoole\Database\PDOPool(Swoole\Database\PDOConfig $config, int $size); + +$pool = new \Swoole\Database\MysqliPool(Swoole\Database\MysqliConfig $config, int $size); + +$pool = new \Swoole\Database\RedisPool(Swoole\Database\RedisConfig $config, int $size); +``` + + * **Parameter** + + * **`$config`** + * **Fungsi**: Objek Config yang sesuai, penggunaan detail bisa lihat [contoh penggunaan](/coroutine/conn_pool?id=contoh-penggunaan) di bawah + * **Bawaan**: Tidak ada + * **Nilai Lain**: [PDOConfig](https://github.com/swoole/library/blob/master/src/core/Database/PDOConfig.php), [RedisConfig](https://github.com/swoole/library/blob/master/src/core/Database/RedisConfig.php), [MysqliConfig](https://github.com/swoole/library/blob/master/src/core/Database/MysqliConfig.php) + + * **`int $size`** + * **Fungsi**: Jumlah connection pool + * **Bawaan**: 64 + * **Nilai Lain**: Tidak ada + +## Contoh Penggunaan + +### PDO + +```php +withHost('127.0.0.1') + ->withPort(3306) + // ->withUnixSocket('/tmp/mysql.sock') + ->withDbName('test') + ->withCharset('utf8mb4') + ->withUsername('root') + ->withPassword('root') + ); + for ($n = N; $n--;) { + Coroutine::create(function () use ($pool) { + $pdo = $pool->get(); + $statement = $pdo->prepare('SELECT ? + ?'); + if (!$statement) { + throw new RuntimeException('Prepare failed'); + } + $a = mt_rand(1, 100); + $b = mt_rand(1, 100); + $result = $statement->execute([$a, $b]); + if (!$result) { + throw new RuntimeException('Execute failed'); + } + $result = $statement->fetchAll(); + if ($a + $b !== (int)$result[0][0]) { + throw new RuntimeException('Bad result'); + } + $pool->put($pdo); + }); + } +}); +$s = microtime(true) - $s; +echo 'Use ' . $s . 's for ' . N . ' queries' . PHP_EOL; +``` + +### Redis + +```php +withHost('127.0.0.1') + ->withPort(6379) + ->withAuth('') + ->withDbIndex(0) + ->withTimeout(1) + ); + for ($n = N; $n--;) { + Coroutine::create(function () use ($pool) { + $redis = $pool->get(); + $result = $redis->set('foo', 'bar'); + if (!$result) { + throw new RuntimeException('Set failed'); + } + $result = $redis->get('foo'); + if ($result !== 'bar') { + throw new RuntimeException('Get failed'); + } + $pool->put($redis); + }); + } +}); +$s = microtime(true) - $s; +echo 'Use ' . $s . 's for ' . (N * 2) . ' queries' . PHP_EOL; +``` + +### Mysqli + +```php +withHost('127.0.0.1') + ->withPort(3306) + // ->withUnixSocket('/tmp/mysql.sock') + ->withDbName('test') + ->withCharset('utf8mb4') + ->withUsername('root') + ->withPassword('root') + ); + for ($n = N; $n--;) { + Coroutine::create(function () use ($pool) { + $mysqli = $pool->get(); + $statement = $mysqli->prepare('SELECT ? + ?'); + if (!$statement) { + throw new RuntimeException('Prepare failed'); + } + $a = mt_rand(1, 100); + $b = mt_rand(1, 100); + if (!$statement->bind_param('dd', $a, $b)) { + throw new RuntimeException('Bind param failed'); + } + if (!$statement->execute()) { + throw new RuntimeException('Execute failed'); + } + if (!$statement->bind_result($result)) { + throw new RuntimeException('Bind result failed'); + } + if (!$statement->fetch()) { + throw new RuntimeException('Fetch failed'); + } + if ($a + $b !== (int)$result) { + throw new RuntimeException('Bad result'); + } + while ($statement->fetch()) { + continue; + } + $pool->put($mysqli); + }); + } +}); +$s = microtime(true) - $s; +echo 'Use ' . $s . 's for ' . N . ' queries' . PHP_EOL; +``` diff --git a/public/id/coroutine/coroutine.md b/public/id/coroutine/coroutine.md new file mode 100644 index 0000000..5386acf --- /dev/null +++ b/public/id/coroutine/coroutine.md @@ -0,0 +1,909 @@ +# API Coroutine + +> Sebaiknya baca [ikhtisar](/coroutine) terlebih dahulu untuk memahami konsep dasar coroutine sebelum membaca bagian ini. + +## Method + +### set() + +Pengaturan coroutine, mengatur opsi terkait coroutine. + +```php +Swoole\Coroutine::set(array $options); +``` + +| Parameter | Stabil Sejak Versi | Fungsi | +|-------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| max_coroutine | - | Mengatur jumlah maksimum global coroutine. Jika terlampaui, coroutine baru tidak dapat dibuat. Di Server akan ditimpa oleh [server->max_coroutine](/server/setting?id=max_coroutine). | +| stack_size/c_stack_size | - | Mengatur ukuran memori C stack awal untuk satu coroutine, bawaan `2M`. | +| log_level | v4.0.0 | Level log, [lihat detail](/consts?id=logging-level). | +| trace_flags | v4.0.0 | Trace flags, [lihat detail](/consts?id=trace-flags). | +| socket_connect_timeout | v4.2.10 | Timeout koneksi, **lihat [aturan timeout klien](/coroutine_client/init?id=timeout-rules)**. | +| socket_read_timeout | v4.3.0 | Timeout baca, **lihat [aturan timeout klien](/coroutine_client/init?id=timeout-rules)**. | +| socket_write_timeout | v4.3.0 | Timeout tulis, **lihat [aturan timeout klien](/coroutine_client/init?id=timeout-rules)**. | +| socket_dns_timeout | v4.4.0 | Timeout resolusi domain, **lihat [aturan timeout klien](/coroutine_client/init?id=timeout-rules)**. | +| socket_timeout | v4.2.10 | Timeout kirim/terima, **lihat [aturan timeout klien](/coroutine_client/init?id=timeout-rules)**. | +| dns_cache_expire | v4.2.11 | Mengatur waktu kedaluwarsa cache DNS Swoole, dalam detik, bawaan 60 detik. | +| dns_cache_capacity | v4.2.11 | Mengatur kapasitas cache DNS Swoole, bawaan 1000. | +| hook_flags | v4.4.0 | Konfigurasi cakupan hook coroutine satu-klik, lihat [Coroutine Satu-Klik](/runtime). | +| enable_preemptive_scheduler | v4.4.0 | Mengaktifkan penjadwalan preemptive coroutine, waktu eksekusi maksimum coroutine adalah 10ms, akan menimpa [konfigurasi ini](/other/config). | +| dns_server | v4.5.0 | Mengatur server untuk query DNS, bawaan `"8.8.8.8"`. | +| exit_condition | v4.5.0 | Masukan `callable` yang mengembalikan bool, kustomisasi kondisi keluar reactor. Contoh: `Co::set(['exit_condition' => function () {return Co::stats()['coroutine_num'] === 0;}]);` | +| enable_deadlock_check | v4.6.0 | Mengatur apakah akan mengaktifkan deteksi deadlock coroutine, bawaan aktif. | +| deadlock_check_disable_trace | v4.6.0 | Mengatur apakah akan menampilkan stack frame deteksi deadlock coroutine. | +| deadlock_check_limit | v4.6.0 | Membatasi jumlah output maksimum saat deteksi deadlock coroutine. | +| deadlock_check_depth | v4.6.0 | Membatasi jumlah stack frame yang dikembalikan saat deteksi deadlock coroutine. | +| max_concurrency | v4.8.2 | Jumlah maksimum permintaan konkuren. | + +### getOptions() + +Mendapatkan opsi terkait coroutine yang telah diatur. + +!> Tersedia sejak Swoole versi >= `v4.6.0` + +```php +Swoole\Coroutine::getOptions(): null|array; +``` + +### create() + +Membuat coroutine baru dan langsung menjalankannya. + +```php +Swoole\Coroutine::create(callable $function, ...$args): int|false +go(callable $function, ...$args): int|false // Lihat konfigurasi use_shortname di php.ini +``` + +* **Parameter** + + * **`callable $function`** + * **Fungsi**: Kode yang akan dieksekusi oleh coroutine, harus berupa `callable`. Jumlah total coroutine yang bisa dibuat dibatasi oleh [server->max_coroutine](/server/setting?id=max_coroutine). + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan `false` jika gagal dibuat + * Mengembalikan `ID` coroutine jika berhasil dibuat + +!> Karena infrastruktur akan memprioritaskan eksekusi kode coroutine anak, `Coroutine::create` baru akan kembali ketika coroutine anak ditangguhkan, lalu melanjutkan eksekusi kode coroutine saat ini. + + * **Urutan Eksekusi** + + Membuat coroutine baru bersarang di dalam coroutine menggunakan `go`. Karena model coroutine Swoole adalah single-process single-thread: + + * Coroutine anak yang dibuat dengan `go` akan dieksekusi terlebih dahulu. Setelah coroutine anak selesai atau ditangguhkan, eksekusi akan kembali ke coroutine induk. + * Jika coroutine induk keluar setelah coroutine anak ditangguhkan, hal itu tidak memengaruhi eksekusi coroutine anak. + + ```php + \Co\run(function() { + go(function () { + Co::sleep(3.0); + go(function () { + Co::sleep(2.0); + echo "co[3] end\n"; + }); + echo "co[2] end\n"; + }); + + Co::sleep(1.0); + echo "co[1] end\n"; + }); + ``` + +* **Overhead Coroutine** + + Setiap coroutine independen dan membutuhkan ruang memori sendiri (stack memory). Di PHP-7.2, infrastruktur mengalokasikan `8K` stack untuk menyimpan variabel coroutine. Ukuran `zval` adalah `16 byte`, jadi stack `8K` maksimal bisa menyimpan `512` variabel. Memori stack coroutine akan otomatis diperluas jika melebihi `8K`. + + Memori stack yang dialokasikan saat coroutine keluar akan dibebaskan. + + * `PHP-7.1`, `PHP-7.0` secara bawaan mengalokasikan `256K` stack memory + * Bisa panggil `Co::set(['stack_size' => 4096])` untuk mengubah ukuran stack memory bawaan + +### defer() + +`defer` digunakan untuk pelepasan resource, akan dipanggil **sebelum coroutine ditutup** (yaitu saat fungsi coroutine selesai dieksekusi), bahkan jika exception dilempar, `defer` yang sudah terdaftar tetap akan dieksekusi. + +!> Swoole versi >= 4.2.9 + +```php +Swoole\Coroutine::defer(callable $function); +defer(callable $function); // API nama pendek +``` + +!> Perlu diperhatikan, urutan pemanggilannya adalah terbalik (LIFO), yaitu `defer` yang didaftarkan lebih awal akan dieksekusi lebih akhir. Urutan terbalik ini sesuai dengan logika pelepasan resource yang benar, karena resource yang dialokasikan kemudian mungkin bergantung pada resource yang dialokasikan sebelumnya. + + * **Contoh** + +```php +go(function () { + defer(function () use ($db) { + $db->close(); + }); +}); +``` + +### exists() + +Memeriksa apakah coroutine yang ditentukan ada. + +```php +Swoole\Coroutine::exists(int $cid = 0): bool +``` + +!> Swoole versi >= v4.3.0 + + * **Contoh** + +```php +\Co\run(function () { + go(function () { + go(function () { + Co::sleep(0.001); + var_dump(Co::exists(Co::getPcid())); // 1: true + }); + go(function () { + Co::sleep(0.003); + var_dump(Co::exists(Co::getPcid())); // 3: false + }); + Co::sleep(0.002); + var_dump(Co::exists(Co::getPcid())); // 2: false + }); +}); +``` + +### getCid() + +Mendapatkan `ID` unik dari coroutine saat ini, aliasnya adalah `getuid`, merupakan bilangan bulat positif unik dalam satu proses. + +```php +Swoole\Coroutine::getCid(): int +``` + +* **Return Value** + + * Mengembalikan `ID` coroutine saat ini jika berhasil + * Mengembalikan `-1` jika sedang tidak berada di lingkungan coroutine + +### getPcid() + +Mendapatkan `ID` induk dari coroutine saat ini. + +```php +Swoole\Coroutine::getPcid([$cid]): int +``` + +!> Swoole versi >= v4.3.0 + +* **Parameter** + + * **`int $cid`** + * **Fungsi**: ID coroutine, parameter opsional. Bisa memasukkan `id` coroutine tertentu untuk mendapatkan `id` induknya. + * **Bawaan**: Coroutine saat ini + * **Nilai Lain**: Tidak ada + + * **Contoh** + +```php +var_dump(Co::getPcid()); +\Co\run(function () { + var_dump(Co::getPcid()); + go(function () { + var_dump(Co::getPcid()); + go(function () { + var_dump(Co::getPcid()); + go(function () { + var_dump(Co::getPcid()); + }); + go(function () { + var_dump(Co::getPcid()); + }); + go(function () { + var_dump(Co::getPcid()); + }); + }); + var_dump(Co::getPcid()); + }); + var_dump(Co::getPcid()); +}); +var_dump(Co::getPcid()); + +// --EXPECT-- + +// bool(false) +// int(-1) +// int(1) +// int(2) +// int(3) +// int(3) +// int(3) +// int(1) +// int(-1) +// bool(false) +``` + +!> Memanggil `getPcid` di luar coroutine bersarang akan mengembalikan `-1` (dibuat dari luar ruang coroutine) +Memanggil `getPcid` di dalam non-coroutine akan mengembalikan `false` (tidak ada coroutine induk) +`0` adalah `id` yang dicadangkan, tidak akan muncul di nilai kembali + +!> Coroutine tidak memiliki hubungan induk-anak yang nyata; coroutine beroperasi secara independen dan terisolasi. `Pcid` ini bisa dipahami sebagai `id` coroutine yang membuat coroutine saat ini. + + * **Penggunaan** + + * **Merangkai beberapa stack panggilan coroutine** + +```php +\Co\run(function () { + go(function () { + $ptrace = Co::getBackTrace(Co::getPcid()); + var_dump(array_merge($ptrace, Co::getBackTrace(Co::getCid()))); + }); +}); +``` + +### getContext() + +Mendapatkan objek konteks dari coroutine saat ini. + +```php +Swoole\Coroutine::getContext([int $cid = 0]): Swoole\Coroutine\Context +``` + +!> Swoole versi >= v4.3.0 + +* **Parameter** + + * **`int $cid`** + * **Fungsi**: `CID` coroutine, parameter opsional + * **Bawaan**: `CID` coroutine saat ini + * **Nilai Lain**: Tidak ada + + * **Fungsi** + + * Konteks otomatis dibersihkan setelah coroutine keluar (jika tidak ada referensi coroutine lain atau variabel global) + * Tanpa overhead registrasi dan pemanggilan `defer` (tidak perlu mendaftarkan method pembersihan, tidak perlu memanggil fungsi untuk membersihkan) + * Tanpa overhead kalkulasi hash untuk konteks yang diimplementasikan dengan array PHP (bermanfaat saat jumlah coroutine sangat besar) + * `Co\Context` menggunakan `ArrayObject`, memenuhi berbagai kebutuhan penyimpanan (bisa sebagai objek, juga bisa dioperasikan sebagai array) + + * **Contoh** + +```php +function func(callable $fn, ...$args) +{ + go(function () use ($fn, $args) { + $fn(...$args); + echo 'Coroutine#' . Co::getCid() . ' exit' . PHP_EOL; + }); +} + +/** +* Compatibility for lower version +* @param object|Resource $object +* @return int +*/ +function php_object_id($object) +{ + static $id = 0; + static $map = []; + $hash = spl_object_hash($object); + return $map[$hash] ?? ($map[$hash] = ++$id); +} + +class Resource +{ + public function __construct() + { + echo __CLASS__ . '#' . php_object_id((object)$this) . ' constructed' . PHP_EOL; + } + + public function __destruct() + { + echo __CLASS__ . '#' . php_object_id((object)$this) . ' destructed' . PHP_EOL; + } +} + +$context = new Co\Context(); +assert($context instanceof ArrayObject); +assert(Co::getContext() === null); +func(function () { + $context = Co::getContext(); + assert($context instanceof Co\Context); + $context['resource1'] = new Resource; + $context->resource2 = new Resource; + func(function () { + Co::getContext()['resource3'] = new Resource; + Co::yield(); + Co::getContext()['resource3']->resource4 = new Resource; + Co::getContext()->resource5 = new Resource; + }); +}); +Co::resume(2); + +Swoole\Event::wait(); + +// --EXPECT-- +// Resource#1 constructed +// Resource#2 constructed +// Resource#3 constructed +// Coroutine#1 exit +// Resource#2 destructed +// Resource#1 destructed +// Resource#4 constructed +// Resource#5 constructed +// Coroutine#2 exit +// Resource#5 destructed +// Resource#3 destructed +// Resource#4 destructed +``` + +### yield() + +Secara manual menyerahkan hak eksekusi coroutine saat ini. Bukan berdasarkan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine) berbasis IO. + +Method ini memiliki alias lain: `Coroutine::suspend()` + +!> Harus digunakan berpasangan dengan method `Coroutine::resume()`. Setelah coroutine `yield`, harus di-`resume` oleh coroutine eksternal lain, jika tidak akan terjadi kebocoran coroutine. + +```php +Swoole\Coroutine::yield(); +``` + + * **Contoh** + +```php +$cid = go(function () { + echo "co 1 start\n"; + Co::yield(); + echo "co 1 end\n"; +}); + +go(function () use ($cid) { + echo "co 2 start\n"; + Co::sleep(0.5); + Co::resume($cid); + echo "co 2 end\n"; +}); +Swoole\Event::wait(); +``` + +### resume() + +Secara manual melanjutkan coroutine agar terus berjalan, bukan berdasarkan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine) berbasis IO. + +!> Saat coroutine saat ini dalam keadaan ditangguhkan, coroutine lain bisa menggunakan `resume` untuk membangunkannya kembali. + +```php +Swoole\Coroutine::resume(int $coroutineId); +``` + +* **Parameter** + + * **`int $coroutineId`** + * **Fungsi**: `ID` coroutine yang akan dilanjutkan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh** + +```php +$id = go(function(){ + $id = Co::getuid(); + echo "start coro $id\n"; + Co::suspend(); + echo "resume coro $id @1\n"; + Co::suspend(); + echo "resume coro $id @2\n"; +}); +echo "start to resume $id @1\n"; +Co::resume($id); +echo "start to resume $id @2\n"; +Co::resume($id); +echo "main\n"; +Swoole\Event::wait(); + +// --EXPECT-- +// start coro 1 +// start to resume 1 @1 +// resume coro 1 @1 +// start to resume 1 @2 +// resume coro 1 @2 +// main +``` + +### list() + +Menelusuri semua coroutine dalam proses saat ini. + +```php +Swoole\Coroutine::list(): Swoole\Coroutine\Iterator +Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator +``` + +!> Sebelum `v4.3.0` gunakan `listCoroutines`. `list` tersedia mulai `v4.1.0`. + +* **Return Value** + + * Mengembalikan iterator, bisa ditelusuri dengan `foreach`, atau dikonversi ke array dengan `iterator_to_array` + +```php +$coros = Swoole\Coroutine::listCoroutines(); +foreach($coros as $cid) +{ + var_dump(Swoole\Coroutine::getBackTrace($cid)); +} +``` + +### stats() + +Mendapatkan status coroutine. + +```php +Swoole\Coroutine::stats(): array +``` + +* **Return Value** + +key | Fungsi +---|--- +event_num | Jumlah event reactor saat ini +signal_listener_num | Jumlah listener sinyal saat ini +aio_task_num | Jumlah tugas IO asinkron (aio di sini merujuk pada file IO atau DNS, bukan network IO lainnya) +aio_worker_num | Jumlah thread pekerja IO asinkron +c_stack_size | Ukuran C stack setiap coroutine +coroutine_num | Jumlah coroutine yang sedang berjalan +coroutine_peak_num | Puncak jumlah coroutine yang berjalan +coroutine_last_cid | ID coroutine terakhir yang dibuat + + * **Contoh** + +```php +var_dump(Swoole\Coroutine::stats()); + +array(1) { + ["c_stack_size"]=> + int(2097152) + ["coroutine_num"]=> + int(132) + ["coroutine_peak_num"]=> + int(2) +} +``` + +### getBackTrace() + +Mendapatkan stack panggilan fungsi coroutine. + +```php +Swoole\Coroutine::getBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array +``` + +!> Swoole versi >= v4.1.0 + +* **Parameter** + + * **`int $cid`** + * **Fungsi**: `CID` coroutine + * **Bawaan**: `CID` coroutine saat ini + * **Nilai Lain**: Tidak ada + + * **`int $options`** + * **Fungsi**: Mengatur opsi + * **Bawaan**: `DEBUG_BACKTRACE_PROVIDE_OBJECT` [apakah mengisi indeks `object`] + * **Nilai Lain**: `DEBUG_BACKTRACE_IGNORE_ARGS` [apakah mengabaikan indeks args, termasuk semua parameter function/method, bisa menghemat memori] + + * **`int limit`** + * **Fungsi**: Membatasi jumlah stack frame yang dikembalikan + * **Bawaan**: `0` + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Jika coroutine yang ditentukan tidak ada, akan mengembalikan `false` + * Jika berhasil mengembalikan array, formatnya sama dengan nilai kembali fungsi [debug_backtrace](https://www.php.net/manual/zh/function.debug-backtrace.php) + + * **Contoh** + +```php +function test1() { + test2(); +} + +function test2() { + while(true) { + Co::sleep(10); + echo __FUNCTION__." \n"; + } +} +\Co\run(function () { + $cid = go(function () { + test1(); + }); + + go(function () use ($cid) { + while(true) { + echo "BackTrace[$cid]:\n-----------------------------------------------\n"; + var_dump(Co::getBackTrace($cid))."\n"; + Co::sleep(3); + } + }); +}); +Swoole\Event::wait(); +``` + +### printBackTrace() + +Mencetak stack panggilan fungsi coroutine. Parameter sama dengan `getBackTrace`. + +!> Tersedia sejak Swoole versi >= `v4.6.0` + +```php +Swoole\Coroutine::printBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0); +``` + +### getElapsed() + +Mendapatkan waktu berjalan coroutine untuk analisis statistik atau menemukan coroutine zombie. + +!> Tersedia sejak Swoole versi >= `v4.5.0` + +```php +Swoole\Coroutine::getElapsed([$cid]): int +``` +* **Parameter** + + * **`int $cid`** + * **Fungsi**: Parameter opsional, `CID` coroutine + * **Bawaan**: `CID` coroutine saat ini + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Waktu berjalan coroutine dalam float, presisi milidetik + +### cancel() + +Digunakan untuk membatalkan suatu coroutine, tapi tidak bisa membatalkan coroutine saat ini. + +!> Tersedia sejak Swoole versi >= `v4.7.0` + +```php +Swoole\Coroutine::cancel(int $cid, bool $throw_exception = false): bool +``` +* **Parameter** + + * **`int $cid`** + * **Fungsi**: `CID` coroutine + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + * **`bool $throw_exception`** + * **Fungsi**: Membatalkan coroutine, dan melempar exception `Swoole\Coroutine\CanceledException` di dalam coroutine yang dibatalkan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + * Tersedia setelah versi `v6.1.0` + +* **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal + * Jika gagal, panggil [swoole_last_error()](/functions?id=swoole_last_error) untuk melihat info error + +#### Catatan +* Saat coroutine sedang membaca/menulis file, tidak bisa dibatalkan, `cancel()` akan gagal dengan kode error `SWOOLE_ERROR_CO_CANNOT_CANCEL` +* Jika `$throw_exception` diatur ke `true`, fungsi ini akan selalu mengembalikan `true` +* Coroutine yang dibatalkan harus menangkap exception `Swoole\Coroutine\CanceledException`, jika tidak akan menghasilkan `Fatal Error` dan menyebabkan proses keluar + +```php +Co\run(function () { + $cid = Co\go(function () { + try { + while (true) { + System::sleep(0.1); + echo "co 2 running\n"; + } + var_dump('end'); + } catch (Swoole\Coroutine\CanceledException $e) { + var_dump('cancelled'); + } + }); + + System::sleep(0.3); + Co::cancel($cid, true); + System::sleep(0.2); + echo "co 1 end\n"; +}); +``` + +### isCanceled() + +Digunakan untuk mengecek apakah operasi saat ini dibatalkan secara manual. + +!> Tersedia sejak Swoole versi >= `v4.7.0` + +```php +Swoole\Coroutine::isCanceled(): bool +``` + +* **Return Value** + + * Jika pembatalan manual berakhir normal, mengembalikan `true`, jika gagal mengembalikan `false` + +#### Contoh + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\System; +use function Swoole\Coroutine\run; +use function Swoole\Coroutine\go; + +run(function () { + $chan = new Coroutine\Channel(1); + $cid = Coroutine::getCid(); + go(function () use ($cid) { + System::sleep(0.002); + assert(Coroutine::cancel($cid) === true); + }); + + assert($chan->push("hello world [1]", 100) === true); + assert(Coroutine::isCanceled() === false); + assert($chan->errCode === SWOOLE_CHANNEL_OK); + + assert($chan->push("hello world [2]", 100) === false); + assert(Coroutine::isCanceled() === true); + assert($chan->errCode === SWOOLE_CHANNEL_CANCELED); + + echo "Done\n"; +}); +``` + +### enableScheduler() + +Mengaktifkan sementara penjadwalan preemptive coroutine. + +!> Tersedia sejak Swoole versi >= `v4.4.0` + +```php +Swoole\Coroutine::enableScheduler(); +``` + +### disableScheduler() + +Menonaktifkan sementara penjadwalan preemptive coroutine. + +!> Tersedia sejak Swoole versi >= `v4.4.0` + +```php +Swoole\Coroutine::disableScheduler(); +``` + +### getStackUsage() + +Mendapatkan penggunaan memori stack PHP saat ini. + +!> Tersedia sejak Swoole versi >= `v4.8.0` + +```php +Swoole\Coroutine::getStackUsage([$cid]): int +``` + +* **Parameter** + + * **`int $cid`** + * **Fungsi**: Parameter opsional, `CID` coroutine + * **Bawaan**: `CID` coroutine saat ini + * **Nilai Lain**: Tidak ada + +### join() + +Menjalankan banyak coroutine secara konkuren. + +!> Tersedia sejak Swoole versi >= `v4.8.0` + +```php +Swoole\Coroutine::join(array $cid_array, float $timeout = -1): bool +``` + +* **Parameter** + + * **`array $cid_array`** + * **Fungsi**: Array `CID` coroutine yang akan dijalankan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Total waktu timeout, setelah timeout akan segera kembali. Coroutine yang sedang berjalan akan tetap selesai, tidak dihentikan. + * **Bawaan**: -1 + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal + * Jika gagal, panggil [swoole_last_error()](/functions?id=swoole_last_error) untuk info error + +* **Contoh Penggunaan** + +```php +use Swoole\Coroutine; + +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; + +run(function () { + $status = Coroutine::join([ + go(function () use (&$result) { + $result['baidu'] = strlen(file_get_contents('https://www.baidu.com/')); + }), + go(function () use (&$result) { + $result['google'] = strlen(file_get_contents('https://www.google.com/')); + }) + ], 1); + var_dump($result, $status, swoole_strerror(swoole_last_error(), 9)); +}); +``` + +## Fungsi + +### batch() + +Menjalankan banyak coroutine secara konkuren, dan mengembalikan nilai kembali dari method coroutine tersebut melalui array. + +!> Tersedia sejak Swoole versi >= `v4.5.2` + +```php +Swoole\Coroutine\batch(array $tasks, float $timeout = -1): array +``` + +* **Parameter** + + * **`array $tasks`** + * **Fungsi**: Array callback method. Jika `key` ditentukan, nilai kembali juga akan diarahkan oleh `key` tersebut. + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Total waktu timeout. Setelah timeout akan segera kembali. Coroutine yang berjalan akan tetap selesai tanpa dihentikan. + * **Bawaan**: -1 + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan array berisi nilai kembali callback. Jika parameter `$tasks` menentukan `key`, nilai kembali akan diarahkan oleh `key` tersebut. + +* **Contoh Penggunaan** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\batch; + +Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); + +$start_time = microtime(true); +Coroutine\run(function () { + $use = microtime(true); + $results = batch([ + 'file_put_contents' => function () { + return file_put_contents(__DIR__ . '/greeter.txt', "Hello,Swoole."); + }, + 'gethostbyname' => function () { + return gethostbyname('localhost'); + }, + 'file_get_contents' => function () { + return file_get_contents(__DIR__ . '/greeter.txt'); + }, + 'sleep' => function () { + sleep(1); + return true; + }, + 'usleep' => function () { + usleep(1000); + return true; + }, + ], 0.1); + $use = microtime(true) - $use; + echo "Use {$use}s, Result:\n"; + var_dump($results); +}); +$end_time = microtime(true) - $start_time; +echo "Use {$end_time}s, Done\n"; +``` + +### parallel() + +Menjalankan banyak coroutine secara konkuren. + +!> Tersedia sejak Swoole versi >= `v4.5.3` + +```php +Swoole\Coroutine\parallel(int $n, callable $fn): void +``` + +* **Parameter** + + * **`int $n`** + * **Fungsi**: Mengatur jumlah maksimum coroutine menjadi `$n` + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $fn`** + * **Fungsi**: Fungsi callback yang akan dijalankan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Contoh Penggunaan** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\System; +use function Swoole\Coroutine\parallel; + +$start_time = microtime(true); +Coroutine\run(function () { + $use = microtime(true); + $results = []; + parallel(2, function () use (&$results) { + System::sleep(0.2); + $results[] = System::gethostbyname('localhost'); + }); + $use = microtime(true) - $use; + echo "Use {$use}s, Result:\n"; + var_dump($results); +}); +$end_time = microtime(true) - $start_time; +echo "Use {$end_time}s, Done\n"; +``` + +### map() + +Mirip dengan [array_map](https://www.php.net/manual/en/function.array-map.php), menerapkan fungsi callback ke setiap elemen array. + +!> Tersedia sejak Swoole versi >= `v4.5.5` + +```php +Swoole\Coroutine\map(array $list, callable $fn, float $timeout = -1): array +``` + +* **Parameter** + + * **`array $list`** + * **Fungsi**: Array untuk menjalankan fungsi `$fn` + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $fn`** + * **Fungsi**: Fungsi callback yang akan diterapkan ke setiap elemen di `$list` + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Total waktu timeout, setelah timeout akan segera kembali. Coroutine yang berjalan akan tetap selesai tanpa dihentikan. + * **Bawaan**: -1 + * **Nilai Lain**: Tidak ada + +* **Contoh Penggunaan** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\map; + +function factorial(int $n): int +{ + return array_product(range($n, 1)); +} + +Coroutine\run(function () { + $results = map([2, 3, 4], 'factorial'); + print_r($results); +}); +``` + +### deadlock_check() + +Deteksi deadlock coroutine, saat dipanggil akan menampilkan informasi stack terkait. + +Bawaan **aktif**, setelah [EventLoop](learn?id=apa-itu-eventloop) berakhir, jika ada deadlock coroutine, infrastruktur akan otomatis memanggilnya. + +Bisa dinonaktifkan dengan mengatur `enable_deadlock_check` di [Coroutine::set](/coroutine/coroutine?id=set). + +!> Tersedia sejak Swoole versi >= `v4.6.0` + +```php +Swoole\Coroutine\deadlock_check(); +``` diff --git a/public/id/coroutine/gdb.md b/public/id/coroutine/gdb.md new file mode 100644 index 0000000..80ac231 --- /dev/null +++ b/public/id/coroutine/gdb.md @@ -0,0 +1,87 @@ +# Debug Coroutine + +Saat menggunakan coroutine `Swoole`, bisa menggunakan metode berikut untuk debugging. + +## Debug GDB + +### Masuk ke gdb + +```shell +gdb php test.php +``` + +### gdbinit + +```shell +(gdb) source /path/to/swoole-src/gdbinit +``` + +### Mengatur breakpoint + +Misalnya fungsi `co::sleep` + +```shell +(gdb) b zim_swoole_coroutine_util_sleep +``` + +### Mencetak semua coroutine dan status dalam proses saat ini + +```shell +(gdb) co_list +coroutine 1 SW_CORO_YIELD +coroutine 2 SW_CORO_RUNNING +``` + +### Mencetak stack panggilan coroutine yang sedang berjalan + +```shell +(gdb) co_bt +coroutine cid:[2] +[0x7ffff148a100] Swoole\Coroutine->sleep(0.500000) [internal function] +[0x7ffff148a0a0] {closure}() /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:7 +[0x7ffff141e0c0] go(object[0x7ffff141e110]) [internal function] +[0x7ffff141e030] (main) /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:10 +``` + +### Mencetak stack panggilan berdasarkan ID coroutine tertentu + +``` shell +(gdb) co_bt 1 +[0x7ffff1487100] Swoole\Coroutine->sleep(0.500000) [internal function] +[0x7ffff14870a0] {closure}() /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:3 +[0x7ffff141e0c0] go(object[0x7ffff141e110]) [internal function] +[0x7ffff141e030] (main) /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:10 +``` + +### Mencetak status coroutine global + +```shell +(gdb) co_status + stack_size: 2097152 + call_stack_size: 1 + active: 1 + coro_num: 2 + max_coro_num: 3000 + peak_coro_num: 2 +``` + +## Debug Kode PHP + +Menelusuri semua coroutine dalam proses saat ini, dan mencetak stack panggilan. + +```php +Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator +``` + +!> Membutuhkan `4.1.0` atau versi yang lebih baru + +* Mengembalikan iterator, bisa ditelusuri dengan `foreach`, atau dikonversi ke array dengan `iterator_to_array` + +```php +use Swoole\Coroutine; +$coros = Coroutine::listCoroutines(); +foreach($coros as $cid) +{ + var_dump(Coroutine::getBackTrace($cid)); +} +``` diff --git a/public/id/coroutine/http_server.md b/public/id/coroutine/http_server.md new file mode 100644 index 0000000..0df7090 --- /dev/null +++ b/public/id/coroutine/http_server.md @@ -0,0 +1,128 @@ +# Server HTTP + +?> Implementasi server HTTP yang sepenuhnya coroutine. `Co\Http\Server` ditulis dalam C++ untuk alasan kinerja parsing HTTP, sehingga bukan subclass dari [Co\Server](/coroutine/server) yang ditulis dalam PHP. + +Perbedaan dengan [Http\Server](/http_server): + +* Bisa dibuat dan dihancurkan secara dinamis saat runtime +* Penanganan koneksi dilakukan di coroutine anak terpisah, di mana `Connect`, `Request`, `Response`, `Close` untuk koneksi klien bersifat serial penuh + +!> Membutuhkan `v4.4.0` atau lebih tinggi + +!> Jika [HTTP2 diaktifkan](/environment?id=opsi-kompilasi) saat kompilasi, dukungan protokol HTTP2 akan diaktifkan secara bawaan, tidak perlu mengonfigurasi [open_http2_protocol](/http_server?id=open_http2_protocol) seperti di `Swoole\Http\Server` (Catatan: **Versi di bawah v4.4.16 memiliki bug yang diketahui untuk dukungan HTTP2, harap upgrade sebelum digunakan**) + +## Nama Pendek + +Bisa menggunakan nama pendek `Co\Http\Server`. + +## Method + +### __construct() + +```php +Swoole\Coroutine\Http\Server::__construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false); +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat IP yang didengarkan [jika UNIXSocket lokal, harus diisi dengan format `unix://tmp/your_file.sock`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port yang didengarkan + * **Bawaan**: 0 (mendengarkan port kosong secara acak) + * **Nilai Lain**: 0~65535 + + * **`bool $ssl`** + * **Fungsi**: Apakah mengaktifkan enkripsi tunnel `SSL/TLS` + * **Bawaan**: false + * **Nilai Lain**: true + + * **`bool $reuse_port`** + * **Fungsi**: Apakah mengaktifkan fitur reuse port, setelah diaktifkan banyak layanan bisa berbagi satu port + * **Bawaan**: false + * **Nilai Lain**: true + +### handle() + +Mendaftarkan fungsi callback untuk menangani permintaan HTTP di jalur yang ditunjukkan parameter `$pattern`. + +```php +Swoole\Coroutine\Http\Server->handle(string $pattern, callable $fn): void +``` + +!> Harus diatur sebelum [Server::start](/coroutine/server?id=start) + + * **Parameter** + + * **`string $pattern`** + * **Fungsi**: Mengatur jalur `URL` [seperti `/index.html`, perhatikan tidak bisa memasukkan `http://domain`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $fn`** + * **Fungsi**: Fungsi penanganan, cara penggunaan lihat callback [OnRequest](/http_server?id=on) di `Swoole\Http\Server`, tidak diulangi di sini. + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + Contoh: + + ```php + function callback(Swoole\Http\Request $req, Swoole\Http\Response $resp) { + $resp->end("hello world"); + } + ``` + + * **Petunjuk** + + * Server setelah berhasil `Accept` (membangun koneksi), akan otomatis membuat coroutine dan menerima permintaan `HTTP` + * `$fn` dieksekusi di dalam ruang coroutine anak baru, jadi tidak perlu membuat coroutine lagi di dalam fungsi + * Klien mendukung [KeepAlive](/coroutine_client/http_client?id=keep_alive), coroutine anak akan terus menerima permintaan baru dalam loop, tanpa keluar + * Klien tidak mendukung `KeepAlive`, coroutine anak akan berhenti menerima permintaan, keluar dan menutup koneksi + + * **Catatan** + + !> -Saat `$pattern` diatur dengan jalur yang sama, pengaturan baru akan menimpa yang lama; + -Jika tidak mengatur fungsi penanganan jalur root dan jalur yang diminta tidak cocok dengan `$pattern` mana pun, Swoole akan mengembalikan error `404`; + -`$pattern` menggunakan metode pencocokan string, tidak mendukung wildcard atau regex, tidak membedakan huruf besar/kecil, algoritma pencocokan adalah prefix match. Contoh: url `/test111` akan cocok dengan aturan `/test`, setelah cocok akan langsung keluar tanpa memeriksa konfigurasi berikutnya; + -Disarankan mengatur fungsi penanganan jalur root, dan menggunakan `$request->server['request_uri']` di fungsi callback untuk routing permintaan. + +### start() + +?> **Menjalankan server.** + +```php +Swoole\Coroutine\Http\Server->start(); +``` + +### shutdown() + +?> **Menghentikan server.** + +```php +Swoole\Coroutine\Http\Server->shutdown(); +``` + +## Contoh Lengkap + +```php +use Swoole\Coroutine\Http\Server; +use function Swoole\Coroutine\run; + +run(function () { + $server = new Server('127.0.0.1', 9502, false); + $server->handle('/', function ($request, $response) { + $response->end("

Index

"); + }); + $server->handle('/test', function ($request, $response) { + $response->end("

Test

"); + }); + $server->handle('/stop', function ($request, $response) use ($server) { + $response->end("

Stop

"); + $server->shutdown(); + }); + $server->start(); +}); +``` diff --git a/public/id/coroutine/iouring.md b/public/id/coroutine/iouring.md new file mode 100644 index 0000000..f606433 --- /dev/null +++ b/public/id/coroutine/iouring.md @@ -0,0 +1,156 @@ +# IO-Uring + +`iouring` adalah mekanisme panggilan `IO` konkuren baru yang disediakan oleh kernel `linux`. Berbeda dengan mekanisme multipleks `epoll`, `iouring` mendukung `IO` disk dan `IO` jaringan secara bersamaan. + +Perlu dicatat bahwa `io_uring != epoll`. Ini adalah dua konsep yang sama sekali berbeda. `iouring` bukan mekanisme event polling, melainkan implementasi panggilan sistem asinkron. Dalam implementasi infrastruktur `Swoole`, +`iouring` memiliki ketergantungan kuat dengan coroutine, hanya tersedia di coroutine `Swoole`. `Swoole` menggunakan `iouring` untuk mengimplementasikan panggilan sistem di ruang pengguna, dipadukan dengan model thread ruang pengguna (coroutine), +menggantikan model `reactor/proactor` tradisional berbasis `epoll/kqueue`, mewujudkan arsitektur konkuren efisien generasi baru. + +## `IO` Blocking Sinkron + +Alur panggilan `IO` klasik adalah sebagai berikut: + +```c +int sock = socket(AF_INET, SOCK_STREAM, 0); +struct sockaddr_in addr; +addr.sin_family = AF_INET; +addr.sin_port = htons(80); +addr.sin_addr.s_addr = inet_addr(dns_lookup("www.qq.com")); +connect(sock, (struct sockaddr *)&addr, sizeof(addr)); +send(sock, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n", 49, 0); +recv(sock, buf, sizeof(buf), 0); +close(sock); +``` + +`socket`, `connect`, `send`, `recv`, `close` adalah fungsi pustaka standar `POSIX` bahasa `C`, yang bersifat blocking sinkron, akan memblokir thread saat ini sampai panggilan berhasil. +Karena itu perlu dikombinasikan dengan multi-thread atau multi-proses untuk mencapai konkurensi. + +## `epoll` `IO` Non-Blocking Asinkron + +Alur panggilan `IO` non-blocking asinkron berbasis `epoll` adalah sebagai berikut: +```cpp +auto epoll = new EpollReactor(); +set_nonblocking(sock); +connect(sock, (struct sockaddr *)&addr, sizeof(addr)); +epoll->add(sock, EPOLLOUT, []() { + getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, sizeof(err)); + if (err != 0) { + // Koneksi gagal + return; + } + send(sock, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n", 49, 0); + epoll->add(sock, EPOLLIN, []() { + // Menerima data respons + recv(sock, buf, sizeof(buf), 0); + close(sock); + }); +}); +``` + +Perlu mendaftarkan listener event `EPOLLIN/EPOLLOUT`, mengatur `socket` ke mode non-blocking, lalu memproses event `IO` di callback event `epoll`. + +## `iouring` + +```c +Coroutine::create([&]() { + Iouring::connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + Iouring::send(sock, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n", 49, 0); + Iouring::recv(sock, buf, sizeof(buf), 0); + Iouring::close(sock); +}); +``` + +Menggunakan `iouring` untuk melakukan panggilan sistem seperti `connect`, `send`, `recv`, `close`. Panggilan sistem tidak lagi sinkron, melainkan asinkron, terdiri dari dua langkah: +1. Mengirim permintaan panggilan sistem ke antrean `SQE`, menangguhkan coroutine saat ini; +2. Menunggu sistem operasi menyelesaikan panggilan sistem, memanen antrean `CQE`, mendapatkan notifikasi `ioring`, membangunkan coroutine yang ditangguhkan, melanjutkan eksekusi. + +Program model `IO` non-blocking asinkron tradisional `epoll` harus sering berpindah antara ruang pengguna dan ruang kernel. +Sedangkan pengiriman dan pemanenan panggilan sistem `iouring` dilakukan secara batch. Perilaku panggilan sistem akan berubah menjadi mode asinkron, tidak perlu berpindah ke ruang kernel, sepenuhnya berjalan di ruang pengguna. Kinerja konkurensi meningkat secara signifikan. +`iouring` mendukung banyak panggilan sistem, termasuk berikut ini: + +| Kategori | Panggilan Sistem | Deskripsi | +|------|---|--------| +| Baca/Tulis File | open, read, write, close | Operasi sistem file | +| Jaringan | socket, connect, accept, send, recv, sendmsg, recvmsg, sendto, recvfrom, shutdown, close | Operasi jaringan | +| Mutex | futex_wait, futex_wakeup | Operasi kunci | +| Sistem File | rename, fstat, mkdir, unlink, rmdir, fsync, rmdir, fsync, fdatasync, ftruncate | Operasi sistem file | +| Proses | sleep, wait, waitpid | Operasi proses | + +## Enkapsulasi `Iouring` + +Kelas `Iouring` `C++` mengenkapsulasi panggilan sistem `iouring`. Parameter dan nilai kembali fungsi-fungsi ini sama dengan fungsi pustaka standar `POSIX` `glibc`. Di dalam coroutine `Swoole`, +fungsi-fungsi ini bisa dipanggil langsung, tanpa perlu berpindah ke ruang kernel. + +```cpp +using namespace swoole; +Coroutine::create([&]() { + // Panggilan sistem Swoole Iouring, hanya menangguhkan coroutine saat ini, coroutine lain tetap bisa berjalan + Iouring::sleep(1); + // Panggilan sistem POSIX, akan berpindah ke ruang kernel, memblokir thread/proses saat ini, coroutine lain tidak bisa berjalan + sleep(1); +}); +``` + +Melihat kode sumber kelas ini, bisa memahami detail implementasi panggilan sistem `iouring`. +- `Iouring::dispatch()`: Mengirim permintaan panggilan sistem ke antrean `SQE` +- `Iouring::execute()`: Mengirim permintaan panggilan sistem ke antrean `SQE`, dan menangguhkan coroutine saat ini +- `Iouring::wakeup()`: Memanen antrean `CQE`, mendapatkan notifikasi `ioring`, membangunkan coroutine yang ditangguhkan + +### Mekanisme Timeout +`socket` blocking sinkron biasanya perlu memanggil `setsockopt` untuk mengatur waktu timeout, contoh: + +```cpp +int timeout = 1000; +setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); +``` + +Model `IO` non-blocking asinkron `epoll` menggunakan timer untuk mengimplementasikan timeout. Sedangkan `iouring` memiliki mekanisme timeout bawaan, tanpa perlu timer tambahan atau pengaturan parameter `socket`. +```cpp +ssize_t Iouring::recv(int fd, void *buf, size_t len, int flags, double timeout) { + INIT_EVENT(IORING_OP_RECV); + io_uring_prep_recv(&event.data, fd, buf, len, flags); + event.set_timeout(timeout); + return execute(&event); +} + +void Iouring::dispatch(IouringEvent *event) { + // ... + io_uring_sqe *sqe = alloc_sqe(); + memcpy(sqe, &event->data, sizeof(event->data)); + io_uring_sqe_set_data(sqe, (void *) event); + + auto timeout_sqe = alloc_sqe(); + memset(timeout_sqe, 0, sizeof(*timeout_sqe)); + io_uring_prep_link_timeout(timeout_sqe, reinterpret_cast<__kernel_timespec *>(&event->timeout), 0); + io_uring_sqe_set_data(timeout_sqe, reinterpret_cast(TIMEOUT_EVENT)); + sqe->flags |= IOSQE_IO_LINK; + + // ... +} +``` + +Di `Facde API` `Iouring`, parameter terakhir dari banyak interface adalah waktu timeout. Contoh: `Iouring::recv()`. +```cpp +ssize_t Iouring::recv(int fd, void *buf, size_t len, int flags, double timeout = -1); +``` +Tipe parameter timeout adalah float, satuannya detik. Nilai bawaan `-1`, berarti tidak mengatur timeout. Granularitas minimum timeout `iouring` adalah nanodetik. Ini lebih presisi daripada timer `epoll`. + +## Enkapsulasi `UringSocket` + +`UringSocket` mengenkapsulasi panggilan sistem `iouring`, sebagai subclass dari `Swoole\Coroutine\Socket`, menggantikan method `connect`, `send`, `recv`, `close`, +Fungsi `IO` jaringan yang sebelumnya diimplementasikan dengan `epoll` IO non-blocking asinkron, sekarang akan menggunakan `iouring`. Contoh: method `read()` akan menggunakan `Iouring::read()`. + +```cpp +ssize_t UringSocket::read(void *_buf, size_t _n) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { + return -1; + } + read_co = Coroutine::get_current_safe(); + ssize_t retval = Iouring::read(socket->get_fd(), _buf, _n, socket->read_timeout); + read_co = nullptr; + check_return_value(retval); + return retval; +} +``` + +`UringSocket` mendukung `SSL`, sama seperti `Swoole\Coroutine\Socket`. Infrastruktur menggunakan `openssl BIO`. diff --git a/public/id/coroutine/multi_call.md b/public/id/coroutine/multi_call.md new file mode 100644 index 0000000..3d1d7d6 --- /dev/null +++ b/public/id/coroutine/multi_call.md @@ -0,0 +1,62 @@ +# Panggilan Konkuren + +[//]: # ( +Di sini fitur setDefer telah dihapus karena klien yang mendukung setDefer disarankan menggunakan one-click coroutine. +) + +Menggunakan `coroutine anak (go)` + `channel` untuk mengimplementasikan permintaan konkuren. + +!> Sebaiknya baca [ikhtisar](/coroutine) terlebih dahulu untuk memahami konsep dasar coroutine sebelum membaca bagian ini. + +### Prinsip Implementasi + +* Di `onRequest`, perlu melakukan dua permintaan `HTTP` secara konkuren. Bisa menggunakan fungsi `go` untuk membuat `2` coroutine anak, meminta banyak `URL` secara konkuren. +* Buat `channel`, gunakan sintaks referensi closure `use` untuk melewatkannya ke coroutine anak. +* Coroutine utama memanggil `chan->pop` secara berulang, menunggu coroutine anak menyelesaikan tugas, `yield` masuk ke keadaan ditangguhkan. +* Saat salah satu dari dua coroutine anak yang berjalan konkuren menyelesaikan permintaan, panggil `chan->push` untuk mengirim data ke coroutine utama. +* Setelah coroutine anak selesai meminta `URL`, keluar, coroutine utama kembali dari keadaan ditangguhkan, melanjutkan eksekusi ke bawah dengan memanggil `$resp->end` untuk mengirim respons. + +### Contoh Penggunaan + +```php +$serv = new Swoole\Http\Server("127.0.0.1", 9503, SWOOLE_BASE); + +$serv->on('request', function ($req, $resp) { + $chan = new Channel(2); + go(function () use ($chan) { + $cli = new Swoole\Coroutine\Http\Client('www.qq.com', 80); + $cli->set(['timeout' => 10]); + $cli->setHeaders([ + 'Host' => "www.qq.com", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $ret = $cli->get('/'); + $chan->push(['www.qq.com' => $cli->body]); + }); + + go(function () use ($chan) { + $cli = new Swoole\Coroutine\Http\Client('www.163.com', 80); + $cli->set(['timeout' => 10]); + $cli->setHeaders([ + 'Host' => "www.163.com", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $ret = $cli->get('/'); + $chan->push(['www.163.com' => $cli->body]); + }); + + $result = []; + for ($i = 0; $i < 2; $i++) + { + $result += $chan->pop(); + } + $resp->end(json_encode($result)); +}); +$serv->start(); +``` + +!> Menggunakan fitur [WaitGroup](/coroutine/wait_group) yang disediakan `Swoole` akan lebih sederhana. diff --git a/public/id/coroutine/notice.md b/public/id/coroutine/notice.md new file mode 100644 index 0000000..bcf1993 --- /dev/null +++ b/public/id/coroutine/notice.md @@ -0,0 +1,301 @@ +# Hal yang Perlu Diketahui dalam Pemrograman Coroutine + +Saat menggunakan fitur [coroutine](/coroutine) Swoole, harap baca bab ini dengan saksama. + +## Paradigma Pemrograman + +* Dilarang menggunakan variabel global di dalam coroutine +* Coroutine yang menggunakan kata kunci `use` untuk membawa variabel eksternal ke dalam lingkup saat ini, dilarang menggunakan referensi +* Komunikasi antar coroutine harus menggunakan [Channel](/coroutine/channel) + +!> Artinya, komunikasi antar coroutine jangan menggunakan variabel global atau mereferensi variabel eksternal ke lingkup saat ini, melainkan gunakan `Channel` + +* Jika proyek memiliki ekstensi yang melakukan `hook` pada `zend_execute_ex` atau `zend_execute_internal`, perlu perhatian khusus pada C stack. Bisa menggunakan [Co::set](/coroutine/coroutine?id=set) untuk mengatur ulang ukuran C stack. + +!> Setelah melakukan `hook` pada kedua fungsi entry ini, sebagian besar panggilan instruksi PHP yang datar akan berubah menjadi panggilan fungsi `C`, meningkatkan konsumsi C stack. + +## Keluar dari Coroutine + +Di versi Swoole lama, menggunakan `exit` untuk keluar paksa dari skrip di dalam coroutine dapat menyebabkan error memori yang mengakibatkan hasil tak terduga atau `coredump`. Di layanan Swoole, menggunakan `exit` akan menyebabkan seluruh process service keluar dan semua coroutine di dalamnya berhenti secara abnormal, menyebabkan masalah serius. Swoole sejak lama melarang developer menggunakan `exit`, namun developer bisa menggunakan cara tidak biasa dengan melempar exception, lalu menangkapnya di lapisan atas untuk mencapai logika keluar yang sama dengan `exit`. + +!> Versi v4.2.2 ke atas mengizinkan skrip (yang belum membuat `http_server`) untuk `exit` hanya dengan coroutine saat ini. + +Swoole **v4.1.0** ke atas mendukung langsung penggunaan `exit` PHP di dalam `coroutine` dan `service event loop`. Dalam kasus ini, infrastruktur secara otomatis akan melempar `Swoole\ExitException` yang bisa ditangkap, sehingga developer dapat menangkapnya di posisi yang diperlukan dan mengimplementasikan logika keluar yang sama seperti PHP asli. + +### Swoole\ExitException + +`Swoole\ExitException` mewarisi `Exception` dan menambahkan dua method baru `getStatus` dan `getFlags`: + +```php +namespace Swoole; + +class ExitException extends \Exception +{ + public function getStatus(): mixed + public function getFlags(): int +} +``` + +#### getStatus() + +Mendapatkan parameter `status` yang diteruskan saat `exit($status)`, mendukung tipe variabel apa pun. + +```php +public function getStatus(): mixed +``` + +#### getFlags() + +Mendapatkan mask informasi lingkungan saat exit. + +```php +public function getFlags(): int +``` + +Berikut mask yang tersedia: + +| Konstanta | Keterangan | +| -- | -- | +| SWOOLE_EXIT_IN_COROUTINE | Keluar di coroutine | +| SWOOLE_EXIT_IN_SERVER | Keluar di Server | + +### Cara Penggunaan + +#### Penggunaan Dasar + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +function route() +{ + controller(); +} + +function controller() +{ + your_code(); +} + +function your_code() +{ + Coroutine::sleep(.001); + exit(1); +} + +run(function () { + try { + route(); + } catch (\Swoole\ExitException $e) { + var_dump($e->getMessage()); + var_dump($e->getStatus() === 1); + var_dump($e->getFlags() === SWOOLE_EXIT_IN_COROUTINE); + } +}); +``` + +#### Keluar dengan Kode Status + +```php +use function Swoole\Coroutine\run; + +$exit_status = 0; +run(function () { + try { + exit(123); + } catch (\Swoole\ExitException $e) { + global $exit_status; + $exit_status = $e->getStatus(); + } +}); +var_dump($exit_status); +``` + +## Penanganan Exception + +Dalam pemrograman coroutine, exception bisa langsung ditangani dengan `try/catch`. **Namun harus ditangkap di dalam coroutine, tidak boleh menangkap exception lintas coroutine**. + +!> Tidak hanya `Exception` yang dilempar di lapisan aplikasi, beberapa error infrastruktur juga bisa ditangkap, seperti `function`, `class`, `method` yang tidak ada. + +### Contoh Salah + +Pada kode di bawah, `try/catch` dan `throw` berada di coroutine yang berbeda, exception tidak bisa ditangkap di dalam coroutine. Saat coroutine keluar dan menemukan exception yang tidak tertangkap, akan menyebabkan fatal error. + +```bash +PHP Fatal error: Uncaught RuntimeException +``` + +```php +try { + Swoole\Coroutine::create(function () { + throw new \RuntimeException(__FILE__, __LINE__); + }); +} +catch (\Throwable $e) { + echo $e; +} +``` + +### Contoh Benar + +Menangkap exception di dalam coroutine. + +```php +function test() { + throw new \RuntimeException(__FILE__, __LINE__); +} + +Swoole\Coroutine::create(function () { + try { + test(); + } + catch (\Throwable $e) { + echo $e; + } +}); +``` + +## Tidak Boleh Terjadi Perpindahan Coroutine di Method Magic __get / __set + +Alasan: [Lihat Analisis Internal PHP7](https://github.com/pangudashu/php7-internal/blob/40645cfe087b373c80738881911ae3b178818f11/3/zend_object.md) + +> **Catatan:** Jika kelas memiliki method __get(), saat mengalokasikan memori properti objek (yaitu: properties_table) akan mengalokasikan satu zval tambahan bertipe HashTable. Setiap kali __get($var) dipanggil, nama $var yang dimasukkan akan disimpan di hash table ini. Tujuannya untuk mencegah pemanggilan rekursif. Contoh: +> +> ***public function __get($var) { return $this->$var; }*** +> +> Situasi ini adalah saat memanggil __get() kemudian mengakses properti yang tidak ada, yang akan memanggil __get() secara rekursif. Jika tidak ada pengecekan terhadap $var yang diminta, rekursi akan terus berlanjut. Jadi sebelum memanggil __get(), pertama-tama akan diperiksa apakah $var saat ini sudah ada di __get(), jika sudah, __get() tidak akan dipanggil lagi. Jika tidak, $var akan dimasukkan sebagai key ke HashTable tersebut, lalu nilai hash diatur ke: *guard |= IN_ISSET, setelah selesai memanggil __get() nilai hash diatur ke: *guard &= ~IN_ISSET. +> +> HashTable ini tidak hanya digunakan untuk __get(), method magic lain juga menggunakannya, jadi tipe nilai hash-nya adalah zend_long, method magic yang berbeda menempati bit yang berbeda. Tidak semua objek akan mengalokasikan HashTable tambahan ini. Saat pembuatan objek, akan ditentukan berdasarkan ***zend_class_entry.ce_flags*** apakah mengandung ***ZEND_ACC_USE_GUARDS***. Saat kompilasi kelas, jika ditemukan method __get(), __set(), __unset(), __isset(), maka ce_flags akan diberi mask ini. + +Setelah perpindahan coroutine keluar, panggilan berikutnya akan dianggap sebagai panggilan rekursif. Masalah ini disebabkan oleh **fitur** PHP, dan setelah berdiskusi dengan tim pengembang PHP, belum ada solusi untuk saat ini. + +Catatan: Meskipun di method magic tidak ada kode yang menyebabkan perpindahan coroutine, namun jika penjadwalan preemptive coroutine diaktifkan, method magic tetap bisa dipindahkan secara paksa oleh coroutine. + +Saran: Implementasikan method `get`/`set` sendiri secara eksplisit + +Tautan issue asli: [#2625](https://github.com/swoole/swoole-src/issues/2625) + +## Error Serius + +Tindakan berikut dapat menyebabkan error serius. + +### Berbagi Satu Koneksi di Antara Banyak Coroutine + +Tidak seperti program blocking sinkron, coroutine memproses permintaan secara konkuren. Oleh karena itu, pada saat yang sama mungkin ada banyak permintaan yang diproses secara paralel. Jika berbagi koneksi klien, akan terjadi kekacauan data antar coroutine. Lihat: [Berbagi Koneksi TCP di Banyak Coroutine](/question/use?id=client-has-already-been-bound-to-another-coroutine) + +### Menggunakan Variabel Statis Kelas / Variabel Global untuk Menyimpan Konteks + +Banyak coroutine dieksekusi secara konkuren, jadi tidak boleh menggunakan variabel statis kelas / variabel global untuk menyimpan konteks coroutine. Menggunakan variabel lokal aman karena nilai variabel lokal otomatis disimpan di stack coroutine, dan coroutine lain tidak bisa mengakses variabel lokal coroutine lain. + +#### Contoh Salah + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9501); + +$_array = []; +$server->on('request', function ($request, $response) { + global $_array; + // Request /a (coroutine 1) + if ($request->server['request_uri'] == '/a') { + $_array['name'] = 'a'; + co::sleep(1.0); + echo $_array['name']; + $response->end($_array['name']); + } + // Request /b (coroutine 2) + else { + $_array['name'] = 'b'; + $response->end(); + } +}); +$server->start(); +``` + +Kirim `2` permintaan konkuren. + +```shell +curl http://127.0.0.1:9501/a +curl http://127.0.0.1:9501/b +``` + +* Coroutine `1` mengatur nilai variabel global `$_array['name']` menjadi `a` +* Coroutine `1` memanggil `co::sleep` dan ditangguhkan +* Coroutine `2` dijalankan, mengubah nilai `$_array['name']` menjadi `b`, coroutine 2 selesai +* Timer kembali, infrastruktur melanjutkan coroutine `1`. Ada ketergantungan konteks dalam logika coroutine `1`. Saat mencetak `$_array['name']`, yang diharapkan adalah `a`, tapi nilai ini sudah diubah oleh coroutine `2`, hasil sebenarnya adalah `b`, menyebabkan kesalahan logika +* Demikian pula, menggunakan variabel statis kelas `Class::$array`, properti objek global `$object->array`, atau superglobal lain `$GLOBALS` untuk menyimpan konteks dalam program coroutine sangat berbahaya. Bisa terjadi perilaku yang tidak sesuai harapan. + +![](../_images/coroutine/notice-1.png) + +#### Contoh Benar: Mengelola Konteks dengan Context + +Bisa menggunakan kelas `Context` untuk mengelola konteks coroutine. Di kelas `Context`, gunakan `Coroutine::getuid` untuk mendapatkan `ID` coroutine, lalu isolasi variabel global antar coroutine yang berbeda, dan bersihkan data konteks saat coroutine keluar. + +```php +use Swoole\Coroutine; + +class Context +{ + protected static $pool = []; + + static function get($key) + { + $cid = Coroutine::getuid(); + if ($cid < 0) + { + return null; + } + if(isset(self::$pool[$cid][$key])){ + return self::$pool[$cid][$key]; + } + return null; + } + + static function put($key, $item) + { + $cid = Coroutine::getuid(); + if ($cid > 0) + { + self::$pool[$cid][$key] = $item; + } + + } + + static function delete($key = null) + { + $cid = Coroutine::getuid(); + if ($cid > 0) + { + if($key){ + unset(self::$pool[$cid][$key]); + }else{ + unset(self::$pool[$cid]); + } + } + } +} +``` + +Penggunaan: + +```php +use Swoole\Coroutine\Context; + +$server = new Swoole\Http\Server('127.0.0.1', 9501); + +$server->on('request', function ($request, $response) { + if ($request->server['request_uri'] == '/a') { + Context::put('name', 'a'); + co::sleep(1.0); + echo Context::get('name'); + $response->end(Context::get('name')); + // Bersihkan saat keluar coroutine + Context::delete('name'); + } else { + Context::put('name', 'b'); + $response->end(); + // Bersihkan saat keluar coroutine + Context::delete(); + } +}); +$server->start(); +``` diff --git a/public/id/coroutine/proc_open.md b/public/id/coroutine/proc_open.md new file mode 100644 index 0000000..a78569f --- /dev/null +++ b/public/id/coroutine/proc_open.md @@ -0,0 +1,48 @@ +# Manajemen Proses Coroutine + +Karena melakukan `fork` proses di dalam ruang coroutine akan membawa konteks coroutine lain bersamanya, infrastruktur melarang penggunaan modul `Process` di dalam `Coroutine`. Bisa menggunakan: + +* `System::exec()` atau `Runtime Hook` + `shell_exec` untuk menjalankan program eksternal +* `Runtime Hook` + `proc_open` untuk komunikasi interaktif antara proses induk-anak + +## Contoh Penggunaan + +### main.php + +```php +use Swoole\Runtime; +use function Swoole\Coroutine\run; + +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +run(function () { + $descriptorspec = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("file", "/tmp/error-output.txt", "a") + ); + + $process = proc_open('php ' . __DIR__ . '/read_stdin.php', $descriptorspec, $pipes); + + $n = 10; + while ($n--) { + fwrite($pipes[0], "hello #$n \n"); + echo fread($pipes[1], 8192); + } + + fclose($pipes[0]); + proc_close($process); +}); +``` + +### read_stdin.php + +```php +while(true) { + $line = fgets(STDIN); + if ($line) { + echo $line; + } else { + break; + } +} +``` diff --git a/public/id/coroutine/scheduler.md b/public/id/coroutine/scheduler.md new file mode 100644 index 0000000..e8ce40d --- /dev/null +++ b/public/id/coroutine/scheduler.md @@ -0,0 +1,180 @@ +# Coroutine\Scheduler + +?> Semua [coroutine](/coroutine) harus [dibuat](/coroutine/coroutine?id=create) di dalam `coroutine container`. Saat program `Swoole` dijalankan, sebagian besar kasus akan otomatis membuat `coroutine container`. Ada tiga cara untuk menjalankan program dengan `Swoole`: + + - Memanggil method [start](/server/methods?id=start) dari program server [gaya asinkron](/server/init). Cara ini akan otomatis membuat `coroutine container` di [fungsi callback event](/server/events), lihat [enable_coroutine](/server/setting?id=enable_coroutine). + - Memanggil method `start` dari 2 modul manajemen proses yang disediakan `Swoole`, yaitu [Process](/process/process) dan [Process\Pool](/process/process_pool). Cara ini akan membuat `coroutine container` saat proses dimulai, lihat parameter `enable_coroutine` di konstruktor kedua modul ini. + - Cara lain dengan menulis coroutine secara langsung untuk menjalankan program, perlu membuat coroutine container terlebih dahulu (fungsi `Coroutine\run()`, bisa dipahami seperti fungsi `main` di Java, C), contoh: + +* **Menjalankan layanan `HTTP` full coroutine** + +```php +use Swoole\Coroutine\Http\Server; +use function Swoole\Coroutine\run; + +run(function () { + $server = new Server('127.0.0.1', 9502, false); + $server->handle('/', function ($request, $response) { + $response->end("

Index

"); + }); + $server->handle('/test', function ($request, $response) { + $response->end("

Test

"); + }); + $server->handle('/stop', function ($request, $response) use ($server) { + $response->end("

Stop

"); + $server->shutdown(); + }); + $server->start(); +}); +echo 1;//tidak akan dieksekusi +``` + +* **Menambahkan 2 coroutine konkuren untuk melakukan sesuatu** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + Coroutine::create(function() { + var_dump(file_get_contents("http://www.xinhuanet.com/")); + }); + + Coroutine::create(function() { + Coroutine::sleep(1); + echo "done\n"; + }); +}); +echo 1;//akan dieksekusi +``` + +!> Tersedia di `Swoole v4.4+`. + +!> Tidak boleh menyarangkan `Coroutine\run()`. +Jika ada event yang belum diproses setelah `Coroutine\run()`, maka kode setelahnya tidak akan dieksekusi. Sebaliknya, jika tidak ada event, eksekusi akan berlanjut ke bawah, dan `Coroutine\run()` bisa dipanggil lagi. + +Fungsi `Coroutine\run()` di atas sebenarnya adalah pembungkus untuk kelas `Swoole\Coroutine\Scheduler` (kelas penjadwal coroutine). Untuk detailnya, lihat method `Swoole\Coroutine\Scheduler`: + +### set() + +?> **Mengatur parameter runtime coroutine.** + +?> Merupakan alias dari method `Coroutine::set`. Lihat dokumentasi [Coroutine::set](/coroutine/coroutine?id=set). + +```php +Swoole\Coroutine\Scheduler->set(array $options): bool +``` + + * **Contoh** + +```php +$sch = new Swoole\Coroutine\Scheduler; +$sch->set(['max_coroutine' => 100]); +``` + +### getOptions() + +?> **Mendapatkan parameter runtime coroutine yang telah diatur.** Tersedia sejak Swoole versi >= `v4.6.0` + +?> Merupakan alias dari method `Coroutine::getOptions`. Lihat dokumentasi [Coroutine::getOptions](/coroutine/coroutine?id=getoptions). + +```php +Swoole\Coroutine\Scheduler->getOptions(): null|array +``` + +### add() + +?> **Menambahkan tugas.** + +```php +Swoole\Coroutine\Scheduler->add(callable $fn, ... $args): bool +``` + + * **Parameter** + + * **`callable $fn`** + * **Fungsi**: Fungsi callback + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`... $args`** + * **Fungsi**: Parameter opsional, akan diteruskan ke coroutine + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine; + +$scheduler = new Coroutine\Scheduler; +$scheduler->add(function ($a, $b) { + Coroutine::sleep(1); + echo assert($a == 'hello') . PHP_EOL; + echo assert($b == 12345) . PHP_EOL; + echo "Done.\n"; +}, "hello", 12345); + +$scheduler->start(); +``` + + * **Catatan** + + !> Berbeda dengan fungsi `go`, coroutine yang ditambahkan di sini tidak akan langsung dieksekusi, melainkan menunggu sampai method `start` dipanggil untuk dijalankan bersama. Jika program hanya menambahkan coroutine tanpa memanggil `start`, fungsi coroutine `$fn` tidak akan dieksekusi. + +### parallel() + +?> **Menambahkan tugas paralel.** + +?> Berbeda dengan method `add`, method `parallel` akan membuat coroutine paralel. Saat `start` dipanggil, akan menjalankan `$num` coroutine `$fn` secara bersamaan. + +```php +Swoole\Coroutine\Scheduler->parallel(int $num, callable $fn, ... $args): bool +``` + + * **Parameter** + + * **`int $num`** + * **Fungsi**: Jumlah coroutine yang akan dijalankan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $fn`** + * **Fungsi**: Fungsi callback + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`... $args`** + * **Fungsi**: Parameter opsional, akan diteruskan ke coroutine + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine; + +$scheduler = new Coroutine\Scheduler; + +$scheduler->parallel(10, function ($t, $n) { + Coroutine::sleep($t); + echo "Co ".Coroutine::getCid()."\n"; +}, 0.05, 'A'); + +$scheduler->start(); +``` + +### start() + +?> **Menjalankan program.** + +?> Menelusuri tugas coroutine yang ditambahkan oleh method `add` dan `parallel`, lalu menjalankannya. + +```php +Swoole\Coroutine\Scheduler->start(): bool +``` + + * **Return Value** + + * Jika berhasil dijalankan, semua tugas yang ditambahkan akan dieksekusi, saat semua coroutine keluar, `start` akan mengembalikan `true` + * Jika gagal, mengembalikan `false`, penyebabnya mungkin sudah berjalan atau sudah ada scheduler lain yang tidak bisa dibuat lagi. diff --git a/public/id/coroutine/server.md b/public/id/coroutine/server.md new file mode 100644 index 0000000..7c75c26 --- /dev/null +++ b/public/id/coroutine/server.md @@ -0,0 +1,243 @@ +# Server TCP + +?> `Swoole\Coroutine\Server` adalah kelas yang sepenuhnya [coroutine](/coroutine), digunakan untuk membuat server `TCP` coroutine, mendukung tipe TCP dan [unixSocket](/learn?id=apa-itu-IPC). + +Perbedaan dengan modul [Server](/server/tcp_init): + +* Pembuatan dan penghancuran dinamis, bisa mendengarkan port secara dinamis saat runtime, juga bisa menutup server secara dinamis +* Proses penanganan koneksi sepenuhnya sinkron, program bisa memproses event `Connect`, `Receive`, `Close` secara berurutan + +!> Tersedia di versi 4.4 ke atas. + +## Nama Pendek + +Bisa menggunakan nama pendek `Co\Server`. + +## Method + +### __construct() + +?> **Method konstruktor.** + +```php +Swoole\Coroutine\Server::__construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false); +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat yang didengarkan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port yang didengarkan [jika 0, akan ditentukan secara acak oleh sistem operasi] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`bool $ssl`** + * **Fungsi**: Apakah mengaktifkan enkripsi SSL + * **Bawaan**: `false` + * **Nilai Lain**: `true` + + * **`bool $reuse_port`** + * **Fungsi**: Apakah mengaktifkan reuse port, efeknya sama dengan konfigurasi di [bagian ini](/server/setting?id=enable_reuse_port) + * **Bawaan**: `false` + * **Nilai Lain**: `true` + * **Versi**: Swoole versi >= v4.4.4 + + * **Petunjuk** + + * **Parameter $host mendukung 3 format** + + * `0.0.0.0/127.0.0.1`: Alamat IPv4 + * `::/::1`: Alamat IPv6 + * `unix:/tmp/test.sock`: Alamat [UnixSocket](/learn?id=apa-itu-IPC) + + * **Exception** + + * Error parameter, gagal bind alamat dan port, gagal `listen` akan melempar exception `Swoole\Exception`. + +### set() + +?> **Mengatur parameter pemrosesan protokol.** + +```php +Swoole\Coroutine\Server->set(array $options); +``` + + * **Parameter Konfigurasi** + + * Parameter `$options` harus berupa array asosiatif satu dimensi, sama persis dengan item konfigurasi yang diterima method [setprotocol](/coroutine_client/socket?id=setprotocol). + + !> Harus diatur sebelum method [start()](/coroutine/server?id=start). + + * **Protokol Panjang** + + ```php + $server = new Swoole\Coroutine\Server('127.0.0.1', $port, $ssl); + $server->set([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + ``` + + * **Pengaturan Sertifikat SSL** + + ```php + $server->set([ + 'ssl_cert_file' => dirname(__DIR__) . '/ssl/server.crt', + 'ssl_key_file' => dirname(__DIR__) . '/ssl/server.key', + ]); + ``` + +### handle() + +?> **Mengatur fungsi penanganan koneksi.** + +!> Harus diatur sebelum [start()](/coroutine/server?id=start). + +```php +Swoole\Coroutine\Server->handle(callable $fn); +``` + + * **Parameter** + + * **`callable $fn`** + * **Fungsi**: Mengatur fungsi penanganan koneksi + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh** + + ```php + $server->handle(function (Swoole\Coroutine\Server\Connection $conn) { + while (true) { + $data = $conn->recv(); + } + }); + ``` + + !> -Setelah server berhasil `Accept` (membangun koneksi), akan otomatis membuat [coroutine](/coroutine?id=penjadwalan-coroutine) dan menjalankan `$fn`; + -`$fn` dieksekusi di dalam ruang coroutine anak baru, jadi tidak perlu membuat coroutine lagi di dalam fungsi; + -`$fn` menerima satu parameter, tipenya objek [Swoole\Coroutine\Server\Connection](/coroutine/server?id=coroutineserverconnection); + -Bisa menggunakan [exportSocket()](/coroutine/server?id=exportsocket) untuk mendapatkan objek Socket koneksi saat ini. + +### shutdown() + +?> **Menghentikan server.** + +?> Infrastruktur mendukung pemanggilan `start` dan `shutdown` berkali-kali. + +```php +Swoole\Coroutine\Server->shutdown(): bool +``` + +### start() + +?> **Menjalankan server.** + +```php +Swoole\Coroutine\Server->start(): bool +``` + + * **Return Value** + + * Gagal dijalankan akan mengembalikan `false`, dan mengatur properti `errCode` + * Berhasil dijalankan akan masuk ke loop, `Accept` koneksi + * Setelah `Accept` (membangun koneksi) akan membuat coroutine baru, dan memanggil fungsi yang ditentukan method handle di dalam coroutine tersebut + + * **Penanganan Error** + + * Saat `Accept` (membangun koneksi) terjadi error `Too many open file`, atau gagal membuat coroutine anak, akan jeda `1` detik lalu melanjutkan `Accept` + * Saat terjadi error, method `start()` akan kembali, informasi error akan dilaporkan sebagai `Warning`. + +## Objek + +### Coroutine\Server\Connection + +Objek `Swoole\Coroutine\Server\Connection` menyediakan empat method: + +#### recv() + +Menerima data, jika protokol diatur, akan mengembalikan paket lengkap setiap kali. + +```php +function recv(float $timeout = 0) +``` + +#### send() + +Mengirim data. + +```php +function send(string $data) +``` + +#### close() + +Menutup koneksi. + +```php +function close(): bool +``` + +#### exportSocket() + +Mendapatkan objek Socket dari koneksi saat ini. Bisa memanggil method infrastruktur lainnya, lihat [Swoole\Coroutine\Socket](/coroutine_client/socket). + +```php +function exportSocket(): Swoole\Coroutine\Socket +``` + +## Contoh Lengkap + +```php +use Swoole\Process; +use Swoole\Coroutine; +use Swoole\Coroutine\Server\Connection; + +// Modul manajemen multi-proses +$pool = new Process\Pool(2); +// Buat coroutine otomatis di setiap callback OnWorkerStart +$pool->set(['enable_coroutine' => true]); +$pool->on('workerStart', function ($pool, $id) { + // Setiap proses mendengarkan port 9501 + $server = new Swoole\Coroutine\Server('127.0.0.1', 9501, false, true); + + // Terima sinyal 15 untuk menutup layanan + Process::signal(SIGTERM, function () use ($server) { + $server->shutdown(); + }); + + // Terima permintaan koneksi baru dan buat coroutine otomatis + $server->handle(function (Connection $conn) { + while (true) { + // Terima data + $data = $conn->recv(1); + + if ($data === '' || $data === false) { + $errCode = swoole_last_error(); + $errMsg = socket_strerror($errCode); + echo "errCode: {$errCode}, errMsg: {$errMsg}\n"; + $conn->close(); + break; + } + + // Kirim data + $conn->send('hello'); + + Coroutine::sleep(1); + } + }); + + // Mulai mendengarkan port + $server->start(); +}); +$pool->start(); +``` + +!> Jika berjalan di lingkungan Cygwin, ubah ke proses tunggal. `$pool = new Swoole\Process\Pool(1);` diff --git a/public/id/coroutine/system.md b/public/id/coroutine/system.md new file mode 100644 index 0000000..d6f00c6 --- /dev/null +++ b/public/id/coroutine/system.md @@ -0,0 +1,681 @@ +# Coroutine\System + +Enkapsulasi coroutine untuk `API` terkait sistem. Modul ini tersedia setelah rilis resmi `v4.4.6`. Sebagian besar `API` diimplementasikan berdasarkan thread pool `AIO`. + +!> Untuk versi sebelum `v4.4.6`, gunakan nama pendek `Co` atau `Swoole\Coroutine`, seperti: `Co::sleep` atau `Swoole\Coroutine::sleep` +Mulai `v4.4.6` **direkomendasikan** menggunakan `Co\System::sleep` atau `Swoole\Coroutine\System::sleep` +Perubahan ini bertujuan untuk menstandarisasi namespace, namun tetap kompatibel ke belakang (artinya penulisan sebelum `v4.4.6` tetap bisa digunakan, tidak perlu diubah). + +## Method + +### statvfs() + +Mendapatkan informasi sistem file. + +!> Tersedia sejak Swoole versi >= v4.2.5 + +```php +Swoole\Coroutine\System::statvfs(string $path): array|false +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Direktori tempat sistem file di-mount [seperti `/`, bisa menggunakan perintah `df` dan `mount -l`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh Penggunaan** + + ```php + Swoole\Coroutine\run(function () { + var_dump(Swoole\Coroutine\System::statvfs('/')); + }); + ``` + * **Contoh Output** + + ```php + array(11) { + ["bsize"]=> + int(4096) + ["frsize"]=> + int(4096) + ["blocks"]=> + int(61068098) + ["bfree"]=> + int(45753580) + ["bavail"]=> + int(42645728) + ["files"]=> + int(15523840) + ["ffree"]=> + int(14909927) + ["favail"]=> + int(14909927) + ["fsid"]=> + int(1002377915335522995) + ["flag"]=> + int(4096) + ["namemax"]=> + int(255) + } + ``` + +### fread() + +Membaca file dalam mode coroutine. + +```php +Swoole\Coroutine\System::fread(resource $handle, int $length = 0): string|false +``` + +!> Di versi di bawah `v4.0.4`, method `fread` tidak mendukung `stream` non-file seperti `STDIN`, `Socket`. Jangan gunakan `fread` untuk resource tersebut. +Di versi `v4.0.4` ke atas, method `fread` mendukung resource `stream` non-file. Infrastruktur akan otomatis memilih menggunakan thread pool `AIO` atau [EventLoop](/learn?id=apa-itu-eventloop). + +!> Method ini sudah tidak digunakan lagi di versi `5.0` dan telah dihapus di versi `6.0`. + + * **Parameter** + + * **`resource $handle`** + * **Fungsi**: Handle file [harus berupa resource `stream` tipe file yang dibuka dengan `fopen`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $length`** + * **Fungsi**: Panjang yang akan dibaca [bawaan `0`, berarti membaca seluruh konten file] + * **Bawaan**: `0` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan string jika berhasil, `false` jika gagal. + + * **Contoh Penggunaan** + + ```php + $fp = fopen(__FILE__, "r"); + Swoole\Coroutine\run(function () use ($fp) + { + $r = Swoole\Coroutine\System::fread($fp); + var_dump($r); + }); + ``` + +### fwrite() + +Menulis data ke file dalam mode coroutine. + +```php +Swoole\Coroutine\System::fwrite(resource $handle, string $data, int $length = 0): int|false +``` + +!> Di versi di bawah `v4.0.4`, method `fwrite` tidak mendukung `stream` non-file seperti `STDIN`, `Socket`. Jangan gunakan `fwrite` untuk resource tersebut. +Di versi `v4.0.4` ke atas, method `fwrite` mendukung resource `stream` non-file. Infrastruktur akan otomatis memilih berdasarkan tipe `stream` antara thread pool `AIO` atau [EventLoop](/learn?id=apa-itu-eventloop). + +!> Method ini sudah tidak digunakan lagi di versi `5.0` dan telah dihapus di versi `6.0`. + + * **Parameter** + + * **`resource $handle`** + * **Fungsi**: Handle file [harus berupa resource `stream` tipe file yang dibuka dengan `fopen`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $data`** + * **Fungsi**: Data yang akan ditulis [bisa berupa teks atau data biner] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $length`** + * **Fungsi**: Panjang yang akan ditulis [bawaan `0`, berarti menulis seluruh konten `$data`, `$length` harus kurang dari panjang `$data`] + * **Bawaan**: `0` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan panjang data jika berhasil, `false` jika gagal. + + * **Contoh Penggunaan** + + ```php + $fp = fopen(__DIR__ . "/test.data", "a+"); + Swoole\Coroutine\run(function () use ($fp) + { + $r = Swoole\Coroutine\System::fwrite($fp, "hello world\n", 5); + var_dump($r); + }); + ``` + +### fgets() + +Membaca file baris per baris dalam mode coroutine. + +Menggunakan buffer `php_stream` dengan ukuran bawaan `8192` byte, ukuran buffer bisa diatur dengan `stream_set_chunk_size`. + +```php +Swoole\Coroutine\System::fgets(resource $handle): string|false +``` + +!> Fungsi `fgets` hanya bisa digunakan untuk resource `stream` tipe file. Tersedia sejak Swoole versi >= `v4.4.4`. + +!> Method ini sudah tidak digunakan lagi di versi `5.0` dan telah dihapus di versi `6.0`. + + * **Parameter** + + * **`resource $handle`** + * **Fungsi**: Handle file [harus berupa resource `stream` tipe file yang dibuka dengan `fopen`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Jika membaca `EOL` (`\r` atau `\n`), akan mengembalikan satu baris data termasuk `EOL` + * Jika tidak membaca `EOL` tapi konten melebihi buffer `php_stream` `8192` byte, akan mengembalikan `8192` byte data tanpa `EOL` + * Saat mencapai akhir file (`EOF`), mengembalikan string kosong, bisa gunakan `feof` untuk mengecek apakah file sudah selesai dibaca + * Mengembalikan `false` jika gagal baca, gunakan [swoole_last_error](/functions?id=swoole_last_error) untuk kode error + + * **Contoh Penggunaan** + + ```php + $fp = fopen(__DIR__ . "/defer_client.php", "r"); + Swoole\Coroutine\run(function () use ($fp) + { + $r = Swoole\Coroutine\System::fgets($fp); + var_dump($r); + }); + ``` + +### readFile() + +Membaca file dalam mode coroutine. + +```php +Swoole\Coroutine\System::readFile(string $filename, int $flags = 0): string|false +``` + + * **Parameter** + + * **`string $filename`** + * **Fungsi**: Nama file + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + * **`int $flags`** + * **Fungsi**: Apakah menggunakan file lock, saat ini hanya mendukung opsi `LOCK_EX`. Perhatikan bahwa infrastruktur akan mengatur shared lock saat membaca file, coroutine lain tetap bisa membaca file tapi tidak bisa menulis. + * **Bawaan**: `0`, berarti tidak menggunakan file lock + + * **Return Value** + + * Mengembalikan string konten jika berhasil, `false` jika gagal. Bisa gunakan [swoole_last_error](/functions?id=swoole_last_error) untuk info error. + * Method `readFile` tidak memiliki batasan ukuran, konten yang dibaca akan disimpan di memori, jadi membaca file yang sangat besar bisa menghabiskan banyak memori. + + * **Contoh Penggunaan** + + ```php + $filename = __DIR__ . "/defer_client.php"; + Swoole\Coroutine\run(function () use ($filename) + { + $r = Swoole\Coroutine\System::readFile($filename); + var_dump($r); + }); + ``` + +### writeFile() + +Menulis file dalam mode coroutine. + +```php +Swoole\Coroutine\System::writeFile(string $filename, string $fileContent, int $flags): bool +``` + + * **Parameter** + + * **`string $filename`** + * **Fungsi**: Nama file [harus memiliki izin tulis, file akan dibuat otomatis jika tidak ada. Jika gagal membuka file, akan segera mengembalikan `false`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $fileContent`** + * **Fungsi**: Konten yang akan ditulis ke file [maksimal `4M`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $flags`** + * **Fungsi**: Opsi penulisan [bawaan akan menghapus konten file saat ini, bisa gunakan `FILE_APPEND` untuk menambahkan ke akhir file] + * **Bawaan**: Tidak ada + * **Nilai Lain**: `LOCK_EX` akan mengunci file secara eksklusif saat menulis, mencegah coroutine lain membaca/menulis file ini. Banyak opsi bisa digabung dengan bitwise OR `|`, seperti `FILE_APPEND | LOCK_EX` + + * **Return Value** + + * Mengembalikan `true` jika berhasil + * Mengembalikan `false` jika gagal + + * **Contoh Penggunaan** + + ```php + $filename = __DIR__ . "/defer_client.php"; + Swoole\Coroutine\run(function () use ($filename) + { + $w = Swoole\Coroutine\System::writeFile($filename, "hello swoole!"); + var_dump($w); + }); + ``` + +### sleep() + +Memasuki keadaan menunggu. + +Setara dengan fungsi `sleep` di `PHP`, bedanya `Coroutine::sleep` diimplementasikan oleh penjadwal [coroutine](/coroutine?id=penjadwalan-coroutine). Infrastruktur akan melakukan `yield` pada coroutine saat ini, menyerahkan time slice, dan menambahkan timer asinkron. Saat timeout tiba, akan melakukan `resume` pada coroutine saat ini untuk melanjutkan eksekusi. + +Menggunakan interface `sleep` bisa dengan mudah mengimplementasikan fungsi timeout. + +```php +Swoole\Coroutine\System::sleep(float $seconds): void +``` + + * **Parameter** + + * **`float $seconds`** + * **Fungsi**: Waktu tidur [harus lebih dari `0`, maksimal satu hari (`86400` detik)] + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Contoh Penggunaan** + + ```php + $server = new Swoole\Http\Server("127.0.0.1", 9502); + + $server->on('Request', function($request, $response) { + // Tunggu 200ms sebelum mengirim respons ke browser + Swoole\Coroutine\System::sleep(0.2); + $response->end("

Hello Swoole!

"); + }); + + $server->start(); + ``` + +### exec() + +Menjalankan perintah shell. Infrastruktur otomatis melakukan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\System::exec(string $cmd): array +``` + + * **Parameter** + + * **`string $cmd`** + * **Fungsi**: Perintah `shell` yang akan dijalankan + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan `false` jika gagal, mengembalikan array jika berhasil, berisi kode status keluar proses, sinyal, dan konten output. + + ```php + array( + 'code' => 0, // Kode status keluar proses + 'signal' => 0, // Sinyal + 'output' => '', // Konten output + ); + ``` + + * **Contoh Penggunaan** + + ```php + Swoole\Coroutine\run(function() { + $ret = Swoole\Coroutine\System::exec("md5sum ".__FILE__); + }); + ``` + + * **Catatan** + + !> Jika eksekusi perintah script terlalu lama, akan menyebabkan timeout keluar. Dalam kasus ini, bisa diatasi dengan memperbesar [socket_read_timeout](/coroutine_client/init?id=aturan-timeout). + +### gethostbyname() + +Meresolusi nama domain menjadi IP. Implementasi simulasi berdasarkan thread pool sinkron, infrastruktur otomatis melakukan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\System::gethostbyname(string $domain, int $family = AF_INET, float $timeout = -1): string|false +``` + + * **Parameter** + + * **`string $domain`** + * **Fungsi**: Nama domain + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $family`** + * **Fungsi**: Family alamat [`AF_INET` mengembalikan alamat `IPv4`, `AF_INET6` mengembalikan alamat `IPv6`] + * **Bawaan**: `AF_INET` + * **Nilai Lain**: `AF_INET6` + + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan alamat `IP` yang sesuai dengan domain jika berhasil, `false` jika gagal. Bisa gunakan [swoole_last_error](/functions?id=swoole_last_error) untuk info error. + + * **Ekstensi** + + * **Kontrol Timeout** + + Parameter `$timeout` bisa mengontrol waktu tunggu coroutine. Jika tidak ada hasil dalam waktu yang ditentukan, coroutine akan segera mengembalikan `false` dan melanjutkan eksekusi ke bawah. Dalam implementasi infrastruktur, tugas asinkron ini akan ditandai sebagai `cancel`, dan `gethostbyname` tetap akan dieksekusi di thread pool `AIO`. + + Bisa memodifikasi `/etc/resolv.conf` untuk mengatur timeout fungsi `C` dasar `gethostbyname` dan `getaddrinfo`. Lihat [Mengatur Timeout dan Retry DNS](/learn_other?id=dns-resolution-timeout-and-retries) + + * **Contoh Penggunaan** + + ```php + Swoole\Coroutine\run(function () { + $ip = Swoole\Coroutine\System::gethostbyname("www.baidu.com", AF_INET, 0.5); + echo $ip; + }); + ``` + +### getaddrinfo() + +Melakukan resolusi DNS, mencari alamat `IP` yang sesuai dengan nama domain. + +Berbeda dengan `gethostbyname`, `getaddrinfo` mendukung lebih banyak pengaturan parameter dan bisa mengembalikan banyak hasil `IP`. + +```php +Swoole\Coroutine\System::getaddrinfo(string $domain, int $family = AF_INET, int $socktype = SOCK_STREAM, int $protocol = STREAM_IPPROTO_TCP, string $service = null, float $timeout = -1): array|false +``` + + * **Parameter** + + * **`string $domain`** + * **Fungsi**: Nama domain + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $family`** + * **Fungsi**: Family alamat [`AF_INET` mengembalikan alamat `IPv4`, `AF_INET6` mengembalikan alamat `IPv6`] + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Lihat dokumentasi `man getaddrinfo` untuk pengaturan parameter lainnya. + + * **`int $socktype`** + * **Fungsi**: Tipe protokol + * **Bawaan**: `SOCK_STREAM` + * **Nilai Lain**: `SOCK_DGRAM`, `SOCK_RAW` + + * **`int $protocol`** + * **Fungsi**: Protokol + * **Bawaan**: `STREAM_IPPROTO_TCP` + * **Nilai Lain**: `STREAM_IPPROTO_UDP`, `STREAM_IPPROTO_STCP`, `STREAM_IPPROTO_TIPC`, `0` + + * **`string $service`** + * **Fungsi**: + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan array berisi banyak alamat `IP` jika berhasil, `false` jika gagal. + + * **Contoh Penggunaan** + + ```php + Swoole\Coroutine\run(function () { + $ips = Swoole\Coroutine\System::getaddrinfo("www.baidu.com"); + var_dump($ips); + }); + ``` + +### dnsLookup() + +Query alamat domain. + +Berbeda dengan `Coroutine\System::gethostbyname`, `Coroutine\System::dnsLookup` diimplementasikan langsung berdasarkan komunikasi jaringan klien `UDP`, bukan menggunakan fungsi `gethostbyname` dari `libc`. + +!> Tersedia sejak Swoole versi >= `v4.4.3`, infrastruktur akan membaca `/etc/resolve.conf` untuk mendapatkan alamat server `DNS`, saat ini hanya mendukung resolusi domain `AF_INET(IPv4)`. Mulai Swoole versi >= `v4.7` bisa menggunakan parameter ketiga untuk mendukung `AF_INET6(IPv6)`. + +```php +Swoole\Coroutine\System::dnsLookup(string $domain, float $timeout = 5, int $type = AF_INET): string|false +``` + + * **Parameter** + + * **`string $domain`** + * **Fungsi**: Nama domain + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `5` + * **Nilai Lain**: Tidak ada + + * **`int $type`** + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `AF_INET` + * **Nilai Lain**: `AF_INET6` + + !> Parameter `$type` tersedia sejak Swoole versi >= `v4.7`. + + * **Return Value** + + * Mengembalikan alamat IP yang sesuai jika berhasil + * Mengembalikan `false` jika gagal, bisa gunakan [swoole_last_error](/functions?id=swoole_last_error) untuk info error + + * **Error Umum** + + * `SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED`: Domain ini tidak bisa di-resolve, query gagal + * `SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT`: Timeout resolusi, server DNS mungkin bermasalah, tidak bisa mengembalikan hasil dalam waktu yang ditentukan + + * **Contoh Penggunaan** + + ```php + Swoole\Coroutine\run(function () { + $ip = Swoole\Coroutine\System::dnsLookup("www.baidu.com"); + echo $ip; + }); + ``` + +### wait() + +Setara dengan [Process::wait](/process/process?id=wait) asli, bedanya API ini adalah versi coroutine yang akan menangguhkan coroutine. Bisa menggantikan fungsi `Swoole\Process::wait` dan `pcntl_wait`. + +!> Tersedia sejak Swoole versi >= `v4.5.0` + +```php +Swoole\Coroutine\System::wait(float $timeout = -1): array|false +``` + +* **Parameter** + + * **`float $timeout`** + * **Fungsi**: Waktu timeout, negatif berarti tidak pernah timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Jika berhasil, mengembalikan array berisi `PID` proses anak, kode status keluar, dan sinyal `KILL` + * Jika gagal, mengembalikan `false` + +!> Setelah setiap proses anak dijalankan, proses induk harus mengirimkan coroutine untuk memanggil `wait()` (atau `waitPid()`) untuk memulihkannya, jika tidak proses anak akan menjadi zombie process, membuang-buang resource proses sistem operasi. +Jika menggunakan coroutine, proses harus dibuat terlebih dahulu, lalu coroutine di dalam proses. Jangan sebaliknya, jika tidak, melakukan fork dengan coroutine akan menjadi sangat kompleks dan sulit ditangani oleh infrastruktur. + +* **Contoh** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\System; +use Swoole\Process; + +$process = new Process(function () { + echo 'Hello Swoole'; +}); +$process->start(); + +Coroutine\run(function () use ($process) { + $status = System::wait(); + assert($status['pid'] === $process->pid); + var_dump($status); +}); +``` + +### waitPid() + +Pada dasarnya sama dengan method `wait` di atas, bedanya API ini bisa menentukan proses tertentu untuk ditunggu. + +!> Tersedia sejak Swoole versi >= `v4.5.0` + +```php +Swoole\Coroutine\System::waitPid(int $pid, float $timeout = -1): array|false +``` + +* **Parameter** + + * **`int $pid`** + * **Fungsi**: ID proses + * **Bawaan**: `-1` (berarti proses apa pun, setara dengan method `wait`) + * **Nilai Lain**: Bilangan natural apa pun + + * **`float $timeout`** + * **Fungsi**: Waktu timeout, negatif berarti tidak pernah timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Jika berhasil, mengembalikan array berisi `PID` proses anak, kode status keluar, dan sinyal `KILL` + * Jika gagal, mengembalikan `false` + +!> Setelah setiap proses anak dijalankan, proses induk harus mengirimkan coroutine untuk memanggil `wait()` (atau `waitPid()`) untuk memulihkannya, jika tidak proses anak akan menjadi zombie process. + +* **Contoh** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\System; +use Swoole\Process; + +$process = new Process(function () { + echo 'Hello Swoole'; +}); +$process->start(); + +Coroutine\run(function () use ($process) { + $status = System::waitPid($process->pid); + var_dump($status); +}); +``` + +### waitSignal() + +Listener sinyal versi coroutine, akan memblokir coroutine saat ini sampai sinyal terpicu. Bisa menggantikan fungsi `Swoole\Process::signal` dan `pcntl_signal`. + +!> Tersedia sejak Swoole versi >= `v4.5.0` + +```php +Swoole\Coroutine\System::waitSignal(int $signo, float $timeout = -1): bool +``` + + * **Parameter** + + * **`int $signo`** + * **Fungsi**: Tipe sinyal + * **Bawaan**: Tidak ada + * **Nilai Lain**: Konstanta seri SIG, seperti `SIGTERM`, `SIGKILL`, dll. + + * **`float $timeout`** + * **Fungsi**: Waktu timeout, negatif berarti tidak pernah timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Mengembalikan `true` jika menerima sinyal + * Mengembalikan `false` jika timeout tidak menerima sinyal + + * **Contoh** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\System; +use Swoole\Process; + +$process = new Process(function () { + Coroutine\run(function () { + $bool = System::waitSignal(SIGUSR1); + var_dump($bool); + }); +}); +$process->start(); +sleep(1); +$process::kill($process->pid, SIGUSR1); +``` + +### waitEvent() + +Listener sinyal versi coroutine, akan memblokir coroutine saat ini sampai sinyal terpicu. Menunggu event IO, bisa menggantikan fungsi terkait `swoole_event`. + +!> Tersedia sejak Swoole versi >= `v4.5` + +```php +Swoole\Coroutine\System::waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int | false +``` + +* **Parameter** + + * **`mixed $socket`** + * **Fungsi**: File descriptor (tipe apa pun yang bisa dikonversi ke fd, seperti objek Socket, resource, dll.) + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $events`** + * **Fungsi**: Tipe event + * **Bawaan**: `SWOOLE_EVENT_READ` + * **Nilai Lain**: `SWOOLE_EVENT_WRITE` atau `SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE` + + * **`float $timeout`** + * **Fungsi**: Waktu timeout, negatif berarti tidak pernah timeout + * **Satuan**: Detik, presisi minimal milidetik (`0.001` detik) + * **Bawaan**: `-1` + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan jumlah tipe event yang terpicu (mungkin beberapa bit), terkait dengan nilai yang dimasukkan di parameter `$events` + * Mengembalikan `false` jika gagal, bisa gunakan [swoole_last_error](/functions?id=swoole_last_error) untuk info error + +* **Contoh** + +> Kode blocking sinkron bisa diubah menjadi non-blocking coroutine melalui API ini. + +```php +use Swoole\Coroutine; + +Coroutine\run(function () { + $client = stream_socket_client('tcp://www.qq.com:80', $errno, $errstr, 30); + $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + assert($events === SWOOLE_EVENT_WRITE); + fwrite($client, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ); + assert($events === SWOOLE_EVENT_READ); + $response = fread($client, 8192); + echo $response; +}); +``` diff --git a/public/id/coroutine/wait_group.md b/public/id/coroutine/wait_group.md new file mode 100644 index 0000000..3e47ac9 --- /dev/null +++ b/public/id/coroutine/wait_group.md @@ -0,0 +1,72 @@ +# Coroutine\WaitGroup + +Di `Swoole4`, bisa menggunakan [Channel](/coroutine/channel) untuk mencapai komunikasi antar coroutine, manajemen ketergantungan, dan sinkronisasi coroutine. Berdasarkan [Channel](/coroutine/channel), bisa dengan mudah mengimplementasikan fungsi `sync.WaitGroup` dari `Golang`. + +## Kode Implementasi + +> Fungsionalitas ini ditulis menggunakan PHP, bukan kode C/C++. Kode sumber implementasi ada di [Library](https://github.com/swoole/library/blob/master/src/core/Coroutine/WaitGroup.php). + +* Method `add` menambah penghitung +* `done` menandakan tugas selesai +* `wait` menunggu semua tugas selesai, lalu melanjutkan eksekusi coroutine saat ini +* Objek `WaitGroup` bisa digunakan kembali, bisa dipakai lagi setelah `add`, `done`, `wait` + +## Contoh Penggunaan + +```php +add(); + // Mulai coroutine pertama + Coroutine::create(function () use ($wg, &$result) { + // Mulai client coroutine, request halaman utama Taobao + $cli = new Client('www.taobao.com', 443, true); + $cli->setHeaders([ + 'Host' => 'www.taobao.com', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $cli->set(['timeout' => 1]); + $cli->get('/index.php'); + + $result['taobao'] = $cli->body; + $cli->close(); + + $wg->done(); + }); + + $wg->add(); + // Mulai coroutine kedua + Coroutine::create(function () use ($wg, &$result) { + // Mulai client coroutine, request halaman utama Baidu + $cli = new Client('www.baidu.com', 443, true); + $cli->setHeaders([ + 'Host' => 'www.baidu.com', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $cli->set(['timeout' => 1]); + $cli->get('/index.php'); + + $result['baidu'] = $cli->body; + $cli->close(); + + $wg->done(); + }); + + // Tangguhkan coroutine saat ini, tunggu semua tugas selesai, lalu lanjutkan + $wg->wait(); + // Di sini $result berisi hasil 2 tugas + var_dump($result); +}); +``` diff --git a/public/id/coroutine/ws_server.md b/public/id/coroutine/ws_server.md new file mode 100644 index 0000000..98281c7 --- /dev/null +++ b/public/id/coroutine/ws_server.md @@ -0,0 +1,243 @@ +# Server WebSocket + +?> Implementasi server WebSocket yang sepenuhnya coroutine, mewarisi dari [Coroutine\Http\Server](/coroutine/http_server), infrastruktur menyediakan dukungan untuk protokol `WebSocket`. Tidak diulangi di sini, hanya perbedaannya. + +!> Bagian ini tersedia setelah v4.4.13. + +## Contoh Lengkap + +```php +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\WebSocket\CloseFrame; +use Swoole\Coroutine\Http\Server; +use function Swoole\Coroutine\run; + +run(function () { + $server = new Server('127.0.0.1', 9502, false); + $server->handle('/websocket', function (Request $request, Response $ws) { + $ws->upgrade(); + while (true) { + $frame = $ws->recv(); + if ($frame === '') { + $ws->close(); + break; + } else if ($frame === false) { + echo 'errorCode: ' . swoole_last_error() . "\n"; + $ws->close(); + break; + } else { + if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { + $ws->close(); + break; + } + $ws->push("Hello {$frame->data}!"); + $ws->push("How are you, {$frame->data}?"); + } + } + }); + + $server->handle('/', function (Request $request, Response $response) { + $response->end(<<Swoole WebSocket Server + +HTML + ); + }); + + $server->start(); +}); +``` + +### Contoh Kirim Massal + +```php +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\WebSocket\CloseFrame; +use Swoole\Coroutine\Http\Server; +use function Swoole\Coroutine\run; + +run(function () { + $server = new Server('127.0.0.1', 9502, false); + $server->handle('/websocket', function (Request $request, Response $ws) { + $ws->upgrade(); + global $wsObjects; + $objectId = spl_object_id($ws); + $wsObjects[$objectId] = $ws; + while (true) { + $frame = $ws->recv(); + if ($frame === '') { + unset($wsObjects[$objectId]); + $ws->close(); + break; + } else if ($frame === false) { + echo 'errorCode: ' . swoole_last_error() . "\n"; + $ws->close(); + break; + } else { + if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { + unset($wsObjects[$objectId]); + $ws->close(); + break; + } + foreach ($wsObjects as $obj) { + $obj->push("Server: {$frame->data}"); + } + } + } + }); + $server->start(); +}); +``` + +## Alur Pemrosesan + +* `$ws->upgrade()`: Mengirim pesan handshake `WebSocket` ke klien +* `while(true)` loop untuk memproses penerimaan dan pengiriman pesan +* `$ws->recv()` Menerima frame pesan `WebSocket` +* `$ws->push()` Mengirim data frame ke lawan bicara +* `$ws->close()` Menutup koneksi + +!> `$ws` adalah objek `Swoole\Http\Response`, cara penggunaan masing-masing method lihat di bawah. + +## Method + +### upgrade() + +Mengirim informasi keberhasilan handshake `WebSocket`. + +!> Method ini jangan digunakan di server [gaya asinkron](/http_server). + +```php +Swoole\Http\Response->upgrade(): bool +``` + +### recv() + +Menerima pesan `WebSocket`. + +!> Method ini jangan digunakan di server [gaya asinkron](/http_server). Saat memanggil method `recv`, coroutine saat ini akan [ditangguhkan](/coroutine?id=penjadwalan-coroutine), menunggu data datang lalu melanjutkan eksekusi coroutine. + +```php +Swoole\Http\Response->recv(float $timeout = 0): Swoole\WebSocket\Frame | false | string +``` + +* **Return Value** + + * Berhasil menerima pesan, mengembalikan objek `Swoole\WebSocket\Frame`, lihat [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) + * Mengembalikan `false`, gunakan [swoole_last_error()](/functions?id=swoole_last_error) untuk kode error + * Koneksi ditutup mengembalikan string kosong + * Cara menangani nilai kembali lihat [contoh kirim massal](/coroutine/ws_server?id=contoh-kirim-massal) + +### push() + +Mengirim data frame `WebSocket`. + +!> Method ini jangan digunakan di server [gaya asinkron](/http_server). Saat mengirim paket data besar, perlu memantau kemampuan tulis, sehingga dapat menyebabkan banyak [perpindahan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Http\Response->push(string|object $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool +``` + +* **Parameter** + + !> Jika `$data` yang dimasukkan adalah objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe), parameter selanjutnya akan diabaikan, mendukung pengiriman berbagai tipe frame. + + * **`string|object $data`** + + * **Fungsi**: Konten yang akan dikirim + * **Bawaan**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $opcode`** + + * **Fungsi**: Menentukan format data yang akan dikirim [bawaan teks]. Untuk mengirim konten biner, parameter `$opcode` perlu diatur ke `WEBSOCKET_OPCODE_BINARY`. + * **Bawaan**: `WEBSOCKET_OPCODE_TEXT` + * **Nilai Lain**: `WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + + * **Fungsi**: Apakah pengiriman selesai + * **Bawaan**: `true` + * **Nilai Lain**: `false` + +### ping() +Mengirim heartbeat `WebSocket`. + +```php +Swoole\Http\Response->ping(string $data = ''): bool +``` +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data heartbeat + * **Bawaan**: String kosong + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan `true` jika berhasil + * Koneksi tidak ada, sudah ditutup, atau belum menyelesaikan `WebSocket`, gagal mengirim mengembalikan `false` + +### disconnect() +Memutus koneksi `WebSocket`. + +```php +Swoole\Http\Response->disconnect(int $code = 1000, string $reason = ''): bool +``` +* **Parameter** + + * **`int $code`** + * **Fungsi**: Kode penutupan + * **Bawaan**: `1000` + * **Nilai Lain**: Tidak ada + + * **`string $reason`** + * **Fungsi**: Alasan penutupan + * **Bawaan**: String kosong + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan `true` jika berhasil + * Koneksi tidak ada, sudah ditutup, atau belum menyelesaikan `WebSocket`, gagal mengembalikan `false` + +### close() + +Menutup koneksi `WebSocket`. + +!> Method ini jangan digunakan di server [gaya asinkron](/http_server). Di versi sebelum v4.4.15, akan memunculkan `Warning` yang bisa diabaikan. + +```php +Swoole\Http\Response->close(): bool +``` + +Method ini akan memutus koneksi `TCP` secara langsung, tidak mengirim frame `Close`, berbeda dengan method `WebSocket\Server::disconnect()`. +Bisa menggunakan method `push()` untuk mengirim frame `Close` sebelum menutup koneksi, untuk memberi tahu klien secara aktif. + +```php +$frame = new Swoole\WebSocket\CloseFrame; +$frame->reason = 'close'; +$ws->push($frame); +$ws->close(); +``` diff --git a/public/id/coroutine_client/client.md b/public/id/coroutine_client/client.md new file mode 100644 index 0000000..6217729 --- /dev/null +++ b/public/id/coroutine_client/client.md @@ -0,0 +1,283 @@ +# Klien TCP/UDP Coroutine + +`Coroutine\Client` menyediakan kode enkapsulasi untuk protokol transport `TCP`, `UDP`, dan [unixSocket](/learn?id=apa-itu-IPC) untuk [Klien Socket](/coroutine_client/socket), cukup menggunakan `new Swoole\Coroutine\Client`. + +* **Prinsip Implementasi** + + * Semua method `Coroutine\Client` yang melibatkan permintaan jaringan akan menjalani penjadwalan coroutine oleh `Swoole`, yang tidak perlu diketahui oleh lapisan bisnis. + * Penggunaan dan method mode sinkron dari `Coroutine\Client` sepenuhnya konsisten dengan [Client](/client). + * Pengaturan timeout `connect` juga berlaku untuk timeout `Connect`, `Recv`, dan `Send`. + +* **Hubungan Pewarisan** + + * `Coroutine\Client` tidak mewarisi dari [Client](/client), tetapi semua method yang disediakan `Client` dapat digunakan di `Coroutine\Client`. Silakan lihat [Swoole\Client](/client?id=method) untuk informasi lebih detail. Tidak akan disebutkan lebih lanjut di sini. + * Di `Coroutine\Client`, method `set` dapat digunakan untuk mengatur [opsi konfigurasi](/client?id=konfigurasi), dan penggunaannya sepenuhnya konsisten dengan `Client->set`. Untuk fungsi yang berbeda penggunaannya, akan dijelaskan secara terpisah di bagian fungsi `set()`. + +* **Contoh Penggunaan** + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + echo $client->recv(); + $client->close(); +}); +``` + +* **Penanganan Protokol** + +Klien coroutine juga mendukung penanganan protokol panjang dan `EOF`, dan metode pengaturannya sepenuhnya konsisten dengan [Swoole\Client](/client?id=konfigurasi). + +```php +$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); +$client->set(array( + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, //Byte ke-N adalah nilai panjang paket + 'package_body_offset' => 4, //Mulai byte ke berapa untuk menghitung panjang + 'package_max_length' => 2000000, //Panjang maksimum protokol +)); +``` + +### connect() + +Menghubungkan ke server jarak jauh. + +```php +Swoole\Coroutine\Client->connect(string $host, int $port, float $timeout = 0.5): bool +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat server jarak jauh【Sistem akan secara otomatis melakukan penjadwalan coroutine untuk meresolusi domain ke alamat IP】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port server jarak jauh + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Timeout untuk I/O jaringan; termasuk `connect/send/recv`, saat timeout terjadi, koneksi akan otomatis di-`close`, lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Satuan nilai**: detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: `0.5s` + * **Nilai lain**: Tidak ada + +* **Catatan** + + * Jika koneksi gagal, akan mengembalikan `false` + * Saat timeout kembali, periksa `$cli->errCode` adalah `110` + +* **Percobaan Ulang Gagal** + +!> Setelah `connect` gagal, jangan langsung mencoba menyambung ulang. Harus menggunakan `close` untuk menutup `socket` yang ada, lalu lakukan `connect` ulang. + +```php +//Koneksi gagal +if ($cli->connect('127.0.0.1', 9501) == false) { + //Tutup socket yang ada + $cli->close(); + //Coba ulang + $cli->connect('127.0.0.1', 9501); +} +``` + +* **Contoh** + +```php +if ($cli->connect('127.0.0.1', 9501)) { + $cli->send('data'); +} else { + echo 'connect failed.'; +} + +if ($cli->connect('/tmp/rpc.sock')) { + $cli->send('data'); +} else { + echo 'connect failed.'; +} +``` + +### isConnected() + +Mengembalikan status koneksi Client + +```php +Swoole\Coroutine\Client->isConnected(): bool +``` + + * **Return Value** + + * Mengembalikan `false`, berarti saat ini tidak terhubung ke server + * Mengembalikan `true`, berarti saat ini terhubung ke server + +!> Method `isConnected` mengembalikan status lapisan aplikasi, hanya menunjukkan bahwa `Client` telah menjalankan `connect` dan berhasil terhubung ke `Server`, dan belum menjalankan `close` untuk menutup koneksi. `Client` dapat menjalankan operasi `send`, `recv`, `close`, tetapi tidak dapat menjalankan `connect` lagi. +Ini tidak menjamin koneksi pasti dapat digunakan; saat menjalankan `send` atau `recv` masih mungkin mengembalikan error, karena lapisan aplikasi tidak dapat memperoleh status koneksi `TCP` di tingkat bawah. Status ketersediaan koneksi yang sebenarnya hanya diperoleh saat lapisan aplikasi berinteraksi dengan kernel saat menjalankan `send` atau `recv`. + +### send() + +Mengirim data. + +```php +Swoole\Coroutine\Client->send(string $data): int|bool +``` + + * **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang akan dikirim, harus bertipe string, mendukung data biner + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * Pengiriman berhasil mengembalikan jumlah byte yang ditulis ke buffer `Socket`. Sistem akan berusaha mengirim semua data semaksimal mungkin. Jika jumlah byte yang dikembalikan berbeda dari panjang `$data` yang diberikan, mungkin `Socket` telah ditutup oleh lawan, dan kode error akan dikembalikan pada pemanggilan `send` atau `recv` berikutnya. + + * Pengiriman gagal mengembalikan false, dapat menggunakan `$client->errCode` untuk mendapatkan penyebab error. + +### recv() + +Method recv digunakan untuk menerima data dari server. + +```php +Swoole\Coroutine\Client->recv(float $timeout = 0): string|bool +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan nilai**: detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + !> Saat mengatur timeout, prioritaskan parameter yang ditentukan, lalu gunakan konfigurasi `timeout` yang diberikan di method `set`. Kode error untuk timeout adalah `ETIMEDOUT` + + * **Return Value** + + * Dengan pengaturan [protokol komunikasi](/client?id=protokol-parsing), `recv` akan mengembalikan data lengkap, panjang dibatasi oleh [package_max_length](/server/setting?id=package_max_length) + * Tanpa pengaturan protokol komunikasi, `recv` mengembalikan maksimum data `64K` + * Tanpa pengaturan protokol komunikasi mengembalikan data mentah, perlu implementasi penanganan protokol jaringan sendiri di kode `PHP` + * `recv` mengembalikan string kosong berarti server menutup koneksi secara aktif, perlu `close` + * `recv` gagal, mengembalikan `false`, periksa `$client->errCode` untuk mendapatkan penyebab error, cara penanganannya lihat [contoh lengkap](/coroutine_client/client?id=contoh-lengkap) di bawah + +### close() + +Menutup koneksi. + +!> `close` tidak memblokir, akan segera kembali. Operasi penutupan tidak ada perpindahan coroutine. + +```php +Swoole\Coroutine\Client->close(): bool +``` + +### peek() + +Mengintip data. + +!> Method `peek` beroperasi langsung pada `socket`, sehingga tidak memicu [Penjadwalan Coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\Client->peek(int $length = 65535): string +``` + + * **Petunjuk** + + * Method `peek` hanya digunakan untuk mengintip data di buffer `socket` kernel tanpa menggeser. Setelah menggunakan `peek`, memanggil `recv` masih dapat membaca data ini. + * Method `peek` bersifat non-blocking, akan segera kembali. Jika ada data di buffer socket, akan mengembalikan konten data. Jika buffer kosong, mengembalikan `false` dan mengatur `$client->errCode`. + * Jika koneksi ditutup, `peek` akan mengembalikan string kosong. + +### set() + +Mengatur parameter klien. + +```php +Swoole\Coroutine\Client->set(array $settings): bool +``` + + * **Parameter Konfigurasi** + + * Silakan lihat [Swoole\Client](/client?id=set). + +* **Perbedaan dengan [Swoole\Client](/client?id=set)** + + Klien coroutine menyediakan kontrol timeout yang lebih terperinci. Dapat mengatur: + + * `timeout`: Total timeout, termasuk timeout koneksi, pengiriman, dan penerimaan + * `connect_timeout`: Timeout koneksi + * `read_timeout`: Timeout penerimaan + * `write_timeout`: Timeout pengiriman + * Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + +* **Contoh** + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(array( + 'timeout' => 0.5, + 'connect_timeout' => 1.0, + 'write_timeout' => 10.0, + 'read_timeout' => 0.5, + )); + + if (!$client->connect('127.0.0.1', 9501, 0.5)) + { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + echo $client->recv(); + $client->close(); +}); +``` + +### Contoh Lengkap + +```php +use Swoole\Coroutine\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', 9501, 0.5)) { + echo "connect failed. Error: {$client->errCode}\n"; + } + $client->send("hello world\n"); + while (true) { + $data = $client->recv(); + if (strlen($data) > 0) { + echo $data; + $client->send(time() . PHP_EOL); + } else { + if ($data === '') { + // Jika benar-benar kosong, tutup koneksi langsung + $client->close(); + break; + } else { + if ($data === false) { + // Dapat menangani berdasarkan logika bisnis dan kode error, misalnya: + // Jika timeout jangan tutup koneksi, tutup untuk kasus lain + if ($client->errCode !== SOCKET_ETIMEDOUT) { + $client->close(); + break; + } + } else { + $client->close(); + break; + } + } + } + \Co::sleep(1); + } +}); +``` diff --git a/public/id/coroutine_client/fastcgi.md b/public/id/coroutine_client/fastcgi.md new file mode 100644 index 0000000..2d17678 --- /dev/null +++ b/public/id/coroutine_client/fastcgi.md @@ -0,0 +1,193 @@ +# Klien FastCGI Coroutine + +PHP-FPM menggunakan protokol biner yang efisien: `Protokol FastCGI` untuk berkomunikasi. Dengan menggunakan klien FastCGI, Anda dapat berinteraksi langsung dengan layanan PHP-FPM tanpa melalui proxy reverse HTTP apa pun. + +[Direktori kode sumber PHP](https://github.com/swoole/library/blob/master/src/core/Coroutine/FastCGI) + +## Contoh Penggunaan Sederhana + +[Contoh kode lainnya](https://github.com/swoole/library/tree/master/examples/fastcgi) + +!> Contoh kode berikut perlu dipanggil dalam coroutine. + +### Panggilan Cepat + +```php +#greeter.php +echo 'Hello ' . ($_POST['who'] ?? 'World'); +``` + +```php +echo \Swoole\Coroutine\FastCGI\Client::call( + '127.0.0.1:9000', // Alamat listening FPM, bisa juga berupa alamat unixsocket seperti unix:/tmp/php-cgi.sock + '/tmp/greeter.php', // File entry yang ingin dijalankan + ['who' => 'Swoole'] // Informasi POST tambahan +); +``` + +### Gaya PSR + +```php +try { + $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1:9000', 9000); + $request = (new \Swoole\FastCGI\HttpRequest()) + ->withScriptFilename(__DIR__ . '/greeter.php') + ->withMethod('POST') + ->withBody(['who' => 'Swoole']); + $response = $client->execute($request); + echo "Result: {$response->getBody()}\n"; +} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { + echo "Error: {$exception->getMessage()}\n"; +} +``` + +### Panggilan Kompleks + +```php +#var.php +var_dump($_SERVER); +var_dump($_GET); +var_dump($_POST); +``` + +```php +try { + $client = new \Swoole\Coroutine\FastCGI\Client('127.0.0.1', 9000); + $request = (new \Swoole\FastCGI\HttpRequest()) + ->withDocumentRoot(__DIR__) + ->withScriptFilename(__DIR__ . '/var.php') + ->withScriptName('var.php') + ->withMethod('POST') + ->withUri('/var?foo=bar&bar=char') + ->withHeader('X-Foo', 'bar') + ->withHeader('X-Bar', 'char') + ->withBody(['foo' => 'bar', 'bar' => 'char']); + $response = $client->execute($request); + echo "Result: \n{$response->getBody()}"; +} catch (\Swoole\Coroutine\FastCGI\Client\Exception $exception) { + echo "Error: {$exception->getMessage()}\n"; +} +``` + +### Proxy Satu Klik WordPress + +!> Penggunaan ini tidak memiliki arti produksi. Dalam produksi, proxy dapat digunakan untuk memproksi permintaan HTTP dari beberapa API lama ke layanan FPM lama (bukan memproksi seluruh situs). + +```php +use Swoole\Constant; +use Swoole\Coroutine\FastCGI\Proxy; +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\Http\Server; + +$documentRoot = '/var/www/html'; # Direktori root proyek WordPress +$server = new Server('0.0.0.0', 80, SWOOLE_BASE); # Port di sini harus sesuai dengan konfigurasi WordPress, umumnya tidak ada port khusus, yaitu 80 +$server->set([ + Constant::OPTION_WORKER_NUM => swoole_cpu_num() * 2, + Constant::OPTION_HTTP_PARSE_COOKIE => false, + Constant::OPTION_HTTP_PARSE_POST => false, + Constant::OPTION_DOCUMENT_ROOT => $documentRoot, + Constant::OPTION_ENABLE_STATIC_HANDLER => true, + Constant::OPTION_STATIC_HANDLER_LOCATIONS => ['/wp-admin', '/wp-content', '/wp-includes'], # Path sumber daya statis +]); +$proxy = new Proxy('127.0.0.1:9000', $documentRoot); # Membuat objek proxy +$server->on('request', function (Request $request, Response $response) use ($proxy) { + $proxy->pass($request, $response); # Proxy permintaan satu klik +}); +$server->start(); +``` + +## Method + +### call + +Static method, langsung membuat koneksi klien baru, mengirim permintaan ke server FPM dan menerima body respons. + +!> FPM hanya mendukung koneksi pendek, jadi dalam keadaan normal, membuat objek persisten tidak terlalu berguna. + +```php +Swoole\Coroutine\FastCGI\Client::call(string $url, string $path, $data = '', float $timeout = -1): string +``` + + * **Parameter** + + * **`string $url`** + * **Fungsi**: Alamat listening FPM【mis. `127.0.0.1:9000`, `unix:/tmp/php-cgi.sock`, dll.】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $path`** + * **Fungsi**: File entry yang ingin dijalankan + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`$data`** + * **Fungsi**: Data permintaan tambahan + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout【Default -1 berarti tidak pernah timeout】 + * **Satuan**: Detik【mendukung float, mis. 1.5 berarti 1s+500ms】 + * **Default**: `-1` + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan konten utama (body) dari respons server + * Akan melempar exception `Swoole\Coroutine\FastCGI\Client\Exception` saat terjadi error + +### __construct + +Method konstruktor objek klien, menentukan server FPM target. + +```php +Swoole\Coroutine\FastCGI\Client::__construct(string $host, int $port = 0) +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat server target【mis. `127.0.0.1`, `unix://tmp/php-fpm.sock`, dll.】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port server target【Tidak diperlukan saat alamat target adalah UNIXSocket】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +### execute + +Menjalankan permintaan, mengembalikan respons. + +```php +Swoole\Coroutine\FastCGI\Client->execute(Request $request, float $timeout = -1): Response +``` + + * **Parameter** + + * **`Swoole\FastCGI\Request|Swoole\FastCGI\HttpRequest $request`** + * **Fungsi**: Objek yang berisi informasi permintaan, biasanya menggunakan `Swoole\FastCGI\HttpRequest` untuk simulasi permintaan HTTP, gunakan kelas permintaan asli `Swoole\FastCGI\Request` untuk kebutuhan khusus + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout【Default `-1` berarti tidak pernah timeout】 + * **Satuan**: detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: `-1` + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan objek Response yang sesuai dengan tipe objek permintaan. Misalnya, `Swoole\FastCGI\HttpRequest` akan mengembalikan objek `Swoole\FastCGI\HttpResponse`, yang berisi informasi respons dari server FPM + * Akan melempar exception `Swoole\Coroutine\FastCGI\Client\Exception` saat terjadi error + +## Kelas Request/Response Terkait + +Karena library tidak dapat memperkenalkan dependensi besar dari implementasi PSR dan loading ekstensi selalu terjadi sebelum eksekusi kode PHP, objek request dan response terkait tidak mewarisi antarmuka PSR. Namun, mereka diimplementasikan dengan gaya PSR sebisa mungkin agar pengembang dapat cepat memulai. + +Kode sumber untuk kelas yang mensimulasikan request dan response HTTP di FastCGI dapat ditemukan di tautan berikut, sangat sederhana, kode adalah dokumentasi: + +[Swoole\FastCGI\HttpRequest](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpRequest.php) +[Swoole\FastCGI\HttpResponse](https://github.com/swoole/library/blob/master/src/core/FastCGI/HttpResponse.php) diff --git a/public/id/coroutine_client/http2_client.md b/public/id/coroutine_client/http2_client.md new file mode 100644 index 0000000..b05f3b7 --- /dev/null +++ b/public/id/coroutine_client/http2_client.md @@ -0,0 +1,288 @@ +# Coroutine\Http2\Client + +Klien Http2 Coroutine + +## Contoh Penggunaan + +```php +use Swoole\Http2\Request; +use Swoole\Coroutine\Http2\Client; +use function Swoole\Coroutine\run; + +run(function () { + $domain = 'www.zhihu.com'; + $cli = new Client($domain, 443, true); + $cli->set([ + 'timeout' => -1, + 'ssl_host_name' => $domain + ]); + $cli->connect(); + $req = new Request(); + $req->method = 'POST'; + $req->path = '/api/v4/answers/300000000/voters'; + $req->headers = [ + 'host' => $domain, + 'user-agent' => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip' + ]; + $req->data = '{"type":"up"}'; + $cli->send($req); + $response = $cli->recv(); + var_dump(assert(json_decode($response->data)->error->code === 10002)); +}); +``` + +## Method + +### __construct() + +Method konstruktor. + +```php +Swoole\Coroutine\Http2\Client::__construct(string $host, int $port, bool $open_ssl = false): void +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat IP host target【Jika `$host` adalah nama domain, akan dilakukan query `DNS` di tingkat bawah】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port target【`Http` biasanya port `80`, `Https` biasanya port `443`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`bool $open_ssl`** + * **Fungsi**: Apakah akan mengaktifkan enkripsi tunnel `TLS/SSL`【situs `https` harus diatur ke `true`】 + * **Default**: `false` + * **Nilai lain**: `true` + + * **Catatan** + + !> -Jika Anda perlu meminta URL eksternal, ubah `timeout` ke nilai yang lebih besar, lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + -`$ssl` memerlukan `openssl`, harus diaktifkan saat mengompilasi `Swoole` [--enable-openssl](/environment?id=opsi-kompilasi) + +### set() + +Mengatur parameter klien, untuk item konfigurasi detail lainnya silakan lihat opsi konfigurasi [Swoole\Client::set](/client?id=konfigurasi) + +```php +Swoole\Coroutine\Http2\Client->set(array $options): void +``` + +### connect() + +Menghubungkan ke server target. Method ini tidak memiliki parameter. + +!> Setelah memanggil `connect`, sistem akan secara otomatis melakukan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). Fungsi `connect` akan kembali saat koneksi berhasil atau gagal. Setelah koneksi terbentuk, Anda dapat menggunakan method `send` untuk mengirim permintaan ke server. + +```php +Swoole\Coroutine\Http2\Client->connect(): bool +``` + + * **Return Value** + + * Koneksi berhasil, mengembalikan `true` + * Koneksi gagal, mengembalikan `false`, periksa properti `errCode` untuk mendapatkan kode error + +### stats() + +Mendapatkan status aliran. + +```php +Swoole\Coroutine\Http2\Client->stats([$key]): array|bool +``` + + * **Contoh** + +```php +var_dump($client->stats(), $client->stats()['local_settings'], $client->stats('local_settings')); +``` + +### isStreamExist() + +Menentukan apakah aliran yang ditentukan ada. + +```php +Swoole\Coroutine\Http2\Client->isStreamExist(int $stream_id): bool +``` + +### send() + +Mengirim permintaan ke server, sistem akan secara otomatis membuat `stream` `Http2`. Dapat mengirim beberapa permintaan secara bersamaan. + +```php +Swoole\Coroutine\Http2\Client->send(Swoole\Http2\Request $request): int|false +``` + + * **Parameter** + + * **`Swoole\Http2\Request $request`** + * **Fungsi**: mengirim objek Swoole\Http2\Request + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan nomor stream, nomor dimulai dari `1` dan bertambah dengan angka ganjil + * Mengembalikan `false` + + * **Petunjuk** + + * **Objek Request** + + !> Objek `Swoole\Http2\Request` tidak memiliki method; Anda menulis informasi terkait permintaan dengan mengatur properti objek. + + * `headers` array, header `HTTP` + * `method` string, mengatur method permintaan, seperti `GET`, `POST` + * `path` string, mengatur path `URL`, seperti `/index.php?a=1&b=2`, harus dimulai dengan / + * `cookies` array, mengatur `COOKIES` + * `data` mengatur `body` permintaan, jika berupa string akan langsung dikirim sebagai `RAW form-data` + * `data` jika berupa array, sistem akan secara otomatis mengemasnya menjadi format `x-www-form-urlencoded` untuk konten `POST` dan mengatur `Content-Type` menjadi `application/x-www-form-urlencoded` + * `pipeline` boolean, jika diatur ke `true`, setelah mengirim `$request`, `stream` tidak akan ditutup, dan Anda dapat terus menulis konten data + + * **pipeline** + + * Secara default, method `send` mengakhiri `Http2 Stream` saat ini setelah mengirim permintaan. Mengaktifkan `pipeline` akan mempertahankan aliran stream, memungkinkan beberapa panggilan method `write` untuk mengirim data frame ke server. Silakan lihat method `write`. + +### write() + +Mengirim lebih banyak data frame ke server, Anda dapat memanggil write beberapa kali untuk menulis data frame ke stream yang sama. + +```php +Swoole\Coroutine\Http2\Client->write(int $streamId, mixed $data, bool $end = false): bool +``` + + * **Parameter** + + * **`int $streamId`** + * **Fungsi**: Nomor stream, dikembalikan oleh method `send` + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`mixed $data`** + * **Fungsi**: Konten data frame, bisa berupa string atau array + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`bool $end`** + * **Fungsi**: Apakah akan menutup stream + * **Default**: `false` + * **Nilai lain**: `true` + + * **Contoh Penggunaan** + +```php +use Swoole\Http2\Request; +use Swoole\Coroutine\Http2\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9518); + $cli->set(['timeout' => 1]); + var_dump($cli->connect()); + + $req3 = new Request(); + $req3->path = "/index.php"; + $req3->headers = [ + 'host' => "localhost", + "user-agent" => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'accept-encoding' => 'gzip', + ]; + $req3->pipeline = true; + $req3->method = "POST"; + $streamId = $cli->send($req3); + $cli->write($streamId, ['int' => rand(1000, 9999)]); + $cli->write($streamId, ['int' => rand(1000, 9999)]); + //end stream + $cli->write($streamId, ['int' => rand(1000, 9999), 'end' => true], true); + var_dump($cli->recv()); + $cli->close(); +}); +``` + +!> Jika ingin menggunakan `write` untuk mengirim data frame dalam segmen, Anda harus mengatur `$request->pipeline` ke `true` saat melakukan permintaan `send` +Setelah data frame dengan `end` `true` dikirim, stream akan ditutup, dan Anda tidak dapat lagi memanggil `write` untuk mengirim data ke stream ini. + +### recv() + +Menerima permintaan. + +!> Memanggil method ini akan memicu [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine) + +```php +Swoole\Coroutine\Http2\Client->recv(float $timeout): Swoole\Http2\Response; +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout, lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Satuan**: detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + +Mengembalikan objek Swoole\Http2\Response + +```php +/**@var $resp Swoole\Http2\Response */ +var_dump($resp->statusCode); // Kode status Http yang dikirim server, seperti 200, 502, dll. +var_dump($resp->headers); // Informasi Header yang dikirim server +var_dump($resp->cookies); // Informasi COOKIE yang diatur server +var_dump($resp->set_cookie_headers); // Informasi COOKIE mentah yang dikembalikan server, termasuk domain dan path +var_dump($resp->data); // Body respons yang dikirim server +``` + +!> Sebelum Swoole versi < [v4.0.4](/version/bc?id=_404), properti `data` adalah `body`; sebelum Swoole versi < [v4.0.3](/version/bc?id=_403), `headers` dan `cookies` dalam bentuk tunggal. + +### read() + +Mirip dengan `recv()`, perbedaannya adalah untuk respons tipe `pipeline`, `read` memungkinkan pembacaan beberapa kali. Setiap kali membaca sebagian konten untuk menghemat memori atau menerima informasi push dengan cepat, sedangkan `recv` selalu menggabungkan semua frame menjadi respons lengkap sebelum kembali. + +!> Memanggil method ini akan menghasilkan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine) + +```php +Swoole\Coroutine\Http2\Client->read(float $timeout): Swoole\Http2\Response; +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout, lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Satuan**: Detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + + Mengembalikan objek Swoole\Http2\Response + +### goaway() + +Frame GOAWAY digunakan untuk memulai penutupan koneksi atau mengirim sinyal status error serius. + +```php +Swoole\Coroutine\Http2\Client->goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data): bool +``` + +### ping() + +Frame PING adalah mekanisme yang digunakan untuk mengukur waktu round-trip minimum dari pengirim dan untuk menentukan apakah koneksi idle masih aktif. + +```php +Swoole\Coroutine\Http2\Client->ping(): bool +``` + +### close() + +Menutup koneksi. + +```php +Swoole\Coroutine\Http2\Client->close(): bool +``` diff --git a/public/id/coroutine_client/http_client.md b/public/id/coroutine_client/http_client.md new file mode 100644 index 0000000..d84553e --- /dev/null +++ b/public/id/coroutine_client/http_client.md @@ -0,0 +1,817 @@ +# Klien HTTP/WebSocket Coroutine + +Klien `HTTP` versi coroutine ditulis murni dalam `C` tanpa bergantung pada pustaka ekstensi pihak ketiga, dengan kinerja sangat tinggi. + +* Mendukung fitur `Http-Chunk`, `Keep-Alive`, dan format `form-data` +* Versi protokol `HTTP` adalah `HTTP/1.1` +* Mendukung upgrade ke klien `WebSocket` +* Dukungan format kompresi `gzip` memerlukan pustaka `zlib` +* Klien hanya mengimplementasikan fitur inti, disarankan menggunakan [Saber](https://github.com/swlib/saber) untuk proyek aktual + +## Properti + +### errCode + +Kode status error. Saat `connect/send/recv/close` gagal atau timeout, nilai `Swoole\Coroutine\Http\Client->errCode` akan diatur secara otomatis. + +```php +Swoole\Coroutine\Http\Client->errCode: int +``` + +Nilai `errCode` sama dengan `Linux errno`. Anda dapat menggunakan `socket_strerror` untuk mengonversi kode error menjadi pesan error. + +```php +// Jika connect ditolak, kode error adalah 111 +// Jika timeout, kode error adalah 110 +echo socket_strerror($client->errCode); +``` + +!> Referensi: [Daftar Kode Error Linux](/other/errno?id=linux) + +### body + +Menyimpan body respons dari permintaan terakhir. + +```php +Swoole\Coroutine\Http\Client->body: string +``` + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('httpbin.org', 80); + $cli->get('/get'); + echo $cli->body; + $cli->close(); +}); +``` + +### statusCode + +Kode status HTTP, seperti 200, 404, dll. Jika kode status negatif, itu menunjukkan ada masalah koneksi. [Pelajari lebih lanjut](/coroutine_client/http_client?id=getstatuscode) + +```php +Swoole\Coroutine\Http\Client->statusCode: int +``` + +## Method + +### __construct() + +Method konstruktor. + +```php +Swoole\Coroutine\Http\Client::__construct(string $host, int $port, bool $ssl = false); +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat host server target【bisa IP atau domain, sistem secara otomatis melakukan resolusi domain. Jika berupa UNIX Socket lokal, harus diisi dengan format seperti `unix://tmp/your_file.sock`. Jika domain, tidak perlu mengisi header protokol `http://` atau `https://`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port host server target + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`bool $ssl`** + * **Fungsi**: Apakah akan mengaktifkan enkripsi tunnel `SSL/TLS`. Jika server target menggunakan HTTPS, parameter `$ssl` harus diatur ke `true` + * **Default**: `false` + * **Nilai lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->setHeaders([ + 'Host' => 'localhost', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $client->set(['timeout' => 1]); + $client->get('/index.php'); + echo $client->body; + $client->close(); +}); +``` + +### set() + +Mengatur parameter klien. + +```php +Swoole\Coroutine\Http\Client->set(array $options); +``` + +Method ini sepenuhnya konsisten dengan parameter yang diterima oleh `Swoole\Client->set`, silakan lihat dokumentasi method [Swoole\Client->set](/client?id=set). + +`Swoole\Coroutine\Http\Client` menambahkan beberapa opsi tambahan untuk mengontrol klien `HTTP` dan `WebSocket`. + +#### Opsi Tambahan + +##### Kontrol Timeout + +Atur opsi `timeout` untuk mengaktifkan deteksi timeout permintaan HTTP. Satuan dalam detik, dengan dukungan milidetik sebagai granularitas terkecil. + +```php +$http->set(['timeout' => 3.0]); +``` + +* Jika koneksi timeout atau server menutup koneksi, `statusCode` akan diatur ke `-1` +* Jika server gagal merespons dalam waktu yang ditentukan, permintaan timeout, dan `statusCode` akan diatur ke `-2` +* Setelah permintaan timeout, koneksi di tingkat bawah akan otomatis diputus +* Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + +##### keep_alive + +Atur opsi `keep_alive` untuk mengaktifkan atau menonaktifkan koneksi persisten HTTP. + +```php +$http->set(['keep_alive' => false]); +``` + +##### websocket_mask + +> Karena peraturan RFC, konfigurasi ini diaktifkan secara default mulai v4.4.0, tetapi dapat menyebabkan penurunan kinerja. Jika server tidak memiliki persyaratan ketat, dapat diatur ke false untuk menonaktifkannya. + +Mengaktifkan atau menonaktifkan masking untuk klien `WebSocket`. Diaktifkan secara default. Saat diaktifkan, masking diterapkan pada data yang dikirim oleh klien WebSocket untuk transformasi data. + +```php +$http->set(['websocket_mask' => false]); +``` + +##### websocket_compression + +> Memerlukan `v4.4.12` atau versi yang lebih tinggi + +Saat diatur ke `true`, **memungkinkan** frame dikompresi menggunakan zlib. Apakah kompresi benar-benar dapat dilakukan tergantung pada apakah server dapat menangani kompresi (ditentukan oleh informasi handshake, lihat `RFC-7692`). + +Untuk benar-benar mengompresi frame tertentu, perlu menggunakan parameter flags `SWOOLE_WEBSOCKET_FLAG_COMPRESS`. Untuk penggunaan spesifik, [lihat bagian ini](/websocket_server?id=kompresi-frame-websocket-(rfc-7692)) + +```php +$http->set(['websocket_compression' => true]); +``` + +##### write_func +> Memerlukan `v5.1.0` atau versi yang lebih tinggi + +Atur fungsi callback `write_func`, mirip dengan opsi `WRITE_FUNCTION` di `CURL`, dapat digunakan untuk menangani konten respons streaming, misalnya output `Event Stream` dari `OpenAI ChatGPT`. + +> Setelah mengatur `write_func`, Anda tidak akan bisa menggunakan method `getContent()` untuk mendapatkan konten respons, dan `$client->body` juga akan kosong +> Di fungsi callback `write_func`, Anda dapat menggunakan `$client->close()` untuk berhenti menerima konten respons dan menutup koneksi + +```php +$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80); +$cli->set(['write_func' => function ($client, $data) { + var_dump($data); +}]); +$cli->get('/'); +``` + +### setMethod() + +Mengatur method permintaan. Hanya berlaku untuk permintaan saat ini, pengaturan method akan segera dibersihkan setelah mengirim permintaan. + +```php +Swoole\Coroutine\Http\Client->setMethod(string $method): void +``` + + * **Parameter** + + * **`string $method`** + * **Fungsi**: Mengatur method + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Harus berupa nama method yang sesuai dengan standar `HTTP`. Jika `$method` diatur salah, permintaan mungkin ditolak oleh server `HTTP`. + + * **Contoh** + +```php +$http->setMethod("PUT"); +``` + +### setHeaders() + +Mengatur header permintaan HTTP. + +```php +Swoole\Coroutine\Http\Client->setHeaders(array $headers): void +``` + + * **Parameter** + + * **`array $headers`** + * **Fungsi**: Mengatur header permintaan【Harus berupa array asosiatif, sistem akan secara otomatis memetakannya ke format header standar `HTTP` dengan format `$key`: `$value`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +!> Header `HTTP` yang diatur oleh `setHeaders` berlaku permanen untuk setiap permintaan selama masa hidup objek `Coroutine\Http\Client`. Memanggil ulang `setHeaders` akan menimpa pengaturan sebelumnya. + +### setCookies() + +Mengatur `Cookie`, nilai akan di-encode dengan `urlencode`. Jika ingin mempertahankan informasi asli, gunakan `setHeaders` untuk mengatur `header` bernama `Cookie` sendiri. + +```php +Swoole\Coroutine\Http\Client->setCookies(array $cookies): void +``` + + * **Parameter** + + * **`array $cookies`** + * **Fungsi**: Mengatur `COOKIE`【Harus berupa array asosiatif】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +!> - `COOKIE` yang diatur akan disimpan terus selama masa hidup objek klien +- `COOKIE` yang diatur oleh server akan digabungkan ke dalam array `cookies`, dan Anda dapat membaca informasi `COOKIE` klien `HTTP` saat ini dengan mengakses properti `$client->cookies` +- Memanggil ulang method `setCookies` akan menimpa status `Cookies` saat ini, yang akan membuang `COOKIE` yang sebelumnya ditetapkan server serta `COOKIE` yang sebelumnya diatur secara manual + +### setData() + +Mengatur body permintaan HTTP. + +```php +Swoole\Coroutine\Http\Client->setData(string|array $data): void +``` + + * **Parameter** + + * **`string|array $data`** + * **Fungsi**: Mengatur body permintaan + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Petunjuk** + + * Jika `$data` diatur dan `$method` tidak diatur, sistem akan secara otomatis mengaturnya sebagai POST + * Jika `$data` adalah array dan `Content-Type` adalah `urlencoded`, sistem akan secara otomatis menjalankan `http_build_query` + * Jika Anda telah menggunakan `addFile` atau `addData` yang mengaktifkan format `form-data`, saat `$data` berupa string akan diabaikan (karena formatnya berbeda), tetapi saat berupa array, sistem akan menambahkan field dari array dalam format `form-data` + +### addFile() + +Menambahkan file POST. + +!> Saat menggunakan `addFile`, `Content-Type` dari `POST` akan otomatis berubah menjadi `form-data`. `addFile` didasarkan pada `sendfile` di tingkat bawah dan dapat mendukung pengiriman file yang sangat besar secara asinkron. + +```php +Swoole\Coroutine\Http\Client->addFile(string $path, string $name, string $mimeType = null, string $filename = null, int $offset = 0, int $length = 0): void +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Path file【Parameter wajib, tidak boleh file kosong atau file yang tidak ada】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $name`** + * **Fungsi**: Nama form【Parameter wajib, `key` dalam parameter `FILES`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $mimeType`** + * **Fungsi**: Format `MIME` file【Parameter opsional, sistem akan secara otomatis menentukannya berdasarkan ekstensi file】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $filename`** + * **Fungsi**: Nama file【Parameter opsional】 + * **Default**: `basename($path)` + * **Nilai lain**: Tidak ada + + * **`int $offset`** + * **Fungsi**: Offset pengunggahan file【Parameter opsional, dapat menentukan untuk mulai mentransfer data dari bagian tengah file. Fitur ini dapat digunakan untuk mendukung resume transmisi.】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $length`** + * **Fungsi**: Ukuran data yang akan dikirim【Parameter opsional】 + * **Default**: Default ke ukuran seluruh file + * **Nilai lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('httpbin.org', 80); + $cli->setHeaders([ + 'Host' => 'httpbin.org' + ]); + $cli->set(['timeout' => -1]); + $cli->addFile(__FILE__, 'file1', 'text/plain'); + $cli->post('/post', ['foo' => 'bar']); + echo $cli->body; + $cli->close(); +}); +``` + +### addData() + +Membangun konten file yang diunggah menggunakan string. + +!> `addData` tersedia di versi `v4.1.0` ke atas + +```php +Swoole\Coroutine\Http\Client->addData(string $data, string $name, string $mimeType = null, string $filename = null): void +``` + + * **Parameter** + + * **`string $data`** + * **Fungsi**: Konten data【Parameter wajib, panjang maksimum tidak boleh melebihi [buffer_output_size](/server/setting?id=buffer_output_size)】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $name`** + * **Fungsi**: Nama form【Parameter wajib, `key` dalam parameter `$_FILES`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $mimeType`** + * **Fungsi**: Format `MIME` file【Parameter opsional, default `application/octet-stream`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $filename`** + * **Fungsi**: Nama file【Parameter opsional, default `$name`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('httpbin.org', 80); + $client->setHeaders([ + 'Host' => 'httpbin.org' + ]); + $client->set(['timeout' => -1]); + $client->addData(Co::readFile(__FILE__), 'file1', 'text/plain'); + $client->post('/post', ['foo' => 'bar']); + echo $client->body; + $client->close(); +}); +``` + +### get() + +Melakukan permintaan GET. + +```php +Swoole\Coroutine\Http\Client->get(string $path): void +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Mengatur path `URL`【mis. `/index.html`, perhatikan bahwa tidak bisa menggunakan `http://domain` di sini】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->setHeaders([ + 'Host' => 'localhost', + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $client->get('/index.php'); + echo $client->body; + $client->close(); +}); +``` + +!> Menggunakan `get` akan mengabaikan method permintaan yang diatur oleh `setMethod` dan memaksa menggunakan `GET`. + +### post() + +Melakukan permintaan POST. + +```php +Swoole\Coroutine\Http\Client->post(string $path, mixed $data): void +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Mengatur path `URL`【mis. `/index.html`, perhatikan bahwa tidak bisa menggunakan `http://domain` di sini】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`mixed $data`** + * **Fungsi**: Data body permintaan + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Jika `$data` adalah array, sistem akan secara otomatis mengemasnya sebagai konten `POST` format `x-www-form-urlencoded` dan mengatur `Content-Type` menjadi `application/x-www-form-urlencoded` + + * **Catatan** + + !> Menggunakan `post` akan mengabaikan method permintaan yang diatur oleh `setMethod` dan memaksa menggunakan `POST` + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 80); + $client->post('/post.php', array('a' => '123', 'b' => '456')); + echo $client->body; + $client->close(); +}); +``` + +### upgrade() + +Meningkatkan ke koneksi `WebSocket`. + +```php +Swoole\Coroutine\Http\Client->upgrade(string $path): bool +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Mengatur path `URL`【mis. `/`, perhatikan bahwa tidak bisa menggunakan `http://domain` di sini】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Petunjuk** + + * Dalam beberapa kasus, meskipun permintaan berhasil dan `upgrade` mengembalikan `true`, server tidak mengatur kode status HTTP menjadi `101`, melainkan `200` atau `403`, yang menunjukkan server menolak permintaan handshake. + * Setelah handshake WebSocket berhasil, Anda dapat menggunakan method `push` untuk mengirim pesan ke server, dan juga dapat memanggil `recv` untuk menerima pesan. + * `upgrade` akan memicu [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + + * **Contoh** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 9501); + $ret = $client->upgrade('/'); + if ($ret) { + while(true) { + $client->push('hello'); + var_dump($client->recv()); + Coroutine::sleep(0.1); + } + } +}); +``` + +### push() + +Mengirim pesan ke server `WebSocket`. + +!> Method `push` hanya dapat dijalankan setelah `upgrade` berhasil. +Method `push` tidak memicu penjadwalan coroutine, akan segera kembali setelah menulis ke buffer pengiriman. + +```php +Swoole\Coroutine\Http\Client->push(mixed $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool +``` + + * **Parameter** + + * **`mixed $data`** + * **Fungsi**: Konten data yang akan dikirim【Default dalam format teks `UTF-8`. Untuk format encoding lain atau data biner, gunakan `WEBSOCKET_OPCODE_BINARY`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Swoole versi >= v4.2.0, `$data` dapat menggunakan objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe), mendukung pengiriman berbagai tipe frame. + + * **`int $opcode`** + * **Fungsi**: Tipe operasi + * **Default**: `WEBSOCKET_OPCODE_TEXT` + * **Nilai lain**: Tidak ada + + !> `$opcode` harus berupa `WebSocket OPCode` yang valid, jika tidak akan gagal dan mencetak pesan error `opcode max 10` + + * **`int|bool $finish`** + * **Fungsi**: Tipe operasi + * **Default**: `SWOOLE_WEBSOCKET_FLAG_FIN` + * **Nilai lain**: Tidak ada + + !> Sejak `v4.4.12`, parameter `finish` (tipe `bool`) diubah menjadi `flags` (tipe `int`) untuk mendukung kompresi `WebSocket`. `finish` yang sesuai dengan `SWOOLE_WEBSOCKET_FLAG_FIN` bernilai `1`. Nilai `bool` asli akan secara implisit dikonversi ke `int`, perubahan ini kompatibel ke belakang tanpa dampak. Selain itu, `flag` kompresi adalah `SWOOLE_WEBSOCKET_FLAG_COMPRESS`. + + * **Return Value** + + * Jika berhasil dikirim, mengembalikan `true` + * Jika koneksi tidak ada, ditutup, atau `WebSocket` belum selesai, pengiriman gagal dan mengembalikan `false` + + * **Kode Error** + +Kode Error | Keterangan +---|--- +8502 | OPCode salah +8503 | Tidak terhubung ke server atau koneksi telah ditutup +8504 | Handshake gagal + +### recv() + +Menerima pesan. Hanya digunakan untuk `WebSocket` dan perlu digunakan bersama `upgrade()`. Lihat contoh. + +```php +Swoole\Coroutine\Http\Client->recv(float $timeout = 0) +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Parameter ini hanya berlaku saat memanggil `upgrade()` untuk meningkatkan ke koneksi `WebSocket` + * **Satuan**: Detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + !> Atur timeout. Parameter yang ditentukan diprioritaskan terlebih dahulu, diikuti oleh konfigurasi `timeout` yang diberikan di method `set`. + + * **Return Value** + + * Mengembalikan objek frame + * Mengembalikan `false`. Periksa properti `errCode` dari `Swoole\Coroutine\Http\Client`. Klien coroutine tidak memiliki callback `onClose`. Saat koneksi ditutup, `recv` mengembalikan `false` dengan `errCode=0`. + + * **Contoh** + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $client = new Client('127.0.0.1', 9501); + $ret = $client->upgrade('/'); + if ($ret) { + while(true) { + $client->push('hello'); + var_dump($client->recv()); + Coroutine::sleep(0.1); + } + } +}); +``` + +### ping() +Mengirim paket heartbeat `WebSocket`. + +```php +Swoole\Coroutine\Http\Client->ping(string $data = ''): bool +``` + * **Parameter** + + * **`string $data`** + * **Fungsi**: Konten data paket heartbeat + * **Default**: String kosong + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil mengirim mengembalikan `true` + * Koneksi tidak ada, ditutup, atau `WebSocket` belum selesai, pengiriman gagal mengembalikan `false` + +### disconnect() +Menutup koneksi `WebSocket`. + +```php +Swoole\Coroutine\Http\Client->disconnect(int $code = 1000, string $reason = ''): bool +``` + * **Parameter** + + * **`int $code`** + * **Fungsi**: Kode penutupan + * **Default**: `1000` + * **Nilai lain**: Tidak ada + + * **`string $reason`** + * **Fungsi**: Alasan penutupan + * **Default**: String kosong + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil menutup mengembalikan `true` + * Koneksi tidak ada, ditutup, atau `WebSocket` belum selesai, gagal menutup mengembalikan `false` + +### download() + +Mengunduh file melalui HTTP. + +!> Perbedaan antara `download` dan `get` adalah `download` akan menulis data yang diterima ke disk, bukan menggabungkan HTTP Body di memori. Oleh karena itu, `download` hanya menggunakan sedikit memori untuk menyelesaikan pengunduhan file yang sangat besar. + +```php +Swoole\Coroutine\Http\Client->download(string $path, string $filename, int $offset = 0): bool +``` + + * **Parameter** + + * **`string $path`** + * **Fungsi**: Mengatur path URL + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $filename`** + * **Fungsi**: Menentukan path file untuk menulis konten yang diunduh【akan secara otomatis ditulis ke properti `downloadFile`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $offset`** + * **Fungsi**: Menentukan offset untuk menulis ke file【opsi ini dapat digunakan untuk mendukung unduhan resumable, dapat dikombinasikan dengan header HTTP `Range: bytes=$offset`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Saat `$offset` adalah `0`, jika file sudah ada, sistem akan secara otomatis mengosongkan file ini. + + * **Return Value** + + * Mengembalikan `true` + * Gagal membuka file atau `fseek()` di tingkat bawah gagal mengembalikan `false` + + * **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $host = 'cdn.jsdelivr.net'; + $client = new Client($host, 443, true); + $client->set(['timeout' => -1]); + $client->setHeaders([ + 'Host' => $host, + 'User-Agent' => 'Chrome/49.0.2587.3', + 'Accept' => '*', + 'Accept-Encoding' => 'gzip' + ]); + $client->download('/gh/swoole/swoole-src/mascot.png', __DIR__ . '/logo.png'); +}); +``` + +### getCookies() + +Mendapatkan konten `cookie` dari respons `HTTP`. + +```php +Swoole\Coroutine\Http\Client->getCookies(): array|false +``` + +!> Informasi Cookie akan di-decode dengan urldecode. Untuk mendapatkan informasi Cookie asli, silakan parsing sendiri sesuai petunjuk di bawah. + +#### Mendapatkan `Cookie` duplikat atau header `Cookie` asli + +```php +var_dump($client->set_cookie_headers); +``` + +### getHeaders() + +Mengembalikan informasi header dari respons `HTTP`. + +```php +Swoole\Coroutine\Http\Client->getHeaders(): array|false +``` + +### getStatusCode() + +Mendapatkan kode status dari respons HTTP. + +```php +Swoole\Coroutine\Http\Client->getStatusCode(): int|false +``` + + * **Petunjuk** + + * **Jika kode status negatif, itu menunjukkan ada masalah koneksi.** + +Kode Status | Konstanta versi v4.2.10 ke atas | Keterangan +---|---|--- +-1 | SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED | Timeout koneksi, server tidak mendengarkan port atau jaringan hilang, dapat membaca $errCode untuk mendapatkan kode error jaringan spesifik +-2 | SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT | Timeout permintaan, server tidak mengembalikan respons dalam waktu timeout yang ditentukan +-3 | SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET | Setelah permintaan klien dikirim, server memutus koneksi secara paksa +-4 | SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED | Klien gagal mengirim (konstanta ini tersedia di Swoole versi >= `v4.5.9`, untuk versi di bawahnya gunakan kode status) + +### getBody() + +Mendapatkan konten body dari respons HTTP. + +```php +Swoole\Coroutine\Http\Client->getBody(): string|false +``` + +### close() + +Menutup koneksi. + +```php +Swoole\Coroutine\Http\Client->close(): bool +``` + +!> Setelah `close`, jika Anda memanggil method seperti `get`, `post` lagi, Swoole akan membantu Anda menyambung ulang ke server. + +### execute() + +Method permintaan `HTTP` yang lebih low-level, perlu memanggil antarmuka seperti [setMethod](/coroutine_client/http_client?id=setmethod) dan [setData](/coroutine_client/http_client?id=setdata) di kode untuk mengatur method dan data permintaan. + +```php +Swoole\Coroutine\Http\Client->execute(string $path): bool +``` + +* **Contoh** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $httpClient = new Client('httpbin.org', 80); + $httpClient->setMethod('POST'); + $httpClient->setData('swoole'); + $status = $httpClient->execute('/post'); + var_dump($status); + var_dump($httpClient->getBody()); +}); +``` + +## Fungsi + +Untuk memudahkan penggunaan `Coroutine\Http\Client`, ditambahkan tiga fungsi: + +!> Swoole versi >= `v4.6.4` tersedia + +### request() + +Melakukan permintaan dengan method permintaan yang ditentukan. + +```php +function request(string $url, string $method, $data = null, array $options = null, array $headers = null, array $cookies = null) +``` + +### post() + +Digunakan untuk melakukan permintaan `POST`. + +```php +function post(string $url, $data, array $options = null, array $headers = null, array $cookies = null) +``` + +### get() + +Digunakan untuk melakukan permintaan `GET`. + +```php +function get(string $url, array $options = null, array $headers = null, array $cookies = null) +``` + +### Contoh Penggunaan + +```php +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; +use function Swoole\Coroutine\Http\get; +use function Swoole\Coroutine\Http\post; +use function Swoole\Coroutine\Http\request; + +run(function () { + go(function () { + $data = get('http://httpbin.org/get?hello=world'); + $body = json_decode($data->getBody()); + assert($body->headers->Host === 'httpbin.org'); + assert($body->args->hello === 'world'); + }); + go(function () { + $random_data = base64_encode(random_bytes(128)); + $data = post('http://httpbin.org/post?hello=world', ['random_data' => $random_data]); + $body = json_decode($data->getBody()); + assert($body->headers->Host === 'httpbin.org'); + assert($body->args->hello === 'world'); + assert($body->form->random_data === $random_data); + }); +}); +``` diff --git a/public/id/coroutine_client/init.md b/public/id/coroutine_client/init.md new file mode 100644 index 0000000..e46f9b7 --- /dev/null +++ b/public/id/coroutine_client/init.md @@ -0,0 +1,82 @@ +# Klien Coroutine + +Berikut adalah kelas bawaan Swoole untuk klien coroutine. Yang ditandai dengan ⚠️ tidak disarankan untuk digunakan lagi. Anda bisa menggunakan fungsi native PHP + [one-click coroutine](/runtime). + +* [Klien TCP/UDP/UnixSocket](coroutine_client/client.md) +* [Klien Socket](coroutine_client/socket.md) +* [Klien HTTP/WebSocket](coroutine_client/http_client.md) +* [Klien HTTP2](coroutine_client/http2_client.md) +* [Klien PostgreSQL](coroutine_client/postgresql.md) +* [Klien FastCGI](coroutine_client/fastcgi.md) +* ⚠️ [Klien Redis](coroutine_client/redis.md) +* ⚠️ [Klien MySQL](coroutine_client/mysql.md) +* [System](/coroutine/system) API Sistem + +## Aturan Timeout + +Semua permintaan jaringan (membangun koneksi, mengirim data, menerima data) bisa mengalami timeout. `Swoole` klien coroutine menyediakan tiga cara untuk mengatur timeout: + +1. Mengirimkan durasi timeout sebagai parameter method, seperti [Co\Client->connect()](/coroutine_client/client?id=connect), [Co\Http\Client->recv()](/coroutine_client/http_client?id=recv), [Co\MySQL->query()](/coroutine_client/mysql?id=query), dan lain-lain. + +!> Cara ini memiliki lingkup dampak terkecil (hanya berlaku untuk pemanggilan fungsi saat ini), dengan prioritas tertinggi (pemanggilan fungsi saat ini akan mengabaikan pengaturan timeout `2` dan `3` di bawah). + +2. Mengatur timeout melalui method `set()` atau `setOption()` dari kelas klien coroutine `Swoole`, contoh: + +```php +$client = new Co\Client(SWOOLE_SOCK_TCP); +//atau +$client = new Co\Http\Client("127.0.0.1", 80); +//atau +$client = new Co\Http2\Client("127.0.0.1", 443, true); +$client->set(array( + 'timeout' => 0.5,//total timeout, termasuk koneksi, pengiriman, dan penerimaan + 'connect_timeout' => 1.0,//timeout koneksi, akan menimpa total timeout pertama + 'write_timeout' => 10.0,//timeout pengiriman, akan menimpa total timeout pertama + 'read_timeout' => 0.5,//timeout penerimaan, akan menimpa total timeout pertama +)); + +//Co\Redis() tidak memiliki konfigurasi write_timeout dan read_timeout +$client = new Co\Redis(); +$client->setOption(array( + 'timeout' => 1.0,//total timeout, termasuk koneksi, pengiriman, dan penerimaan + 'connect_timeout' => 0.5,//timeout koneksi, akan menimpa total timeout pertama +)); + +//Co\MySQL() tidak memiliki fitur konfigurasi set +$client = new Co\MySQL(); + +//Co\Socket dikonfigurasi melalui setOption +$socket = new Co\Socket(AF_INET, SOCK_STREAM, SOL_TCP); +$timeout = array('sec'=>1, 'usec'=>500000); +$socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $timeout);//timeout penerimaan data +$socket->setOption(SOL_SOCKET, SO_SNDTIMEO, $timeout);//konfigurasi timeout koneksi dan pengiriman data +``` + +!> Cara ini hanya memengaruhi kelas saat ini dan akan ditimpa oleh cara ke-`1`, mengabaikan konfigurasi cara ke-`3` di bawah. + +3. Seperti yang terlihat, aturan pengaturan timeout pada `2` cara di atas rumit dan tidak konsisten. Untuk menghindari kehati-hatian berlebihan dari pengembang, mulai versi `v4.2.10` semua klien coroutine menyediakan aturan pengaturan timeout global yang seragam dengan dampak terbesar dan prioritas terendah, sebagai berikut: + +```php +Co::set([ + 'socket_timeout' => 5, + 'socket_connect_timeout' => 1, + 'socket_read_timeout' => 1, + 'socket_write_timeout' => 1, +]); +``` + ++ `-1`: menunjukkan tidak pernah timeout ++ `0`: menunjukkan tidak mengubah timeout ++ `nilai lain > 0`: menunjukkan pengaturan timer timeout untuk jumlah detik yang sesuai, dengan presisi maksimum `1 milidetik`, bertipe float, `0.5` berarti `500 milidetik` ++ `socket_connect_timeout`: menunjukkan timeout untuk membangun koneksi TCP, **default `1 detik`** , mulai versi `v4.5.x` **default `2 detik`** ++ `socket_timeout`: menunjukkan timeout untuk operasi baca/tulis TCP, **default `-1`** , mulai versi `v4.5.x` **default `60 detik`**. Jika ingin mengatur baca dan tulis secara terpisah, lihat konfigurasi di bawah ++ `socket_read_timeout`: ditambahkan di `v4.3`, menunjukkan timeout operasi **baca** TCP, **default `-1`** , mulai versi `v4.5.x` **default `60 detik`** ++ `socket_write_timeout`: ditambahkan di `v4.3`, menunjukkan timeout operasi **tulis** TCP, **default `-1`** , mulai versi `v4.5.x` **default `60 detik`** + +!> **Artinya:** sebelum versi `v4.5.x` dari klien coroutine `Swoole`, jika tidak ada timeout yang diatur menggunakan cara `1` atau `2` di atas, default timeout koneksi adalah `1 detik`, sedangkan operasi baca/tulis tidak pernah timeout; +mulai versi `v4.5.x`, default timeout koneksi adalah `60 detik`, dan timeout operasi baca/tulis adalah `60 detik`; +Jika timeout global diubah di tengah jalan, itu tidak akan berlaku untuk socket yang sudah dibuat. + +### Timeout Library Jaringan Resmi PHP + +Selain klien coroutine yang disediakan `Swoole` di atas, dalam [one-click coroutine](/runtime) menggunakan method native PHP, dan timeout-nya dipengaruhi oleh konfigurasi [default_socket_timeout](http://php.net/manual/zh/filesystem.configuration.php). Pengembang dapat mengaturnya secara terpisah melalui `ini_set('default_socket_timeout', 60)`, dengan nilai default 60. diff --git a/public/id/coroutine_client/mysql.md b/public/id/coroutine_client/mysql.md new file mode 100644 index 0000000..597043c --- /dev/null +++ b/public/id/coroutine_client/mysql.md @@ -0,0 +1,447 @@ +# Coroutine\MySQL + +Klien MySQL coroutine. + +!> Klien ini tidak lagi direkomendasikan. Disarankan menggunakan `Swoole\Runtime::enableCoroutine` + `pdo_mysql` atau `mysqli`, yaitu [one-click coroutine](/runtime) untuk klien `MySQL` native. +!> Setelah `Swoole 6.0`, klien `MySQL` coroutine ini telah dihapus. + +## Contoh Penggunaan + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $swoole_mysql = new MySQL(); + $swoole_mysql->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', + ]); + $res = $swoole_mysql->query('select sleep(1)'); + var_dump($res); +}); +``` + +## Fitur defer + +Silakan lihat bagian [Klien Konkuren](/coroutine/multi_call). + +## Stored Procedure + +Sejak versi `4.0.0`, mendukung stored procedure `MySQL` dan pengambilan beberapa result set. + +## MySQL8.0 + +`Swoole-4.0.1` atau versi lebih tinggi mendukung semua kemampuan verifikasi keamanan `MySQL8`, sehingga Anda dapat langsung menggunakan klien tanpa harus mengatur ulang pengaturan password. + +### Versi di bawah 4.0.1 + +`MySQL-8.0` menggunakan plugin `caching_sha2_password` yang lebih aman secara default. Jika Anda upgrade dari `5.x`, Anda dapat langsung menggunakan semua fitur `MySQL`. Untuk `MySQL` yang baru dibuat, Anda perlu menjalankan perintah berikut di baris perintah `MySQL` untuk membuatnya kompatibel: + +```SQL +ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; +flush privileges; +``` + +Ganti `'root'@'localhost'` dalam pernyataan dengan user yang Anda gunakan, dan `password` dengan password user tersebut. + +Jika masih tidak dapat digunakan, atur `default_authentication_plugin = mysql_native_password` di my.cnf. + +## Properti + +### serverInfo + +Informasi koneksi, disimpan sebagai array yang diteruskan ke fungsi koneksi. + +### sock + +Deskriptor file yang digunakan untuk koneksi. + +### connected + +Apakah sudah terhubung ke server `MySQL`. + +!> Lihat [Ketidaksesuaian properti connected dengan status koneksi](/question/use?id=ketidaksesuaian-properti-connected-dengan-status-koneksi) + +### connect_error + +Pesan error saat menjalankan `connect` ke server. + +### connect_errno + +Kode error saat menjalankan `connect` ke server, bertipe integer. + +### error + +Pesan error yang dikembalikan server saat menjalankan perintah `MySQL`. + +### errno + +Kode error yang dikembalikan server saat menjalankan perintah `MySQL`, bertipe integer. + +### affected_rows + +Jumlah baris yang terpengaruh. + +### insert_id + +`id` dari record terakhir yang disisipkan. + +## Method + +### connect() + +Membangun koneksi MySQL. + +```php +Swoole\Coroutine\MySQL->connect(array $serverInfo): bool +``` + +!> `$serverInfo`: Parameter diteruskan dalam format array. + +```php +[ + 'host' => 'Alamat IP MySQL', // Jika UNIX Socket lokal, harus diisi dengan format seperti `unix://tmp/your_file.sock` + 'user' => 'User database', + 'password' => 'Password database', + 'database' => 'Nama database', + 'port' => 'Port MySQL default 3306 parameter opsional', + 'timeout' => 'Waktu timeout koneksi', // Hanya memengaruhi timeout koneksi, tidak memengaruhi method query dan execute, lihat `aturan timeout klien` + 'charset' => 'Set karakter', + 'strict_type' => false, // Aktifkan mode ketat, data yang dikembalikan method query juga akan dikonversi ke tipe kuat + 'fetch_mode' => true, // Aktifkan mode fetch, bisa menggunakan fetch/fetchAll seperti pdo untuk mengambil baris demi baris atau seluruh result set (versi 4.0 ke atas) +] +``` + +### query() + +Menjalankan statement SQL. + +```php +Swoole\Coroutine\MySQL->query(string $sql, float $timeout = 0): array|false +``` + + * **Parameter** + + * **`string $sql`** + * **Fungsi**: Statement SQL + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Durasi timeout 【Jika server `MySQL` gagal mengembalikan data dalam waktu yang ditentukan, sistem akan mengembalikan `false`, mengatur kode error ke `110`, dan memutus koneksi】 + * **Satuan**: Detik, dengan presisi minimum milidetik (`0.001` detik) + * **Default**: `0` + * **Nilai lain**: Tidak ada + * **Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout)** + + * **Return Value** + + * Timeout/error mengembalikan `false`, selain itu mengembalikan hasil query dalam bentuk `array` + + * **Penerimaan Tertunda** + + !> Setelah mengatur `defer`, memanggil `query` akan langsung mengembalikan `true`. Memanggil `recv` akan masuk ke penungguan coroutine dan mengembalikan hasil query. + + * **Contoh** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $swoole_mysql = new MySQL(); + $swoole_mysql->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', + ]); + $res = $swoole_mysql->query('show tables'); + if ($res === false) { + return; + } + var_dump($res); +}); +``` + +### prepare() + +Mengirim permintaan prepared SQL ke server MySQL. + +!> `prepare` harus digunakan bersama dengan `execute`. Setelah permintaan prepared berhasil, panggil method `execute` untuk mengirim parameter data ke server `MySQL`. + +```php +Swoole\Coroutine\MySQL->prepare(string $sql, float $timeout): Swoole\Coroutine\MySQL\Statement|false; +``` + + * **Parameter** + + * **`string $sql`** + * **Fungsi**: Statement prepared【menggunakan `?` sebagai placeholder parameter】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: Detik, dengan presisi minimum milidetik (`0.001` detik) + * **Default**: `0` + * **Nilai lain**: Tidak ada + * **Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout)** + + * **Return Value** + + * Mengembalikan `false`, dapat memeriksa `$db->error` dan `$db->errno` untuk menentukan penyebab error + * Mengembalikan objek `Coroutine\MySQL\Statement`, dapat memanggil method [execute](/coroutine_client/mysql?id=statement-execute) objek untuk mengirim parameter + + * **Contoh** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $ret1 = $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); + if ($stmt == false) { + var_dump($db->errno, $db->error); + } else { + $ret2 = $stmt->execute(array(10)); + var_dump($ret2); + } +}); +``` + +### escape() + +Melakukan escape karakter khusus dalam statement SQL untuk mencegah serangan SQL injection. Diimplementasikan berdasarkan fungsi yang disediakan oleh `mysqlnd` dan memerlukan ekstensi `mysqlnd` di `PHP`. + +!> Saat kompilasi perlu menambahkan [--enable-mysqlnd](/environment?id=opsi-kompilasi) untuk mengaktifkannya. + +```php +Swoole\Coroutine\MySQL->escape(string $str): string +``` + + * **Parameter** + + * **`string $str`** + * **Fungsi**: Karakter yang akan di-escape + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $data = $db->escape("abc'efg\r\n"); +}); +``` + +### begin() + +Memulai transaksi. Digabungkan dengan `commit` dan `rollback` untuk menangani transaksi `MySQL`. + +```php +Swoole\Coroutine\MySQL->begin(): bool +``` + +!> Memulai transaksi `MySQL`. Mengembalikan `true`, gagal mengembalikan `false`. Periksa `$db->errno` untuk mendapatkan kode error. + +!> Dengan objek koneksi `MySQL` yang sama, hanya satu transaksi yang dapat dimulai dalam satu waktu; +harus menunggu transaksi sebelumnya di-`commit` atau di-`rollback` untuk memulai transaksi baru; +jika tidak, sistem akan melempar exception `Swoole\MySQL\Exception` dengan `code` `21`. + + * **Contoh** + + ```php + $db->begin(); + $db->query("update userinfo set level = 22 where id = 1"); + $db->commit(); + ``` + +### commit() + +Menyelesaikan transaksi. + +!> Harus digunakan bersama dengan `begin`. + +```php +Swoole\Coroutine\MySQL->commit(): bool +``` + +!> Mengembalikan `true`, gagal mengembalikan `false`. Periksa `$db->errno` untuk mendapatkan kode error. + +### rollback() + +Membatalkan transaksi. + +!> Harus digunakan bersama dengan `begin`. + +```php +Swoole\Coroutine\MySQL->rollback(): bool +``` + +!> Mengembalikan `true`, gagal mengembalikan `false`. Periksa `$db->errno` untuk mendapatkan kode error. + +### Statement->execute() + +Mengirim parameter data prepared SQL ke server MySQL. + +!> `execute` harus digunakan bersama dengan `prepare`, `prepare` harus dipanggil sebelum `execute` untuk memulai permintaan prepared. + +!> Method `execute` dapat dipanggil beberapa kali. + +```php +Swoole\Coroutine\MySQL\Statement->execute(array $params, float $timeout = -1): array|bool +``` + + * **Parameter** + + * **`array $params`** + * **Fungsi**: Parameter data prepared【Harus sama jumlahnya dengan parameter statement `prepare`. `$params` harus array dengan indeks numerik, urutan parameter sama dengan statement `prepare`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Waktu timeout【Jika server `MySQL` gagal mengembalikan data dalam waktu yang ditentukan, sistem akan mengembalikan `false`, mengatur kode error ke `110`, dan memutus koneksi】 + * **Satuan**: Detik, dengan presisi minimum milidetik (`0.001` detik) + * **Default**: `-1` + * **Nilai lain**: Tidak ada + * **Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout)** + + * **Return Value** + + * Mengembalikan `true`, jika parameter `fetch_mode` di `connect` diatur ke `true` + * Mengembalikan array dataset, jika bukan kasus di atas + * Mengembalikan `false`, dapat memeriksa `$db->error` dan `$db->errno` untuk menentukan penyebab error + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\MySQL; +use function Swoole\Coroutine\run; + +run(function () { + $db = new MySQL(); + $ret1 = $db->connect([ + 'host' => '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=? and name=?'); + if ($stmt == false) { + var_dump($db->errno, $db->error); + } else { + $ret2 = $stmt->execute(array(10, 'rango')); + var_dump($ret2); + + $ret3 = $stmt->execute(array(13, 'alvin')); + var_dump($ret3); + } +}); +``` + +### Statement->fetch() + +Mengambil baris berikutnya dari result set. + +```php +Swoole\Coroutine\MySQL\Statement->fetch(): ?array +``` + +!> Swoole versi >= `4.0-rc1`, perlu menambahkan opsi `fetch_mode => true` saat connect + + * **Contoh** + +```php +$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); +$stmt->execute(); +while ($ret = $stmt->fetch()) { + var_dump($ret); +} +``` + +!> Mulai dari driver `MySQL` baru di `v4.4.0`, `fetch` harus digunakan dengan cara seperti kode contoh sampai membaca `NULL`, jika tidak, permintaan baru tidak dapat dilakukan (karena mekanisme baca sesuai kebutuhan, dapat menghemat memori) + +### Statement->fetchAll() + +Mengembalikan array yang berisi semua baris dalam result set. + +```php +Swoole\Coroutine\MySQL\Statement->fetchAll():? array +``` + +!> Swoole versi >= `4.0-rc1`, perlu menambahkan opsi `fetch_mode => true` saat `connect` + + * **Contoh** + +```php +$stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); +$stmt->execute(); +$stmt->fetchAll(); +``` + +### Statement->nextResult() + +Maju ke hasil respons berikutnya dalam handle statement multi-respons (misalnya, beberapa hasil dari stored procedure). + +```php +Swoole\Coroutine\MySQL\Statement->nextResult():? bool +``` + + * **Return Value** + + * Mengembalikan `TRUE` + * Mengembalikan `FALSE` + * Tidak ada hasil berikutnya mengembalikan `NULL` + + * **Contoh** + + * **Mode non-fetch** + + ```php + $stmt = $db->prepare('CALL reply(?)'); + $res = $stmt->execute(['hello mysql!']); + do { + var_dump($res); + } while ($res = $stmt->nextResult()); + var_dump($stmt->affected_rows); + ``` + + * **Mode fetch** + + ```php + $stmt = $db->prepare('CALL reply(?)'); + $stmt->execute(['hello mysql!']); + do { + $res = $stmt->fetchAll(); + var_dump($res); + } while ($stmt->nextResult()); + var_dump($stmt->affected_rows); + ``` + +!> Mulai dari driver `MySQL` baru di `v4.4.0`, `fetch` harus dibaca sampai `NULL` menggunakan cara kode contoh, jika tidak, permintaan baru tidak dapat dilakukan (karena mekanisme baca sesuai kebutuhan, dapat menghemat memori) diff --git a/public/id/coroutine_client/postgresql-old.md b/public/id/coroutine_client/postgresql-old.md new file mode 100644 index 0000000..859a7c1 --- /dev/null +++ b/public/id/coroutine_client/postgresql-old.md @@ -0,0 +1,366 @@ +# Coroutine\PostgreSQL Versi Lama + +Klien `PostgreSQL` coroutine. Perlu mengompilasi ekstensi [ext-postgresql](https://github.com/swoole/ext-postgresql) untuk mengaktifkan fitur ini. + +> Dokumentasi ini hanya berlaku untuk Swoole < 5.0 + +## Kompilasi dan Instalasi + +Unduh kode sumber dari: [https://github.com/swoole/ext-postgresql](https://github.com/swoole/ext-postgresql), pastikan menginstal versi rilis yang sesuai dengan versi Swoole. + +* Pastikan pustaka `libpq` sudah terinstal di sistem +* Di `mac`, setelah menginstal `postgresql`, pustaka `libpq` sudah termasuk. Mungkin ada perbedaan antar lingkungan; di `ubuntu` mungkin perlu `apt-get install libpq-dev`, di `centos` mungkin perlu `yum install postgresql10-devel` +* Anda juga dapat menentukan direktori pustaka `libpq` secara terpisah, misalnya: `./configure --with-libpq-dir=/etc/postgresql` + +## Contoh Penggunaan + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + if (!$conn) { + var_dump($pg->error); + return; + } + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + var_dump($arr); +}); +``` + +### Penanganan Transaksi + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN'); + $result = $pg->query('SELECT * FROM test'); + $arr = $pg->fetchAll($result); + $pg->query('COMMIT'); + var_dump($arr); +}); +``` + +## Properti + +### error + +Mendapatkan informasi error. + +## Method + +### connect() + +Membangun koneksi coroutine non-blocking ke `postgresql`. + +```php +Swoole\Coroutine\PostgreSQL->connect(string $connection_string): bool +``` + +!> `$connection_string` adalah informasi koneksi. Mengembalikan true jika koneksi berhasil, false jika gagal. Anda dapat menggunakan properti [error](/coroutine_client/postgresql?id=error) untuk mendapatkan informasi error. + * **Contoh** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + var_dump($pg->error, $conn); +}); +``` + +### query() + +Menjalankan statement SQL. Mengirim perintah coroutine asinkron non-blocking. + +```php +Swoole\Coroutine\PostgreSQL->query(string $sql): resource; +``` + + * **Parameter** + + * **`string $sql`** + * **Fungsi**: Statement SQL + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Contoh** + + * **select** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + var_dump($arr); + }); + ``` + + * **Mengembalikan insert id** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + $result = $pg->query("insert into test (id,text) VALUES (24,'text') RETURNING id ;"); + $arr = $pg->fetchRow($result); + var_dump($arr); + }); + ``` + + * **transaction** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN;'); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchAll($result); + $pg->query('COMMIT;'); + var_dump($arr); + }); + ``` + +### fetchAll() + +```php +Swoole\Coroutine\PostgreSQL->fetchAll(resource $queryResult, $resultType = SW_PGSQL_ASSOC):? array; +``` + + * **Parameter** + * **`$resultType`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_ASSOC` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Mengambil semua baris dari hasil sebagai array. + +### affectedRows() + +Mengembalikan jumlah record yang terpengaruh. + +```php +Swoole\Coroutine\PostgreSQL->affectedRows(resource $queryResult): int +``` + +### numRows() + +Mengembalikan jumlah baris. + +```php +Swoole\Coroutine\PostgreSQL->numRows(resource $queryResult): int +``` + +### fetchObject() + +Mengambil satu baris sebagai objek. + +```php +Swoole\Coroutine\PostgreSQL->fetchObject(resource $queryResult, int $row): object; +``` + + * **Contoh** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + + $row = 0; + for ($row = 0; $row < $pg->numRows($result); $row++) { + $data = $pg->fetchObject($result, $row); + echo $data->id . " \n "; + } +}); +``` +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + + $row = 0; + while ($data = $pg->fetchObject($result, $row)) { + echo $data->id . " \n "; + $row++; + } +}); +``` + +### fetchAssoc() + +Mengambil satu baris sebagai array asosiatif. + +```php +Swoole\Coroutine\PostgreSQL->fetchAssoc(resource $queryResult, int $row): array +``` + +### fetchArray() + +Mengambil satu baris sebagai array. + +```php +Swoole\Coroutine\PostgreSQL->fetchArray(resource $queryResult, int $row, $resultType = SW_PGSQL_BOTH): array|false +``` + + * **Parameter** + * **`int $row`** + * **Fungsi**: `row` adalah nomor baris (record) yang ingin diambil. Baris pertama adalah `0`. + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + * **`$resultType`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_BOTH` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Mengembalikan array yang sesuai dengan baris (tuple/record) yang diambil. Jika tidak ada lagi baris yang tersedia, mengembalikan `false`. + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + $arr = $pg->fetchArray($result, 1, SW_PGSQL_ASSOC); + var_dump($arr); +}); +``` + +### fetchRow() + +Mengambil satu baris data (record) dari resource `result` yang ditentukan dan mengembalikannya sebagai array. Setiap kolom yang diperoleh disimpan secara berurutan dalam array, dimulai dari offset `0`. + +```php +Swoole\Coroutine\PostgreSQL->fetchRow(resource $queryResult, int $row, $resultType = SW_PGSQL_NUM): array|false +``` + + * **Parameter** + * **`int $row`** + * **Fungsi**: `row` adalah nomor baris (record) yang akan diambil. Baris pertama adalah `0`. + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + * **`$resultType`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_NUM` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Array yang dikembalikan sesuai dengan baris yang diekstrak. Jika tidak ada lagi baris `row` yang dapat diekstrak, mengembalikan `false`. + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->query('SELECT * FROM test;'); + while ($row = $pg->fetchRow($result)) { + echo "name: $row[0] mobile: $row[1]" . PHP_EOL; + } +}); +``` + +### metaData() + +Melihat metadata tabel. Versi coroutine asinkron non-blocking. + +```php +Swoole\Coroutine\PostgreSQL->metaData(string $tableName): array +``` + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->metaData('test'); + var_dump($result); +}); +``` + +### prepare() + +Preprocessing. + +```php +Swoole\Coroutine\PostgreSQL->prepare(string $name, string $sql); +Swoole\Coroutine\PostgreSQL->execute(string $name, array $bind); +``` + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=112"); + $pg->prepare("my_query", "select * from test where id > $1 and id < $2"); + $res = $pg->execute("my_query", array(1, 3)); + $arr = $pg->fetchAll($res); + var_dump($arr); +}); +``` diff --git a/public/id/coroutine_client/postgresql.md b/public/id/coroutine_client/postgresql.md new file mode 100644 index 0000000..5c78c34 --- /dev/null +++ b/public/id/coroutine_client/postgresql.md @@ -0,0 +1,372 @@ +# Coroutine\PostgreSQL + +Klien `PostgreSQL` coroutine. + +!> Direkonstruksi ulang di Swoole 5.0, dengan penggunaan yang sangat berbeda dari versi lama. Jika Anda menggunakan versi lama, silakan lihat [dokumentasi lama](/coroutine_client/postgresql-old.md). + +!> Setelah Swoole 6.0, klien `PostgreSQL` coroutine telah dihapus, silakan gunakan [pdo_pgsql coroutine](/runtime?id=swoole_hook_pdo_pgsql) sebagai gantinya + +## Kompilasi dan Instalasi + +* Pastikan pustaka `libpq` sudah terinstal di sistem +* Di `mac`, setelah menginstal `postgresql`, pustaka `libpq` sudah termasuk. Mungkin ada perbedaan antar lingkungan; di `ubuntu` mungkin perlu `apt-get install libpq-dev`, di `centos` mungkin perlu `yum install postgresql10-devel` +* Saat mengompilasi Swoole, tambahkan opsi kompilasi: `./configure --enable-swoole-pgsql` + +## Contoh Penggunaan + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + if (!$conn) { + var_dump($pg->error); + return; + } + $stmt = $pg->query('SELECT * FROM test;'); + $arr = $stmt->fetchAll(); + var_dump($arr); +}); +``` + +### Penanganan Transaksi + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN'); + $stmt = $pg->query('SELECT * FROM test'); + $arr = $stmt->fetchAll(); + $pg->query('COMMIT'); + var_dump($arr); +}); +``` + +## Properti + +### error + +Mendapatkan pesan error. + +## Method + +### connect() + +Membangun koneksi coroutine non-blocking ke `postgresql`. + +```php +Swoole\Coroutine\PostgreSQL->connect(string $conninfo, float $timeout = 2): bool +``` + +!> `$conninfo` adalah informasi koneksi. Mengembalikan true jika koneksi berhasil, false jika gagal. Anda dapat menggunakan properti [error](/coroutine_client/postgresql?id=error) untuk mendapatkan informasi error. + * **Contoh** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + var_dump($pg->error, $conn); +}); +``` + +### query() + +Menjalankan statement SQL. Mengirim perintah coroutine asinkron non-blocking. + +```php +Swoole\Coroutine\PostgreSQL->query(string $sql): \Swoole\Coroutine\PostgreSQLStatement|false; +``` + + * **Parameter** + + * **`string $sql`** + * **Fungsi**: Statement SQL + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Contoh** + + * **select** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $stmt = $pg->query('SELECT * FROM test;'); + $arr = $stmt->fetchAll(); + var_dump($arr); + }); + ``` + + * **Mengembalikan insert id** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password="); + $stmt = $pg->query("insert into test (id,text) VALUES (24,'text') RETURNING id ;"); + $arr = $stmt->fetchRow(); + var_dump($arr); + }); + ``` + + * **transaction** + + ```php + use Swoole\Coroutine\PostgreSQL; + use function Swoole\Coroutine\run; + + run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=root password="); + $pg->query('BEGIN;'); + $stmt = $pg->query('SELECT * FROM test;'); + $arr = $stmt->fetchAll(); + $pg->query('COMMIT;'); + var_dump($arr); + }); + ``` + +### metaData() + +Melihat metadata tabel. Versi coroutine asinkron non-blocking. + +```php +Swoole\Coroutine\PostgreSQL->metaData(string $tableName): array +``` + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $result = $pg->metaData('test'); + var_dump($result); +}); +``` + +### prepare() + +Preprocessing. + +```php +$stmt = Swoole\Coroutine\PostgreSQL->prepare(string $sql); +$stmt->execute(array $params); +``` + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=112"); + $stmt = $pg->prepare("select * from test where id > $1 and id < $2"); + $res = $stmt->execute(array(1, 3)); + $arr = $stmt->fetchAll(); + var_dump($arr); +}); +``` + +## PostgreSQLStatement + +Nama kelas: `Swoole\Coroutine\PostgreSQLStatement` + +Semua query akan mengembalikan objek `PostgreSQLStatement` + +### fetchAll() + +```php +Swoole\Coroutine\PostgreSQLStatement->fetchAll(int $result_type = SW_PGSQL_ASSOC): false|array; +``` + + * **Parameter** + * **`$result_type`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_ASSOC` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Mengambil semua baris dari hasil sebagai array. + +### affectedRows() + +Mengembalikan jumlah record yang terpengaruh. + +```php +Swoole\Coroutine\PostgreSQLStatement->affectedRows(): int +``` + +### numRows() + +Mengembalikan jumlah baris. + +```php +Swoole\Coroutine\PostgreSQLStatement->numRows(): int +``` + +### fetchObject() + +Mengambil satu baris sebagai objek. + +```php +Swoole\Coroutine\PostgreSQLStatement->fetchObject(int $row, ?string $class_name = null, array $ctor_params = []): object; +``` + + * **Contoh** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $stmt = $pg->query('SELECT * FROM test;'); + + $row = 0; + for ($row = 0; $row < $stmt->numRows(); $row++) { + $data = $stmt->fetchObject($row); + echo $data->id . " \n "; + } +}); +``` +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $stmt = $pg->query('SELECT * FROM test;'); + + $row = 0; + while ($data = $stmt->fetchObject($row)) { + echo $data->id . " \n "; + $row++; + } +}); +``` + +### fetchAssoc() + +Mengambil satu baris sebagai array asosiatif. + +```php +Swoole\Coroutine\PostgreSQLStatement->fetchAssoc(int $row, int $result_type = SW_PGSQL_ASSOC): array +``` + +### fetchArray() + +Mengambil satu baris sebagai array. + +```php +Swoole\Coroutine\PostgreSQLStatement->fetchArray(int $row, int $result_type = SW_PGSQL_BOTH): array|false +``` + + * **Parameter** + * **`int $row`** + * **Fungsi**: `row` adalah nomor baris (record) yang ingin diambil. Baris pertama adalah `0`. + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + * **`$result_type`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_BOTH` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Mengembalikan array yang sesuai dengan baris (tuple/record) yang diambil. Jika tidak ada lagi baris yang tersedia, mengembalikan `false`. + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $stmt = $pg->query('SELECT * FROM test;'); + $arr = $stmt->fetchArray(1, SW_PGSQL_ASSOC); + var_dump($arr); +}); +``` + +### fetchRow() + +Mengambil satu baris data (record) berdasarkan resource `result` yang ditentukan dan mengembalikannya sebagai array. Setiap kolom yang diperoleh disimpan secara berurutan dalam array, dimulai dari offset `0`. + +```php +Swoole\Coroutine\PostgreSQLStatement->fetchRow(int $row, int $result_type = SW_PGSQL_NUM): array|false +``` + + * **Parameter** + * **`int $row`** + * **Fungsi**: `row` adalah nomor baris (record) yang akan diambil. Baris pertama adalah `0`. + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + * **`$result_type`** + * **Fungsi**: Konstanta. Parameter opsional yang mengontrol bagaimana nilai kembali diinisialisasi. + * **Default**: `SW_PGSQL_NUM` + * **Nilai lain**: Tidak ada + + Nilai | Return Value + ---|--- + SW_PGSQL_ASSOC | Mengembalikan array asosiatif dengan nama field sebagai kunci + SW_PGSQL_NUM | Mengembalikan array dengan nomor field sebagai kunci + SW_PGSQL_BOTH | Mengembalikan array dengan keduanya sebagai kunci + + * **Return Value** + + * Array yang dikembalikan sesuai dengan baris yang diekstrak. Jika tidak ada lagi baris `row` yang dapat diekstrak, mengembalikan `false`. + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\PostgreSQL; +use function Swoole\Coroutine\run; + +run(function () { + $pg = new PostgreSQL(); + $conn = $pg->connect("host=127.0.0.1 port=5432 dbname=test user=wuzhenyu"); + $stmt = $pg->query('SELECT * FROM test;'); + while ($row = $stmt->fetchRow()) { + echo "name: $row[0] mobile: $row[1]" . PHP_EOL; + } +}); +``` diff --git a/public/id/coroutine_client/redis.md b/public/id/coroutine_client/redis.md new file mode 100644 index 0000000..290ebf3 --- /dev/null +++ b/public/id/coroutine_client/redis.md @@ -0,0 +1,310 @@ +# Klien Redis Coroutine + +!> Klien ini tidak lagi direkomendasikan. Disarankan menggunakan `Swoole\Runtime::enableCoroutine + phpredis` atau `predis`, yaitu [one-click coroutine](/runtime) untuk klien `redis` native `PHP`. + +!> Setelah `Swoole 6.0`, klien Redis coroutine ini telah dihapus. + +## Contoh Penggunaan + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $val = $redis->get('key'); +}); +``` + +!> `subscribe` `pSubscribe` tidak dapat digunakan dengan `defer(true)`. + +## Method + +!> Penggunaan method pada dasarnya konsisten dengan [phpredis](https://github.com/phpredis/phpredis). + +Berikut adalah penjelasan yang berbeda dari implementasi [phpredis](https://github.com/phpredis/phpredis): + +1. Perintah Redis yang belum diimplementasikan: `scan object sort migrate hscan sscan zscan`; + +2. Penggunaan `subscribe pSubscribe`, tidak perlu mengatur fungsi callback; + +3. Dukungan untuk serialisasi variabel PHP, atur parameter ketiga method `connect()` ke `true` untuk mengaktifkan serialisasi variabel `PHP`, default `false` + +### __construct() + +Method konstruktor klien Redis coroutine, dapat mengatur opsi konfigurasi koneksi `Redis`, konsisten dengan parameter method `setOptions()`. + +```php +Swoole\Coroutine\Redis::__construct(array $options = null); +``` + +### setOptions() + +Method ini ditambahkan setelah versi 4.2.10, digunakan untuk mengatur beberapa konfigurasi klien `Redis` setelah konstruksi dan koneksi. + +Fungsi ini bergaya Swoole, perlu dikonfigurasi melalui array pasangan `Key-Value`. + +```php +Swoole\Coroutine\Redis->setOptions(array $options): void +``` + + * **Opsi yang Dapat Dikonfigurasi** + +Key | Keterangan +---|--- +`connect_timeout` | Timeout koneksi, default adalah `socket_connect_timeout` coroutine global (1 detik) +`timeout` | Timeout, default adalah `socket_timeout` coroutine global, lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) +`serialize` | Serialisasi otomatis, default nonaktif +`reconnect` | Jumlah percobaan koneksi otomatis. Jika koneksi ditutup normal karena timeout atau alasan lain, saat permintaan berikutnya, akan secara otomatis mencoba menyambung sebelum mengirim permintaan. Default `1` kali (`true`). Setelah gagal dengan jumlah yang ditentukan, tidak akan terus mencoba dan perlu menyambung manual. Mekanisme ini hanya digunakan untuk menjaga koneksi, tidak akan mengirim ulang permintaan yang menyebabkan error pada interface non-idempoten. +`compatibility_mode` | Solusi kompatibilitas untuk ketidaksesuaian hasil fungsi `hmGet/hGetAll/zRange/zRevRange/zRangeByScore/zRevRangeByScore` dengan `php-redis`. Setelah diaktifkan, `Co\Redis` dan `php-redis` mengembalikan hasil yang konsisten. Default nonaktif【Opsi ini tersedia di `v4.4.0` atau versi lebih tinggi】 + +### set() + +Menyimpan data. + +```php +Swoole\Coroutine\Redis->set(string $key, mixed $value, array|int $option): bool +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: Kunci data + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $value`** + * **Fungsi**: Konten data【tipe non-string akan otomatis diserialisasi】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $options`** + * **Fungsi**: Opsi + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Penjelasan `$option`: + `int`: Mengatur waktu kedaluwarsa, mis. `3600` + `array`: Pengaturan kedaluwarsa lanjutan, mis. `['nx', 'ex' => 10]` 、`['xx', 'px' => 1000]` + + !> `px`: Menunjukkan waktu kedaluwarsa dalam milidetik + `ex`: Menunjukkan waktu kedaluwarsa dalam detik + `nx`: Mengatur timeout jika tidak ada + `xx`: Mengatur timeout jika sudah ada + +### request() + +Mengirim perintah khusus ke server Redis. Mirip dengan rawCommand di phpredis. + +```php +Swoole\Coroutine\Redis->request(array $args): void +``` + + * **Parameter** + + * **`array $args`** + * **Fungsi**: Daftar argumen, harus dalam format array.【Elemen pertama harus berupa perintah `Redis`, elemen lainnya adalah parameter perintah, yang akan secara otomatis dikemas menjadi permintaan protokol `Redis` untuk dikirim.】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + +Tergantung pada cara server `Redis` menangani perintah. Mungkin mengembalikan tipe angka, boolean, string, array, dll. + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); // Jika UNIX Socket lokal, parameter host harus diisi dengan format seperti `unix://tmp/your_file.sock` + $res = $redis->request(['object', 'encoding', 'key1']); + var_dump($res); +}); +``` + +## Properti + +### errCode + +Kode error. + +Kode Error | Keterangan +---|--- +1 | Error in read or write +2 | Everything else... +3 | End of file +4 | Protocol error +5 | Out of memory + +### errMsg + +Pesan error. + +### connected + +Memeriksa apakah klien `Redis` saat ini terhubung ke server. + +## Konstanta + +Digunakan untuk method `multi($mode)`, default mode `SWOOLE_REDIS_MODE_MULTI`: + +* SWOOLE_REDIS_MODE_MULTI +* SWOOLE_REDIS_MODE_PIPELINE + +Digunakan untuk menentukan nilai kembali perintah `type()`: + +* SWOOLE_REDIS_TYPE_NOT_FOUND +* SWOOLE_REDIS_TYPE_STRING +* SWOOLE_REDIS_TYPE_SET +* SWOOLE_REDIS_TYPE_LIST +* SWOOLE_REDIS_TYPE_ZSET +* SWOOLE_REDIS_TYPE_HASH + +## Mode Transaksi + +Mode transaksi Redis dapat diimplementasikan menggunakan `multi` dan `exec`. + + * **Petunjuk** + + * Gunakan perintah `multi` untuk memulai transaksi, lalu semua perintah akan diantrekan untuk dieksekusi + * Gunakan perintah `exec` untuk mengeksekusi semua operasi dalam transaksi dan mengembalikan semua hasil sekaligus + + * **Contoh Penggunaan** + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $redis->multi(); + $redis->set('key3', 'rango'); + $redis->get('key1'); + $redis->get('key2'); + $redis->get('key3'); + + $result = $redis->exec(); + var_dump($result); +}); +``` + +## Mode Langganan + +!> Tersedia di Swoole versi >= v4.2.13, **versi 4.2.12 ke bawah ada BUG pada mode langganan** + +### Berlangganan + +Berbeda dengan `phpredis`, `subscribe/psubscribe` bergaya coroutine. + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // atau gunakan psubscribe + { + while ($msg = $redis->recv()) { + // msg adalah array, berisi informasi berikut + // $type # tipe nilai kembali: menunjukkan keberhasilan langganan + // $name # nama channel yang dilanggan atau nama channel sumber + // $info # jumlah channel yang sudah dilanggan atau konten informasi + list($type, $name, $info) = $msg; + if ($type == 'subscribe') { // atau psubscribe + // Pesan keberhasilan langganan channel, satu untuk setiap channel + } else if ($type == 'unsubscribe' && $info == 0){ // atau punsubscribe + break; // Menerima pesan berhenti berlangganan, dan sisa channel yang dilanggan adalah 0, berhenti menerima, keluar dari loop + } else if ($type == 'message') { // Jika psubscribe, ini adalah pmessage + var_dump($name); // Cetak nama channel sumber + var_dump($info); // Cetak pesan + // balabalaba.... // Proses pesan + if ($need_unsubscribe) { // Dalam kondisi tertentu perlu berhenti berlangganan + $redis->unsubscribe(); // Lanjutkan recv menunggu proses berhenti berlangganan selesai + } + } + } + } +}); +``` + +### Berhenti Berlangganan + +Berhenti berlangganan menggunakan `unsubscribe/punsubscribe`, `$redis->unsubscribe(['channel1'])` + +Pada saat ini, `$redis->recv()` akan menerima pesan berhenti berlangganan. Jika berhenti berlangganan beberapa channel, akan menerima beberapa pesan. + +!> Catatan: Setelah berhenti berlangganan, pastikan untuk melanjutkan `recv()` sampai menerima pesan berhenti berlangganan terakhir (`$msg[2] == 0`). Setelah menerima pesan ini, mode langganan akan keluar. + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + if ($redis->subscribe(['channel1', 'channel2', 'channel3'])) // or use psubscribe + { + while ($msg = $redis->recv()) { + // msg is an array containing the following information + // $type # return type: show subscription success + // $name # subscribed channel name or source channel name + // $info # the number of channels or information content currently subscribed + list($type, $name, $info) = $msg; + if ($type == 'subscribe') // or psubscribe + { + // channel subscription success message + } + else if ($type == 'unsubscribe' && $info == 0) // or punsubscribe + { + break; // received the unsubscribe message, and the number of channels remaining for the subscription is 0, no longer received, break the loop + } + else if ($type == 'message') // if it's psubscribe,here is pmessage + { + // print source channel name + var_dump($name); + // print message + var_dump($info); + // handle message + if ($need_unsubscribe) // in some cases, you need to unsubscribe + { + $redis->unsubscribe(); // continue recv to wait unsubscribe finished + } + } + } + } +}); +``` + +## Mode Kompatibilitas + +Masalah ketidaksesuaian format hasil yang dikembalikan oleh perintah `Co\Redis` `hmGet/hGetAll/zrange/zrevrange/zrangebyscore/zrevrangebyscore` dengan ekstensi `phpredis` telah diselesaikan [#2529](https://github.com/swoole/swoole-src/pull/2529). + +Untuk memastikan konsistensi hasil antara `Co\Redis` dan `phpredis` untuk kompatibilitas dengan versi lama, tambahkan konfigurasi `$redis->setOptions(['compatibility_mode' => true]);`, setelah itu hasil akan konsisten. + +!> Tersedia di Swoole versi >= `v4.4.0` + +```php +use Swoole\Coroutine\Redis; +use function Swoole\Coroutine\run; + +run(function () { + $redis = new Redis(); + $redis->setOptions(['compatibility_mode' => true]); + $redis->connect('127.0.0.1', 6379); + + $co_get_val = $redis->get('novalue'); + $co_zrank_val = $redis->zRank('novalue', 1); + $co_hgetall_val = $redis->hGetAll('hkey'); + $co_hmget_val = $redis->hmGet('hkey', array(3, 5)); + $co_zrange_val = $redis->zRange('zkey', 0, 99, true); + $co_zrevrange_val = $redis->zRevRange('zkey', 0, 99, true); + $co_zrangebyscore_val = $redis->zRangeByScore('zkey', 0, 99, ['withscores' => true]); + $co_zrevrangebyscore_val = $redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true]); +}); +``` diff --git a/public/id/coroutine_client/socket.md b/public/id/coroutine_client/socket.md new file mode 100644 index 0000000..a59b8ae --- /dev/null +++ b/public/id/coroutine_client/socket.md @@ -0,0 +1,1528 @@ +# Coroutine\Socket + +Modul `Swoole\Coroutine\Socket` dibandingkan dengan [server bergaya coroutine](/server/co_init) dan modul terkait [klien coroutine](/coroutine_client/init) `Socket` dapat mencapai operasi `IO` yang lebih terperinci. + +!> Bisa menggunakan nama pendek `Co\Socket` untuk menyederhanakan nama kelas. Modul ini cukup low-level, pengguna disarankan memiliki pengalaman pemrograman Socket. + +## Contoh Lengkap + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + $socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + + $retval = $socket->connect('127.0.0.1', 9601); + while ($retval) + { + $n = $socket->send('hello'); + var_dump($n); + + $data = $socket->recv(); + var_dump($data); + + //Jika terjadi error atau lawan menutup koneksi, lokal juga perlu ditutup + if ($data === '' || $data === false) { + echo "errCode: {$socket->errCode}\n"; + $socket->close(); + break; + } + + Coroutine::sleep(1.0); + } + + var_dump($retval, $socket->errCode, $socket->errMsg); +}); +``` + +## Penjadwalan Coroutine + +Antarmuka operasi `IO` yang disediakan modul `Coroutine\Socket` semuanya bergaya pemrograman sinkron, dan secara otomatis menggunakan penjadwal [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine) untuk mewujudkan [IO Asinkron](/learn?id=io-sinkron-dan-io-asinkron). + +## Kode Error + +Saat menjalankan panggilan sistem terkait `socket`, mungkin mengembalikan error -1, dan sistem akan mengatur properti `Coroutine\Socket->errCode` ke nomor error sistem `errno`, silakan lihat dokumentasi `man` yang sesuai. Misalnya, saat `$socket->accept()` mengembalikan error, arti `errCode` bisa ditemukan di dokumentasi kode error yang tercantum di `man accept`. + +## Properti + +### fd + +`ID` deskriptor file yang sesuai dengan `socket` + +### errCode + +Kode error + +## Method + +### __construct() + +Method konstruktor. Membangun objek `Coroutine\Socket`. + +```php +Swoole\Coroutine\Socket::__construct(int $domain, int $type, int $protocol); +``` + +!> Detailnya bisa dilihat di dokumentasi `man socket`. + + * **Parameter** + + * **`int $domain`** + * **Fungsi**: Domain protokol【Dapat menggunakan `AF_INET`, `AF_INET6`, `AF_UNIX`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $type`** + * **Fungsi**: Tipe【Dapat menggunakan `SOCK_STREAM`, `SOCK_DGRAM`, `SOCK_RAW`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $protocol`** + * **Fungsi**: Protokol【Dapat menggunakan `IPPROTO_TCP`, `IPPROTO_UDP`, `IPPROTO_STCP`, `IPPROTO_TIPC`, `0`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +!> Method konstruktor memanggil panggilan sistem `socket` untuk membuat handle `socket`. Jika panggilan gagal, akan melempar exception `Swoole\Coroutine\Socket\Exception`. Juga mengatur properti `$socket->errCode`. Penyebab kegagalan panggilan sistem dapat ditentukan berdasarkan nilai properti ini. + +### getOption() + +Mendapatkan konfigurasi. + +!> Method ini sesuai dengan panggilan sistem `getsockopt`, untuk detailnya silakan lihat dokumentasi `man getsockopt`. +Method ini setara dengan fungsi `socket_get_option` dari ekstensi `sockets`, dapat dilihat di [dokumentasi PHP](https://www.php.net/manual/zh/function.socket-get-option.php). + +!> Swoole versi >= v4.3.2 + +```php +Swoole\Coroutine\Socket->getOption(int $level, int $optname): mixed +``` + + * **Parameter** + + * **`int $level`** + * **Fungsi**: Menentukan tingkat protokol tempat opsi berada + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Misalnya, untuk mengambil opsi di tingkat socket, parameter `level` akan menggunakan `SOL_SOCKET`. + Tingkat lain dapat digunakan dengan menentukan nomor protokol untuk tingkat tersebut, seperti `TCP`. Nomor protokol dapat ditemukan menggunakan fungsi [getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php). + + * **`int $optname`** + * **Fungsi**: Opsi socket yang tersedia sama dengan fungsi [socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php) + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +### setOption() + +Mengatur konfigurasi. + +!> Method ini sesuai dengan panggilan sistem `setsockopt`, untuk detailnya silakan lihat dokumentasi `man setsockopt`. Method ini setara dengan fungsi `socket_set_option` dari ekstensi `sockets`, dapat dilihat di [dokumentasi PHP](https://www.php.net/manual/zh/function.socket-set-option.php). + +!> Swoole versi >= v4.3.2 + +```php +Swoole\Coroutine\Socket->setOption(int $level, int $optname, mixed $optval): bool +``` + + * **Parameter** + + * **`int $level`** + * **Fungsi**: Menentukan tingkat protokol tempat opsi berada + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + !> Misalnya, untuk mengambil opsi di tingkat socket, parameter `level` akan menggunakan `SOL_SOCKET`. + Tingkat lain dapat digunakan dengan menentukan nomor protokol untuk tingkat tersebut, seperti `TCP`. Anda dapat menggunakan fungsi [getprotobyname](https://www.php.net/manual/zh/function.getprotobyname.php) untuk menemukan nomor protokol. + + * **`int $optname`** + * **Fungsi**: Opsi socket yang tersedia sama dengan fungsi [socket_get_option()](https://www.php.net/manual/zh/function.socket-get-option.php) + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $optval`** + * **Fungsi**: Nilai opsi【Bisa `int`, `bool`, `string`, `array`. Tergantung pada `level` dan `optname`.】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + +### setProtocol() + +Memberi `socket` kemampuan menangani protokol, dapat mengkonfigurasi apakah akan mengaktifkan enkripsi `SSL` dan mengatasi [masalah batasan paket TCP](/learn?id=masalah-batasan-paket-tcp) dll. + +!> Swoole versi >= v4.3.2 + +```php +Swoole\Coroutine\Socket->setProtocol(array $settings): bool +``` + + * **Parameter yang didukung di `$settings`** + +Parameter | Tipe +---|--- +open_ssl | bool +ssl_cert_file | string +ssl_key_file | string +open_eof_check | bool +open_eof_split | bool +open_mqtt_protocol | bool +open_fastcgi_protocol | bool +open_length_check | bool +package_eof | string +package_length_type | string +package_length_offset | int +package_body_offset | int +package_length_func | callable +package_max_length | int + +!> Arti semua parameter di atas sepenuhnya konsisten dengan [Server->set()](/server/setting?id=open_eof_check), tidak akan diulangi di sini. + + * **Contoh** + +```php +$socket->setProtocol([ + 'open_length_check' => true, + 'package_max_length' => 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, +]); +``` + +### bind() + +Mengikat alamat dan port. + +!> Method ini tidak melibatkan operasi `IO`, tidak akan menyebabkan perpindahan coroutine + +```php +Swoole\Coroutine\Socket->bind(string $address, int $port = 0): bool +``` + + * **Parameter** + + * **`string $address`** + * **Fungsi**: Alamat yang akan diikat【mis. `0.0.0.0`, `127.0.0.1`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port yang akan diikat【Default `0`, sistem akan secara acak menetapkan port yang tersedia. Bisa menggunakan method [getsockname](/coroutine_client/socket?id=getsockname) untuk mendapatkan `port` yang ditetapkan sistem】 + * **Default**: `0` + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan `true` + * Mengembalikan `false`, periksa properti `errCode` untuk mendapatkan penyebab kegagalan + +### listen() + +Mendengarkan `Socket`. + +!> Method ini tidak melibatkan operasi `IO`, tidak akan menyebabkan perpindahan coroutine + +```php +Swoole\Coroutine\Socket->listen(int $backlog = 0): bool +``` + + * **Parameter** + + * **`int $backlog`** + * **Fungsi**: Panjang antrian pendengaran【Default `0`, sistem menggunakan `epoll` untuk mewujudkan `IO` asinkron, tidak ada blocking, sehingga kepentingan `backlog` tidak terlalu tinggi】 + * **Default**: `0` + * **Nilai lain**: Tidak ada + + !> Jika ada logika blocking atau memakan waktu dalam aplikasi, dan `accept` tidak menerima koneksi tepat waktu, koneksi baru akan menumpuk di antrian pendengaran `backlog`. Jika melebihi panjang `backlog`, layanan akan menolak koneksi baru. + + * **Return Value** + + * Mengembalikan `true` + * Mengembalikan `false`, periksa properti `errCode` untuk mendapatkan penyebab kegagalan + + * **Parameter Kernel** + + Nilai maksimum `backlog` dibatasi oleh parameter kernel `net.core.somaxconn`. Di `Linux`, alat `sysctl` dapat digunakan untuk menyesuaikan semua parameter `kernel` secara dinamis. Penyesuaian dinamis berarti nilai parameter kernel berlaku segera setelah dimodifikasi. Namun, efek ini hanya terbatas pada tingkat OS, aplikasi harus dimulai ulang agar benar-benar berlaku. Perintah `sysctl -a` akan menampilkan semua parameter kernel dan nilainya. + + ```shell + sysctl -w net.core.somaxconn=2048 + ``` + + Perintah di atas mengubah nilai parameter kernel `net.core.somaxconn` menjadi `2048`. Meskipun perubahan ini berlaku segera, akan kembali ke nilai default setelah reboot. Untuk menyimpan perubahan secara permanen, ubah `/etc/sysctl.conf`, tambahkan `net.core.somaxconn=2048`, lalu jalankan perintah `sysctl -p`. + +### accept() + +Menerima koneksi yang dimulai oleh klien. + +Memanggil method ini akan segera menangguhkan coroutine saat ini, bergabung ke [EventLoop](/learn?id=apa-itu-eventloop) untuk mendengarkan event readable. Saat koneksi masuk melalui `Socket`, coroutine akan secara otomatis dibangunkan, dan objek `Socket` yang sesuai dengan koneksi klien akan dikembalikan. + +!> Method ini harus digunakan setelah method `listen`, dan cocok untuk sisi `Server`. + +```php +Swoole\Coroutine\Socket->accept(float $timeout = 0): Coroutine\Socket|false; +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Mengatur timeout【Setelah mengatur parameter timeout, timer akan diatur di tingkat bawah. Jika tidak ada koneksi klien yang datang dalam waktu yang ditentukan, method `accept` akan mengembalikan `false`】 + * **Satuan nilai**: Detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan `false` saat timeout atau error dalam panggilan sistem `accept`. Bisa menggunakan properti `errCode` untuk mendapatkan kode error. Untuk error timeout, kode error adalah `ETIMEDOUT` + * Mengembalikan `socket` koneksi klien saat berhasil, juga bertipe `Swoole\Coroutine\Socket`. Operasi seperti `send`, `recv`, `close` dapat dilakukan padanya. + + * **Contoh** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { +$socket = new Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +$socket->bind('127.0.0.1', 9601); +$socket->listen(128); + + while(true) { + echo "Accept: \n"; + $client = $socket->accept(); + if ($client === false) { + var_dump($socket->errCode); + } else { + var_dump($client); + } + } +}); +``` + +### connect() + +Menghubungkan ke server target. + +Memanggil method ini akan memulai panggilan sistem `connect` asinkron, menangguhkan coroutine saat ini, dan mendengarkan keterbacaan di tingkat bawah. Saat koneksi berhasil atau gagal, coroutine akan dilanjutkan. + +Method ini cocok untuk sisi `Client`, mendukung `IPv4`, `IPv6`, dan [unixSocket](/learn?id=apa-itu-ipc). + +```php +Swoole\Coroutine\Socket->connect(string $host, int $port = 0, float $timeout = 0): bool +``` + + * **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat server target【mis. `127.0.0.1`, `192.168.1.100`, `/tmp/php-fpm.sock`, `www.baidu.com`, dll. Bisa memberikan alamat `IP`, path `Unix Socket`, atau nama domain. Jika nama domain, resolusi `DNS` asinkron akan dilakukan secara otomatis di tingkat bawah tanpa menyebabkan blocking】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port server target【Port harus diatur saat `domain` dari `Socket` adalah `AF_INET` atau `AF_INET6`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur periode timeout【Sistem di tingkat bawah akan mengatur timer, jika koneksi tidak dapat dibuat dalam waktu yang ditentukan, `connect` akan mengembalikan `false`】 + * **Satuan**: Detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Mengembalikan `false` saat timeout atau error dalam panggilan sistem `connect`, dan dapat menggunakan properti `errCode` untuk mendapatkan kode error, di mana kode error timeout adalah `ETIMEDOUT` + * Mengembalikan `true` saat berhasil + +### checkLiveness() + +Memeriksa ketersediaan koneksi melalui panggilan sistem (tidak valid saat terputus secara tidak normal, hanya dapat mendeteksi pemutusan koneksi lawan saat close normal). + +!> Tersedia di Swoole versi >= `v4.5.0` + +```php +Swoole\Coroutine\Socket->checkLiveness(): bool +``` + + * **Return Value** + + * Mengembalikan `true` saat koneksi aktif, sebaliknya `false` + +### send() + +Mengirim data ke lawan. + +!> Method `send` akan segera menjalankan panggilan sistem `send` untuk mengirim data. Saat panggilan sistem `send` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event writable, menangguhkan coroutine saat ini, menunggu event writable terpicu, lalu menjalankan ulang panggilan sistem `send` untuk mengirim data, dan membangunkan coroutine. + +!> Jika `send` terlalu cepat dan `recv` terlalu lambat, pada akhirnya buffer sistem operasi akan penuh. Coroutine saat ini akan ditangguhkan di method `send`, dan Anda dapat menyesuaikan buffer, [/proc/sys/net/core/wmem_max dan SO_SNDBUF](https://stackoverflow.com/questions/21856517/whats-the-practical-limit-on-the-size-of-single-packet-transmitted-over-domain) + +```php +Swoole\Coroutine\Socket->send(string $data, float $timeout = 0): int|false +``` + + * **Parameter** + + * **`string $data`** + * **Fungsi**: Konten data yang akan dikirim【bisa teks atau data biner】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil mengirim mengembalikan jumlah byte yang ditulis, **perhatikan bahwa data yang ditulis mungkin kurang dari panjang parameter `$data`**. Kode lapisan aplikasi perlu membandingkan nilai kembali dengan `strlen($data)` untuk menentukan apakah pengiriman selesai + * Gagal mengirim mengembalikan `false`, dan mengatur properti `errCode` + +### sendAll() + +Mengirim data ke lawan. Tidak seperti method `send`, `sendAll` akan berusaha mengirim data selengkap mungkin, mengirim semua data berhasil atau berhenti saat menemui error. + +!> Method `sendAll` akan segera menjalankan beberapa panggilan sistem `send` untuk mengirim data. Saat panggilan sistem `send` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event writable, menangguhkan coroutine saat ini, menunggu event writable terpicu, lalu menjalankan ulang panggilan sistem `send` untuk mengirim data hingga semua data terkirim atau terjadi error, membangunkan coroutine yang sesuai. + +!> Swoole versi >= v4.3.0 + +```php +Swoole\Coroutine\Socket->sendAll(string $data, float $timeout = 0) : int | false; +``` + + * **Parameter** + + * **`string $data`** + * **Fungsi**: Konten data yang akan dikirim【bisa teks atau data biner】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur nilai timeout + * **Satuan**: Detik (mendukung float, mis. `1.5` berarti `1s`+`500ms`) + * **Default**: Lihat [Aturan Timeout Klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * `sendAll` memastikan semua data terkirim berhasil. Namun, selama `sendAll`, lawan mungkin memutuskan koneksi. Pada saat itu, sebagian data mungkin telah berhasil terkirim. Nilai kembali akan menjadi panjang data yang berhasil terkirim. Kode lapisan aplikasi perlu membandingkan apakah nilai kembali sama dengan `strlen($data)` untuk menentukan apakah pengiriman selesai, dan berdasarkan kebutuhan bisnis, memutuskan apakah perlu melanjutkan pengiriman. + * Gagal mengirim mengembalikan `false`, dan mengatur properti `errCode` + +### peek() + +Mengintip data di buffer baca, setara dengan `recv(length, MSG_PEEK)` dalam panggilan sistem. + +!> `peek` selesai seketika, tidak menangguhkan coroutine, tetapi ada satu overhead panggilan sistem + +```php +Swoole\Coroutine\Socket->peek(int $length = 65535): string|false +``` + + * **Parameter** + + * **`int $length`** + * **Fungsi**: Menentukan ukuran memori untuk menyalin data yang diintip (perhatikan: memori akan dialokasikan di sini, panjang yang terlalu besar dapat menyebabkan kehabisan memori) + * **Satuan**: byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil mengintip mengembalikan data + * Gagal mengintip mengembalikan `false`, dan mengatur properti `errCode` + +### recv() + +Menerima data. + +!> Method `recv` akan segera menangguhkan coroutine saat ini dan mendengarkan event readable, menunggu lawan mengirim data, saat event readable terpicu, menjalankan panggilan sistem `recv` untuk mengambil data dari buffer socket, dan membangunkan coroutine. + +```php +Swoole\Coroutine\Socket->recv(int $length = 65535, float $timeout = 0): string|false +``` + + * **Parameter** + + * **`int $length`** + * **Fungsi**: Menentukan ukuran memori untuk menerima data (perhatikan: memori akan dialokasikan di sini, panjang yang terlalu besar dapat menyebabkan kehabisan memori) + * **Satuan**: byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil menerima mengembalikan data aktual + * Gagal menerima mengembalikan `false`, dan mengatur properti `errCode` + * Timeout penerimaan, kode error adalah `ETIMEDOUT` + +!> Nilai kembali belum tentu sama dengan panjang yang diharapkan, perlu memeriksa sendiri panjang data yang diterima pada pemanggilan ini. Jika perlu memastikan mendapatkan data dengan panjang tertentu dalam satu pemanggilan, gunakan method `recvAll` atau lakukan perulangan sendiri. +Untuk masalah batasan paket TCP, lihat method `setProtocol()`, atau gunakan `sendto()`. + +### recvAll() + +Menerima data. Berbeda dengan `recv`, `recvAll` akan berusaha menerima data dengan panjang respons selengkap mungkin, sampai penerimaan selesai atau terjadi error. + +!> Method `recvAll` akan segera menangguhkan coroutine saat ini dan mendengarkan event readable. Saat data dikirim dari lawan, saat event readable terpicu, akan menjalankan panggilan sistem `recv` untuk mengambil data dari buffer `socket`, mengulangi perilaku ini sampai menerima data dengan panjang yang ditentukan atau terjadi error, lalu membangunkan coroutine. + +!> Swoole versi >= v4.3.0 + +```php +Swoole\Coroutine\Socket->recvAll(int $length = 65535, float $timeout = 0): string|false +``` + + * **Parameter** + + * **`int $length`** + * **Fungsi**: Ukuran data yang diharapkan akan diterima (perhatikan: memori dialokasikan di sini, panjang yang berlebihan dapat menyebabkan kehabisan memori) + * **Satuan**: byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Jika berhasil diterima, mengembalikan data aktual, dan panjang string yang dikembalikan sesuai dengan panjang parameter + * Jika gagal diterima, mengembalikan `false` dan mengatur properti `errCode` + * Jika timeout, kode error adalah `ETIMEDOUT` + +### readVector() + +Menerima data dalam segmen. + +!> Method `readVector` akan segera menjalankan panggilan sistem `readv` untuk membaca data. Saat panggilan sistem `readv` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event readable, menangguhkan coroutine saat ini, menunggu event readable terpicu, lalu menjalankan ulang panggilan sistem `readv` untuk membaca data, dan membangunkan coroutine. + +!> Swoole versi >= v4.5.7 + +```php +Swoole\Coroutine\Socket->readVector(array $io_vector, float $timeout = 0): array|false +``` + + * **Parameter** + + * **`array $io_vector`** + * **Fungsi**: Ukuran data segmen yang diharapkan akan diterima + * **Satuan**: Byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur waktu timeout + * **Satuan**: Detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Data segmen berhasil diterima + * Mengembalikan array kosong saat gagal menerima, dan mengatur properti `errCode` + * Timeout, kode error adalah `ETIMEDOUT` + + * **Contoh** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// Jika lawan mengirim helloworld +$ret = $socket->readVector([5, 5]); +// Maka, $ret adalah ['hello', 'world'] +``` + +### readVectorAll() + +Menerima data dalam segmen. + +!> Method `readVectorAll` akan segera menjalankan beberapa panggilan sistem `readv` untuk membaca data. Saat panggilan sistem `readv` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event readable, menangguhkan coroutine saat ini, menunggu event readable terpicu, lalu menjalankan ulang panggilan sistem `readv` untuk membaca data sampai data selesai dibaca atau terjadi error, membangunkan coroutine yang sesuai. + +!> Swoole versi >= v4.5.7 + +```php +Swoole\Coroutine\Socket->readVectorAll(array $io_vector, float $timeout = 0): array|false +``` + + * **Parameter** + + * **`array $io_vector`** + * **Fungsi**: Ukuran data segmen yang diharapkan akan diterima + * **Satuan**: Byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur timeout + * **Satuan**: Detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Jika berhasil, mengembalikan data segmen + * Jika gagal menerima, mengembalikan array kosong dan mengatur properti `errCode` + * Jika timeout terjadi, kode error adalah `ETIMEDOUT` + +### writeVector() + +Mengirim data dalam segmen. + +!> Method `writeVector` akan segera menjalankan panggilan sistem `writev` untuk mengirim data. Saat panggilan sistem `writev` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event writable, menangguhkan coroutine saat ini, menunggu event writable terpicu, lalu menjalankan ulang panggilan sistem `writev` untuk mengirim data, dan membangunkan coroutine. + +!> Swoole versi >= v4.5.7 + +```php +Swoole\Coroutine\Socket->writeVector(array $io_vector, float $timeout = 0): int|false +``` + + * **Parameter** + + * **`array $io_vector`** + * **Fungsi**: Data segmen yang akan dikirim + * **Satuan**: byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur timeout + * **Satuan**: detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Jika pengiriman berhasil, mengembalikan jumlah byte yang ditulis. **Perhatikan bahwa data yang ditulis mungkin kurang dari total panjang parameter `$io_vector`**. Kode lapisan aplikasi perlu membandingkan apakah nilai kembali sama dengan total panjang parameter `$io_vector` untuk menentukan apakah pengiriman selesai. + * Jika pengiriman gagal, mengembalikan `false` dan mengatur properti `errCode`. + + * **Contoh** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// Ini akan mengirim 'helloworld' ke lawan sesuai urutan dalam array +$socket->writeVector(['hello', 'world']); +``` + +### writeVectorAll() + +Mengirim data ke lawan. Berbeda dengan method `writeVector`, `writeVectorAll` akan berusaha mengirim data selengkap mungkin, baik berhasil mengirim semua data atau berhenti saat menemui error. + +!> Method `writeVectorAll` akan segera menjalankan beberapa panggilan sistem `writev` untuk mengirim data. Saat panggilan sistem `writev` mengembalikan error `EAGAIN`, sistem akan secara otomatis mendengarkan event writable, menangguhkan coroutine saat ini, menunggu event writable terpicu, lalu menjalankan ulang panggilan sistem `writev` untuk mengirim data sampai semua data terkirim atau terjadi error, membangunkan coroutine yang sesuai. + +!> Swoole versi >= v4.5.7 + +```php +Swoole\Coroutine\Socket->writeVectorAll(array $io_vector, float $timeout = 0): int|false +``` + + * **Parameter** + + * **`array $io_vector`** + * **Fungsi**: Segmen data yang akan dikirim + * **Satuan**: Byte + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur timeout + * **Satuan**: Detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [Aturan Timeout Klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * `writeVectorAll` memastikan semua data terkirim berhasil. Namun, koneksi mungkin ditutup oleh lawan selama `writeVectorAll`. Dalam kasus ini, sebagian data mungkin telah berhasil terkirim. Nilai kembali akan menunjukkan panjang data yang berhasil ini. Kode lapisan aplikasi perlu membandingkan nilai kembali ini dengan total panjang parameter `$io_vector` untuk menentukan apakah pengiriman selesai, dan memutuskan apakah perlu melanjutkan berdasarkan kebutuhan bisnis. + * Mengembalikan `false` untuk kegagalan pengiriman, dan mengatur properti `errCode`. + + * **Contoh** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); +// Ini akan mengirim 'helloworld' ke lawan sesuai urutan dalam array +$socket->writeVectorAll(['hello', 'world']); +``` + +### recvPacket() + +Untuk objek Socket yang telah mengatur protokol melalui method `setProtocol`, Anda dapat memanggil method ini untuk menerima paket data protokol yang lengkap. + +!> Swoole versi >= v4.4.0 + +```php +Swoole\Coroutine\Socket->recvPacket(float $timeout = 0): string|false +``` + + * **Parameter** + * **`float $timeout`** + * **Fungsi**: mengatur waktu timeout + * **Satuan**: detik【mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil menerima mengembalikan paket data protokol lengkap + * Gagal menerima mengembalikan `false`, dan mengatur properti `errCode` + * Timeout penerimaan, kode error adalah `ETIMEDOUT` + +### recvLine() + +Digunakan untuk mengatasi masalah kompatibilitas [socket_read](https://www.php.net/manual/en/function.socket-read.php) + +```php +Swoole\Coroutine\Socket->recvLine(int $length = 65535, float $timeout = 0): string|false +``` + +### recvWithBuffer() + +Digunakan untuk mengatasi masalah pembuatan banyak panggilan sistem saat menerima byte demi byte dengan `recv(1)` + +```php +Swoole\Coroutine\Socket->recvWithBuffer(int $length = 65535, float $timeout = 0): string|false +``` + +### recvfrom() + +Menerima data, dan mengatur alamat dan port host sumber. Digunakan untuk `socket` tipe `SOCK_DGRAM`. + +!> Method ini akan menyebabkan [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine), sistem akan segera menangguhkan coroutine saat ini dan mendengarkan event readable. Saat event readable terpicu, data diterima dan panggilan sistem `recvfrom` dijalankan untuk mendapatkan paket data. + +```php +Swoole\Coroutine\Socket->recvfrom(array &$peer, float $timeout = 0): string|false +``` + +* **Parameter** + + * **`array $peer`** + * **Fungsi**: Alamat dan port lawan, tipe referensi.【Diatur menjadi array yang berisi `address` dan `port` saat fungsi berhasil kembali】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`float $timeout`** + * **Fungsi**: Mengatur timeout.【Jika tidak ada data yang kembali dalam waktu yang ditentukan, method `recvfrom` akan mengembalikan `false`】 + * **Satuan**: Detik【Mendukung float, mis. `1.5` berarti `1s`+`500ms`】 + * **Default**: Lihat [aturan timeout klien](/coroutine_client/init?id=aturan-timeout) + * **Nilai lain**: Tidak ada + +* **Return Value** + + * Jika data berhasil diterima, mengembalikan konten data dan mengatur `$peer` sebagai array + * Jika gagal, mengembalikan `false`, mengatur properti `errCode`, dan tidak mengubah konten `$peer` + +* **Contoh** + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + $socket = new Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $socket->bind('127.0.0.1', 9601); + while (true) { + $peer = null; + $data = $socket->recvfrom($peer); + echo "[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\n"; + $socket->sendto($peer['address'], $peer['port'], "Swoole: $data"); + } +}); +``` + +### sendto() + +Mengirim data ke alamat dan port yang ditentukan. Digunakan untuk `socket` tipe `SOCK_DGRAM`. + +!> Method ini tidak memiliki [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine), sistem akan segera memanggil `sendto` untuk mengirim data ke host target. Method ini tidak mendengarkan ketersediaan tulis. `sendto` mungkin mengembalikan `false` karena buffer penuh, jadi perlu ditangani sendiri atau gunakan method `send`. + +```php +Swoole\Coroutine\Socket->sendto(string $address, int $port, string $data): int|false +``` + + * **Parameter** + + * **`string $address`** + * **Fungsi**: Alamat `IP` host target atau path [unixSocket](/learn?id=apa-itu-ipc)【`sendto` tidak mendukung nama domain. Saat menggunakan `AF_INET` atau `AF_INET6`, alamat IP yang valid harus diberikan, jika tidak, pengiriman akan gagal】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`int $port`** + * **Fungsi**: Port host target【Atur ke `0` saat mengirim siaran】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **`string $data`** + * **Fungsi**: Data yang akan dikirim【Bisa teks atau konten biner. Perhatikan bahwa panjang maksimum paket yang dikirim untuk `SOCK_DGRAM` adalah `64K`】 + * **Default**: Tidak ada + * **Nilai lain**: Tidak ada + + * **Return Value** + + * Berhasil mengirim mengembalikan jumlah byte yang dikirim + * Gagal mengirim mengembalikan `false`, dan mengatur properti `errCode` + + * **Contoh** + +```php +$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); +$socket->sendto('127.0.0.1', 9601, 'Hello'); +``` + +### getsockname() + +Mendapatkan informasi alamat dan port socket. + +!> Method ini tidak memiliki overhead [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\Socket->getsockname(): array|false +``` + + * **Return Value** + + * Mengembalikan array yang berisi `address` dan `port` + * Mengembalikan `false`, dan mengatur properti `errCode` + +### getpeername() + +Mendapatkan informasi alamat dan port lawan dari `socket`, hanya untuk `socket` tipe `SOCK_STREAM` yang memiliki koneksi. + +?> Method ini tidak memiliki overhead [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\Socket->getpeername(): array|false +``` + + * **Return Value** + + * Mengembalikan array yang berisi `address` dan `port` + * Mengembalikan `false`, dengan properti `errCode` diatur + +### close() + +Menutup `Socket`. + +!> Saat objek `Swoole\Coroutine\Socket` di-destruksi, method `close` akan otomatis dijalankan tanpa overhead [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). + +```php +Swoole\Coroutine\Socket->close(): bool +``` + + * **Return Value** + + * Berhasil menutup mengembalikan `true` + * Mengembalikan `false` + +### isClosed() + +Apakah `Socket` sudah ditutup. + +```php +Swoole\Coroutine\Socket->isClosed(): bool +``` + +## Konstanta + +Setara dengan konstanta yang disediakan oleh ekstensi `sockets`, dan tidak akan bertentangan dengan ekstensi `sockets`. + +!> Nilai mungkin berbeda di sistem yang berbeda. Kode berikut hanya untuk ilustrasi, jangan gunakan nilai-nilai ini. + +```php +define ('AF_UNIX', 1); +define ('AF_INET', 2); + +/** + * Only available if compiled with IPv6 support. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('AF_INET6', 10); +define ('SOCK_STREAM', 1); +define ('SOCK_DGRAM', 2); +define ('SOCK_RAW', 3); +define ('SOCK_SEQPACKET', 5); +define ('SOCK_RDM', 4); +define ('MSG_OOB', 1); +define ('MSG_WAITALL', 256); +define ('MSG_CTRUNC', 8); +define ('MSG_TRUNC', 32); +define ('MSG_PEEK', 2); +define ('MSG_DONTROUTE', 4); + +/** + * Not available on Windows platforms. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('MSG_EOR', 128); + +/** + * Not available on Windows platforms. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('MSG_EOF', 512); +define ('MSG_CONFIRM', 2048); +define ('MSG_ERRQUEUE', 8192); +define ('MSG_NOSIGNAL', 16384); +define ('MSG_DONTWAIT', 64); +define ('MSG_MORE', 32768); +define ('MSG_WAITFORONE', 65536); +define ('MSG_CMSG_CLOEXEC', 1073741824); +define ('SO_DEBUG', 1); +define ('SO_REUSEADDR', 2); + +/** + * This constant is only available in PHP 5.4.10 or later on platforms that + * support the SO_REUSEPORT socket option: this + * includes Mac OS X and FreeBSD, but does not include Linux or Windows. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SO_REUSEPORT', 15); +define ('SO_KEEPALIVE', 9); +define ('SO_DONTROUTE', 5); +define ('SO_LINGER', 13); +define ('SO_BROADCAST', 6); +define ('SO_OOBINLINE', 10); +define ('SO_SNDBUF', 7); +define ('SO_RCVBUF', 8); +define ('SO_SNDLOWAT', 19); +define ('SO_RCVLOWAT', 18); +define ('SO_SNDTIMEO', 21); +define ('SO_RCVTIMEO', 20); +define ('SO_TYPE', 3); +define ('SO_ERROR', 4); +define ('SO_BINDTODEVICE', 25); +define ('SOL_SOCKET', 1); +define ('SOMAXCONN', 128); + +/** + * Used to disable Nagle TCP algorithm. + * Added in PHP 5.2.7. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('TCP_NODELAY', 1); +define ('PHP_NORMAL_READ', 1); +define ('PHP_BINARY_READ', 2); +define ('MCAST_JOIN_GROUP', 42); +define ('MCAST_LEAVE_GROUP', 45); +define ('MCAST_BLOCK_SOURCE', 43); +define ('MCAST_UNBLOCK_SOURCE', 44); +define ('MCAST_JOIN_SOURCE_GROUP', 46); +define ('MCAST_LEAVE_SOURCE_GROUP', 47); +define ('IP_MULTICAST_IF', 32); +define ('IP_MULTICAST_TTL', 33); +define ('IP_MULTICAST_LOOP', 34); +define ('IPV6_MULTICAST_IF', 17); +define ('IPV6_MULTICAST_HOPS', 18); +define ('IPV6_MULTICAST_LOOP', 19); +define ('IPV6_V6ONLY', 27); + +/** + * Operation not permitted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPERM', 1); + +/** + * No such file or directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOENT', 2); + +/** + * Interrupted system call. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINTR', 4); + +/** + * I/O error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EIO', 5); + +/** + * No such device or address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENXIO', 6); + +/** + * Arg list too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_E2BIG', 7); + +/** + * Bad file number. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADF', 9); + +/** + * Try again. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EAGAIN', 11); + +/** + * Out of memory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMEM', 12); + +/** + * Permission denied. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EACCES', 13); + +/** + * Bad address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EFAULT', 14); + +/** + * Block device required. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTBLK', 15); + +/** + * Device or resource busy. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBUSY', 16); + +/** + * File exists. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EEXIST', 17); + +/** + * Cross-device link. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EXDEV', 18); + +/** + * No such device. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENODEV', 19); + +/** + * Not a directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTDIR', 20); + +/** + * Is a directory. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISDIR', 21); + +/** + * Invalid argument. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINVAL', 22); + +/** + * File table overflow. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENFILE', 23); + +/** + * Too many open files. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMFILE', 24); + +/** + * Not a typewriter. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTTY', 25); + +/** + * No space left on device. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSPC', 28); + +/** + * Illegal seek. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESPIPE', 29); + +/** + * Read-only file system. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EROFS', 30); + +/** + * Too many links. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMLINK', 31); + +/** + * Broken pipe. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPIPE', 32); + +/** + * File name too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENAMETOOLONG', 36); + +/** + * No record locks available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOLCK', 37); + +/** + * Function not implemented. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSYS', 38); + +/** + * Directory not empty. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTEMPTY', 39); + +/** + * Too many symbolic links encountered. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ELOOP', 40); + +/** + * Operation would block. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EWOULDBLOCK', 11); + +/** + * No message of desired type. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMSG', 42); + +/** + * Identifier removed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EIDRM', 43); + +/** + * Channel number out of range. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECHRNG', 44); + +/** + * Level 2 not synchronized. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL2NSYNC', 45); + +/** + * Level 3 halted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL3HLT', 46); + +/** + * Level 3 reset. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL3RST', 47); + +/** + * Link number out of range. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ELNRNG', 48); + +/** + * Protocol driver not attached. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EUNATCH', 49); + +/** + * No CSI structure available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOCSI', 50); + +/** + * Level 2 halted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EL2HLT', 51); + +/** + * Invalid exchange. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADE', 52); + +/** + * Invalid request descriptor. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADR', 53); + +/** + * Exchange full. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EXFULL', 54); + +/** + * No anode. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOANO', 55); + +/** + * Invalid request code. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADRQC', 56); + +/** + * Invalid slot. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADSLT', 57); + +/** + * Device not a stream. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSTR', 60); + +/** + * No data available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENODATA', 61); + +/** + * Timer expired. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETIME', 62); + +/** + * Out of streams resources. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOSR', 63); + +/** + * Machine is not on the network. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENONET', 64); + +/** + * Object is remote. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMOTE', 66); + +/** + * Link has been severed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOLINK', 67); + +/** + * Advertise error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EADV', 68); + +/** + * Srmount error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESRMNT', 69); + +/** + * Communication error on send. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECOMM', 70); + +/** + * Protocol error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTO', 71); + +/** + * Multihop attempted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMULTIHOP', 72); + +/** + * Not a data message. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADMSG', 74); + +/** + * Name not unique on network. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTUNIQ', 76); + +/** + * File descriptor in bad state. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EBADFD', 77); + +/** + * Remote address changed. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMCHG', 78); + +/** + * Interrupted system call should be restarted. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ERESTART', 85); + +/** + * Streams pipe error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESTRPIPE', 86); + +/** + * Too many users. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EUSERS', 87); + +/** + * Socket operation on non-socket. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTSOCK', 88); + +/** + * Destination address required. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EDESTADDRREQ', 89); + +/** + * Message too long. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMSGSIZE', 90); + +/** + * Protocol wrong type for socket. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTOTYPE', 91); +define ('SOCKET_ENOPROTOOPT', 92); + +/** + * Protocol not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPROTONOSUPPORT', 93); + +/** + * Socket type not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESOCKTNOSUPPORT', 94); + +/** + * Operation not supported on transport endpoint. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EOPNOTSUPP', 95); + +/** + * Protocol family not supported. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EPFNOSUPPORT', 96); + +/** + * Address family not supported by protocol. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EAFNOSUPPORT', 97); +define ('SOCKET_EADDRINUSE', 98); + +/** + * Cannot assign requested address. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EADDRNOTAVAIL', 99); + +/** + * Network is down. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETDOWN', 100); + +/** + * Network is unreachable. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETUNREACH', 101); + +/** + * Network dropped connection because of reset. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENETRESET', 102); + +/** + * Software caused connection abort. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNABORTED', 103); + +/** + * Connection reset by peer. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNRESET', 104); + +/** + * No buffer space available. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOBUFS', 105); + +/** + * Transport endpoint is already connected. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISCONN', 106); + +/** + * Transport endpoint is not connected. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOTCONN', 107); + +/** + * Cannot send after transport endpoint shutdown. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ESHUTDOWN', 108); + +/** + * Too many references: cannot splice. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETOOMANYREFS', 109); + +/** + * Connection timed out. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ETIMEDOUT', 110); + +/** + * Connection refused. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ECONNREFUSED', 111); + +/** + * Host is down. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EHOSTDOWN', 112); + +/** + * No route to host. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EHOSTUNREACH', 113); + +/** + * Operation already in progress. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EALREADY', 114); + +/** + * Operation now in progress. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EINPROGRESS', 115); + +/** + * Is a named type file. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EISNAM', 120); + +/** + * Remote I/O error. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EREMOTEIO', 121); + +/** + * Quota exceeded. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EDQUOT', 122); + +/** + * No medium found. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_ENOMEDIUM', 123); + +/** + * Wrong medium type. + * @link http://php.net/manual/en/sockets.constants.php + */ +define ('SOCKET_EMEDIUMTYPE', 124); +define ('IPPROTO_IP', 0); +define ('IPPROTO_IPV6', 41); +define ('SOL_TCP', 6); +define ('SOL_UDP', 17); +define ('IPV6_UNICAST_HOPS', 16); +define ('IPV6_RECVPKTINFO', 49); +define ('IPV6_PKTINFO', 50); +define ('IPV6_RECVHOPLIMIT', 51); +define ('IPV6_HOPLIMIT', 52); +define ('IPV6_RECVTCLASS', 66); +define ('IPV6_TCLASS', 67); +define ('SCM_RIGHTS', 1); +define ('SCM_CREDENTIALS', 2); +define ('SO_PASSCRED', 16); +``` diff --git a/public/id/environment.md b/public/id/environment.md new file mode 100644 index 0000000..e5146f8 --- /dev/null +++ b/public/id/environment.md @@ -0,0 +1,326 @@ +# Memasang Swoole + +Ekstensi `Swoole` dibuat mengikuti standar ekstensi `PHP` pada umumnya. Gunakan `phpize` untuk menghasilkan skrip deteksi kompilasi, `./configure` untuk deteksi konfigurasi kompilasi, `make` untuk kompilasi, dan `make install` untuk instalasi. + +* Kalau gak ada kebutuhan khusus, pastikan kompilasi dan pasang `Swoole` versi terbaru dari [Swoole](https://github.com/swoole/swoole-src/releases/). +* Kalau user saat ini bukan `root`, mungkin gak punya izin nulis di direktori instalasi `PHP`, jadi perlu pake `sudo` atau `su` saat instalasi. +* Kalau update kode langsung di branch `git`, sebelum kompilasi ulang wajib jalanin `make clean`. +* Cuma mendukung tiga sistem operasi: `Linux` (kernel 2.3.32 ke atas), `FreeBSD`, dan `MacOS`. +* Sistem Linux versi lawas (kayak `CentOS 6`) bisa pake `devtools` dari `RedHat` untuk kompilasi, [dokumentasi referensi](https://blog.csdn.net/ppdouble/article/details/52894271). +* Di platform `Windows`, bisa pake `WSL (Windows Subsystem for Linux)` atau `CygWin`. +* Beberapa ekstensi gak kompatibel sama ekstensi `Swoole`, lihat [konflik ekstensi](/getting_started/extension). + +## Persiapan Instalasi + +Sebelum instalasi, pastikan sistem sudah punya software berikut: + +- Versi `4.8` butuh `PHP-7.2` atau lebih tinggi +- Versi `5.0` butuh `PHP-8.0` atau lebih tinggi +- Versi `6.0` butuh `PHP-8.1` atau lebih tinggi +- `gcc-4.8` atau lebih tinggi +- `make` +- `autoconf` + +## Instalasi Cepat + +> 1. Unduh kode sumber swoole + +* [https://github.com/swoole/swoole-src/releases](https://github.com/swoole/swoole-src/releases) +* [https://pecl.php.net/package/swoole](https://pecl.php.net/package/swoole) +* [https://gitee.com/swoole/swoole/tags](https://gitee.com/swoole/swoole/tags) + +> 2. Kompilasi dan instal dari kode sumber + +Setelah unduh paket kode sumber, masuk ke direktori kode sumber di terminal, lalu jalankan perintah berikut untuk kompilasi dan instalasi: + +!> ubuntu gak ada phpize bisa jalanin: `sudo apt-get install php-dev` buat instal phpize + +```shell +cd swoole-src && \ +phpize && \ +./configure && \ +sudo make && sudo make install +``` + +> 3. Aktifkan ekstensi + +Setelah kompilasi dan instalasi berhasil, tambahin `extension=swoole.so` di `php.ini` buat mengaktifkan ekstensi Swoole + +## Contoh Kompilasi Lengkap Lanjutan + +!> Developer yang baru pertama kali kenal Swoole, coba dulu kompilasi simpel di atas. Kalau ada kebutuhan lebih lanjut, sesuaikan parameter kompilasi di contoh bawah sesuai kebutuhan dan versi. [Referensi parameter kompilasi](/environment?id=编译选项) + +Skrip di bawah bakal unduh dan kompilasi kode sumber dari branch `master`. Pastikan semua dependensi udah terinstall, kalau gak bakal muncul berbagai error dependensi. + +```shell +mkdir -p ~/build && \ +cd ~/build && \ +rm -rf ./swoole-src && \ +curl -o ./tmp/swoole.tar.gz https://github.com/swoole/swoole-src/archive/master.tar.gz -L && \ +tar zxvf ./tmp/swoole.tar.gz && \ +mv swoole-src* swoole-src && \ +cd swoole-src && \ +phpize && \ +./configure \ +--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql && \ +sudo make && sudo make install +``` + +## PECL + +> Catatan: Rilis PECL lebih lambat dari rilis GitHub + +Proyek Swoole udah masuk ke library ekstensi resmi PHP. Selain unduh dan kompilasi manual, bisa juga pake perintah `pecl` dari PHP untuk unduh dan instal sekali jalan: + +```shell +pecl install swoole +``` + +Saat instal Swoole lewat PECL, proses instalasi bakal nanya apakah mau aktifin fitur tertentu. Ini juga bisa dikasih sebelum jalanin instalasi, contohnya: + +```shell +pecl install -D 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole + +#atau +pecl install --configureoptions 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="yes" enable-swoole-json="no" enable-swoole-curl="yes" enable-cares="yes"' swoole +``` + +## PIE + +Proyek Swoole juga mendukung instalasi lewat PIE, tools instalasi ekstensi PHP yang baru. + +```shell +pie install swoole/swoole:5.1.5 +``` + +Saat instal Swoole lewat PIE, proses instalasi bakal nanya apakah mau aktifin fitur tertentu. Ini juga bisa dikasih sebelum jalanin instalasi, contohnya: + +```shell +pie install swoole/swoole:5.1.5 --enable-socket --enable-swoole-curl +``` + +## Tambahin Swoole ke php.ini + +Terakhir, setelah kompilasi dan instalasi berhasil, ubah `php.ini` dengan nambahin: + +```ini +extension=swoole.so +``` + +Cek apakah `swoole.so` udah ke-load dengan `php -m`. Kalau belum, mungkin path `php.ini`-nya salah. +Bisa pake `php --ini` buat cari path absolut `php.ini`. Bagian `Loaded Configuration File` nunjukin file php.ini yang di-load. Kalau nilainya `none`, berarti gak ada file `php.ini` yang di-load, dan harus bikin sendiri. + +!> Dukungan versi `PHP` ngikutin jadwal maintain resmi dari PHP, lihat [Jadwal Dukungan Versi PHP](http://php.net/supported-versions.php) + +## Kompilasi di Platform Lain + +Platform ARM (Raspberry Pi) + +* Pake kompilasi silang `GCC` +* Saat kompilasi `Swoole`, perlu ubah manual `Makefile` dengan hapus parameter kompilasi `-O2` + +Platform MIPS (Router OpenWrt) + +* Pake kompilasi silang GCC + +Windows WSL + +`Windows 10` punya dukungan subsistem `Linux`, jadi `Swoole` bisa dipake di lingkungan `BashOnWindows`. Perintah instalasi: + +```shell +apt-get install php7.0 php7.0-curl php7.0-gd php7.0-gmp php7.0-json php7.0-mysql php7.0-opcache php7.0-readline php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-bcmath php7.0-bz2 php7.0-intl php7.0-mbstring php7.0-mcrypt php7.0-soap php7.0-xsl php7.0-zip +pecl install swoole +echo 'extension=swoole.so' >> /etc/php/7.0/mods-available/swoole.ini +cd /etc/php/7.0/cli/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +cd /etc/php/7.0/fpm/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +``` + +!> Di lingkungan `WSL`, opsi `daemonize` harus dimatiin +Untuk `WSL` versi di bawah `17101`, setelah `configure` perlu ubah `config.h` buat matiin `HAVE_SIGNALFD` + +## Docker Image Resmi + +- GitHub: [https://github.com/swoole/docker-swoole](https://github.com/swoole/docker-swoole) +- dockerhub: [https://hub.docker.com/r/phpswoole/swoole](https://hub.docker.com/r/phpswoole/swoole) + +## Opsi Kompilasi + +Ini parameter tambahan buat konfigurasi kompilasi `./configure`, dipake buat ngaktifin fitur tertentu. + +### Parameter Umum + +#### --enable-openssl + +Mengaktifkan dukungan `SSL`, parameter ini bakal dihapus setelah versi `6.2` dan jadi wajib, `SSL/TLS` bakal selalu tersedia. + +> Pake dynamic library `libssl.so` dari sistem operasi + +#### --with-openssl-dir + +Mengaktifkan dukungan `SSL` dan specify path library `openssl`, perlu dikasih parameter path, contoh: `--with-openssl-dir=/opt/openssl/`. +Parameter ini masih berlaku setelah versi `6.2`, tapi cuma buat ngubah path default library `openssl`. Kalau gak diset, bakal pake library `openssl` bawaan sistem. + +#### --enable-http2 + +Mengaktifkan dukungan `HTTP2` + +> Tergantung library `nghttp2`. Setelah versi `V4.3.0` gak perlu install dependensi lagi, udah built-in. Tapi tetep perlu tambahin parameter kompilasi ini buat aktifin dukungan `http2`. `Swoole5` udah aktifin parameter ini secara default. + +#### --enable-swoole-json + +Mengaktifkan dukungan buat [swoole_substr_json_decode](/functions?id=swoole_substr_json_decode), mulai `Swoole5` parameter ini udah aktif secara default. + +> Tergantung ekstensi `json`, tersedia sejak versi `v4.5.7` + +#### --enable-swoole-curl + +Mengaktifkan dukungan buat [SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl). Pastiin `php` dan `Swoole` pake shared library dan header `libcurl` yang sama, kalo gak bakal muncul masalah yang gak terduga. + +> Tersedia sejak versi `v4.6.0`. Kalau kompilasi error `curl/curl.h: No such file or directory`, lihat [masalah instalasi](/question/install?id=libcurl) + +#### --enable-cares + +Mengaktifkan dukungan `c-ares` + +> Tergantung library `c-ares`, tersedia sejak versi `v4.7.0`. Kalau kompilasi error `ares.h: No such file or directory`, lihat [masalah instalasi](/question/install?id=libcares) + +#### --with-jemalloc-dir + +Mengaktifkan dukungan `jemalloc` + +#### --enable-brotli + +Mengaktifkan dukungan kompresi `libbrotli` + +#### --with-brotli-dir + +Mengaktifkan dukungan kompresi `libbrotli` dan specify path library `libbrotli`, perlu dikasih parameter path, contoh: `--with-brotli-dir=/opt/brotli/` + +#### --enable-swoole-pgsql + +Mengaktifkan coroutine database `PostgreSQL`. + +> Sebelum `Swoole5.0` pake coroutine client buat coroutine `PostgreSQL`. Setelah `Swoole5.1`, selain pake coroutine client, bisa juga pake native `pdo_pgsql` buat coroutine `PostgreSQL`. + +#### --with-swoole-odbc + +Mengaktifkan coroutine buat `pdo_odbc`. Setelah parameter ini diaktifin, semua database yang mendukung interface `odbc` bisa pake coroutine. + +> Tersedia sejak versi `v5.1.0`, butuh library `unixodbc-dev` + +Contoh konfigurasi: + +``` +with-swoole-odbc="unixODBC,/usr" +``` + +#### --with-swoole-oracle + +Mengaktifkan coroutine buat `pdo_oci`. Setelah parameter ini diaktifin, semua operasi CRUD di database `oracle` bakal nge-trigger coroutine. + +> Tersedia sejak versi `v5.1.0` + +#### --enable-swoole-sqlite + +Mengaktifkan coroutine buat `pdo_sqlite`. Setelah parameter ini diaktifin, semua operasi CRUD di database `sqlite` bakal nge-trigger coroutine. + +> Tersedia sejak versi `v5.1.0` + +#### --enable-swoole-thread + +Mengaktifkan mode multi-thread `swoole`. Dengan opsi kompilasi ini, `Swoole` bakal berubah dari model multi-process single-thread jadi single-process multi-thread. + +> Tersedia sejak versi `v6.0`, dan `PHP` harus dalam mode `ZTS` + +#### --enable-iouring + +Dengan opsi kompilasi ini, file async processing `swoole` bakal berubah dari simulasi thread pool jadi `iouring`. +`iouring` adalah fitur eksklusif sistem `Linux` yang bisa ningkatin performa `IO` filesystem secara signifikan. Tapi butuh kernel versi tinggi, cek dulu apakah kernel `Linux` yang dipake mendukung `iouring`. + +> Tersedia sejak versi `v6.0`, dan butuh library `liburing`. Kalau performa disk udah bagus, performa kedua mode gak jauh beda. Cuma kalo tekanan `I/O` besar, mode `iouring` bakal lebih unggul dari mode async thread. + +Kalau pake `iouring` di container `docker` dan muncul error `Create io_uring failed, the error code is 38`, coba solusi berikut: + +1. Upgrade kernel container `docker` ke versi `5.1.0` ke atas +2. Pake parameter `--privileged` buat jalanin container +3. Tambahin parameter `--security-opt seccomp:unconfined` pas jalan, biar container `docker` bisa pake fitur `io_uring` + +#### --enable-uring-socket +Kalau diaktifin, `io_uring` bakal dipake sebagai ganti `epoll/kqueue` buat handle `socket`, performa konkurensi bakal naik drastis. Mempengaruhi semua implementasi `Swoole\Coroutine\Socket`. + +Termasuk: +- `Swoole\Coroutine\Socket` +- `Swoole\Coroutine\Client` +- `Swoole\Coroutine\Server` +- `Swoole\Coroutine\Http\Client` +- `Swoole\Coroutine\Http\Server` +- `Swoole\Coroutine\Http2\Client` +- `PHP Stream Runtime Hook`, termasuk ekstensi `pdo-mysql`, `mysqli`, `redis` + +Semua modul di atas pake `uring-socket`, performa konkurensi bakal naik drastis. + +Untuk modul server asynchronous kayak `Swoole\Server`, `Swoole\Http\Server`, `Swoole\WebSocket\Server`, `Event`, `Timer`, serta `curl`, `pdo_pgsql` dll, gak berpengaruh dan tetep pake `epoll/kqueue`. + +> Tersedia sejak versi `v6.2`, opsi ini tergantung library `liburing`, cuma berlaku kalo `--enable-iouring` diaktifin + +#### --enable-zstd + +Dengan opsi kompilasi ini, server dan client `http` bisa pake tools kompresi berperforma tinggi `Zstd` buat kompresi respons. + +> Tersedia sejak versi `v6.0`, dan butuh library `libzstd`. + +### Parameter Khusus + +!> **Kalo gak ada alasan historis, gak disarankan diaktifin** + +#### --enable-mysqlnd + +Mengaktifkan dukungan `mysqlnd` dan method `Coroutine\MySQL::escape`. Dengan parameter ini, `PHP` harus punya modul `mysqlnd`, kalo gak `Swoole` gak bakal bisa jalan. + +> Tergantung ekstensi `mysqlnd` + +#### --enable-sockets + +Nambahin dukungan buat resource `sockets` di PHP. Dengan parameter ini, [Swoole\Event::add](/event?id=add) bisa nambahin koneksi dari ekstensi `sockets` ke [event loop](/learn?id=什么是eventloop) Swoole. +Method [getSocket()](/server/methods?id=getsocket) di `Server` dan `Client` juga tergantung parameter kompilasi ini. + +> Tergantung ekstensi `sockets`. Setelah versi `v4.3.2`, peran parameter ini berkurang karena [Coroutine\Socket](/coroutine_client/socket) bawaan Swoole udah bisa ngelakuin sebagian besar tugas. + +### Parameter Debug + +!> **Jangan diaktifin di production** + +#### --enable-debug + +Mengaktifkan mode debug. Pake `gdb` buat tracing perlu nambahin parameter ini pas kompilasi `Swoole`. + +#### --enable-debug-log + +Mengaktifkan log DEBUG kernel. **(Swoole versi >= 4.2.0)** + +#### --enable-trace-log + +Mengaktifkan trace log. Dengan opsi ini, swoole bakal nge-print berbagai log debug detail. Cuma dipake pas pengembangan kernel. + +#### --enable-swoole-coro-time + +Mengaktifkan kalkulasi waktu jalan coroutine. Dengan opsi ini, bisa pake Swoole\Coroutine::getExecuteTime() buat ngitung waktu eksekusi coroutine, gak termasuk waktu tunggu I/O. + +### Parameter Kompilasi PHP + +#### --enable-swoole + +Mengaktifkan kompilasi statis ekstensi Swoole ke dalam PHP. Dengan langkah-langkah di bawah, opsi `--enable-swoole` bakal muncul. + +```shell +cp -r /home/swoole-src /home/php-src/ext +cd /home/php-src +./buildconf --force +./configure --help | grep swoole +``` + +!> Opsi ini dipake pas kompilasi PHP, bukan Swoole + +## Pertanyaan Umum + +* [Masalah Umum Instalasi Swoole](/question/install) diff --git a/public/id/event.md b/public/id/event.md new file mode 100644 index 0000000..afe0e37 --- /dev/null +++ b/public/id/event.md @@ -0,0 +1,340 @@ +# Event + +Ekstensi `Swoole` juga menyediakan antarmuka untuk mengoperasikan event loop level bawah `epoll/kqueue/poll/select` secara langsung. `Socket` yang dibuat oleh ekstensi lain, `socket` yang dibuat oleh ekstensi `stream/socket` dalam kode `PHP` dll dapat ditambahkan ke [EventLoop](/learn?id=什么是eventloop) Swoole. +Jika tidak, `$fd` pihak ketiga yang merupakan IO sinkron akan menyebabkan EventLoop Swoole tidak dapat dijalankan, [lihat studi kasus](/learn?id=同步io转换成异步io). + +!> Modul `Event` cukup level bawah, merupakan enkapsulasi dasar `epoll`. Pengguna disarankan memiliki pengalaman pemrograman IO multiplexing. + +## Prioritas Event + +1. Fungsi callback penanganan sinyal yang diatur melalui `Process::signal` +2. Fungsi callback timer yang diatur melalui `Timer::tick` dan `Timer::after` +3. Fungsi eksekusi tertunda yang diatur melalui `Event::defer` +4. Fungsi callback periodik yang diatur melalui `Event::cycle` + +## Method + +### add() + +Menambahkan `socket` ke pendengar event `reactor` level bawah. Fungsi ini dapat digunakan di mode `Server` atau `Client`. + +```php +Swoole\Event::add(mixed $sock, callable $read_callback, callable $write_callback = null, int $flags = null): bool +``` + +!> Saat digunakan di program `Server`, harus digunakan setelah proses `Worker` dimulai. Jangan memanggil antarmuka `IO` asinkron sebelum `Server::start`. + +* **Parameter** + + * **`mixed $sock`** + * **Fungsi**: Deskriptor file, resource `stream`, resource `sockets`, `object` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $read_callback`** + * **Fungsi**: Fungsi callback event dapat dibaca + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $write_callback`** + * **Fungsi**: Fungsi callback event dapat ditulis [parameter ini bisa berupa string nama fungsi, objek+method, method statis kelas, atau fungsi anonim. Saat `socket` ini dapat dibaca atau ditulis, fungsi yang ditentukan akan dipanggil.] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $flags`** + * **Fungsi**: Masker jenis event + * **Default**: Tidak ada + * **Nilai Lain**: `SWOOLE_EVENT_READ` mendengarkan event dapat dibaca, `SWOOLE_EVENT_WRITE` mendengarkan event dapat ditulis, `SWOOLE_EVENT_READ|SWOOLE_EVENT_WRITE` mendengarkan event dapat dibaca dan ditulis secara bersamaan + +* **$sock 4 jenis** + +Jenis | Penjelasan +---|--- +int | Deskriptor file, termasuk `Swoole\Client->$sock`, `Swoole\Process->$pipe` atau `fd` lainnya +resource stream | Resource yang dibuat oleh `stream_socket_client`/`fsockopen` +resource sockets | Resource yang dibuat oleh `socket_create` di ekstensi `sockets`, perlu menambahkan [./configure --enable-sockets](/environment?id=编译选项) saat kompilasi +object | `Swoole\Process` atau `Swoole\Client`, level bawah otomatis dikonversi menjadi [UnixSocket](/learn?id=什么是IPC) (`Process`) atau `socket` koneksi klien (`Swoole\Client`) + +* **Return Value** + + * Berhasil menambahkan pendengar event mengembalikan `true` + * Gagal menambahkan mengembalikan `false`, gunakan `swoole_last_error` untuk mendapatkan kode error + * `socket` yang sudah ditambahkan tidak bisa ditambahkan lagi. Menggunakan `swoole_event_set` dapat mengubah fungsi callback dan jenis event dari `socket` + + !> Saat menggunakan `Swoole\Event::add` untuk menambahkan `socket` ke pendengar event, level bawah akan otomatis mengatur `socket` tersebut ke mode non-blocking + +* **Contoh Penggunaan** + +```php +$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); +fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + +Swoole\Event::add($fp, function($fp) { + $resp = fread($fp, 8192); + //Setelah pemrosesan socket selesai, hapus socket dari event epoll + Swoole\Event::del($fp); + fclose($fp); +}); +echo "Finish\n"; //Swoole\Event::add tidak memblokir proses, baris ini akan dijalankan secara berurutan +``` + +* **Fungsi Callback** + + * Di fungsi callback event dapat dibaca `($read_callback)` harus menggunakan `fread`, `recv` dll untuk membaca data dari buffer `socket`, jika tidak event akan terus terpicu. Jika tidak ingin melanjutkan membaca, harus menggunakan `Swoole\Event::del` untuk menghapus pendengar event + * Di fungsi callback event dapat ditulis `($write_callback)`, setelah menulis ke `socket` harus memanggil `Swoole\Event::del` untuk menghapus pendengar event, jika tidak event dapat ditulis akan terus terpicu + * Jika `fread`, `socket_recv`, `socket_read`, `Swoole\Client::recv` mengembalikan `false` dan kode error `EAGAIN`, berarti buffer penerima `socket` saat ini tidak memiliki data. Maka perlu menambahkan pendengar dapat dibaca dan menunggu pemberitahuan [EventLoop](/learn?id=什么是eventloop) + * Jika `fwrite`, `socket_write`, `socket_send`, `Swoole\Client::send` mengembalikan `false` dan kode error `EAGAIN`, berarti buffer pengirim `socket` sudah penuh, tidak bisa mengirim data sementara. Perlu mendengarkan event dapat ditulis dan menunggu pemberitahuan [EventLoop](/learn?id=什么是eventloop) + +### set() + +Mengubah fungsi callback dan masker pendengar event. + +```php +Swoole\Event::set($fd, mixed $read_callback, mixed $write_callback, int $flags): bool +``` + +* **Parameter** + + * Parameter sama persis dengan [Event::add](/event?id=add). Jika `$fd` yang dimasukkan tidak ada di [EventLoop](/learn?id=什么是eventloop), mengembalikan `false`. + * Saat `$read_callback` tidak `null`, akan mengubah fungsi callback event dapat dibaca menjadi fungsi yang ditentukan. + * Saat `$write_callback` tidak `null`, akan mengubah fungsi callback event dapat ditulis menjadi fungsi yang ditentukan. + * Jika `$flags` adalah `SWOOLE_EVENT_READ`, berarti hanya mendengarkan event dapat dibaca, berhenti mendengarkan event dapat ditulis. + * Jika `$flags` adalah `SWOOLE_EVENT_WRITE`, berarti hanya mendengarkan event dapat ditulis, berhenti mendengarkan event dapat dibaca. + + !> Perhatikan jika mendengarkan event `SWOOLE_EVENT_READ`, tetapi saat ini tidak mengatur `read_callback`, level bawah akan langsung mengembalikan `false`, penambahan gagal. `SWOOLE_EVENT_WRITE` juga sama. + +* **Perubahan Status** + + * Jika menggunakan `Event::add` atau `Event::set` mengatur callback event dapat dibaca, tetapi tidak mendengarkan event dapat dibaca `SWOOLE_EVENT_READ`, level bawah hanya menyimpan informasi fungsi callback, tidak menghasilkan event callback apa pun. + * Dapat menggunakan `Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`, mengubah jenis event yang didengarkan, level bawah akan memicu event dapat dibaca. + +* **Melepaskan Fungsi Callback** + +!> Perhatikan `Event::set` hanya bisa mengganti fungsi callback, tetapi tidak bisa melepaskan fungsi callback event. Contoh: `Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)`, `read_callback` dan `write_callback` yang dimasukkan adalah `null`, berarti tidak mengubah fungsi callback yang diatur oleh `Event::add`, bukan mengatur fungsi callback event menjadi `null`. + +Hanya saat memanggil `Event::del` untuk membersihkan pendengar event, level bawah akan melepaskan fungsi callback event `read_callback` dan `write_callback`. + +### isset() + +Memeriksa apakah `$fd` yang dimasukkan sudah ditambahkan ke pendengar event. + +```php +Swoole\Event::isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE): bool +``` + +* **Parameter** + + * **`mixed $fd`** + * **Fungsi**: Deskriptor file socket apa pun [lihat dokumentasi [Event::add](/event?id=add)] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $events`** + * **Fungsi**: Jenis event yang diperiksa + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **$events** + +Jenis Event | Penjelasan +---|--- +`SWOOLE_EVENT_READ` | Apakah mendengarkan event dapat dibaca +`SWOOLE_EVENT_WRITE` | Apakah mendengarkan event dapat ditulis +`SWOOLE_EVENT_READ \| SWOOLE_EVENT_WRITE` | Mendengarkan event dapat dibaca atau ditulis + +* **Contoh Penggunaan** + +```php +use Swoole\Event; + +$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); +fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + +Event::add($fp, function($fp) { + $resp = fread($fp, 8192); + Swoole\Event::del($fp); + fclose($fp); +}, null, SWOOLE_EVENT_READ); +var_dump(Event::isset($fp, SWOOLE_EVENT_READ)); //mengembalikan true +var_dump(Event::isset($fp, SWOOLE_EVENT_WRITE)); //mengembalikan false +var_dump(Event::isset($fp, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)); //mengembalikan true +``` + +### write() + +Digunakan untuk socket yang dibuat dengan ekstensi `stream/sockets` bawaan PHP, menggunakan fungsi `fwrite/socket_send` dll untuk mengirim data ke lawan. Saat jumlah data yang dikirim besar dan buffer tulis socket penuh, akan terjadi blocking atau mengembalikan error [EAGAIN](/other/errno?id=linux). + +Fungsi `Event::write` dapat membuat pengiriman data resource `stream/sockets` menjadi **asinkron**. Saat buffer penuh atau mengembalikan [EAGAIN](/other/errno?id=linux), level bawah Swoole akan menambahkan data ke antrian pengiriman, dan mendengarkan dapat ditulis. Saat socket dapat ditulis, level bawah Swoole akan otomatis menulis + +```php +Swoole\Event::write(mixed $fd, miexd $data): bool +``` + +* **Parameter** + + * **`mixed $fd`** + * **Fungsi**: Deskriptor file socket apa pun [lihat dokumentasi [Event::add](/event?id=add)] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`miexd $data`** + * **Fungsi**: Data yang akan dikirim [panjang data yang dikirim tidak boleh melebihi ukuran buffer `Socket`] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +!> `Event::write` tidak dapat digunakan untuk resource `stream/sockets` dengan enkripsi tunnel seperti `SSL/TLS` +Setelah operasi `Event::write` berhasil, `$socket` akan otomatis diatur ke mode non-blocking + +* **Contoh Penggunaan** + +```php +use Swoole\Event; + +$fp = stream_socket_client('tcp://127.0.0.1:9501'); +$data = str_repeat('A', 1024 * 1024*2); + +Event::add($fp, function($fp) { + echo fread($fp); +}); + +Event::write($fp, $data); +``` + +#### Logika level bawah Swoole setelah buffer socket penuh + +Jika terus menulis ke `socket` dan lawan tidak membaca cukup cepat, buffer `socket` akan penuh. Level bawah `Swoole` akan menyimpan data di buffer memori, sampai event dapat ditulis terpicu lalu menulis ke `socket`. + +Jika buffer memori juga penuh, level bawah `Swoole` akan melemparkan error `pipe buffer overflow, reactor will block.` dan masuk ke blocking. + +!> Mengembalikan `false` saat buffer penuh adalah operasi atom, hanya akan terjadi semua berhasil atau semua gagal + +### del() + +Menghapus `socket` yang didengarkan dari `reactor`. `Event::del` harus digunakan berpasangan dengan `Event::add`. + +```php +Swoole\Event::del(mixed $sock): bool +``` + +!> Harus menggunakan `Event::del` untuk menghapus pendengar event sebelum operasi `close` pada `socket`, jika tidak dapat menyebabkan kebocoran memori + +* **Parameter** + + * **`mixed $sock`** + * **Fungsi**: Deskriptor file `socket` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +### exit() + +Keluar dari event loop. + +!> Fungsi ini hanya valid di program `Client` + +```php +Swoole\Event::exit(): void +``` + +### defer() + +Menjalankan fungsi di awal event loop berikutnya. + +```php +Swoole\Event::defer(mixed $callback_function); +``` + +!> Fungsi callback `Event::defer` akan dijalankan setelah event loop `EventLoop` saat ini selesai, sebelum event loop berikutnya dimulai. + +* **Parameter** + + * **`mixed $callback_function`** + * **Fungsi**: Fungsi yang akan dijalankan saat waktu habis [harus dapat dipanggil. Fungsi callback tidak menerima parameter apa pun. Dapat menggunakan sintaks `use` fungsi anonim untuk meneruskan parameter ke fungsi callback; selama eksekusi fungsi `$callback_function`, menambahkan tugas `defer` baru tetap akan selesai dalam event loop saat ini] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Contoh Penggunaan** + +```php +Swoole\Event::defer(function(){ + echo "After EventLoop\n"; +}); +``` + +### cycle() + +Mendefinisikan fungsi eksekusi periodik event loop. Fungsi ini akan dipanggil di akhir setiap event loop. + +```php +Swoole\Event::cycle(callable $callback, bool $before = false): bool +``` + +* **Parameter** + + * **`callable $callback_function`** + * **Fungsi**: Fungsi callback yang akan diatur [`$callback` menjadi `null` berarti menghapus fungsi `cycle`, jika sudah ada fungsi cycle, mengatur ulang akan menimpa pengaturan sebelumnya] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`bool $before`** + * **Fungsi**: Memanggil fungsi ini sebelum [EventLoop](/learn?id=什么是eventloop) + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +!> Dapat memiliki dua fungsi callback `before=true` dan `before=false` secara bersamaan. + +* **Contoh Penggunaan** + +```php +Swoole\Timer::tick(2000, function ($id) { + var_dump($id); +}); + +Swoole\Event::cycle(function () { + echo "hello [1]\n"; + Swoole\Event::cycle(function () { + echo "hello [2]\n"; + Swoole\Event::cycle(null); + }); +}); +``` + +### wait() + +Memulai pendengar event. + +!> Letakkan fungsi ini di akhir program PHP + +```php +Swoole\Event::wait(); +``` + +* **Contoh Penggunaan** + +```php +Swoole\Timer::tick(1000, function () { + echo "hello\n"; +}); + +Swoole\Event::wait(); +``` + +### dispatch() + +Memulai pendengar event. + +!> Hanya menjalankan operasi `reactor->wait` sekali, setara dengan memanggil `epoll_wait` secara manual di platform `Linux`. Berbeda dengan `Event::dispatch`, `Event::wait` mempertahankan loop di dalam level bawah. + +```php +Swoole\Event::dispatch(); +``` + +* **Contoh Penggunaan** + +```php +while(true) +{ + Event::dispatch(); +} +``` + +Tujuan fungsi ini adalah untuk kompatibilitas dengan beberapa framework, seperti `amp`, yang mengontrol loop `reactor` sendiri di dalam framework. Dengan menggunakan `Event::wait`, level bawah Swoole mempertahankan kendali, sehingga tidak bisa diserahkan ke framework. diff --git a/public/id/favicon.ico b/public/id/favicon.ico new file mode 100644 index 0000000..4d72fbb Binary files /dev/null and b/public/id/favicon.ico differ diff --git a/public/id/file/engine.md b/public/id/file/engine.md new file mode 100644 index 0000000..7c3d6bf --- /dev/null +++ b/public/id/file/engine.md @@ -0,0 +1,22 @@ +# Operasi File Asinkron + +[Korutinisasi satu klik](/runtime) `Swoole` bisa mengubah operasi file `PHP` dari sinkron blocking menjadi eksekusi asinkron. `Swoole` memiliki dua strategi file asinkron bawaan. + +## Thread Pool + +* `Thread Pool` adalah operasi file asinkron default `Swoole`. Saat pengguna melakukan operasi file, `Swoole` akan mengirimkan operasi ini langsung ke `Thread Pool`, di mana child thread bertanggung jawab menyelesaikan operasi file. Setelah selesai, korutin akan dialihkan kembali. +* Semua fungsi operasi file `PHP` bisa diimplementasikan secara asinkron melalui `Thread Pool`, misalnya `file_get_contents`, `fopen`, dll. +* Tidak perlu library dependensi, kompatibilitas tinggi, bisa langsung digunakan. + +## io_uring + +* `io_uring` adalah strategi bawaan setelah `Swoole v6.0`, berdasarkan `io_uring` dan `epoll` untuk mencapai asinkron. +* Throughput tinggi, bisa menangani banyak operasi file asinkron. +* `io_uring` memiliki persyaratan tinggi untuk versi kernel Linux. Disarankan menggunakan Linux 5.12+, liburing 2.6+. +* Karena berdasarkan `file descriptor`, hanya mendukung beberapa fungsi operasi file `PHP`. + +!> Hanya bisa digunakan setelah sistem menginstal `liburing` dan kompilasi `Swoole` mengaktifkan `--enable-iouring`. + +!> Mengaktifkan `io_uring` tidak akan menggantikan mode `Thread Pool`. Beberapa fungsi yang tidak bisa di-korutinisasi oleh `io_uring` tetap akan diproses oleh `Thread Pool`. + +!> `io_uring` hanya mendukung fungsi `file_get_contents`, `file_put_contents`, `fopen`, `fclose`, `fread`, `fwrite`, `mkdir`, `unlink`, `fsync`, `fdatasync`, `rename`, `fstat`, `lstat`, `filesize`. diff --git a/public/id/file/setting.md b/public/id/file/setting.md new file mode 100644 index 0000000..361d6ae --- /dev/null +++ b/public/id/file/setting.md @@ -0,0 +1,72 @@ +# Konfigurasi + +`Swoole` mengatur beberapa parameter kunci yang memengaruhi karakteristik operasi file `asinkron`, bisa diatur melalui `swoole_async_set` atau `Swoole\Server->set()`. + +Contoh: + +```php + 20, + 'aio_max_wait_time' => 60, + 'aio_max_idle_time' => 60, + 'iouring_entries' => 1024, + 'iouring_workers' => 16, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL +]); + +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set([ + 'aio_worker_num' => 20, + 'aio_max_wait_time' => 60, + 'aio_max_idle_time' => 60, + 'iouring_entries' => 1024, + 'iouring_workers' => 16, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL +]); +``` + +### aio_core_worker_num + +?> Mengatur jumlah minimum thread di thread pool, nilai default adalah `jumlah inti CPU`. + +### aio_worker_num + +?> Mengatur jumlah maksimum thread di thread pool, nilai default adalah `jumlah inti CPU * 8`. + +### aio_max_wait_time + +?> Mengatur waktu tunggu maksimum thread di thread pool, nilai default adalah `0`. + +### aio_max_idle_time + +?> Mengatur waktu idle thread di thread pool, nilai default adalah `1s`. + +### iouring_entries + +?> Mengatur ukuran antrian `io_uring`, default `8192`. Jika nilai yang diberikan bukan `pangkat 2`, kernel akan mengubahnya ke pangkat 2 terdekat yang lebih besar dari nilai tersebut. + +!> Jika nilai yang diberikan terlalu besar, kernel akan melempar exception dan menghentikan program. + +!> Hanya bisa digunakan jika sistem menginstal `liburing` dan kompilasi `Swoole` mengaktifkan `--enable-iouring`. + +### iouring_workers + +?> Mengatur jumlah worker thread `io_uring`, nilai default adalah `jumlah inti CPU * 4`. + +!> Jika nilai yang diberikan terlalu besar, kernel akan melempar exception dan menghentikan program. + +!> Hanya bisa digunakan jika sistem menginstal `liburing` dan kompilasi `Swoole` mengaktifkan `--enable-iouring`. + +### iouring_flag + +?> Mengatur mode kerja `io_uring`, nilai default adalah `SWOOLE_IOURING_DEFAULT`. + +- `SWOOLE_IOURING_DEFAULT`, mode interrupt-driven. Mengirim request `I/O` melalui system call `io_uring_enter`, lalu langsung memeriksa status completion queue untuk menentukan apakah sudah selesai. +- `SWOOLE_IOURING_SQPOLL`, mode kernel polling. Kernel akan membuat kernel thread untuk mengirim dan memanen request `I/O`, hampir sepenuhnya menghilangkan context switch user/kernel, performa lebih baik. + +!> Jika mode yang diberikan salah, kernel akan menggunakan `SWOOLE_IOURING_DEFAULT` mode interrupt-driven. + +!> `SWOOLE_IOURING_SQPOLL` mengorbankan sebagian performa CPU untuk mendapatkan performa baca/tulis disk (IOPS) yang lebih tinggi, sehingga QPS mungkin lebih rendah dari mode default. + +!> Jika tekanan server tidak besar, bisa menggunakan `SWOOLE_IOURING_DEFAULT` untuk QPS lebih tinggi. Jika bottleneck performa server ada di disk, bisa menggunakan `SWOOLE_IOURING_SQPOLL`. diff --git a/public/id/functions.md b/public/id/functions.md new file mode 100644 index 0000000..6192b1a --- /dev/null +++ b/public/id/functions.md @@ -0,0 +1,309 @@ +# Daftar Fungsi + +Swoole selain menyediakan fungsi terkait komunikasi jaringan, juga menyediakan beberapa fungsi untuk memperoleh informasi sistem yang dapat digunakan oleh program PHP. + +## swoole_set_process_name() + +Digunakan untuk mengatur nama proses. Setelah mengubah nama proses, perintah `ps` tidak lagi menampilkan `php your_file.php`, melainkan string yang telah ditentukan. + +Fungsi ini menerima satu parameter string. + +Fungsi ini memiliki fungsi yang sama dengan [cli_set_process_title](https://www.php.net/manual/zh/function.cli-set-process-title.php) yang disediakan oleh PHP 5.5. Namun `swoole_set_process_name` dapat digunakan pada versi di atas PHP 5.2. Kompatibilitas `swoole_set_process_name` lebih rendah dibandingkan `cli_set_process_title`, oleh karena itu jika fungsi `cli_set_process_title` tersedia, maka sebaiknya gunakan `cli_set_process_title`. + +```php +function swoole_set_process_name(string $name): void +``` + +Contoh penggunaan: + +```php +swoole_set_process_name("swoole server"); +``` + +### Cara Mengganti Nama Setiap Proses Swoole Server + +* Ubah nama proses utama saat [onStart](/server/events?id=onstart) dipanggil +* Ubah nama proses manajer (`manager`) saat [onManagerStart](/server/events?id=onmanagerstart) dipanggil +* Ubah nama proses worker saat [onWorkerStart](/server/events?id=onworkerstart) dipanggil + +!> Kernel Linux versi lama dan Mac OSX tidak mendukung penggantian nama proses + +## swoole_strerror() + +Mengonversi kode error menjadi pesan error. + +Prototipe fungsi: + +```php +function swoole_strerror(int $errno, int $error_type = 1): string +``` + +Tipe error: + +* `1`: `Unix Errno` standar, dihasilkan oleh kesalahan panggilan sistem, seperti `EAGAIN`, `ETIMEDOUT`, dll. +* `2`: Kode error `getaddrinfo`, dihasilkan oleh operasi `DNS` +* `9`: Kode error internal Swoole, diperoleh menggunakan `swoole_last_error()` + +Contoh penggunaan: + +```php +var_dump(swoole_strerror(swoole_last_error(), 9)); +``` + +## swoole_version() + +Mendapatkan nomor versi ekstensi swoole, misalnya `1.6.10` + +```php +function swoole_version(): string +``` + +Contoh penggunaan: + +```php +var_dump(SWOOLE_VERSION); // Variabel global SWOOLE_VERSION juga merepresentasikan versi ekstensi swoole +var_dump(swoole_version()); +/** +Nilai kembalian: +string(6) "1.9.23" +string(6) "1.9.23" +**/ +``` + +## swoole_errno() + +Mendapatkan kode error dari panggilan sistem terbaru, setara dengan variabel `errno` di `C/C++`. + +```php +function swoole_errno(): int +``` + +Nilai kode error tergantung pada sistem operasi. Gunakan `swoole_strerror` untuk mengonversi error menjadi pesan error. + +## swoole_get_local_ip() + +Fungsi ini digunakan untuk mendapatkan alamat IP dari semua antarmuka jaringan pada mesin ini. + +```php +function swoole_get_local_ip(): array +``` + +Contoh penggunaan: + +```php +// Mendapatkan alamat IP semua antarmuka jaringan pada mesin ini +$list = swoole_get_local_ip(); +print_r($list); +/** +Return Value +Array +( + [eno1] => 10.10.28.228 + [br-1e72ecd47449] => 172.20.0.1 + [docker0] => 172.17.0.1 +) +**/ +``` + +!> Catatan +* Saat ini hanya mengembalikan alamat IPv4, hasilnya akan memfilter alamat loopback lokal 127.0.0.1. +* Array hasil adalah array asosiatif dengan nama interface sebagai kunci. Contohnya `array("eth0" => "192.168.1.100")` +* Fungsi ini akan memanggil panggilan sistem `ioctl` secara langsung untuk mendapatkan informasi antarmuka, tanpa cache di tingkat bawah + +## swoole_clear_dns_cache() + +Menghapus cache DNS bawaan swoole, berlaku untuk `swoole_client` dan `swoole_async_dns_lookup`. + +```php +function swoole_clear_dns_cache() +``` + +## swoole_get_local_mac() + +Mendapatkan alamat `Mac` dari kartu jaringan lokal. + +```php +function swoole_get_local_mac(): array +``` + +* Mengembalikan alamat `Mac` dari semua kartu jaringan jika berhasil dipanggil + +```php +array(4) { + ["lo"]=> + string(17) "00:00:00:00:00:00" + ["eno1"]=> + string(17) "64:00:6A:65:51:32" + ["docker0"]=> + string(17) "02:42:21:9B:12:05" + ["vboxnet0"]=> + string(17) "0A:00:27:00:00:00" +} +``` + +## swoole_cpu_num() + +Mendapatkan jumlah inti CPU pada mesin ini. + +```php +function swoole_cpu_num(): int +``` + +* Mengembalikan jumlah inti CPU jika berhasil dipanggil, misalnya: + +```shell +php -r "echo swoole_cpu_num();" +``` + +## swoole_last_error() + +Mendapatkan kode error terbaru dari lapisan bawah Swoole. + +```php +function swoole_last_error(): int +``` + +Gunakan `swoole_strerror(swoole_last_error(), 9)` untuk mengonversi kode error menjadi pesan error. Untuk daftar lengkap kode error, lihat [Daftar Kode Error Swoole](/other/errno?id=swoole) + +## swoole_mime_type_add() + +Menambahkan tipe mime baru ke tabel tipe mime bawaan. + +```php +function swoole_mime_type_add(string $suffix, string $mime_type): bool +``` + +## swoole_mime_type_set() + +Memodifikasi suatu tipe mime, mengembalikan `false` jika gagal (misalnya tidak ada). + +```php +function swoole_mime_type_set(string $suffix, string $mime_type): bool +``` + +## swoole_mime_type_delete() + +Menghapus suatu tipe mime, mengembalikan `false` jika gagal (misalnya tidak ada). + +```php +function swoole_mime_type_delete(string $suffix): bool +``` + +## swoole_mime_type_get() + +Mendapatkan tipe mime yang sesuai dengan nama file. + +```php +function swoole_mime_type_get(string $filename): string +``` + +## swoole_mime_type_exists() + +Memeriksa apakah tipe mime yang sesuai dengan ekstensi (suffix) ada. + +```php +function swoole_mime_type_exists(string $suffix): bool +``` + +## swoole_substr_json_decode() + +Deserialisasi JSON tanpa salinan (zero-copy), selain `$offset` dan `$length`, parameter lainnya sama dengan [json_decode](https://www.php.net/manual/en/function.json-decode.php). + +!> Tersedia di Swoole versi >= `v4.5.6`, mulai versi `v4.5.7` perlu menambahkan parameter [--enable-swoole-json](/environment?id=通用参数) saat kompilasi. Untuk skenario penggunaan, lihat [Swoole 4.5.6 Mendukung Zero-Copy JSON atau PHP Deserialization](https://wenda.swoole.com/detail/107587) + +```php +function swoole_substr_json_decode(string $packet, int $offset, int $length, bool $assoc = false, int $depth = 512, int $options = 0) +``` + +* **Contoh** + +```php +$val = json_encode(['hello' => 'swoole']); +$str = pack('N', strlen($val)) . $val . "\r\n"; +$l = strlen($str) - 6; +var_dump(json_decode(substr($str, 4, $l), true)); +var_dump(swoole_substr_json_decode($str, 4, $l, true)); +``` + +## swoole_substr_unserialize() + +Deserialisasi PHP tanpa salinan (zero-copy), selain `$offset` dan `$length`, parameter lainnya sama dengan [unserialize](https://www.php.net/manual/en/function.unserialize.php). + +!> Tersedia di Swoole versi >= `v4.5.6`. Untuk skenario penggunaan, lihat [Swoole 4.5.6 Mendukung Zero-Copy JSON atau PHP Deserialization](https://wenda.swoole.com/detail/107587) + +```php +function swoole_substr_unserialize(string $packet, int $offset, int $length, array $options= []) +``` + +* **Contoh** + +```php +$val = serialize('hello'); +$str = pack('N', strlen($val)) . $val . "\r\n"; +$l = strlen($str) - 6; +var_dump(unserialize(substr($str, 4, $l))); +var_dump(swoole_substr_unserialize($str, 4, $l)); +``` + +## swoole_error_log() + +Mengeluarkan informasi error ke dalam log. `$level` adalah [level log](/consts?id=日志等级). + +!> Tersedia di Swoole versi >= `v4.5.8` + +```php +function swoole_error_log(int $level, string $msg) +``` + +## swoole_clear_error() + +Menghapus error pada socket atau error pada kode error terakhir. + +!> Tersedia di Swoole versi >= `v4.6.0` + +```php +function swoole_clear_error() +``` + +## swoole_coroutine_socketpair() + +Versi korutin dari [socket_create_pair](https://www.php.net/manual/en/function.socket-create-pair.php). + +!> Tersedia di Swoole versi >= `v4.6.0` + +```php +function swoole_coroutine_socketpair(int $domain , int $type , int $protocol): array|bool +``` + +## swoole_async_set + +Fungsi ini dapat mengatur opsi terkait I/O asinkron. + +```php +function swoole_async_set(array $settings) +``` + +- enable_signalfd mengaktifkan atau menonaktifkan penggunaan fitur `signalfd` +- enable_coroutine mengaktifkan atau menonaktifkan korutin bawaan, [lihat detailnya](/server/setting?id=enable_coroutine) +- aio_core_worker_num mengatur jumlah minimum proses AIO +- aio_worker_num mengatur jumlah maksimum proses AIO + +## swoole_error_log_ex() + +Menulis log dengan level dan kode error yang ditentukan. + +```php +function swoole_error_log_ex(int $level, int $error, string $msg) +``` + +!> Tersedia di Swoole versi >= `v4.8.1` + +## swoole_ignore_error() + +Mengabaikan log error untuk kode error yang ditentukan. + +```php +function swoole_ignore_error(int $error) +``` + +!> Tersedia di Swoole versi >= `v4.8.1` diff --git a/public/id/getting_started/extension.md b/public/id/getting_started/extension.md new file mode 100644 index 0000000..7ffe5c0 --- /dev/null +++ b/public/id/getting_started/extension.md @@ -0,0 +1,23 @@ +# Konflik Ekstensi + +Beberapa ekstensi `PHP` untuk tracing dan debugging banyak pakai global variable, yang bisa bikin coroutine `Swoole` crash. Matikan ekstensi berikut: + +* phptrace +* aop +* molten +* xhprof +* phalcon (coroutine `Swoole` nggak bisa jalan di framework `phalcon`) + + +## Dukungan Xdebug +Mulai versi `5.1`, kamu bisa langsung pakai ekstensi `xdebug` buat debugging program `Swoole`, cukup lewat parameter command line atau edit `php.ini`. + +```ini +swoole.enable_fiber_mock=On +``` + +Atau + +```shell +php -d swoole.enable_fiber_mock=On your_file.php +``` diff --git a/public/id/getting_started/notice.md b/public/id/getting_started/notice.md new file mode 100644 index 0000000..7b04e78 --- /dev/null +++ b/public/id/getting_started/notice.md @@ -0,0 +1,334 @@ +# Tips Pemrograman + +Bagian ini akan menjelaskan secara detail perbedaan antara pemrograman coroutine dan pemrograman sinkron, serta hal-hal yang perlu diperhatikan. + +## Catatan Penting + +* Jangan pakai `sleep` atau fungsi tidur lainnya di dalam kode, karena bakal bikin seluruh proses jadi blocking; di coroutine bisa pake [Co::sleep()](/coroutine/system?id=sleep) atau pake `sleep` setelah [one-click coroutine transformation](/runtime); referensi: [Dampak sleep/usleep](/getting_started/notice?id=dampak-sleepusleep) +* `exit/die` itu berbahaya, bisa bikin proses `Worker` keluar; referensi: [Dampak fungsi exit/die](/getting_started/notice?id=dampak-fungsi-exitdie) +* Bisa pake `register_shutdown_function` buat menangkap fatal error, biar bisa bersihin sesuatu saat proses keluar secara abnormal; referensi: [Menangkap fatal error saat Server jalan](/getting_started/notice?id=menangkap-fatal-error-saat-server-jalan) +* Kalau kode `PHP` ada exception yang thrown, wajib di-`try/catch` di dalam callback function, kalau nggak bisa bikin worker process keluar; referensi: [Menangkap exception dan error](/getting_started/notice?id=menangkap-exception-dan-error) +* `set_exception_handler` nggak didukung, harus pake `try/catch` buat handle exception; +* Worker process jangan saling pake client `Redis` atau `MySQL` yang sama; kode pembuatan koneksi `Redis/MySQL` bisa ditaruh di callback `onWorkerStart`. Referensi: [Apa boleh pake 1 koneksi Redis atau MySQL barengan](/question/use?id=apa-boleh-pake-1-koneksi-redis-atau-mysql-barengan) + +## Pemrograman Coroutine + +Mau pake fitur `Coroutine`, baca dulu [Panduan Pemrograman Coroutine](/coroutine/notice) dengan saksama. + +## Pemrograman Konkuren + +Harap diingat, beda sama mode `synchronous blocking`, di mode `coroutine` program jalan secara **concurrent**. Dalam waktu yang sama, `Server` bakal menghadapi banyak request, jadi **aplikasi harus bikin resource dan context yang berbeda-beda buat tiap client atau request**. Kalau nggak, bisa terjadi kekacauan data dan logika antar client atau request yang berbeda. + +## Definisi Class/Fungsi Dobel + +Kesalahan klasik yang sering dilakukan pemula. Karena `Swoole` itu menetap di memori, definisi class/fungsi yang sudah di-load dari file nggak bakal dilepas. Makanya, waktu include file PHP yang berisi definisi class/fungsi, WAJIB pake `include_once` atau `require_once`. Kalau nggak, bakal kena fatal error `cannot redeclare function/class`. + +## Manajemen Memori + +!> Perhatian ekstra waktu nulis `Server` atau proses menetap lainnya. + +Siklus hidup variabel dan manajemen memori di PHP daemon process itu beda banget sama program Web biasa. Prinsip dasar manajemen memori setelah `Server` jalan itu sama kayak program PHP-cli biasa. Detailnya silakan baca artikel soal manajemen memori di `Zend VM`. + +### Variabel Lokal + +Setelah callback event selesai dijalankan, semua objek dan variabel lokal bakal kehapus otomatis, nggak perlu di-`unset`. Kalau variabelnya bertipe resource, resource terkait juga bakal dibebasin sama PHP. + +```php +function test() +{ + $a = new Object; + $b = fopen('/data/t.log', 'r+'); + $c = new swoole_client(SWOOLE_SYNC); + $d = new swoole_client(SWOOLE_SYNC); + global $e; + $e['client'] = $d; +} +``` + +* `$a`, `$b`, `$c` adalah variabel lokal. Pas fungsi ini `return`, ke-`3` variabel ini bakal langsung dibebasin, memorinya langsung dilepas, handle file resource IO yang terbuka juga langsung ditutup. +* `$d` juga variabel lokal, tapi sebelum `return` dia disimpen ke variabel global `$e`, jadi nggak bakal dibebasin. Pas `unset($e['client'])` dijalankan dan udah nggak ada `variabel PHP` lain yang masih ngereferensi `$d`, baru `$d` bakal dibebasin. + +### Variabel Global + +Di `PHP`, ada `3` jenis variabel global. + +* Variabel yang dideklarasi pake keyword `global` +* Class static variable dan function static variable yang dideklarasi pake keyword `static` +* Superglobal variable `PHP`, termasuk `$_GET`, `$_POST`, `$GLOBALS`, dll + +Variabel global, objek, dan class static variable yang tersimpan di objek `Server` nggak bakal dibebasin otomatis. Developer harus ngurus sendiri penghancuran variabel dan objek ini. + +```php +class Test +{ + static $array = array(); + static $string = ''; +} + +function onReceive($serv, $fd, $reactorId, $data) +{ + Test::$array[] = $fd; + Test::$string .= $data; +} +``` + +* Di callback event, hati-hati sama variabel non-lokal bertipe `array`. Operasi kayak `TestClass::$array[] = "string"` bisa bikin memory leak, parahnya bisa overflow. Kalo perlu, bersihin array gede-gede. +* Di callback event, concatenation string variabel non-lokal juga musti hati-hati soal memory leak, misalnya `TestClass::$string .= $data`. Bisa bocor memori, parahnya bisa overflow. + +### Solusi + +* Buat program `Server` yang synchronous blocking dan stateless, bisa set [max_request](/server/setting?id=max_request) dan [task_max_request](/server/setting?id=task_max_request). Pas [Worker process](/learn?id=worker-process) / [Task process](/learn?id=taskworker-process) selesai jalan atau mencapai batas tugas, proses bakal keluar otomatis, dan semua variabel/objek/resource di proses itu bakal dibebasin. +* Di dalem program, pake `unset` di `onClose` atau pake `timer` buat bersihin variabel dan resource tepat waktu. + +## Isolasi Proses + +Isolasi proses juga sering bikin pusing pemula. Kok nilai variabel global diubah tapi nggak keapply? Soalnya variabel global di proses yang beda-beda itu ruang memorinya terisolasi, jadi nggak ngaruh. + +Makanya, waktu bikin `Server` pake `Swoole`, musti paham soal `isolasi proses`. Proses `Worker` yang beda di `Swoole\Server` itu terisolasi satu sama lain. Waktu ngoding, operasi variabel global, timer, event listener, cuman berlaku di proses saat itu aja. + +* Variabel PHP nggak dishare antar proses yang beda. Bahkan variabel global sekalipun, kalo diubah di proses A, nggak bakal ngefek di proses B. +* Kalo perlu sharing data antar Worker process yang beda, bisa pake `Redis`, `MySQL`, `file`, `Swoole\Table`, `APCu`, `shmget`, dll. +* File handle di proses yang beda itu terisolasi. Koneksi Socket yang dibuat atau file yang dibuka di proses A, nggak valid di proses B. Bahkan kalo fd-nya dikirim ke proses B pun tetep nggak bisa dipake. + +Contoh: + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$i = 1; + +$server->on('Request', function ($request, $response) { + global $i; + $response->end($i++); +}); + +$server->start(); +``` + +Di server multi-proses, variabel `$i` meskipun dideklarasi global (`global`), karena isolasi proses, misal ada `4` worker process, pas `$i++` di `proses 1`, cuman `$i` di `proses 1` aja yang jadi `2`. Tiga proses lainnya nilai `$i` tetep `1`. + +Cara yang bener adalah pake struktur data [Swoole\Atomic](/memory/atomic) atau [Swoole\Table](/memory/table) yang disediain Swoole buat nyimpen data. Kayak kode di atas, bisa pake `Swoole\Atomic`. + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$atomic = new Swoole\Atomic(1); + +$server->on('Request', function ($request, $response) use ($atomic) { + $response->end($atomic->add(1)); +}); + +$server->start(); +``` + +!> Data `Swoole\Atomic` dibangun di atas shared memory. Pas pake method `add` buat nambah `1`, perubahannya juga berlaku di worker process lain. + +Komponen [Table](/memory/table), [Atomic](/memory/atomic), dan [Lock](/memory/lock) yang disediain Swoole bisa dipake buat pemrograman multi-proses, tapi harus dibuat sebelum `Server->start`. Selain itu, koneksi TCP client yang dikelola `Server` juga bisa dioperasiin antar proses, kayak `Server->send` dan `Server->close`. + +## Bersihin Cache stat + +PHP nambahin `Cache` di level bawah buat panggilan sistem `stat`. Pas pake fungsi kayak `stat`, `fstat`, `filemtime`, dll, level bawah bisa kena cache dan balikin data histori. + +Bisa pake fungsi [clearstatcache](https://www.php.net/manual/en/function.clearstatcache.php) buat bersihin cache `stat` file. + +## Angka Acak mt_rand + +Di `Swoole`, kalo `mt_rand` dipanggil di proses induk, pas dipanggil lagi di proses anak yang berbeda hasilnya bakal sama. Makanya musti panggil `mt_srand` buat reseed ulang di tiap proses anak. + +!> Fungsi `PHP` kayak `shuffle` dan `array_rand` yang bergantung sama angka acak juga bakal kena dampaknya. + +Contoh: + +```php +mt_rand(0, 1); + +//Mulai +$worker_num = 16; + +//fork process +for($i = 0; $i < $worker_num; $i++) { + $process = new Swoole\Process('child_async', false, 2); + $pid = $process->start(); +} + +//jalankan proses secara asinkron +function child_async(Swoole\Process $worker) { + mt_srand(); //reseed ulang + echo mt_rand(0, 100).PHP_EOL; + $worker->exit(); +} +``` + +## Menangkap Exception dan Error + +### Exception/Error yang Bisa Ditangkap + +Di `PHP` kira-kira ada tiga jenis exception/error yang bisa ditangkap: + +1. `Error`: tipe error khusus yang dilempar oleh kernel `PHP`, kayak class nggak ada, fungsi nggak ada, parameter fungsi salah. Di kode `PHP`, jangan pake `Error` buat nge-throw exception. +2. `Exception`: class dasar exception yang harus dipake developer aplikasi. +3. `ErrorException`: class exception khusus yang nugas buat ngonversi `Warning`/`Notice` `PHP` jadi exception lewat `set_error_handler`. Ke depannya, `PHP` berencana ngonversi semua `Warning`/`Notice` jadi exception biar program `PHP` bisa handle error dengan lebih baik dan terkontrol. + +!> Semua class di atas implement interface `Throwable`. Artinya, pake `try {} catch(Throwable $e) {}` bisa nangkep semua exception/error yang bisa di-throw. + +Contoh 1: +```php +try { + test(); +} +catch(Throwable $e) { + var_dump($e); +} +``` +Contoh 2: +```php +try { + test(); +} +catch (Error $e) { + var_dump($e); +} +catch(Exception $e) { + var_dump($e); +} +``` + +### Fatal Error dan Exception yang Nggak Bisa Ditangkap + +Salah satu level error penting di `PHP`, kayak pas exception/error nggak ketangkep, memori habis, atau error waktu kompilasi (class yang di-inherit nggak ada), bakal ngelempar `Fatal Error` di level `E_ERROR`. Ini terjadi pas program kena error yang nggak bisa dipulihkan. `PHP` nggak bisa nangkep error level kayak gini, cuma bisa pake `register_shutdown_function` buat ngelakuin sesuatu setelahnya. + +### Menangkap Runtime Exception/Error di Coroutine + +Di pemrograman coroutine `Swoole4`, kalo ada error yang di-throw di kode suatu coroutine, bisa bikin seluruh proses keluar dan semua coroutine di proses itu berhenti. Di level teratas coroutine, bisa pake `try/catch` buat nangkep exception/error, dan cuman Coroutine yang error aja yang berhenti. + +```php +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +run(function () { + Coroutine::create(function () { + try { + call_user_func($func); + } + catch (Error $e) { + var_dump($e); + } + catch(Exception $e) { + var_dump($e); + } + }); + + //Error di Coroutine 1 nggak ngefek Coroutine 2 + Coroutine::create(function () { + Coroutine::sleep(5); + echo 2; + }); +}); +``` + +### Menangkap Fatal Error Saat Server Jalan + +Pas `Server` jalan, kalo terjadi fatal error, koneksi client nggak bakal dapet respons. Misalnya web server, kalo ada fatal error, harusnya ngirim `HTTP 500` ke client. + +Di PHP, bisa pake kombinasi fungsi `register_shutdown_function` + `error_get_last` buat nangkep fatal error, dan kirim info errornya ke koneksi client. + +Kode contohnya: + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + register_shutdown_function(function () use ($response) { + $error = error_get_last(); + var_dump($error); + switch ($error['type'] ?? null) { + case E_ERROR : + case E_PARSE : + case E_CORE_ERROR : + case E_COMPILE_ERROR : + // log atau kirim: + // error_log($message); + // $server->send($fd, $error['message']); + $response->status(500); + $response->end($error['message']); + break; + } + }); + exit(0); +}); +$http->start(); +``` + +## Dampak Penggunaan + +### Dampak sleep/usleep + +Di program asynchronous I/O, **jangan pake sleep/usleep/time_sleep_until/time_nanosleep**. (Selanjutnya `sleep` dipake buat nyebut semua fungsi tidur) + +* Fungsi `sleep` bikin process masuk ke mode tidur dan blocking +* Sistem operasi bakal ngebangunin process setelah waktu yang ditentuin +* Selama `sleep`, cuman sinyal yang bisa ngeinterupsi +* Karena penanganan sinyal Swoole pake `signalfd`, ngirim sinyal pun nggak bisa ngeinterupsi `sleep` + +[Swoole\Event::add](/event?id=add), [Swoole\Timer::tick](/timer?id=tick), [Swoole\Timer::after](/timer?id=after), dan [Swoole\Process::signal](/process/process?id=signal) yang disediain Swoole bakal berhenti kerja setelah process `sleep`. [Swoole\Server](/server/tcp_init) juga nggak bakal bisa handle request baru. + +#### Contoh + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->set(['worker_num' => 1]); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + sleep(100); + $server->send($fd, 'Swoole: '.$data); +}); +$server->start(); +``` + +!> Di event [onReceive](/server/events?id=onreceive) jalanin fungsi `sleep`, `Server` nggak bakal bisa nerima request client selama 100 detik. + +### Dampak Fungsi exit/die + +Di program `Swoole`, `exit/die` dilarang dipake. Kalo di kode PHP ada `exit/die`, [Worker process](/learn?id=worker-process), [Task process](/learn?id=taskworker-process), [User process](/server/methods?id=addprocess), dan process `Swoole\Process` yang sedang jalan bakal langsung keluar. + +Pas pake `exit/die`, `Worker` process bakal keluar karena abnormal, terus dihidupin lagi sama `master` process. Jadinya process keluar terus-terusan dan muncul banyak log alert. + +Sebaiknya ganti `exit/die` pake `try/catch` buat interupsi eksekusi dan keluar dari call stack fungsi PHP. + +```php +Swoole\Coroutine\run(function () { + try + { + exit(0); + } catch (Swoole\ExitException $e) + { + echo $e->getMessage()."\n"; + } +}); +``` + +!> `Swoole\ExitException` didukung langsung di Swoole versi `v4.1.0` ke atas buat pake `exit` PHP di coroutine dan `Server`. Dalam kasus ini, level bawah bakal otomatis ngelempar `Swoole\ExitException` yang bisa ditangkap, jadi developer bisa nangkep dan ngelakuin logika exit kayak PHP asli. Detailnya lihat [Keluar dari Coroutine](/coroutine/notice?id=keluar-dari-coroutine); + +Penanganan exception lebih bersahabat daripada `exit/die` karena exception itu terkontrol, sedangkan `exit/die` nggak terkontrol. Pake `try/catch` di lapisan paling luar buat nangkep exception, dan cuman tugas yang sedang jalan aja yang berhenti. Worker process bisa lanjut handle request baru. Sementara `exit/die` bikin process langsung keluar, semua variabel dan resource yang disimpen di process itu hancur. Kalo masih ada tugas lain yang harus dikerjain di dalem process, ketemu `exit/die` semuanya bakal kebuang. + +### Dampak While Loop + +Kalo program asynchronous kena infinite loop, event nggak bakal bisa kepicu. Program asynchronous I/O pake `Reactor model`, pas jalan musti nge-poll di `reactor->wait`. Kalo kena infinite loop, kendali program bakal nyangkut di `while`, `reactor` nggak dapet kendali, nggak bisa deteksi event, dan akhirnya callback function IO event nggak bakal kepicu. + +!> Kode komputasi intensif tanpa operasi IO apa pun nggak bisa disebut blocking. + +#### Contoh Program + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set(['worker_num' => 1]); +$server->on('receive', function ($server, $fd, $reactorId, $data) { + $i = 0; + while(1) + { + $i++; + } + $server->send($fd, 'Swoole: '.$data); +}); +$server->start(); +``` + +!> Ada infinite loop di event [onReceive](/server/events?id=onreceive), `server` nggak bakal bisa nerima request client baru lagi. Server musti nunggu loop selesai dulu baru bisa lanjut proses event baru. diff --git a/public/id/http_server.md b/public/id/http_server.md new file mode 100644 index 0000000..298aafc --- /dev/null +++ b/public/id/http_server.md @@ -0,0 +1,1152 @@ +# Http\Server + +?> `Http\Server` mewarisi dari [Server](/server/init), jadi semua `API` dan opsi konfigurasi yang disediakan `Server` dapat digunakan, model prosesnya juga sama. Silakan lihat bab [Server](/server/init). + +Dukungan server `HTTP` bawaan, hanya dengan beberapa baris kode dapat menulis server `HTTP` multi-proses konkuren tinggi, performa tinggi, [IO asinkron](/learn?id=同步io异步io). + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); +$http->start(); +``` + +Dengan menggunakan alat `Apache bench` untuk pengujian tekanan, di PC biasa `Inter Core-I5 4-core + 8G memory`, `Http\Server` dapat mencapai hampir `110 ribu QPS`. + +Jauh melampaui server `Http` bawaan `PHP-FPM`, `Golang`, `Node.js`. Performa hampir setara dengan penanganan file statis `Nginx`. + +```shell +ab -c 200 -n 200000 -k http://127.0.0.1:9501/ +``` + +* **Menggunakan Protokol HTTP2** + + * Menggunakan protokol `HTTP2` di bawah `SSL` harus menginstal `openssl`, dan versi `openssl` yang tinggi harus mendukung `TLS1.2`, `ALPN`, `NPN` + * Saat kompilasi perlu mengaktifkan [--enable-http2](/environment?id=编译选项) + * Mulai Swoole5, protokol http2 diaktifkan secara default + +```shell +./configure --enable-openssl --enable-http2 +``` + +Atur [open_http2_protocol](/http_server?id=open_http2_protocol) server `HTTP` menjadi `true` + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set([ + 'ssl_cert_file' => $ssl_dir . '/ssl.crt', + 'ssl_key_file' => $ssl_dir . '/ssl.key', + 'open_http2_protocol' => true, +]); +``` + +* **Konfigurasi Nginx + Swoole** + +!> Karena dukungan `Http\Server` terhadap protokol `HTTP` tidak lengkap, disarankan hanya sebagai server aplikasi untuk menangani request dinamis, dan menambahkan `Nginx` sebagai proxy di depan. + +```nginx +server { + listen 80; + server_name swoole.test; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_pass http://127.0.0.1:9501; + } +} +``` + +?> Dapat membaca `$request->header['x-real-ip']` untuk mendapatkan `IP` asli klien + +## Method + +### on() + +?> **Mendaftarkan fungsi callback event.** + +?> Sama dengan [Callback Server](/server/events), perbedaannya adalah: + +* `Http\Server->on` tidak menerima pengaturan callback [onConnect](/server/events?id=onconnect)/[onReceive](/server/events?id=onreceive) +* `Http\Server->on` tambahan menerima 1 jenis event baru `onRequest`, request dari klien dijalankan di event `Request` + +```php +$http_server->on('request', function(\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + $response->end("

hello swoole

"); +}); +``` + +Setelah menerima request HTTP lengkap, fungsi ini akan dipanggil. Fungsi callback memiliki `2` parameter: + +* [Swoole\Http\Request](/http_server?id=httpRequest), objek informasi request `HTTP`, berisi informasi terkait `header/get/post/cookie` +* [Swoole\Http\Response](/http_server?id=httpResponse), objek respons `HTTP`, mendukung operasi `HTTP` seperti `cookie/header/status` + +!> Saat fungsi callback [onRequest](/http_server?id=on) kembali, level bawah akan menghancurkan objek `$request` dan `$response` + +### start() + +?> **Memulai server HTTP** + +?> Setelah mulai, mulai mendengarkan port dan menerima request `HTTP` baru. + +```php +Swoole\Http\Server->start(); +``` + +## Swoole\Http\Request + +Objek request `HTTP`, menyimpan informasi terkait request klien `HTTP`, termasuk `GET`, `POST`, `COOKIE`, `Header` dll. + +!> Jangan menggunakan simbol `&` untuk mereferensi objek `Http\Request` + +### header + +?> **Informasi header request `HTTP`. Tipe array, semua `key` huruf kecil.** + +```php +Swoole\Http\Request->header: array +``` + +* **Contoh** + +```php +echo $request->header['host']; +echo $request->header['accept-language']; +``` + +### server + +?> **Informasi server terkait request `HTTP`.** + +?> Setara dengan array `$_SERVER` di `PHP`. Berisi method request `HTTP`, path `URL`, IP klien dll. + +```php +Swoole\Http\Request->server: array +``` + +Semua `key` array adalah huruf kecil, dan konsisten dengan array `$_SERVER` di `PHP` + +* **Contoh** + +```php +echo $request->server['request_time']; +``` + +key | Penjelasan +---|--- +query_string | Parameter `GET` request, misal: `id=1&cid=2` Jika tidak ada parameter `GET`, item ini tidak ada +request_method | Method request, `GET/POST` dll +request_uri | Alamat akses tanpa parameter `GET`, misal `/favicon.ico` +path_info | Sama dengan `request_uri` +request_time | `request_time` diatur di `Worker`, di mode [SWOOLE_PROCESS](/learn?id=swoole_process) ada proses `dispatch`, sehingga mungkin ada selisih dengan waktu penerimaan paket sebenarnya. Terutama saat jumlah request melebihi kapasitas pemrosesan server, `request_time` mungkin jauh tertinggal dari waktu penerimaan paket sebenarnya. Dapat menggunakan method `$server->getClientInfo` untuk mendapatkan `last_time` guna waktu penerimaan paket yang akurat. +request_time_float | Timestamp mulai request, dalam mikrodetik, tipe `float`, misal `1576220199.2725` +server_protocol | Nomor versi protokol server, `HTTP`: `HTTP/1.0` atau `HTTP/1.1`, `HTTP2`: `HTTP/2` +server_port | Port yang didengarkan server +remote_port | Port klien +remote_addr | Alamat `IP` klien +master_time | Waktu komunikasi terakhir koneksi + +### get + +?> **Parameter `GET` request `HTTP`, setara dengan `$_GET` di `PHP`, format array.** + +```php +Swoole\Http\Request->get: array +``` + +* **Contoh** + +```php +// Misal: index.php?hello=123 +echo $request->get['hello']; +// Mendapatkan semua parameter GET +var_dump($request->get); +``` + +* **Catatan** + +!> Untuk mencegah serangan `HASH`, parameter `GET` maksimal tidak boleh melebihi `128` + +### post + +?> **Parameter `POST` request `HTTP`, format array** + +```php +Swoole\Http\Request->post: array +``` + +* **Contoh** + +```php +echo $request->post['hello']; +``` + +* **Catatan** + +!> - Ukuran gabungan `POST` dan `Header` tidak boleh melebihi pengaturan [package_max_length](/server/setting?id=package_max_length), jika tidak akan dianggap request berbahaya +- Jumlah maksimum parameter `POST` tidak boleh melebihi `128` + +### cookie + +?> **Informasi `COOKIE` yang dibawa request `HTTP`, format array pasangan kunci-nilai.** + +```php +Swoole\Http\Request->cookie: array +``` + +* **Contoh** + +```php +echo $request->cookie['username']; +``` + +### files + +?> **Informasi upload file.** + +?> Tipe array dua dimensi dengan nama `form` sebagai `key`. Sama dengan `$_FILES` di `PHP`. Ukuran file maksimum tidak boleh melebihi nilai yang diatur [package_max_length](/server/setting?id=package_max_length). Karena Swoole menggunakan memori saat mengurai pesan, semakin besar pesan, semakin besar penggunaan memori, jadi jangan gunakan `Swoole\Http\Server` untuk menangani upload file besar atau desain fitur resumable upload oleh pengguna sendiri. + +```php +Swoole\Http\Request->files: array +``` + +* **Contoh** + +```php +Array +( + [name] => facepalm.jpg // Nama file yang dimasukkan saat upload browser + [type] => image/jpeg // Tipe MIME + [tmp_name] => /tmp/swoole.upfile.n3FmFr // File sementara upload, nama file diawali /tmp/swoole.upfile + [error] => 0 + [size] => 15476 // Ukuran file +) +``` + +* **Catatan** + +!> Saat objek `Swoole\Http\Request` dihancurkan, file sementara upload akan otomatis dihapus + +### getContent() + +!> Versi Swoole >= `v4.5.0` tersedia, di versi rendah dapat menggunakan alias `rawContent` (alias ini akan dipertahankan secara permanen, kompatibel ke bawah) + +?> **Mendapatkan body `POST` mentah.** + +?> Digunakan untuk request HTTP `POST` non-format `application/x-www-form-urlencoded`. Mengembalikan data `POST` mentah, fungsi ini setara dengan `fopen('php://input')` di `PHP` + +```php +Swoole\Http\Request->getContent(): string|false +``` + +* **Return Value** + + * Mengembalikan pesan, jika konteks koneksi tidak ada mengembalikan `false` + +!> Beberapa situasi server tidak perlu mengurai parameter request HTTP `POST`, melalui konfigurasi [http_parse_post](/http_server?id=http_parse_post), dapat menonaktifkan penguraian data `POST`. + +### getData() + +?> **Mendapatkan pesan request `Http` asli lengkap, perhatikan tidak bisa digunakan di `Http2`. Termasuk `Http Header` dan `Http Body`** + +```php +Swoole\Http\Request->getData(): string|false +``` + +* **Return Value** + + * Mengembalikan pesan, jika konteks koneksi tidak ada atau dalam mode `Http2` mengembalikan `false` + +### create() + +?> **Membuat objek `Swoole\Http\Request`.** + +!> Versi Swoole >= `v4.6.0` tersedia + +```php +Swoole\Http\Request->create(array $options): Swoole\Http\Request +``` + +* **Parameter** + + * **`array $options`** + * **Fungsi**: Parameter opsional, digunakan untuk mengatur konfigurasi objek `Request` + +| Parameter | Default | Penjelasan | +| ---------------------------------------------------- | ------------- | ------------------------------------------------------------------ | +| [parse_cookie](/http_server?id=http_parse_cookie) | true | Mengatur apakah mengurai `Cookie` | +| [parse_body](/http_server?id=http_parse_post) | true | Mengatur apakah mengurai `Http Body` | +| [parse_files](/http_server?id=http_parse_files) | true | Mengatur sakelar penguraian upload file | +| enable_compression | true, jika server tidak mendukung pesan terkompresi, default false | Mengatur apakah mengaktifkan kompresi | +| compression_level | 1 | Mengatur level kompresi, rentang 1-9, semakin tinggi level semakin kecil ukuran setelah kompresi, tetapi konsumsi CPU lebih banyak | +| upload_tmp_dir | /tmp | Lokasi penyimpanan file sementara, untuk upload file | + +* **Return Value** + + * Mengembalikan objek `Swoole\Http\Request` + +* **Contoh** +```php +Swoole\Http\Request::create([ + 'parse_cookie' => true, + 'parse_body' => true, + 'parse_files' => true, + 'enable_compression' => true, + 'compression_level' => 1, + 'upload_tmp_dir' => '/tmp', +]); +``` + +### parse() + +?> **Mengurai paket data request `HTTP`, mengembalikan panjang paket data yang berhasil diurai.** + +!> Versi Swoole >= `v4.6.0` tersedia + +```php +Swoole\Http\Request->parse(string $data): int|false +``` + +* **Parameter** + + * **`string $data`** + * Pesan yang akan diurai + +* **Return Value** + + * Berhasil mengurai mengembalikan panjang pesan yang diurai, konteks koneksi tidak ada atau konteks sudah berakhir mengembalikan `false` + +### isCompleted() + +?> **Mendapatkan apakah paket data request `HTTP` saat ini sudah mencapai akhir.** + +!> Versi Swoole >= `v4.6.0` tersedia + +```php +Swoole\Http\Request->isCompleted(): bool +``` + +* **Return Value** + + * `true` berarti sudah akhir, `false` berarti konteks koneksi sudah berakhir atau belum sampai akhir + +* **Contoh** + +```php +use Swoole\Http\Request; + +$data = "GET /index.html?hello=world&test=2123 HTTP/1.1\r\n"; +$data .= "Host: 127.0.0.1\r\n"; +$data .= "Connection: keep-alive\r\n"; +$data .= "Pragma: no-cache\r\n"; +$data .= "Cache-Control: no-cache\r\n"; +$data .= "Upgrade-Insecure-Requests: \r\n"; +$data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\n"; +$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"; +$data .= "Accept-Encoding: gzip, deflate, br\r\n"; +$data .= "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\r\n"; +$data .= "Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\r\n"; + +/** @var Request $req */ +$req = Request::create(['parse_cookie' => false]); +var_dump($req); + +var_dump($req->isCompleted()); +var_dump($req->parse($data)); + +var_dump($req->parse("\r\n")); +var_dump($req->isCompleted()); + +var_dump($req); +// Penguraian cookie dimatikan, jadi akan null +var_dump($req->cookie); +``` + +### getMethod() + +?> **Mendapatkan method request `HTTP` saat ini.** + +!> Versi Swoole >= `v4.6.2` tersedia + +```php +Swoole\Http\Request->getMethod(): string|false +``` +* **Return Value** + + * Mengembalikan method request huruf besar, `false` berarti konteks koneksi tidak ada + +```php +var_dump($request->server['request_method']); +var_dump($request->getMethod()); +``` + +## Swoole\Http\Response + +Objek respons `HTTP`, dengan memanggil method objek ini, mengirim respons `HTTP`. + +?> Saat objek `Response` dihancurkan, jika belum memanggil [end](/http_server?id=end) untuk mengirim respons `HTTP`, level bawah akan otomatis menjalankan `end("")`; + +!> Jangan menggunakan simbol `&` untuk mereferensi objek `Http\Response` + +### header() :id=setheader + +?> **Mengatur informasi Header respons HTTP** [alias `setHeader`] + +```php +Swoole\Http\Response->header(string $key, string $value, bool $format = true): bool; +``` + +* **Parameter** + + * **`string $key`** + * **Fungsi**: `Key` dari `HTTP` header + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $value`** + * **Fungsi**: `Value` dari `HTTP` header + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`bool $format`** + * **Fungsi**: Apakah perlu memformat `Key` sesuai konvensi `HTTP` [default `true` akan otomatis diformat] + * **Default**: `true` + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Gagal mengatur, mengembalikan `false` + * Berhasil mengatur, mengembalikan `true` + +* **Catatan** + + - Pengaturan `header` harus dilakukan sebelum method `end` + - `$key` harus sepenuhnya sesuai dengan konvensi `HTTP`, setiap kata diawali huruf besar, tidak boleh mengandung karakter China, garis bawah atau karakter khusus lainnya + - `$value` harus diisi + - Jika `$ucwords` diatur `true`, level bawah akan otomatis memformat `$key` sesuai konvensi + - Pengaturan ulang header `HTTP` dengan `$key` yang sama akan menimpa, diambil yang terakhir + - Jika klien mengatur `Accept-Encoding`, maka server tidak bisa mengatur respons `Content-Length`, `Swoole` mendeteksi situasi ini akan mengabaikan nilai `Content-Length` dan mengeluarkan peringatan + - Jika `Content-Length` sudah diatur, tidak boleh memanggil `Swoole\Http\Response::write()`, `Swoole` mendeteksi situasi ini akan mengabaikan nilai `Content-Length` dan mengeluarkan peringatan + +!> Swoole versi >= `v4.6.0`, mendukung pengaturan ulang header `HTTP` dengan `$key` yang sama, dan `$value` mendukung berbagai tipe, seperti `array`, `object`, `int`, `float`, level bawah akan melakukan konversi `toString`, dan akan menghapus spasi akhir serta baris baru. + +* **Contoh** + +```php +$response->header('content-type', 'image/jpeg', true); + +$response->header('Content-Length', '100002 '); +$response->header('Test-Value', [ + "a\r\n", + 'd5678', + "e \n ", + null, + 5678, + 3.1415926, +]); +$response->header('Foo', new SplFileInfo('bar')); +``` + +### trailer() + +?> **Menambahkan informasi `Header` ke akhir respons `HTTP`, hanya tersedia di `HTTP2`, digunakan untuk pemeriksaan integritas pesan, tanda tangan digital dll.** + +```php +Swoole\Http\Response->trailer(string $key, string $value): bool; +``` + +* **Parameter** + + * **`string $key`** + * **Fungsi**: `Key` dari `HTTP` header + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $value`** + * **Fungsi**: `Value` dari `HTTP` header + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Gagal mengatur, mengembalikan `false` + * Berhasil mengatur, mengembalikan `true` + +* **Catatan** + + !> Pengaturan ulang header `Http` dengan `$key` yang sama akan menimpa, diambil yang terakhir. + +* **Contoh** + +```php +$response->trailer('grpc-status', 0); +$response->trailer('grpc-message', ''); +``` + +### cookie() + +?> **Mengatur informasi `cookie` respons `HTTP`. Alias `setCookie`. Parameter method ini konsisten dengan `setcookie` di `PHP`.** + +```php +Swoole\Http\Response->cookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = ''): bool; +``` + +* **Parameter** + + * **`string $key`** + * **Fungsi**: `Key` dari `Cookie` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $value`** + * **Fungsi**: `Value` dari `Cookie` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $expire`** + * **Fungsi**: `Waktu kedaluwarsa` dari `Cookie` + * **Default**: 0, tidak kedaluwarsa + * **Nilai Lain**: Tidak ada + + * **`string $path`** + * **Fungsi**: `Path server untuk Cookie` + * **Default**: / + * **Nilai Lain**: Tidak ada + + * **`string $domain`** + * **Fungsi**: `Domain untuk Cookie` + * **Default**: '' + * **Nilai Lain**: Tidak ada + + * **`bool $secure`** + * **Fungsi**: `Apakah mentransmisikan Cookie melalui koneksi HTTPS yang aman` + * **Default**: '' + * **Nilai Lain**: Tidak ada + + * **`bool $httponly`** + * **Fungsi**: `Apakah mengizinkan JavaScript browser mengakses Cookie dengan atribut HttpOnly`, `true` berarti tidak mengizinkan, `false` berarti mengizinkan + * **Default**: false + * **Nilai Lain**: Tidak ada + + * **`string $samesite`** + * **Fungsi**: `Membatasi Cookie pihak ketiga untuk mengurangi risiko keamanan`, nilai opsional `Strict`, `Lax`, `None` + * **Default**: '' + * **Nilai Lain**: Tidak ada + + * **`string $priority`** + * **Fungsi**: `Prioritas Cookie, saat jumlah Cookie melebihi batas, prioritas rendah akan dihapus lebih dulu`, nilai opsional `Low`, `Medium`, `High` + * **Default**: '' + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Gagal mengatur, mengembalikan `false` + * Berhasil mengatur, mengembalikan `true` + +* **Catatan** + + !> - Pengaturan `cookie` harus dilakukan sebelum method [end](/http_server?id=end) + - Parameter `$samesite` didukung mulai versi `v4.4.6`, parameter `$priority` didukung mulai versi `v4.5.8` + - `Swoole` akan otomatis melakukan `urlencode` pada `$value`, dapat menggunakan method `rawCookie()` untuk menonaktifkan pengkodean `$value` + - `Swoole` mengizinkan pengaturan beberapa `COOKIE` dengan `$key` yang sama + +### rawCookie() + +?> **Mengatur informasi `cookie` respons `HTTP`** + +!> Parameter `rawCookie()` sama dengan `cookie()` di atas, hanya saja tidak melakukan pengkodean + +### status() + +?> **Mengirim kode status `Http`. Alias `setStatusCode()`** + +```php +Swoole\Http\Response->status(int $http_status_code, string $reason = ''): bool +``` + +* **Parameter** + + * **`int $http_status_code`** + * **Fungsi**: Mengatur `HttpCode` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $reason`** + * **Fungsi**: Alasan kode status + * **Default**: '' + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Gagal mengatur, mengembalikan `false` + * Berhasil mengatur, mengembalikan `true` + +* **Catatan** + + * Jika hanya memasukkan parameter pertama `$http_status_code` harus berupa `HttpCode` yang valid, seperti `200`, `502`, `301`, `404` dll, jika tidak akan diatur ke kode status `200` + * Jika mengatur parameter kedua `$reason`, `$http_status_code` dapat berupa nilai numerik apa pun, termasuk `HttpCode` yang tidak terdefinisi, seperti `499` + * Method `status` harus dijalankan sebelum [$response->end()](/http_server?id=end) + +### gzip() + +!> Method ini tidak digunakan lagi di `4.1.0` atau versi lebih tinggi, silakan lihat [http_compression](/http_server?id=http_compression); di versi baru gunakan opsi konfigurasi `http_compression` sebagai pengganti method `gzip`. +Alasan utamanya adalah method `gzip()` tidak memeriksa header `Accept-Encoding` yang dimasukkan browser klien, jika klien tidak mendukung kompresi `gzip`, memaksakan penggunaan dapat menyebabkan klien tidak bisa mendekompresi. +Opsi konfigurasi baru `http_compression` akan secara otomatis memilih apakah akan kompresi berdasarkan header `Accept-Encoding` klien, dan secara otomatis memilih algoritma kompresi terbaik. + +?> **Mengaktifkan kompresi `HTTP GZIP`. Kompresi dapat mengurangi ukuran konten `HTML`, menghemat bandwidth jaringan, dan meningkatkan waktu respons. Harus menjalankan `gzip` sebelum mengirim konten dengan `write/end`, jika tidak akan melempar error.** +```php +Swoole\Http\Response->gzip(int $level = 1); +``` + +* **Parameter** + + * **`int $level`** + * **Fungsi**: Level kompresi, semakin tinggi level semakin kecil ukuran setelah kompresi, tetapi konsumsi `CPU` lebih banyak. + * **Default**: 1 + * **Nilai Lain**: `1-9` + +!> Setelah memanggil method `gzip`, level bawah akan otomatis menambahkan header pengkodean `Http`, kode PHP seharusnya tidak mengatur header `Http` terkait lagi; gambar format `jpg/png/gif` sudah terkompresi, tidak perlu kompresi lagi + +!> Fungsi `gzip` bergantung pada library `zlib`, saat mengompilasi swoole level bawah akan mendeteksi apakah sistem memiliki `zlib`, jika tidak ada, method `gzip` tidak akan tersedia. Dapat menggunakan `yum` atau `apt-get` untuk menginstal library `zlib`: + +```shell +sudo apt-get install libz-dev +``` + +### redirect() + +?> **Mengirim pengalihan `Http`. Memanggil method ini akan otomatis `end` dan mengakhiri respons.** + +```php +Swoole\Http\Response->redirect(string $url, int $http_code = 302): bool +``` + +* **Parameter** + + * **`string $url`** + * **Fungsi**: Alamat baru pengalihan, dikirim sebagai header `Location` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $http_code`** + * **Fungsi**: Kode status [default `302` pengalihan sementara, masukkan `301` berarti pengalihan permanen] + * **Default**: `302` + * **Nilai Lain**: Tidak ada + + * **Return Value** + + * Berhasil dipanggil, mengembalikan `true`, gagal atau konteks koneksi tidak ada, mengembalikan `false` + +* **Contoh** + +```php +$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); + +$http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->redirect("http://www.baidu.com/", 301); +}); + +$http->start(); +``` + +### write() + +?> **Mengaktifkan `Http Chunk` untuk mengirim konten respons ke browser secara bertahap.** + +?> Tentang `Http Chunk` dapat merujuk ke dokumen standar protokol `Http`. + +```php +Swoole\Http\Response->write(string $data): bool +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang akan dikirim [panjang maksimum tidak boleh melebihi `2M`, dikontrol oleh opsi konfigurasi [buffer_output_size](/server/setting?id=buffer_output_size)] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil dipanggil, mengembalikan `true`, gagal atau konteks koneksi tidak ada, mengembalikan `false` + +* **Catatan** + + * Setelah menggunakan `write` untuk mengirim data bertahap, method [end](/http_server?id=end) tidak akan menerima parameter apa pun, memanggil `end` hanya akan mengirim `Chunk` dengan panjang `0` yang menandakan data selesai dikirim + * Jika melalui Swoole\Http\Response::header() mengatur `Content-Length`, lalu memanggil method ini, `Swoole` akan mengabaikan pengaturan `Content-Length` dan mengeluarkan peringatan + * `Http2` tidak bisa menggunakan fungsi ini, jika tidak akan mengeluarkan peringatan + * Jika klien mendukung kompresi respons, `Swoole\Http\Response::write()` akan memaksa menonaktifkan kompresi + +### sendfile() + +?> **Mengirim file ke browser.** + +```php +Swoole\Http\Response->sendfile(string $filename, int $offset = 0, int $length = 0): bool +``` + +* **Parameter** + + * **`string $filename`** + * **Fungsi**: Nama file yang akan dikirim [file tidak ada atau tidak ada izin akses `sendfile` akan gagal] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $offset`** + * **Fungsi**: Offset upload file [dapat menentukan mulai transmisi data dari bagian tengah file. Fitur ini dapat digunakan untuk mendukung resumable upload] + * **Default**: `0` + * **Nilai Lain**: Tidak ada + + * **`int $length`** + * **Fungsi**: Ukuran data yang dikirim + * **Default**: Ukuran file + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil dipanggil, mengembalikan `true`, gagal atau konteks koneksi tidak ada, mengembalikan `false` + +* **Catatan** + + * Level bawah tidak dapat menyimpulkan format MIME file yang akan dikirim, oleh karena itu kode aplikasi perlu menentukan `Content-Type` + * Sebelum memanggil `sendfile` tidak boleh menggunakan method `write` untuk mengirim `Http-Chunk` + * Setelah memanggil `sendfile`, level bawah akan otomatis menjalankan `end` + * `sendfile` tidak mendukung kompresi `gzip` + +* **Contoh** + +```php +$response->header('Content-Type', 'image/jpeg'); +$response->sendfile(__DIR__.$request->server['request_uri']); +``` + +### end() + +?> **Mengirim body respons `Http` dan mengakhiri pemrosesan request.** + +```php +Swoole\Http\Response->end(string $html): bool +``` + +* **Parameter** + + * **`string $html`** + * **Fungsi**: Konten yang akan dikirim + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil dipanggil, mengembalikan `true`, gagal atau konteks koneksi tidak ada, mengembalikan `false` + +* **Catatan** + + * `end` hanya bisa dipanggil sekali, jika perlu mengirim data ke klien beberapa kali, gunakan method [write](/http_server?id=write) + * Jika klien mengaktifkan [KeepAlive](/coroutine_client/http_client?id=keep_alive), koneksi akan dipertahankan, server akan menunggu request berikutnya + * Jika klien tidak mengaktifkan `KeepAlive`, server akan memutus koneksi + * Konten yang akan dikirim `end`, karena dibatasi [output_buffer_size](/server/setting?id=buffer_output_size), default `2M`, jika lebih dari batas ini maka respons akan gagal dan melempar error berikut: + +!> Solusinya: gunakan [sendfile](/http_server?id=sendfile), [write](/http_server?id=write) atau atur [output_buffer_size](/server/setting?id=buffer_output_size) + +```bash +WARNING finish (ERRNO 1203): The length of data [262144] exceeds the output buffer size[131072], please use the sendfile, chunked transfer mode or adjust the output_buffer_size +``` + +### detach() + +?> **Memisahkan objek respons.** Setelah menggunakan method ini, saat objek `$response` dihancurkan tidak akan otomatis [end](/http_server?id=httpresponse), digunakan bersama [Http\Response::create](/http_server?id=create) dan [Server->send](/server/methods?id=send). + +```php +Swoole\Http\Response->detach(): bool +``` + +* **Return Value** + + * Berhasil dipanggil, mengembalikan `true`, gagal atau konteks koneksi tidak ada, mengembalikan `false` + +* **Contoh** + + * **Respons Lintas Proses** + + ?> Beberapa situasi, perlu mengirim respons ke klien di [Task process](/learn?id=taskworker进程). Ini dapat menggunakan `detach` untuk membuat objek `$response` independen. Di [Task process](/learn?id=taskworker进程) dapat membangun ulang `$response`, mengirim respons request `Http`. + + ```php + $http = new Swoole\Http\Server("0.0.0.0", 9501); + + $http->set(['task_worker_num' => 1, 'worker_num' => 1]); + + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->task(strval($resp->fd)); + }); + + $http->on('finish', function () { + echo "task finish"; + }); + + $http->on('task', function ($serv, $task_id, $worker_id, $data) { + var_dump($data); + $resp = Swoole\Http\Response::create($data); + $resp->end("in task"); + echo "async task\n"; + }); + + $http->start(); + ``` + + * **Mengirim Konten Apa Pun** + + ?> Beberapa skenario khusus, perlu mengirim konten respons khusus ke klien. Method `end` bawaan objek `Http\Response` tidak dapat memenuhi kebutuhan, dapat menggunakan `detach` untuk memisahkan objek respons, lalu merakit sendiri data respons protokol HTTP, dan menggunakan `Server->send` untuk mengirim data. + + ```php + $http = new Swoole\Http\Server("0.0.0.0", 9501); + + $http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->send($resp->fd, "HTTP/1.1 200 OK\r\nServer: server\r\n\r\nHello World\n"); + }); + + $http->start(); + ``` + +### create() + +?> **Membuat objek `Swoole\Http\Response` baru.** + +!> Sebelum menggunakan method ini, pastikan memanggil method `detach` untuk memisahkan objek `$response` lama, jika tidak dapat menyebabkan pengiriman konten respons dua kali untuk request yang sama. + +```php +Swoole\Http\Response::create(object|array|int $server = -1, int $fd = -1): Swoole\Http\Response +``` + +* **Parameter** + + * **`int $server`** + * **Fungsi**: Objek `Swoole\Server` atau `Swoole\Coroutine\Socket`, array (array hanya bisa dua parameter, pertama objek `Swoole\Server`, kedua objek `Swoole\Http\Request`), atau deskriptor file + * **Default**: -1 + * **Nilai Lain**: Tidak ada + + * **`int $fd`** + * **Fungsi**: Deskriptor file. Jika parameter `$server` adalah objek `Swoole\Server`, `$fd` wajib diisi + * **Default**: -1 + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil dipanggil mengembalikan objek `Swoole\Http\Response` baru, gagal mengembalikan `false` + +* **Contoh** + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + // Contoh 1 + $resp2 = Swoole\Http\Response::create($req->fd); + // Contoh 2 + $resp2 = Swoole\Http\Response::create($http, $req->fd); + // Contoh 3 + $resp2 = Swoole\Http\Response::create([$http, $req]); + // Contoh 4 + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->connect('127.0.0.1', 9501) + $resp2 = Swoole\Http\Response::create($socket); + $resp2->end("hello world"); +}); + +$http->start(); +``` + +### isWritable() + +?> **Memeriksa apakah objek `Swoole\Http\Response` sudah berakhir (`end`) atau sudah dipisahkan (`detach`).** + +```php +Swoole\Http\Response->isWritable(): bool +``` + +* **Return Value** + + * Objek `Swoole\Http\Response` belum berakhir atau belum dipisahkan mengembalikan `true`, jika tidak mengembalikan `false` + +!> Versi Swoole >= `v4.6.0` tersedia + +* **Contoh** + +```php +use Swoole\Http\Server; +use Swoole\Http\Request; +use Swoole\Http\Response; + +$http = new Server('0.0.0.0', 9501); + +$http->on('request', function (Request $req, Response $resp) { + var_dump($resp->isWritable()); // true + $resp->end('hello'); + var_dump($resp->isWritable()); // false + $resp->setStatusCode(403); // http response is unavailable (maybe it has been ended or detached) +}); + +$http->start(); +``` + +## Opsi Konfigurasi + +### http_parse_cookie + +?> **Konfigurasi untuk objek `Swoole\Http\Request`, menonaktifkan penguraian `Cookie`, akan menyimpan informasi `Cookies` mentah yang belum diolah di `header`. Default aktif** + +```php +$server->set([ + 'http_parse_cookie' => false, +]); +``` + +### http_parse_post + +?> **Konfigurasi untuk objek `Swoole\Http\Request`, mengatur sakelar penguraian pesan POST, default aktif** + +* Saat diatur `true` akan otomatis mengurai body request `Content-Type: x-www-form-urlencoded` ke array `POST`. +* Saat diatur `false` akan menonaktifkan penguraian `POST`. + +```php +$server->set([ + 'http_parse_post' => false, +]); +``` + +### http_parse_files + +?> **Konfigurasi untuk objek `Swoole\Http\Request`, mengatur sakelar penguraian upload file. Default aktif** + +```php +$server->set([ + 'http_parse_files' => false, +]); +``` + +### http_compression + +?> **Konfigurasi untuk objek `Swoole\Http\Response`, mengaktifkan kompresi. Default aktif.** + +!> - `http-chunk` tidak mendukung kompresi terpisah per segmen, jika menggunakan method [write](/http_server?id=write), kompresi akan dipaksa dimatikan. +- `http_compression` tersedia di `v4.1.0` atau versi lebih tinggi + +```php +$server->set([ + 'http_compression' => false, +]); +``` + +Saat ini mendukung tiga format kompresi: `gzip`, `br`, `deflate`. Level bawah akan secara otomatis memilih metode kompresi berdasarkan header `Accept-Encoding` yang dimasukkan browser klien (prioritas algoritma kompresi: `br` > `gzip` > `deflate`). + +**Ketergantungan:** + +`gzip` dan `deflate` bergantung pada library `zlib`, saat mengompilasi `Swoole` level bawah akan mendeteksi apakah sistem memiliki `zlib`. + +Dapat menggunakan `yum` atau `apt-get` untuk menginstal library `zlib`: + +```shell +sudo apt-get install libz-dev +``` + +Format kompresi `br` bergantung pada library `brotli` dari `google`, cara instalasi silakan cari `install brotli on linux`, saat mengompilasi `Swoole` level bawah akan mendeteksi apakah sistem memiliki `brotli`. + +### http_compression_level / compression_level / http_gzip_level + +?> **Level kompresi, konfigurasi untuk objek `Swoole\Http\Response`** + +!> `$level` Level kompresi, rentang `1-9`, semakin tinggi level semakin kecil ukuran setelah kompresi, tetapi konsumsi `CPU` lebih banyak. Default `1`, maksimal `9` + +### http_compression_min_length / compression_min_length + +?> **Mengatur byte minimum untuk mengaktifkan kompresi, konfigurasi untuk objek `Swoole\Http\Response`, hanya mengaktifkan kompresi jika melebihi nilai opsi ini. Default 20 byte.** + +!> Versi Swoole >= `v4.6.3` tersedia + +```php +$server->set([ + 'compression_min_length' => 128, +]); +``` + +### upload_tmp_dir + +?> **Mengatur direktori sementara upload file. Panjang direktori maksimum tidak boleh melebihi `220` byte** + +```php +$server->set([ + 'upload_tmp_dir' => '/data/uploadfiles/', +]); +``` + +### upload_max_filesize + +?> **Mengatur nilai maksimum upload file** + +```php +$server->set([ + 'upload_max_filesize' => 5 * 1024, +]); +``` + +### enable_static_handler + +Mengaktifkan fungsi penanganan request file statis, perlu digunakan bersama `document_root`. Default `false` + +### http_autoindex + +Mengaktifkan fungsi `http autoindex`. Default tidak aktif + +### http_index_files + +Digunakan bersama `http_autoindex`, menentukan daftar file yang perlu diindeks + +```php +$server->set([ + 'document_root' => '/data/webroot/example.com', + 'enable_static_handler' => true, + 'http_autoindex' => true, + 'http_index_files' => ['indesx.html', 'index.txt'], +]); +``` + +### http_compression_types / compression_types + +?> **Mengatur tipe respons yang perlu dikompresi, konfigurasi untuk objek `Swoole\Http\Response`** + +```php +$server->set([ + 'http_compression_types' => [ + 'text/html', + 'application/json' + ], + ]); +``` + +!> Versi Swoole >= `v4.8.12` tersedia + +### static_handler_locations + +?> **Mengatur path untuk penangan statis. Tipe array, default tidak aktif.** + +!> Versi Swoole >= `v4.4.0` tersedia + +```php +$server->set([ + 'static_handler_locations' => ['/static', '/app/images'], +]); +``` + +* Mirip dengan direktif `location` di `Nginx`, dapat menentukan satu atau lebih path sebagai path statis. Hanya `URL` di path yang ditentukan akan mengaktifkan penangan file statis, jika tidak akan dianggap request dinamis. +* Item `location` harus diawali / +* Mendukung path bertingkat, seperti `/app/images` +* Setelah mengaktifkan `static_handler_locations`, jika file yang diminta tidak ada, akan langsung mengembalikan error 404 + +### open_http2_protocol + +?> **Mengaktifkan penguraian protokol `HTTP2`** [Nilai default: `false`] + +!> Perlu mengaktifkan opsi [--enable-http2](/environment?id=编译选项) saat kompilasi, mulai `Swoole5` http2 dikompilasi secara default. + +### document_root + +?> **Konfigurasi direktori root file statis, digunakan bersama `enable_static_handler`.** + +!> Fitur ini cukup sederhana, jangan digunakan langsung di lingkungan publik + +```php +$server->set([ + 'document_root' => '/data/webroot/example.com', // Untuk versi v4.4.0 ke bawah, harus path absolut + 'enable_static_handler' => true, +]); +``` + +* Setelah mengatur `document_root` dan mengatur `enable_static_handler` menjadi `true`, saat level bawah menerima request `Http` akan memeriksa dulu apakah file ada di path document_root, jika ada akan langsung mengirim konten file ke klien, tidak memicu callback [onRequest](/http_server?id=on). +* Saat menggunakan fitur penanganan file statis, kode PHP dinamis dan file statis harus diisolasi, file statis disimpan di direktori tertentu + +### max_concurrency + +?> **Dapat membatasi jumlah request konkuren maksimum layanan `HTTP1/2`, setelah melebihi akan mengembalikan error `503`. Nilai default 4294967295, yaitu nilai maksimum unsigned int** + +```php +$server->set([ + 'max_concurrency' => 1000, +]); +``` + +### worker_max_concurrency + +?> **Setelah mengaktifkan korutinisasi satu-klik, proses `worker` akan terus menerima request. Untuk menghindari tekanan berlebih, kita dapat mengatur `worker_max_concurrency` untuk membatasi jumlah eksekusi request proses `worker`. Saat jumlah request melebihi nilai ini, proses `worker` akan menyimpan sementara request berlebih di antrian. Nilai default 4294967295, yaitu nilai maksimum unsigned int. Jika tidak mengatur `worker_max_concurrency` tetapi mengatur `max_concurrency`, level bawah akan otomatis mengatur `worker_max_concurrency` sama dengan `max_concurrency`** + +```php +$server->set([ + 'worker_max_concurrency' => 1000, +]); +``` + +!> Versi Swoole >= `v5.0.0` tersedia + +### http2_header_table_size + +?> Mendefinisikan ukuran maksimum `header table` koneksi jaringan HTTP/2. + +```php +$server->set([ + 'http2_header_table_size' => 0x1 +]) +``` + +### http2_enable_push + +?> Konfigurasi ini digunakan untuk mengaktifkan atau menonaktifkan push HTTP2. + +```php +$server->set([ + 'http2_enable_push' => 0x2 +]) +``` + +### http2_max_concurrent_streams + +?> Mengatur jumlah maksimum stream multipleks yang diterima dalam setiap koneksi jaringan HTTP/2. + +```php +$server->set([ + 'http2_max_concurrent_streams' => 0x3 +]) +``` + +### http2_init_window_size + +?> Mengatur ukuran inisialisasi jendela kontrol aliran HTTP/2. + +```php +$server->set([ + 'http2_init_window_size' => 0x4 +]) +``` + +### http2_max_frame_size + +?> Mengatur ukuran maksimum body dari satu frame protokol HTTP/2 yang dikirim melalui koneksi jaringan HTTP/2. + +```php +$server->set([ + 'http2_max_frame_size' => 0x5 +]) +``` + +### http2_max_header_list_size + +?> Mengatur ukuran maksimum header yang dapat dikirim dalam request pada stream HTTP/2. + +```php +$server->set([ + 'http2_max_header_list_size' => 0x6 +]) +``` diff --git a/public/id/index.html b/public/id/index.html new file mode 100644 index 0000000..9bafd8d --- /dev/null +++ b/public/id/index.html @@ -0,0 +1,109 @@ + + + + + Swoole Documents (简体中文) + + + + + + + + + + + + +
+ + + + + + + + + + diff --git a/public/id/learn.md b/public/id/learn.md new file mode 100644 index 0000000..2b94996 --- /dev/null +++ b/public/id/learn.md @@ -0,0 +1,390 @@ +# Pengetahuan Dasar + +## Empat Cara Mengatur Fungsi Callback + +* **Anonymous Function** + +```php +$server->on('Request', function ($req, $resp) use ($a, $b, $c) { + echo "hello world"; +}); +``` +!> Gunakan `use` untuk mengirim parameter ke anonymous function + +* **Static Class Method** + +```php +class A +{ + static function test($req, $resp) + { + echo "hello world"; + } +} +$server->on('Request', 'A::test'); +$server->on('Request', array('A', 'test')); +$server->on('Request', [A::class, 'test']); +``` +!> Static method yang digunakan harus `public` + +* **Function** + +```php +function my_onRequest($req, $resp) +{ + echo "hello world"; +} +$server->on('Request', 'my_onRequest'); +``` + +* **Object Method** + +```php +class A +{ + function test($req, $resp) + { + echo "hello world"; + } +} + +$object = new A(); +$server->on('Request', array($object, 'test')); +``` + +!> Method yang digunakan harus `public` + +## Synchronous IO dan Asynchronous IO + +Di `Swoole4+`, semua kode bisnis ditulis secara sinkron (era `Swoole1.x` mendukung penulisan asinkron, tetapi sekarang client asinkron sudah dihapus; kebutuhan yang sama bisa diimplementasikan dengan coroutine client). Tidak ada beban mental sama sekali, sesuai dengan kebiasaan berpikir manusia. Namun, penulisan sinkron di tingkat bawah mungkin memiliki perbedaan `synchronous IO / asynchronous IO`. + +Baik itu synchronous IO maupun asynchronous IO, `Swoole/Server` tetap bisa mempertahankan banyak koneksi client `TCP` (lihat [SWOOLE_PROCESS mode](/learn?id=swoole_process)). Layanan Anda bersifat blocking atau non-blocking tidak perlu mengatur parameter khusus—itu tergantung pada apakah kode Anda memiliki operasi synchronous IO. + +**Apa itu synchronous IO:** + +Contoh sederhana: saat eksekusi sampai `MySQL->query`, proses tidak melakukan apa pun dan menunggu hasil dari MySQL. Setelah hasilnya kembali, kode baru berjalan. Karena itu, kemampuan konkurensi layanan synchronous IO sangat rendah. + +**Kode seperti apa yang termasuk synchronous IO:** + +* Tanpa mengaktifkan [one-click coroutine](/runtime), sebagian besar operasi IO di kode Anda adalah synchronous IO. Setelah coroutine diaktifkan, operasi tersebut berubah menjadi asynchronous IO, dan proses tidak akan diam menunggu—lihat [coroutine scheduling](/coroutine?id=coroutine-scheduling). +* Beberapa `IO` tidak bisa diubah dengan one-click coroutine, misalnya `MongoDB` (kami yakin `Swoole` akan menyelesaikan ini). Perhatikan hal ini saat menulis kode. + +!> [Coroutine](/coroutine) bertujuan meningkatkan konkurensi. Jika aplikasi Anda tidak membutuhkan konkurensi tinggi, atau harus menggunakan operasi IO yang tidak bisa dibuat asinkron (misalnya MongoDB di atas), Anda bisa tidak mengaktifkan [one-click coroutine](/runtime), matikan [enable_coroutine](/server/setting?id=enable_coroutine), dan perbanyak proses `Worker`. Ini akan mirip dengan model `Fpm/Apache`. Perlu dicatat, karena `Swoole` adalah proses yang menetap, bahkan synchronous IO pun akan meningkat kinerjanya secara signifikan. Banyak perusahaan menerapkan cara ini. + +### Mengubah Synchronous IO menjadi Asynchronous IO + +[Subbab sebelumnya](/learn?id=synchronous-io-dan-asynchronous-io) menjelaskan apa itu synchronous/asynchronous IO. Di `Swoole`, dalam beberapa kasus operasi `IO` sinkron bisa diubah menjadi asinkron. + +- Setelah mengaktifkan [one-click coroutine](/runtime), operasi seperti `MySQL`, `Redis`, `Curl` akan berubah menjadi asynchronous IO. +- Menggunakan modul [Event](/event) untuk mengelola event secara manual, tambahkan `fd` ke [EventLoop](/learn?id=apa-itu-eventloop) untuk mengubahnya menjadi asynchronous IO. Contoh: + +```php +// Memantau perubahan file menggunakan inotify +$fd = inotify_init(); +// Tambahkan $fd ke EventLoop Swoole +Swoole\Event::add($fd, function () use ($fd){ + $var = inotify_read($fd); // Membaca file yang berubah setelah perubahan file. + var_dump($var); +}); +``` + +Kode di atas, jika tidak memanggil `Swoole\Event::add` untuk membuat IO asinkron, maka `inotify_read()` langsung akan memblokir proses Worker, dan permintaan lain tidak akan diproses. + +- Gunakan method [sendMessage()](/server/methods?id=sendMessage) milik `Swoole\Server` untuk komunikasi antar proses. Secara default `sendMessage` adalah synchronous IO, tetapi dalam beberapa kasus `Swoole` akan mengubahnya menjadi asynchronous IO. Contoh menggunakan [User Process](/server/methods?id=addprocess): + +```php +$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_BASE); +$serv->set( + [ + 'worker_num' => 1, + ] +); + +$serv->on('pipeMessage', function ($serv, $src_worker_id, $data) { + echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; + sleep(10);// Jika tidak menerima data dari sendMessage, buffer akan cepat penuh +}); + +$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { + +}); + +// Kasus 1: Synchronous IO (perilaku default) +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + while (1) { + var_dump($serv->sendMessage("big string", 0));// Defaultnya, jika buffer penuh, akan blocking di sini + } +}, false); + +// Kasus 2: Aktifkan dukungan coroutine untuk UserProcess melalui parameter enable_coroutine. +// Untuk mencegah coroutine lain tidak mendapat jadwal dari EventLoop, +// Swoole akan mengubah sendMessage menjadi asynchronous IO +$enable_coroutine = true; +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + while (1) { + var_dump($serv->sendMessage("big string", 0));// Jika buffer penuh, tidak blocking, tapi akan error + } +}, false, 1, $enable_coroutine); + +// Kasus 3: Di dalam UserProcess, jika ada async callback (misal timer, Swoole\Event::add, dll.), +// untuk mencegah callback lain tidak mendapat jadwal dari EventLoop, +// Swoole akan mengubah sendMessage menjadi asynchronous IO +$userProcess = new Swoole\Process(function ($worker) use ($serv) { + swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { + echo "timer\n"; + }); + while (1) { + var_dump($serv->sendMessage("big string", 0));// Jika buffer penuh, tidak blocking, tapi akan error + } +}, false); + +$serv->addProcess($userProcess); + +$serv->start(); +``` + +- Hal yang sama berlaku untuk komunikasi antar proses [Task Process](/learn?id=taskworker-process) melalui `sendMessage()`. Bedanya, dukungan coroutine di task process diaktifkan melalui konfigurasi Server [task_enable_coroutine](/server/setting?id=task_enable_coroutine), dan tidak ada `kasus 3`. Artinya, task process tidak akan mengubah sendMessage menjadi asynchronous IO hanya karena ada async callback. + +## Apa itu EventLoop + +`EventLoop`, atau event loop, bisa dipahami sederhananya sebagai `epoll_wait`. Semua handle (fd) dari event yang akan terjadi dimasukkan ke dalam `epoll_wait`, termasuk event seperti readable, writable, error, dll. + +Proses yang bersangkutan akan terblokir pada fungsi kernel `epoll_wait`. Ketika terjadi event (atau timeout), fungsi `epoll_wait` akan keluar dari blocking dan mengembalikan hasil, lalu memanggil fungsi PHP yang sesuai. Misalnya, saat menerima data dari client, akan memanggil callback `onReceive`. + +Ketika banyak fd dimasukkan ke dalam `epoll_wait` dan banyak event terjadi bersamaan, fungsi `epoll_wait` saat kembali akan memanggil callback yang sesuai satu per satu. Ini disebut satu putaran event loop, yaitu IO multiplexing. Kemudian akan memblokir lagi untuk memanggil `epoll_wait` ke putaran event loop berikutnya. + +## Masalah Batas Paket TCP + +Kode di [Mulai Cepat](/start/start_tcp_server) berjalan normal tanpa konkurensi, tetapi saat konkurensi tinggi akan muncul masalah batas paket TCP. Protokol `TCP` secara mekanisme internal menyelesaikan masalah urutan dan retransmisi packet loss dari `UDP`, tetapi dibandingkan `UDP`, `TCP` membawa masalah baru. Protokol `TCP` bersifat stream, paket data tidak memiliki batas, dan aplikasi yang menggunakan komunikasi `TCP` akan menghadapi masalah ini, yang dikenal sebagai TCP sticky packet problem. + +Karena komunikasi `TCP` bersifat stream, saat menerima `1` paket besar, paket tersebut bisa dipecah menjadi beberapa paket untuk dikirim. Beberapa `Send` di tingkat bawah juga bisa digabung menjadi satu pengiriman. Diperlukan dua operasi untuk mengatasinya: + +* Packet splitting: `Server` menerima beberapa paket data, perlu memisahkannya +* Packet merging: Data yang diterima `Server` hanya sebagian dari paket, perlu di-cache dan digabung menjadi paket utuh + +Karena itu, komunikasi jaringan TCP perlu menetapkan protokol komunikasi. Protokol komunikasi TCP umum meliputi `HTTP`, `HTTPS`, `FTP`, `SMTP`, `POP3`, `IMAP`, `SSH`, `Redis`, `Memcache`, `MySQL`. + +Perlu dicatat, Swoole sudah memiliki parser bawaan untuk banyak protokol umum guna mengatasi masalah batas paket TCP di server-protokol tersebut. Cukup dengan konfigurasi sederhana—lihat [open_http_protocol](/server/setting?id=open_http_protocol) / [open_http2_protocol](/http_server?id=open_http2_protocol) / [open_websocket_protocol](/server/setting?id=open_websocket_protocol) / [open_mqtt_protocol](/server/setting?id=open_mqtt_protocol) + +Selain protokol umum, Anda juga bisa menentukan protokol kustom. `Swoole` mendukung `2` jenis protokol komunikasi jaringan kustom. + +* **EOF Protocol** + +Prinsip protokol `EOF` adalah menambahkan karakter khusus di akhir setiap paket data untuk menandakan paket selesai. Misalnya, `Memcache`, `FTP`, `SMTP` menggunakan `\r\n` sebagai penanda akhir. Saat mengirim data, cukup tambahkan `\r\n` di akhir paket. Saat menggunakan protokol `EOF`, pastikan `EOF` tidak muncul di tengah paket data, karena dapat menyebabkan kesalahan pemisahan paket. + +Dalam kode `Server` dan `Client`, cukup atur `2` parameter untuk menggunakan protokol `EOF`. + +```php +$server->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +$client->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +``` + +Namun konfigurasi `EOF` di atas memiliki kinerja lebih rendah karena Swoole akan memeriksa setiap byte untuk melihat apakah data adalah `\r\n`. Selain cara di atas, bisa juga diatur seperti ini: + +```php +$server->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n", +)); +$client->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n", +)); +``` + +Konfigurasi ini memiliki kinerja lebih baik karena tidak perlu memeriksa semua byte, tetapi hanya bisa menyelesaikan masalah `packet splitting`, bukan `packet merging`. Artinya, mungkin `onReceive` akan menerima beberapa permintaan dari client sekaligus, dan Anda perlu memisahkannya sendiri, misalnya `explode("\r\n", $data)`. Kegunaan utama konfigurasi ini adalah untuk layanan tipe request-response (misalnya mengetik perintah di terminal), di mana tidak perlu memikirkan pemisahan data. Soalnya, client harus menunggu respons server sebelum mengirim permintaan kedua dan tidak akan mengirim `2` permintaan bersamaan. + +* **Fixed-length Header + Body Protocol** + +Metode fixed-length header sangat umum dan sering terlihat di program server. Protokol ini bercirikan setiap paket data selalu terdiri dari header + body. Header berisi field yang menentukan panjang body atau seluruh paket, biasanya direpresentasikan dengan `2-byte` / `4-byte` integer. Setelah menerima header, server dapat mengontrol secara tepat berapa banyak data yang masih perlu diterima untuk mendapatkan paket lengkap. Konfigurasi `Swoole` mendukung protokol ini dengan baik, dan Anda bisa mengatur `4` parameter secara fleksibel untuk menangani semua situasi. + +`Server` memproses paket data di fungsi callback [onReceive](/server/events?id=onreceive). Setelah protokol diatur, event [onReceive](/server/events?id=onreceive) hanya akan dipicu ketika paket data lengkap diterima. Client setelah mengatur protokol, panggilan [$client->recv()](/client?id=recv) tidak perlu lagi memberikan panjang data—fungsi `recv` akan kembali setelah menerima paket lengkap atau terjadi error. + +```php +$server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'n', //lihat php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 2, +)); +``` + +!> Untuk arti setiap konfigurasi, lihat bagian [Configuration](/server/setting?id=open_length_check) di bab `Server/Client` + +## Apa itu IPC + +Ada banyak cara komunikasi antar dua proses dalam satu host (IPC). Swoole menggunakan `2` cara: `Unix Socket` dan `sysvmsg`. Berikut penjelasannya: + +- **Unix Socket** + + Nama lengkap UNIX Domain Socket, disingkat `UDS`. Menggunakan API socket (socket, bind, listen, connect, read, write, close, dll.). Tidak seperti TCP/IP, UDS tidak perlu menentukan ip dan port, melainkan menggunakan nama file (misalnya `/tmp/php-fcgi.sock` antara FPM dan Nginx). UDS adalah implementasi kernel Linux untuk komunikasi full-memory tanpa konsumsi `IO`. Dalam pengujian `1` proses `write` dan `1` proses `read`, masing-masing `1024` byte data, `100` ribu komunikasi hanya membutuhkan `1,02` detik. Fungsinya sangat kuat. `Swoole` menggunakan cara IPC ini secara default. + + * **`SOCK_STREAM` dan `SOCK_DGRAM`** + + - Di `Swoole`, komunikasi `UDS` memiliki dua tipe: `SOCK_STREAM` dan `SOCK_DGRAM`. Sederhananya, ini seperti perbedaan antara TCP dan UDP. Saat menggunakan `SOCK_STREAM`, Anda tetap perlu mempertimbangkan [TCP packet boundary problem](/learn?id=masalah-batas-paket-tcp). + - Saat menggunakan `SOCK_DGRAM`, Anda tidak perlu memikirkan masalah batas paket TCP. Setiap data yang dikirim dengan `send()` memiliki batas—ukuran data yang dikirim sama dengan yang diterima, tanpa masalah packet loss atau ketidakurutan dalam transmisi. Urutan `send` menulis dan `recv` membaca sepenuhnya konsisten. Jika `send` berhasil, pasti bisa di-`recv`. + + Untuk IPC dengan data yang relatif kecil, `SOCK_DGRAM` sangat cocok. **Karena setiap paket `IP` memiliki batas maksimal 64k, saat menggunakan `SOCK_DGRAM` untuk IPC, pengiriman data sekali tidak boleh melebihi 64k. Perhatikan juga bahwa jika kecepatan menerima paket terlalu lambat, buffer OS akan penuh dan paket akan dibuang—karena UDP mengizinkan packet loss. Anda bisa memperbesar ukuran buffer**. + +- **sysvmsg** + + Yaitu `message queue` yang disediakan Linux. Cara `IPC` ini menggunakan nama file sebagai `key` untuk berkomunikasi. Cara ini sangat tidak fleksibel dan jarang digunakan dalam proyek nyata. + + * **Cara IPC ini hanya berguna dalam dua skenario:** + + - Mencegah kehilangan data: jika seluruh service mati, pesan dalam antrean tetap ada dan bisa dilanjutkan setelah restart. **Namun tetap ada masalah dirty data**. + - Pengiriman data dari luar: misalnya, `Worker process` di Swoole mengirim tugas ke `Task process` melalui message queue. Proses pihak ketiga juga bisa mengirim tugas ke antrean untuk dikonsumsi Task, bahkan Anda bisa menambahkan pesan secara manual ke antrean dari command line. + +## Perbedaan Master Process, Reactor Thread, Worker Process, Task Process, Manager Process :id=diff-process + +### Master Process + +* Master process adalah proses multi-thread, lihat [Process/Thread Structure Diagram](/server/init?id=process-thread-structure-diagram) + +### Reactor Thread + +* Reactor thread adalah thread yang dibuat di dalam Master process +* Bertanggung jawab memelihara koneksi `TCP` client, menangani network `IO`, memproses protokol, mengirim dan menerima data +* Tidak mengeksekusi kode `PHP` apa pun +* Menyangga, menggabung, dan memisahkan data dari `TCP` client menjadi paket request yang lengkap + +### Worker Process + +* Menerima paket data request dari `Reactor` thread dan mengeksekusi fungsi callback `PHP` untuk memproses data +* Menghasilkan data respons dan mengirimkannya ke `Reactor` thread, yang kemudian mengirimkannya ke `TCP` client +* Bisa dalam mode asynchronous non-blocking atau synchronous blocking +* `Worker` berjalan sebagai multi-process + +### TaskWorker Process + +* Menerima tugas dari `Worker` process melalui method Swoole\Server->[task](/server/methods?id=task) / [taskwait](/server/methods?id=taskwait) / [taskCo](/server/methods?id=taskCo) / [taskWaitMulti](/server/methods?id=taskWaitMulti) +* Memproses tugas dan mengembalikan hasil data ke `Worker` process (menggunakan [Swoole\Server->finish](/server/methods?id=finish)) +* Sepenuhnya dalam mode **synchronous blocking** +* `TaskWorker` berjalan sebagai multi-process, [contoh task lengkap](/start/start_task) + +### Manager Process + +* Bertanggung jawab membuat/mendaur ulang proses `worker` / `task` + +Hubungan mereka bisa dipahami dengan: `Reactor` itu seperti `nginx`, `Worker` itu seperti `PHP-FPM`. `Reactor` thread memproses request jaringan secara async dan paralel, lalu meneruskannya ke `Worker` process untuk diproses. Komunikasi antara `Reactor` dan `Worker` dilakukan melalui [unixSocket](/learn?id=apa-itu-ipc). + +Dalam aplikasi `PHP-FPM`, tugas sering dikirim secara asinkron ke antrean seperti `Redis`, dan beberapa proses `PHP` dijalankan di latar belakang untuk memproses tugas-tugas tersebut. `TaskWorker` yang disediakan `Swoole` adalah solusi yang lebih lengkap, menggabungkan pengiriman tugas, antrean, dan manajemen proses pemrosesan tugas `PHP` menjadi satu kesatuan. Melalui `API` tingkat bawah yang disediakan, pemrosesan tugas asinkron bisa diimplementasikan dengan sangat mudah. Selain itu, `TaskWorker` bisa mengembalikan hasil ke `Worker` setelah tugas selesai. + +`Reactor`, `Worker`, dan `TaskWorker` di `Swoole` dapat digabungkan secara erat untuk menyediakan penggunaan tingkat yang lebih tinggi. + +Analogi sederhana: anggap `Server` adalah pabrik, maka `Reactor` adalah sales yang menerima pesanan pelanggan. `Worker` adalah pekerja yang memproduksi barang setelah sales menerima pesanan. Sedangkan `TaskWorker` bisa dipahami sebagai staf administrasi yang membantu `Worker` mengerjakan tugas-tugas sampingan, sehingga `Worker` bisa fokus bekerja. + +Seperti pada gambar: + +![process_demo](_images/server/process_demo.png) + +## Pengertian Tiga Mode Operasi Server + +Di parameter ketiga konstruktor `Swoole\Server`, Anda bisa mengisi `3` nilai konstanta -- [SWOOLE_BASE](/learn?id=swoole_base), [SWOOLE_PROCESS](/learn?id=swoole_process), dan [SWOOLE_THREAD](/learn?id=swoole_thread). Berikut perbedaan serta kelebihan dan kekurangan ketiga mode tersebut. + +### SWOOLE_PROCESS + +Di mode SWOOLE_PROCESS, semua koneksi TCP client `Server` terhubung dengan [Master Process](/learn?id=reactor-thread). Implementasi internalnya cukup kompleks, menggunakan banyak mekanisme komunikasi antar proses dan manajemen proses. Cocok untuk skenario dengan logika bisnis yang sangat kompleks. `Swoole` menyediakan mekanisme manajemen proses dan perlindungan memori yang lengkap. Bahkan dengan logika bisnis yang sangat rumit, server bisa berjalan stabil dalam jangka panjang. + +`Swoole` menyediakan fungsionalitas `Buffer` di [Reactor](/learn?id=reactor-thread) thread untuk menangani banyak koneksi lambat dan client jahat yang mengirim byte demi byte. + +#### Kelebihan mode process: + +* Koneksi dan pengiriman data request terpisah, sehingga tidak ada ketidakseimbangan beban `Worker` process akibat perbedaan volume data koneksi +* Saat terjadi error fatal di `Worker` process, koneksi tidak terputus +* Bisa menerapkan konkurensi satu koneksi—hanya mempertahankan sedikit koneksi `TCP`, request bisa diproses secara konkuren di beberapa `Worker` process + +#### Kekurangan mode process: + +* Ada overhead `2` kali `IPC`. `Master` process dan `Worker` process perlu berkomunikasi menggunakan [unixSocket](/learn?id=apa-itu-ipc) +* `SWOOLE_PROCESS` tidak mendukung PHP ZTS. Dalam kasus ini, Anda hanya bisa menggunakan `SWOOLE_BASE` atau mengatur [single_thread](/server/setting?id=single_thread) menjadi true + +### SWOOLE_BASE + +Mode SWOOLE_BASE adalah `Server` asynchronous non-blocking tradisional. Sepenuhnya sama dengan program seperti `Nginx` dan `Node.js`. + +Parameter [worker_num](/server/setting?id=worker_num) tetap berlaku di mode `BASE`—akan menjalankan beberapa `Worker` process. + +Ketika ada koneksi TCP masuk, semua Worker process bersaing untuk koneksi tersebut, dan akhirnya satu worker process berhasil membuat koneksi TCP langsung dengan client. Setelah itu, semua pengiriman dan penerimaan data koneksi ini langsung berkomunikasi dengan worker tersebut, tanpa melalui Reactor thread Master process. + +* Di mode `BASE` tidak ada peran `Master` process, hanya ada peran [Manager](/learn?id=manager-process). +* Setiap `Worker` process menjalankan dua peran sekaligus: [Reactor](/learn?id=reactor-thread) thread ala SWOOLE_PROCESS dan `Worker` process. +* Di mode `BASE`, `Manager` process bersifat opsional. Saat `worker_num=1` dan tidak menggunakan fitur `Task` dan `MaxRequest`, Swoole akan langsung membuat satu `Worker` process tanpa membuat `Manager` process. + +#### Kelebihan mode BASE: + +* Mode `BASE` tidak memiliki overhead `IPC`, kinerjanya lebih baik +* Kode mode `BASE` lebih sederhana, tidak mudah error + +#### Kekurangan mode BASE: + +* Koneksi `TCP` dipertahankan di `Worker` process, jadi jika suatu `Worker` process mati, semua koneksi di dalamnya akan ditutup +* Sedikit koneksi `TCP` panjang tidak bisa memanfaatkan semua `Worker` process +* Koneksi `TCP` terikat dengan `Worker`. Dalam aplikasi koneksi panjang, beberapa koneksi memiliki volume data besar sehingga beban `Worker` process tempat koneksi tersebut berada menjadi sangat tinggi. Sementara koneksi lain volumenya kecil, membuat beban `Worker` process rendah. Tidak ada keseimbangan antar `Worker` process. +* Jika ada operasi blocking dalam fungsi callback, `Server` akan terdegradasi ke mode sinkron, yang dapat menyebabkan antrean [backlog](/server/setting?id=backlog) TCP penuh. + +#### Skenario yang cocok untuk mode BASE: + +Jika tidak perlu interaksi antar koneksi client, mode `BASE` bisa digunakan. Contoh: server `Memcache`, `HTTP`, dll. + +#### Batasan mode BASE: + +Di mode `BASE`, [method Server](/server/methods) selain [send](/server/methods?id=send) dan [close](/server/methods?id=close) **tidak mendukung** eksekusi lintas proses. + +!> Di versi v4.5.x mode `BASE`, hanya method `send` yang mendukung eksekusi lintas proses; di versi v4.6.x, hanya method `send` dan `close` yang mendukung. + +### SWOOLE_THREAD + +SWOOLE_THREAD adalah mode operasi baru yang diperkenalkan di `Swoole 6.0`. Dengan memanfaatkan mode `PHP ZTS`, sekarang kita bisa menjalankan service dengan mode multi-thread. + +Parameter [worker_num](/server/setting?id=worker_num) tetap berlaku di mode `THREAD`, hanya saja bukan membuat multi-process, melainkan membuat multi-thread. Beberapa `Worker` thread akan dijalankan. + +Hanya akan ada satu proses. Sub-proses akan berubah menjadi sub-thread yang bertanggung jawab menerima request client. + +#### Kelebihan mode THREAD: +* Komunikasi antar proses lebih sederhana, tanpa overhead komunikasi IPC tambahan +* Debug program lebih mudah—karena hanya satu proses, `gdb -p` lebih sederhana +* Memiliki kemudahan pemrograman IO konkuren dengan coroutine, sekaligus keunggulan eksekusi paralel multi-thread dan berbagi memori stack + +#### Kekurangan mode THREAD: +* Jika terjadi Crash atau memanggil Process::exit(), seluruh proses akan keluar. Anda perlu menyiapkan logika error retry dan reconnection di client, serta menggunakan supervisor dan docker/k8s untuk restart otomatis setelah proses keluar. +* `ZTS` dan operasi lock mungkin memiliki overhead tambahan—kinerja bisa sekitar `10%` lebih rendah dibanding model multi-process `NTS`. Jika service Anda stateless, tetap disarankan menggunakan model multi-process `NTS`. +* Tidak mendukung pengiriman objek dan resource antar thread. + +#### Skenario yang cocok untuk mode THREAD: +* Mode THREAD lebih efisien untuk pengembangan game server dan communication server. + +## Perbedaan Process, Process\Pool, dan UserProcess :id=process-diff + +### Process + +[Process](/process/process) adalah modul manajemen proses yang disediakan Swoole untuk menggantikan `pcntl` PHP. + +* Memudahkan komunikasi antar proses +* Mendukung redirect input dan output standar—di child process, `echo` tidak akan mencetak ke layar, tetapi menulis ke pipe. Membaca input keyboard bisa dialihkan menjadi membaca data dari pipe. +* Menyediakan interface [exec](/process/process?id=exec)—proses yang dibuat bisa menjalankan program lain dan berkomunikasi dengan mudah dengan parent process `PHP`. + +!> Modul `Process` tidak bisa digunakan di lingkungan coroutine. Gunakan `runtime hook` + `proc_open` untuk mengatasinya. Lihat [Coroutine Process Management](/coroutine/proc_open) + +### Process\Pool + +[Process\Pool](/process/process_pool) membungkus modul manajemen proses Server ke dalam class PHP, mendukung penggunaan process manager Swoole dalam kode PHP. + +Dalam proyek nyata, Anda sering perlu menulis script yang berjalan lama, seperti consumer antrean multi-process berbasis `Redis`, `Kafka`, `RabbitMQ`, crawler multi-process, dan lain-lain. Developer perlu menggunakan ekstensi `pcntl` dan `posix` untuk pemrograman multi-process, tetapi juga harus memiliki pemahaman mendalam tentang pemrograman sistem `Linux`—jika tidak, masalah mudah muncul. Menggunakan process manager dari Swoole bisa menyederhanakan pemrograman script multi-process secara signifikan. + +* Menjamin stabilitas work process +* Mendukung signal handling +* Mendukung message queue dan pengiriman pesan `TCP-Socket` + +### UserProcess + +`UserProcess` adalah work process kustom yang ditambahkan melalui [addProcess](/server/methods?id=addprocess). Biasanya digunakan untuk membuat work process khusus untuk monitoring, pelaporan, atau tugas spesifik lainnya. + +Meskipun `UserProcess` dikelola oleh [Manager process](/learn?id=manager-process), ia adalah proses yang relatif independen dibandingkan [Worker process](/learn?id=worker-process) dan digunakan untuk menjalankan fungsi kustom. diff --git a/public/id/learn_other.md b/public/id/learn_other.md new file mode 100644 index 0000000..bed22bd --- /dev/null +++ b/public/id/learn_other.md @@ -0,0 +1,71 @@ +# Pengetahuan Lainnya + +## Mengatur Timeout dan Retry DNS Resolution + +Dalam pemrograman jaringan, `gethostbyname` dan `getaddrinfo` sering digunakan untuk resolusi domain. Dua fungsi `C` ini tidak menyediakan parameter timeout. Sebenarnya, Anda bisa mengubah `/etc/resolv.conf` untuk mengatur timeout dan logika retry. + +!> Lihat dokumentasi `man resolv.conf` + +### Multiple NameServer + +``` +nameserver 192.168.1.3 +nameserver 192.168.1.5 +option rotate +``` + +Anda bisa mengonfigurasi beberapa `nameserver`. Sistem akan melakukan polling secara otomatis—jika query ke `nameserver` pertama gagal, akan beralih ke `nameserver` kedua untuk retry. + +Konfigurasi `option rotate` berfungsi untuk melakukan load balancing `nameserver` menggunakan mode round-robin. + +### Timeout Control + +``` +option timeout:1 attempts:2 +``` + +* `timeout`: Mengontrol timeout penerimaan `UDP`, dalam satuan detik, default `5` detik +* `attempts`: Mengontrol jumlah percobaan. Jika dikonfigurasi `2`, berarti maksimal `2` kali percobaan, default `5` kali + +Misalkan ada `2` `nameserver`, `attempts` `2`, timeout `1` detik. Jika semua server DNS tidak merespons, waktu tunggu maksimal adalah `4` detik (`2x2x1`). + +### Call Trace + +Gunakan [strace](/other/tools?id=strace) untuk melacak panggilan. + +Atur `nameserver` ke dua `IP` yang tidak ada, lalu jalankan kode `PHP`: `var_dump(gethostbyname('www.baidu.com'));` untuk resolusi domain. + +``` +socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 +connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.16")}, 16) = 0 +poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) +sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=3, events=POLLIN}], 1, 1000 + +) = 0 (Timeout) +socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 4 +connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.20.128.18")}, 16) = 0 +poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) +sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=4, events=POLLIN}], 1, 1000 + + +) = 0 (Timeout) +poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) +sendto(3, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=3, events=POLLIN}], 1, 1000 + + +) = 0 (Timeout) +poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) +sendto(4, "\346\5\1\0\0\1\0\0\0\0\0\0\3www\5baidu\3com\0\0\1\0\1", 31, MSG_NOSIGNAL, NULL, 0) = 31 +poll([{fd=4, events=POLLIN}], 1, 1000 + + + +) = 0 (Timeout) +close(3) = 0 +close(4) = 0 +``` + +Terlihat di sini total ada `4` kali percobaan ulang, dengan timeout panggilan `poll` diatur ke `1000ms` (`1 detik`). diff --git a/public/id/library.md b/public/id/library.md new file mode 100644 index 0000000..1a4bfbf --- /dev/null +++ b/public/id/library.md @@ -0,0 +1,27 @@ +# Library + +Setelah versi 4, Swoole mengintegrasikan modul [Library](https://github.com/swoole/library), **menggunakan kode PHP untuk menulis fungsionalitas kernel**, sehingga infrastruktur dasar menjadi lebih stabil dan andal. + +!> Modul ini juga bisa diinstal secara terpisah melalui composer. Saat menginstal secara terpisah, Anda perlu menonaktifkan library bawaan ekstensi dengan mengatur `swoole.enable_library=Off` di `php.ini`. + +Saat ini menyediakan komponen alat berikut: + +- [Coroutine\WaitGroup](https://github.com/swoole/library/blob/master/src/core/Coroutine/WaitGroup.php) untuk menunggu tugas coroutine konkuren, [dokumentasi](/coroutine/wait_group) +- [Coroutine\FastCGI](https://github.com/swoole/library/tree/master/src/core/Coroutine/FastCGI) FastCGI client, [dokumentasi](/coroutine_client/fastcgi) +- [Coroutine\Server](https://github.com/swoole/library/blob/master/src/core/Coroutine/Server.php) Coroutine Server, [dokumentasi](/coroutine/server) +- [Coroutine\Barrier](https://github.com/swoole/library/blob/master/src/core/Coroutine/Barrier.php) Coroutine barrier, [dokumentasi](/coroutine/barrier) + +- [CURL hook](https://github.com/swoole/library/tree/master/src/core/Curl) CURL dengan dukungan coroutine, [dokumentasi](/runtime?id=swoole_hook_curl) +- [Database](https://github.com/swoole/library/tree/master/src/core/Database) Enkapsulasi tingkat lanjut untuk berbagai connection pool database dan object proxy, [dokumentasi](/coroutine/conn_pool?id=database) +- [ConnectionPool](https://github.com/swoole/library/blob/master/src/core/ConnectionPool.php) Connection pool mentah, [dokumentasi](/coroutine/conn_pool?id=connectionpool) +- [Process\Manager](https://github.com/swoole/library/blob/master/src/core/Process/Manager.php) Process manager, [dokumentasi](/process/process_manager) + +- [StringObject](https://github.com/swoole/library/blob/master/src/core/StringObject.php), [ArrayObject](https://github.com/swoole/library/blob/master/src/core/ArrayObject.php), [MultibyteStringObject](https://github.com/swoole/library/blob/master/src/core/MultibyteStringObject.php) Pemrograman Array dan String berorientasi objek + +- [functions](https://github.com/swoole/library/blob/master/src/core/Coroutine/functions.php) Menyediakan beberapa fungsi coroutine, [dokumentasi](/coroutine/coroutine?id=functions) +- [Constant](https://github.com/swoole/library/tree/master/src/core/Constant.php) Konstanta konfigurasi umum +- [HTTP Status](https://github.com/swoole/library/blob/master/src/core/Http/Status.php) Kode status HTTP + +## Kode Contoh + +[Examples](https://github.com/swoole/library/tree/master/examples) diff --git a/public/id/manifest.json b/public/id/manifest.json new file mode 100644 index 0000000..a88a0c3 --- /dev/null +++ b/public/id/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "Swoole", + "short_name": "Swoole", + "icons": [ + { + "src": "/_images/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/_images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "/", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#5C9DFF", + "splash_pages": null +} diff --git a/public/id/memory/atomic.md b/public/id/memory/atomic.md new file mode 100644 index 0000000..93dafe1 --- /dev/null +++ b/public/id/memory/atomic.md @@ -0,0 +1,217 @@ +# Atomic Counter Tanpa Lock Antar Proses/Thread + +`Atomic` adalah class operasi hitung atomik yang disediakan `Swoole` di level bawah, memudahkan penambahan/pengurangan integer tanpa lock. + +* Pakai shared memory, bisa operasi hitung antar proses berbeda +* Berdasarkan instruksi atomik `CPU` dari `gcc/clang`, tanpa perlu lock +* Di program server, harus dibuat sebelum `Server->start` supaya bisa dipake di `Worker` process +* Default pake tipe `32` bit unsigned, kalo perlu `64` bit signed integer, bisa pake `Swoole\Atomic\Long` +* Mode multi-thread perlu pake `Swoole\Thread\Atomic` dan `Swoole\Thread\Atomic\Long`, selain namespace beda, interface-nya sama persis dengan `Swoole\Atomic` dan `Swoole\Atomic\Long`. + +!> Jangan bikin counter di callback kayak [onReceive](/server/events?id=onreceive), nanti memory terus naik, bocor memory. + +!> Dukung atomic counter `64` bit signed long integer, perlu pake `new Swoole\Atomic\Long`. `Atomic\Long` nggak dukung method `wait` dan `wakeup`. + +## Contoh Lengkap + +```php +$atomic = new Swoole\Atomic(); + +$serv = new Swoole\Server('127.0.0.1', '9501'); +$serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null' +]); +$serv->on("start", function ($serv) use ($atomic) { + if ($atomic->add() == 2) { + $serv->shutdown(); + } +}); +$serv->on("ManagerStart", function ($serv) use ($atomic) { + if ($atomic->add() == 2) { + $serv->shutdown(); + } +}); +$serv->on("ManagerStop", function ($serv) { + echo "shutdown\n"; +}); +$serv->on("Receive", function () { + +}); +$serv->start(); +``` + +## Method + +### __construct() + +Konstruktor. Bikin object atomic counter. + +```php +Swoole\Atomic::__construct(int $init_value = 0); +``` + + * **Parameter** + + * **`int $init_value`** + * **Fungsi**: Tentukan nilai inisialisasi + * **Default**: `0` + * **Nilai lain**: tidak ada + +!> -`Atomic` cuma bisa operasi integer `32` bit unsigned, maksimal `4.2` miliar, nggak dukung negatif; +-Di `Server`, atomic counter harus dibuat sebelum `Server->start`; +-Di [Process](/process/process), atomic counter harus dibuat sebelum `Process->start`. + +### add() + +Tambah hitungan. + +```php +Swoole\Atomic->add(int $add_value = 1): int +``` + + * **Parameter** + + * **`int $add_value`** + * **Fungsi**: Nilai yang ditambah【harus positif】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + * Method `add` balikin nilai hasil setelah operasi + +!> Kalo ditambah nilai asli sampe lebih dari `4.2` miliar, bakal overflow, bit tinggi dibuang. + +### sub() + +Kurang hitungan. + +```php +Swoole\Atomic->sub(int $sub_value = 1): int +``` + + * **Parameter** + + * **`int $sub_value`** + * **Fungsi**: Nilai yang dikurang【harus positif】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + * Method `sub` balikin nilai hasil setelah operasi + +!> Kalo nilai asli dikurang sampe di bawah `0` bakal overflow, bit tinggi dibuang. + +### get() + +Dapetin nilai hitungan saat ini. + +```php +Swoole\Atomic->get(): int +``` + + * **Return Value** + + * Balik nilai numerik saat ini + +### set() + +Set nilai saat ini ke angka yang ditentukan. + +```php +Swoole\Atomic->set(int $value): void +``` + + * **Parameter** + + * **`int $value`** + * **Fungsi**: Nilai target yang mau diset + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### cmpset() + +Kalo nilai saat ini sama dengan parameter `1`, set nilai saat ini jadi parameter `2`. + +```php +Swoole\Atomic->cmpset(int $cmp_value, int $set_value): bool +``` + + * **Parameter** + + * **`int $cmp_value`** + * **Fungsi**: Kalo nilai saat ini sama `$cmp_value` balik `true`, lalu set nilai jadi `$set_value`. Kalo beda balik `false`【harus integer kurang dari `4.2` miliar】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $set_value`** + * **Fungsi**: Kalo nilai saat ini sama `$cmp_value` balik `true`, lalu set nilai jadi `$set_value`. Kalo beda balik `false`【harus integer kurang dari `4.2` miliar】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### wait() + +Set ke status wait. + +!> Kalo nilai atomic counter `0`, program masuk status nunggu. Proses lain panggil `wakeup` bisa bangunin program lagi. Implementasi ini berbasis `Linux Futex`. Pake fitur ini, cuma pake `4` byte memory udah bisa mewujudkan fungsi nunggu, notif, lock. Di platform yang nggak dukung `Futex`, internal pake loop `usleep(1000)` simulasi. + +```php +Swoole\Atomic->wait(float $timeout = 1.0): bool +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Waktu timeout【set `-1` artinya nggak pernah timeout, bakal nunggu sampe ada proses lain yang bangunin】 + * **Satuan**: detik【dukung float, contoh `1.5` artinya `1s` + `500ms`】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + * Timeout balik `false`, kode error `EAGAIN`, bisa pake `swoole_errno` + * Sukses balik `true`, artinya ada proses lain yang berhasil bangunin lock saat ini lewat `wakeup` + + * **Lingkungan Coroutine** + + `wait` bakal blocking seluruh proses, bukan cuma coroutine. Jadi jangan pake `Atomic->wait()` di lingkungan coroutine biar nggak bikin proses gantung. + +!> -Pas pake fitur `wait/wakeup`, nilai atomic counter cuma boleh `0` atau `1`, kalo nggak bakal bermasalah; +-Kalo nilai atomic counter `1`, artinya nggak perlu masuk status nunggu, resource lagi tersedia. Fungsi `wait` bakal langsung balik `true`. + + * **Contoh Penggunaan** + + ```php + $n = new Swoole\Atomic; + if (pcntl_fork() > 0) { + echo "master start\n"; + $n->wait(1.5); + echo "master end\n"; + } else { + echo "child start\n"; + sleep(1); + $n->wakeup(); + echo "child end\n"; + } + ``` + +### wakeup() + +Bangunin proses lain yang ada di status wait. + +```php +Swoole\Atomic->wakeup(int $n = 1): bool +``` + + * **Parameter** + + * **`int $n`** + * **Fungsi**: Jumlah proses yang mau dibangunin + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +* Kalo atomic counter saat ini `0`, artinya nggak ada proses yang `wait`, `wakeup` bakal langsung balik `true`; +* Kalo atomic counter saat ini `1`, artinya ada proses yang `wait`, `wakeup` bakal bangunin proses yang nunggu, dan balik `true`; +* Setelah proses yang dibangunin balik, atomic counter diset jadi `0`, saat itu bisa panggil `wakeup` lagi buat bangunin proses lain yang `wait`. diff --git a/public/id/memory/coroutine_lock.md b/public/id/memory/coroutine_lock.md new file mode 100644 index 0000000..80c48e0 --- /dev/null +++ b/public/id/memory/coroutine_lock.md @@ -0,0 +1,93 @@ +# Coroutine Lock + +* `Swoole 6.0` nambahin coroutine lock yang bisa dishare antar proses dan thread. Lock ini desainnya non-blocking, bisa mewujudkan sinkronisasi coroutine yang efisien di lingkungan multi-proses dan multi-thread. +* Kalo compile pake `--enable-iouring` dan kernel Linux dukung `io_uring futex`, coroutine lock Swoole bakal berbasis `io_uring futex` buat sinkronisasi. Coroutine bakal nunggu lock dengan antrian yang efisien, performa naik signifikan. +* Kalo `io_uring futex` nggak diaktifkan, coroutine lock bakal pake mekanisme sleep berbasis exponential backoff, yaitu setiap kali gagal dapet lock, waktu tunggu naik 2^n milidetik (n = jumlah kegagalan). Cara ini walau hindari busy waiting, tapi nambah overhead CPU dan delay. +* Coroutine lock desainnya reentrant, ngizinin coroutine yang megang lock buat ngelakuin operasi lock berkali-kali dengan aman. + +!> Jangan bikin lock di callback kayak [onReceive](/server/events?id=onreceive), nanti memory terus naik, bocor memory. + +!> Lock dan unlock harus dilakukan di coroutine yang sama, kalo nggak kondisi statis bakal rusak. + +## Contoh Penggunaan +```php +use Swoole\Coroutine\Lock; +use Swoole\Coroutine\WaitGroup; +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; + +$lock = new Lock(); +$waitGroup = new WaitGroup(); + +run(function() use ($lock, $waitGroup) { + go(function() use ($lock, $waitGroup) { + $waitGroup->add(); + $lock->lock(); + sleep(1); + $lock->unlock(); + $waitGroup->done(); + }); + + go(function() use ($lock, $waitGroup) { + $waitGroup->add(); + $lock->lock(); // nunggu coroutine yang megang lock buat unlock + sleep(1); + $lock->unlock(); + $waitGroup->done(); + }); + + echo 'Lock tidak nge-block proses'; + $waitGroup->wait(); +}); +``` + +## Method + +### __construct() + +Konstruktor. + +```php +Swoole\Coroutine\Lock::__construct(); +``` + +### lock() + +Pas operasi lock dilakukan, kalo lock lagi dipegang coroutine lain, coroutine saat ini bakal secara aktif lepas kendali CPU dan masuk status gantung. Setelah coroutine yang megang lock panggil `unlock()` buat lepas lock, coroutine yang nunggu bakal dibangunin dan coba dapetin lock lagi. + +```php +Swoole\Coroutine\Lock::lock(): bool; +``` + +* **Return Value** + + * Lock sukses balik `true`, saat itu bisa ubah variable shared. + * Lock gagal balik `false`. + +### trylock() + +Pas operasi lock dilakukan, kalo lock lagi dipegang coroutine lain, fungsi ini langsung balik `false`, tanpa gantung coroutine atau lepas kendali CPU. Desain non-blocking ini ngasih fleksibilitas ke pemanggil buat handle kompetisi, misalnya: coba lagi, menyerah, atau jalanin logic lain. + +```php +Swoole\Coroutine\Lock::trylock(): bool; +``` + +* **Return Value** + + * Lock sukses balik `true`, saat itu bisa ubah variable shared. + * Lock gagal balik `false`, terserah user mau ngapain selanjutnya. + +### unlock() + +Pas coroutine yang megang lock panggil `unlock()` buat lepas lock: + * Kalo `io_uring futex` diaktifkan: sistem bakal bangunin satu coroutine dari antrian tunggu secara tepat, memastikan perpindahan lock yang efisien dan teratur. + * Kalo `io_uring futex` nggak diaktifkan: coroutine yang nunggu harus nunggu sampe waktu backoff-nya selesai, lalu bersaing lagi buat dapetin lock. + +```php +Swoole\Coroutine\Lock::unlock(): bool; +``` + +* **Return Value** + + * Unlock sukses balik `true`. + * Unlock gagal balik `false`. diff --git a/public/id/memory/lock.md b/public/id/memory/lock.md new file mode 100644 index 0000000..d2d57b6 --- /dev/null +++ b/public/id/memory/lock.md @@ -0,0 +1,185 @@ +# Lock Antar Proses/Thread + +* Di kode `PHP` bisa dengan gampang bikin lock `Swoole\Lock` buat sinkronisasi data. Class `Lock` dukung `3` tipe lock. +* Mode multi-thread perlu pake `Swoole\Thread\Lock`, selain namespace beda, interface-nya sama persis dengan `Swoole\Lock`. + +| Tipe Lock | Keterangan | +|-------------------|------------| +| SWOOLE_MUTEX | Mutex lock | +| SWOOLE_RWLOCK | Read-write lock | +| SWOOLE_SPINLOCK | Spin lock | + +!> Jangan bikin lock di callback kayak [onReceive](/server/events?id=onreceive), nanti memory terus naik, bocor memory. + +## Contoh Penggunaan + +```php +$lock = new Swoole\Lock(SWOOLE_MUTEX); +echo "[Master]create lock\n"; +$lock->lock(); +if (pcntl_fork() > 0) { + sleep(1); + $lock->unlock(); +} else { + echo "[Child] Wait Lock\n"; + $lock->lock(); + echo "[Child] Get Lock\n"; + $lock->unlock(); + exit("[Child] exit\n"); +} +echo "[Master]release lock\n"; +unset($lock); +sleep(1); +echo "[Master]exit\n"; +``` + +## Peringatan + +!> Lock nggak bisa dipake di coroutine. Hati-hati, jangan pake `API` yang bisa picu switch coroutine di antara operasi `lock` dan `unlock`. + +### Contoh Salah + +!> Kode ini `100%` deadlock di mode coroutine. + +```php +$lock = new Swoole\Lock(); +$c = 2; + +while ($c--) { + go(function () use ($lock) { + $lock->lock(); + Co::sleep(1); + $lock->unlock(); + }); +} +``` + +## Method + +### __construct() + +Konstruktor. + +```php +Swoole\Lock::__construct(int $type = SWOOLE_MUTEX); +``` + +!> Jangan looping bikin/hancurin object lock, nanti bocor memory. + + * **Parameter** + + * **`int $type`** + * **Fungsi**: Tipe lock + * **Default**: `SWOOLE_MUTEX`【Mutex lock】 + * **Nilai lain**: tidak ada + +!> Setiap tipe lock dukung method yang beda. Misalnya read-write lock bisa dukung `$lock->lock_read()`. Selain file lock, tipe lock lain harus dibuat di parent process, biar child process hasil `fork` bisa bersaing dapetin lock. + +### lock() + +Operasi lock. Kalo ada proses lain yang megang lock, ini bakal blocking sampe proses yang megang lock `unlock()`. + +#### Setelah versi 6.1.0 +```php +Swoole\Lock->lock(int $operation, float $timeout = -1): bool +``` + + * **Parameter** + + * **`int $operation`** + * **Fungsi**: Tipe operasi lock + * **Default**: `LOCK_EX`【Exclusive lock】 + * **Nilai lain**: + * `LOCK_EX`: Exclusive lock + * `LOCK_SH`: Shared lock (read-only lock, cuma `SWOOLE_RWLOCK` yang dukung) + * `LOCK_NB`: Non-blocking lock (langsung balik kalo nggak dapet lock) + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: detik【dukung float, contoh `1.5` artinya `1s` + `500ms`】 + * **Default**: `-1`【artinya nggak pernah timeout】 + * **Nilai lain**: tidak ada + + * **Return Value** + + * Kalo dalam waktu yang ditentukan nggak dapet lock, balik `false` + * Lock sukses balik `true` + +Selain `unlock()`, method lain udah deprecated dan nggak bisa dipake, termasuk: +- `trylock()` +- `lock_read()` +- `trylock_read()` +- `lockwait()` + +#### Sebelum versi 6.1.0 + +```php +Swoole\Lock->lock(): bool +``` + +### unlock() + +Lepas lock. + +```php +Swoole\Lock->unlock(): bool +``` + +### trylock() + +Operasi lock. Bedanya sama `lock`, `trylock()` nggak blocking, langsung balik. + +```php +Swoole\Lock->trylock(): bool +``` + + * **Return Value** + + * Lock sukses balik `true`, saat itu bisa ubah variable shared + * Lock gagal balik `false`, artinya ada proses lain yang megang lock + +### lock_read() + +Read-only lock. + +```php +Swoole\Lock->lock_read(): bool +``` + +* Selagi megang read lock, proses lain tetep bisa dapet read lock dan lanjut baca; +* Tapi nggak bisa `$lock->lock()` atau `$lock->trylock()`, dua method ini dapetin exclusive lock. Pas exclusive lock dipasang, proses lain nggak bisa lakukan operasi lock apa pun, termasuk read lock; +* Kalo proses lain dapet exclusive lock (panggil `$lock->lock()`/`$lock->trylock()`), `$lock->lock_read()` bakal blocking sampe proses yang megang exclusive lock lepas lock. + +!> Cuma lock tipe `SWOOLE_RWLOCK` yang dukung read-only lock + +### trylock_read() + +Lock. Method ini sama kayak `lock_read()`, tapi non-blocking. + +```php +Swoole\Lock->trylock_read(): bool +``` + +!> Panggilan langsung balik, harus cek return value buat menentukan apakah dapet lock. + +### lockwait() + +Operasi lock. Fungsinya sama kayak `lock()`, tapi `lockwait()` bisa set timeout. + +```php +Swoole\Lock->lockwait(float $timeout = 1.0): bool +``` + + * **Parameter** + + * **`float $timeout`** + * **Fungsi**: Waktu timeout + * **Satuan**: detik【dukung float, contoh `1.5` artinya `1s` + `500ms`】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + * Kalo dalam waktu yang ditentukan nggak dapet lock, balik `false` + * Lock sukses balik `true` + +!> Cuma lock tipe `Mutex` yang dukung `lockwait` diff --git a/public/id/memory/table.md b/public/id/memory/table.md new file mode 100644 index 0000000..ba902b7 --- /dev/null +++ b/public/id/memory/table.md @@ -0,0 +1,446 @@ +# Table Shared Memory Berperforma Tinggi + +Karena bahasa `PHP` nggak dukung multi-thread, `Swoole` pake mode multi-proses. Di mode multi-proses ada isolasi memory proses, jadi ngubah variable `global` dan superglobal di worker process, nggak berpengaruh di proses lain. + +> Kalo set `worker_num=1`, nggak ada isolasi proses, bisa pake variable global buat nyimpen data + +```php +$fds = array(); +$server->on('connect', function ($server, $fd){ + echo "connection open: {$fd}\n"; + global $fds; + $fds[] = $fd; + var_dump($fds); +}); +``` + +`$fds` walau variable global, cuma berlaku di proses saat ini. `Swoole` server internal bakal bikin banyak `Worker` process, pas `var_dump($fds)` dicetak, nilainya cuma `fd` dari sebagian koneksi. + +Solusinya pake external storage: + +* Database, kayak: `MySQL`, `MongoDB` +* Cache server, kayak: `Redis`, `Memcache` +* File disk, kalo akses multi-proses bersamaan perlu lock + +Operasi database biasa dan file disk, ada banyak waktu tunggu `IO`. Makanya disarankan pake: + +* `Redis` in-memory database, baca/tulis cepet banget, tapi ada masalah koneksi TCP, performa juga bukan yang tertinggi. +* `/dev/shm` filesystem in-memory, semua operasi baca/tulis di memory, tanpa `IO`, performa tinggi banget, tapi data nggak terformat dan ada masalah sinkronisasi. + +?> Selain storage di atas, disarankan pake shared memory buat nyimpen data. `Swoole\Table` adalah struktur data konkuren berperforma tinggi berbasis shared memory dan lock. Buat ngatasin masalah sharing data dan sinkronisasi lock multi-proses/multi-thread. Kapasitas memory `Table` nggak dibatasin `memory_limit` PHP + +!> Jangan baca/tulis `Table` pake cara array, harus pake API yang disediakan dokumen; +Object `Table\Row` yang diambil cara array adalah object sekali pakai, jangan andelin buat operasi berlebihan. +Mulai `v4.7.0`, nggak dukung lagi baca/tulis `Table` pake cara array, dan object `Table\Row` dihapus. + +* **Kelebihan** + + * Performa kuat, satu thread bisa baca/tulis `200` ribu kali per detik; + * Kode aplikasi nggak perlu lock, `Table` punya row lock spin lock bawaan, semua operasi aman multi-thread/multi-proses. User nggak perlu mikirin sinkronisasi data; + * Dukung multi-proses, `Table` bisa dipake buat sharing data antar proses; + * Pake row lock, bukan global lock. Cuma kalo 2 proses di `CPU` time yang sama baca data yang sama baru terjadi rebutan lock. + +* **Iterasi** + +!> Jangan lakukan operasi hapus selama iterasi (bisa ambil semua `key` dulu baru hapus) + +Class `Table` implement interface iterator dan `Countable`, bisa pake `foreach` buat iterasi, pake `count` buat hitung jumlah baris. + +```php +foreach($table as $row) +{ + var_dump($row); +} +echo count($table); +``` + +## Properti + +### size + +Dapetin jumlah maksimum baris table. + +```php +Swoole\Table->size; +``` + +### memorySize + +Dapetin ukuran memory yang beneran dipake, satuan byte. + +```php +Swoole\Table->memorySize; +``` + +## Method + +### __construct() + +Buat memory table. + +```php +Swoole\Table::__construct(int $size, float $conflict_proportion = 0.2); +``` + + * **Parameter** + + * **`int $size`** + * **Fungsi**: Jumlah maksimum baris table + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + !> Karena `Table` dibangun di atas shared memory, nggak bisa di-expand secara dinamis. Jadi `$size` harus dihitung dan diset sebelum dibuat. Jumlah maksimum baris yang bisa disimpan `Table` berbanding lurus dengan `$size`, tapi nggak persis sama. Misal `$size` `1024`, jumlah baris yang beneran bisa disimpan **kurang dari** `1024`. Kalo `$size` kegedean, memory mesin kurang, `Table` bakal gagal dibuat. + + * **`float $conflict_proportion`** + * **Fungsi**: Rasio maksimum konflik hash + * **Default**: `0.2` (yaitu `20%`) + * **Nilai lain**: Minimal `0.2`, maksimal `1` + + * **Perhitungan Kapasitas** + + * Kalo `$size` bukan pangkat `2`, kayak `1024`, `8192`, `65536`, dll, internal bakal otomatis sesuaikan ke angka terdekat. Kalo kurang dari `1024`, default jadi `1024`, yaitu `1024` adalah minimum. Mulai `v4.4.6` minimum jadi `64`. + * Total memory yang dipake `Table` adalah (`Panjang struktur HashTable` + `Panjang KEY 64 byte` + `nilai $size`) * (`1 + nilai $conflict_proportion sebagai hash conflict`) * (`ukuran kolom`). + * Kalo data `Key` dan rasio konflik Hash lebih dari `20%`, kapasitas blok memory cadangan kurang, pas `set` data baru bakal error `Unable to allocate memory`, balik `false`, simpan gagal. Saat itu perlu perbesar `$size` dan restart service. + * Kalo memory cukup, usahakan set nilai ini agak gede. + +### column() + +Nambahin kolom di memory table. + +```php +Swoole\Table->column(string $name, int $type, int $size = 0); +``` + + * **Parameter** + + * **`string $name`** + * **Fungsi**: Nama field + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $type`** + * **Fungsi**: Tipe field + * **Default**: tidak ada + * **Nilai lain**: `Table::TYPE_INT`, `Table::TYPE_FLOAT`, `Table::TYPE_STRING` + + * **`int $size`** + * **Fungsi**: Panjang maksimum string【field tipe string wajib specify `$size`】 + * **Satuan**: byte + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **Penjelasan Tipe `$type`** + +Tipe | Keterangan +---|--- +Table::TYPE_INT | Default 8 byte +Table::TYPE_STRING | Setelah diset, string yang diset nggak boleh lebih dari `$size` +Table::TYPE_FLOAT | Pake 8 byte memory + +### create() + +Buat memory table. Setelah struktur table didefinisikan, jalanin `create` buat minta memory ke OS, bikin table. + +```php +Swoole\Table->create(): bool +``` + +Pake method `create` buat bikin table, bisa baca properti [memorySize](/memory/table?id=memorysize) buat dapetin ukuran memory yang beneran dipake + + * **Tips** + + * Sebelum panggil `create`, nggak bisa pake method baca/tulis kayak `set`, `get` + * Setelah panggil `create`, nggak bisa pake method `column` buat nambah field baru + * Memory sistem kurang, minta gagal, `create` balik `false` + * Minta memory sukses, `create` balik `true` + + !> `Table` pake shared memory buat nyimpen data. Sebelum bikin child process, harus jalanin `Table->create()`; + `Table` di `Server`, `Table->create()` harus dijalanin sebelum `Server->start()`. + + * **Contoh Penggunaan** + +```php +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); +$table->create(); + +$worker = new Swoole\Process(function () {}, false, false); +$worker->start(); + +//$serv = new Swoole\Server('127.0.0.1', 9501); +//$serv->start(); +``` + +### set() + +Set data baris. `Table` pake cara `key-value` buat akses data. + +```php +Swoole\Table->set(string $key, array $value): bool +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: `key` data + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + !> `$key` yang sama berarti baris data yang sama. Kalo `set` pake `key` yang sama, bakal timpa data sebelumnya. Panjang maksimum `key` 63 byte + + * **`array $value`** + * **Fungsi**: `value` data + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + !> Harus array, harus sama persis dengan `$name` yang didefinisikan di field + + * **Return Value** + + * Set sukses balik `true` + * Gagal balik `false`, mungkin karena konflik Hash kebanyakan, space dinamis nggak bisa alokasi memory. Bisa perbesar parameter ke-2 konstruktor + +!> -`Table->set()` bisa set nilai semua field, atau coba ubah sebagian field; + -`Table->set()` sebelum diset, semua field baris data kosong; + -`set`/`get`/`del` udah ada row lock, jadi nggak perlu panggil `lock`; + -**Key bukan binary safe, harus string, jangan masukin data binary.** + + * **Contoh Penggunaan** + +```php +$table->set('1', ['id' => 1, 'name' => 'test1', 'age' => 20]); +$table->set('2', ['id' => 2, 'name' => 'test2', 'age' => 21]); +$table->set('3', ['id' => 3, 'name' => 'test3', 'age' => 19]); +``` + + * **Set String Melebihi Panjang Maksimum** + + Kalo panjang string yang dimasukin lebih dari ukuran maksimum yang ditentukan pas definisi kolom, internal bakal otomatis potong. + + ```php + $table->column('str_value', Swoole\Table::TYPE_STRING, 5); + $table->set('hello', array('str_value' => 'world 123456789')); + var_dump($table->get('hello')); + ``` + + * Kolom `str_value` maksimal 5 byte, tapi `set` pake string lebih dari `5` byte + * Internal otomatis potong 5 byte, jadinya nilai `str_value` `world` + +!> Mulai `v4.3`, internal alignment memory. Panjang string harus kelipatan 8. Misal panjang 5 otomatis di-align ke 8 byte, jadi nilai `str_value` `world 12` + +### incr() + +Operasi increment atomik. + +```php +Swoole\Table->incr(string $key, string $column, mixed $incrby = 1): int +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: `key` data【kalo baris `$key` nggak ada, default nilai kolom `0`】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`string $column`** + * **Fungsi**: Nama kolom【cuma dukung float dan integer】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`string $incrby`** + * **Fungsi**: Nilai increment【kalo kolom `int`, `$incrby` harus `int`. Kalo kolom `float`, `$incrby` harus `float`】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + Balik nilai hasil akhir + +### decr() + +Operasi decrement atomik. + +```php +Swoole\Table->decr(string $key, string $column, mixed $decrby = 1): int +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: `key` data【kalo baris `$key` nggak ada, default nilai kolom `0`】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`string $column`** + * **Fungsi**: Nama kolom【cuma dukung float dan integer】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`string $decrby`** + * **Fungsi**: Nilai decrement【kalo kolom `int`, `$decrby` harus `int`. Kalo kolom `float`, `$decrby` harus `float`】 + * **Default**: `1` + * **Nilai lain**: tidak ada + + * **Return Value** + + Balik nilai hasil akhir + + !> Kalo nilai `0` di-decrement bakal jadi negatif + +### get() + +Dapetin satu baris data. + +```php +Swoole\Table->get(string $key, string $field = null): array|false +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: `key` data【harus string】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`string $field`** + * **Fungsi**: Kalo specify `$field`, cuma balikin nilai field itu, bukan seluruh record + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **Return Value** + + * Kalo `$key` nggak ada, balik `false` + * Sukses balik array hasil + * Kalo specify `$field`, cuma balikin nilai field itu, bukan seluruh record + +### exist() + +Cek apakah suatu key ada di table. + +```php +Swoole\Table->exist(string $key): bool +``` + + * **Parameter** + + * **`string $key`** + * **Fungsi**: `key` data【harus string】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### count() + +Balikin jumlah entri di table. + +```php +Swoole\Table->count(): int +``` + +### del() + +Hapus data. + +!> `Key` bukan binary safe, harus string, jangan masukin data binary; **Jangan hapus pas iterasi**. + +```php +Swoole\Table->del(string $key): bool +``` + + * **Return Value** + + * Kalo data `$key` nggak ada, balik `false` + * Hapus sukses balik `true` + +### stats() + +Dapetin status `Swoole\Table`. + +```php +Swoole\Table->stats(): array +``` + +!> Swoole >= `v4.8.0` + +## Fungsi Bantuan :id=swoole_table + +Biar user gampang bikin `Swoole\Table`. + +```php +function swoole_table(int $size, string $fields): Swoole\Table +``` + +!> Swoole >= `v4.6.0`. Format `$fields` `foo:i/foo:s:num/foo:f` + +| Singkatan | Panjang | Tipe | +| --------- | ------- | ----------------- | +| i | int | Table::TYPE_INT | +| s | string | Table::TYPE_STRING | +| f | float | Table::TYPE_FLOAT | + +Contoh: + +```php +$table = swoole_table(1024, 'fd:int, reactor_id:i, data:s:64'); +var_dump($table); + +$table = new Swoole\Table(1024, 0.25); +$table->column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); +$table->create(); +var_dump($table); +``` + +## Contoh Lengkap + +```php +column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); +$table->create(); + +$serv = new Swoole\Server('127.0.0.1', 9501); +$serv->set(['dispatch_mode' => 1]); +$serv->table = $table; + +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + $cmd = explode(" ", trim($data)); + + //get + if ($cmd[0] == 'get') + { + //get self + if (count($cmd) < 2) + { + $cmd[1] = $fd; + } + $get_fd = intval($cmd[1]); + $info = $serv->table->get($get_fd); + $serv->send($fd, var_export($info, true)."\n"); + } + //set + elseif ($cmd[0] == 'set') + { + $ret = $serv->table->set($fd, array('reactor_id' => $reactor_id, 'fd' => $fd, 'data' => $cmd[1])); + if ($ret === false) + { + $serv->send($fd, "ERROR\n"); + } + else + { + $serv->send($fd, "OK\n"); + } + } + else + { + $serv->send($fd, "command error.\n"); + } +}); + +$serv->start(); +``` diff --git a/public/id/other/alias.md b/public/id/other/alias.md new file mode 100644 index 0000000..fbda66c --- /dev/null +++ b/public/id/other/alias.md @@ -0,0 +1,106 @@ +# Ringkasan Alias Fungsi + +## Nama Pendek Korutin + +Menyederhanakan penulisan nama `API` terkait korutin. Bisa mengubah pengaturan `php.ini` `swoole.use_shortname=On/Off` untuk mengaktifkan/menonaktifkan nama pendek, default aktif. + +Semua nama class dengan prefiks `Swoole\Coroutine` dipetakan ke `Co`. Selain itu ada beberapa pemetaan lain: + +### Membuat Korutin + +```php +// Swoole\Coroutine::create setara dengan fungsi go +go(function () { + Co::sleep(0.5); + echo 'hello'; +}); +go('test'); +go([$object, 'method']); +``` + +### Operasi Channel + +```php +// Coroutine\Channel bisa disingkat menjadi chan +$c = new chan(1); +$c->push($data); +$c->pop(); +``` + +### Eksekusi Tertunda + +```php +// Swoole\Coroutine::defer bisa langsung menggunakan defer +defer(function () use ($db) { + $db->close(); +}); +``` + +## Method Nama Pendek + +!> Cara berikut `go` dan `defer`, tersedia di Swoole versi >= `v4.6.3` + +```php +use function Swoole\Coroutine\go; +use function Swoole\Coroutine\run; +use function Swoole\Coroutine\defer; + +run(function () { + defer(function () { + echo "co1 end\n"; + }); + sleep(1); + go(function () { + usleep(100000); + defer(function () { + echo "co2 end\n"; + }); + echo "co2\n"; + }); + echo "co1\n"; +}); +``` + +## API Sistem Korutin + +Di versi `4.4.4`, `API` korutin terkait operasi sistem dipindahkan dari class `Swoole\Coroutine` ke class `Swoole\Coroutine\System`. Menjadi modul baru yang independen. Untuk kompatibilitas ke bawah, method alias di atas class `Coroutine` tetap dipertahankan. + +* Contoh `Swoole\Coroutine::sleep` setara dengan `Swoole\Coroutine\System::sleep` +* Contoh `Swoole\Coroutine::fgets` setara dengan `Swoole\Coroutine\System::fgets` + +## Pemetaan Alias Class Pendek + +!> Disarankan menggunakan gaya namespace. + +| Gaya Class Underscore | Gaya Namespace | +| ----------------------------- | ---------------------------- | +| swoole_server | Swoole\Server | +| swoole_client | Swoole\Client | +| swoole_process | Swoole\Process | +| swoole_timer | Swoole\Timer | +| swoole_table | Swoole\Table | +| swoole_lock | Swoole\Lock | +| swoole_atomic | Swoole\Atomic | +| swoole_atomic_long | Swoole\Atomic\Long | +| swoole_buffer | Swoole\Buffer | +| swoole_redis | Swoole\Redis | +| swoole_error | Swoole\Error | +| swoole_event | Swoole\Event | +| swoole_http_server | Swoole\Http\Server | +| swoole_http_client | Swoole\Http\Client | +| swoole_http_request | Swoole\Http\Request | +| swoole_http_response | Swoole\Http\Response | +| swoole_websocket_server | Swoole\WebSocket\Server | +| swoole_connection_iterator | Swoole\Connection\Iterator | +| swoole_exception | Swoole\Exception | +| swoole_http2_request | Swoole\Http2\Request | +| swoole_http2_response | Swoole\Http2\Response | +| swoole_process_pool | Swoole\Process\Pool | +| swoole_redis_server | Swoole\Redis\Server | +| swoole_runtime | Swoole\Runtime | +| swoole_server_port | Swoole\Server\Port | +| swoole_server_task | Swoole\Server\Task | +| swoole_table_row | Swoole\Table\Row | +| swoole_timer_iterator | Swoole\Timer\Iterator | +| swoole_websocket_closeframe | Swoole\Websocket\Closeframe | +| swoole_websocket_frame | Swoole\Websocket\Frame | diff --git a/public/id/other/config.md b/public/id/other/config.md new file mode 100644 index 0000000..8e79efb --- /dev/null +++ b/public/id/other/config.md @@ -0,0 +1,10 @@ +# Konfigurasi ini + +Konfigurasi | Default | Fungsi +---|---|--- +swoole.enable_coroutine | On | `On`, `Off` mengaktifkan/mematikan korutin bawaan, [detail](/server/setting?id=enable_coroutine). +swoole.display_errors | On | Mengaktifkan/menonaktifkan pesan error `Swoole`. +swoole.unixsock_buffer_size | 8M | Mengatur ukuran buffer `Socket` untuk komunikasi antar proses, setara dengan [socket_buffer_size](/server/setting?id=socket_buffer_size). +swoole.use_shortname | On | Mengaktifkan/menonaktifkan alias pendek, [detail](/other/alias?id=coroutine-short-names). +swoole.enable_preemptive_scheduler | Off | Mencegah beberapa korutin menggunakan CPU terlalu lama (10ms CPU time) sehingga korutin lain tidak mendapat [jadwal](/coroutine?id=coroutine-scheduling), [contoh](https://github.com/swoole/swoole-src/tree/master/tests/swoole_coroutine_scheduler/preemptive). +swoole.enable_library | On | Mengaktifkan/menonaktifkan library bawaan ekstensi diff --git a/public/id/other/discussion.md b/public/id/other/discussion.md new file mode 100644 index 0000000..1421599 --- /dev/null +++ b/public/id/other/discussion.md @@ -0,0 +1,30 @@ +# Komunikasi Online + +## Feedback Issue + +Respon tercepat: [GitHub Issue](https://github.com/swoole/swoole-src/issues/) (Saat bertanya, harap hormati template issue dan aturan komunitas GitHub) + +Lihat cara [melaporkan bug](//wiki.swoole.com/#/other/issue) + +## Grup QQ + +* Grup 8: 1061662449 (2000 anggota, masih bisa join) +* Grup 7: 810919826 (2000 anggota, penuh) +* Grup 6: 705119448 (1000 anggota, penuh) +* Grup 5: 399424487 (2000 anggota, penuh) +* Grup 4: 193772828 (2000 anggota, penuh) +* Grup 2: 364153814 (2000 anggota, penuh) +* Grup 1: 321637118 (2000 anggota, penuh) +* Grup VIP: 626330624 (Anggota seumur hidup: 599 RMB) + +## Grup WeChat + +Karena tidak bisa langsung join grup WeChat, silakan tambahkan QR code di bawah sebagai teman, dan sebutkan: `Gabung grup diskusi Swoole`. Setelah disetujui, kamu akan diundang ke grup. + +![CS WeChat](../_images/wechat/kefu.png ':size=200') + +## Official Account WeChat + +Cari atau scan QR code di bawah untuk mengikuti official account `Swoole`, bisa mendapatkan info terbaru tentang progres proyek, rilis versi, acara dan pertemuan, proyek open source sekitar, studi kasus produk, dll. + +![Official Account WeChat](../_images/wechat/mp.png ':size=600x200') diff --git a/public/id/other/donate.md b/public/id/other/donate.md new file mode 100644 index 0000000..78117b5 --- /dev/null +++ b/public/id/other/donate.md @@ -0,0 +1,141 @@ +# Donasi untuk Proyek Swoole + +Donasi kamu adalah dukungan terbesar untuk tim pengembang `Swoole`. Kami akan terus mengembangkan dan memeliharanya. Donasi kamu akan digunakan untuk: + +* Pengembangan yang berkelanjutan dan mendalam +* Pembangunan dan pemeliharaan dokumentasi dan komunitas + +> [Klik untuk donasi proyek open source Swoole](https://gitee.com/swoole/swoole?donate=true) + +## Daftar Donasi + +Terima kasih kepada perusahaan dan individu berikut yang telah mendonasi untuk tim pengembang `Swoole`. + +!> Jika kamu sudah mendonasi `Swoole` tetapi tidak ada dalam daftar di bawah, atau ingin mengubah informasi terkait, silakan hubungi customer service Shivo. + +| Nama | Jumlah | LOGO | +| :----------------------------------------------------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [shopxo.net](https://shopxo.net/) | 1024 RMB | [![ShopXO](https://wenda-1252906962.file.myqcloud.com/images/sponsors/shopxo.png)](https://shopxo.net/) | +| [SegmentFault](https://segmentfault.com/) | 1024 RMB | [![](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/segmentfault.svg "SegmentFault")](https://segmentfault.com/) | +| [Guojiang Club](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | 3000 RMB | [![](https://wiki.swoole.com/static/uploads/wiki/202001/19/273190976660.jpg "Guojiang Club :size=autox150")](https://guojiang.club/?utm_source=swoole&utm_campaign=sponsor) | +| [Easysoft](https://www.easycorp.cn/) | 3000 RMB | [![ShopXO](https://wenda-1252906962.file.myqcloud.com/images/sponsors/easysoft.png)](https://www.easycorp.cn/) | +| [Six Star Education](https://www.sixstaredu.com/) | 4000 RMB | [![](https://wiki.swoole.com/static/uploads/wiki/201812/03/197630887599.jpg "Six Star Education :size=autox150")](https://www.sixstaredu.com/) | +| [codecasts.com](https://codecasts.com/) | 8000 RMB | [![](https://user-images.githubusercontent.com/6011686/28741127-791014ae-7442-11e7-929e-114e5047d9e5.png "codecasts")](https://codecasts.com/) | +| [51talk](http://www.51talk.com/) | 10000 RMB| [![](https://static.51talk.com/static/images/html/www_new/common/log.png?v=2017-12-21 "51talk")](http://www.51talk.com/) | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JumlahDonaturPesanChannel
1024.00Gao Yang SunnySwoole adalah proyek bagus, banyak developer di SegmentFault yang memperhatikannya. Semoga semakin baik, semoga komunitas Swoole semakin kuat!WeChat
66.66XinuTerima kasih, terima kasih Tieling TV, terima kasih Liaoning TV, salah naskah, terima kasih untuk proyek open source yang luar biasa, meningkatkan daya saing PHPer!OSChina
128.00KamuTerima kasih untuk proyek open source-mu! Jangan banyak omongOSChina
10.00endlessvastTerima kasih untuk proyek open source-mu!OSChina
100.000x7c6Terima kasih untuk proyek open source-mu!OSChina
50.00WeKeeyTerima kasih untuk proyek open source-mu!OSChina
200.00Gao JiufengSemoga ekosistem client korutin Swoole semakin baikOSChina
5000.00zhiwei*@gmail.comPlatform SMS Meilian SoftAlipay
3072.00wanjochan wan***@gmail.comKarena mulai digunakan di produksi, memutuskan menambah 1kAlipay
3000.00Easysoft http://www.cnezsoft.com/-OSChina
2998.00Kimi jian***@gmail.comSemoga Rango terus mengembangkan Swoole dengan baikAlipay
1024.00Chen He dan***@gmail.com-Alipay
1000.00Imooc Singwa (www.imooc.com/t/255838)-Alipay
1000.00Han DonghuiProyek open source butuh kontribusi semua orang, apalagi proyek sebagus Swoole, masa kamu tidak donasi?Alipay
1000.00Xu Jiexing jes***@jesin.net-Alipay
1000.00eBag Laundry—Su Gaofeng, eBag Laundry—He Songtao, eBag Laundry—Wang Jianwu
eBag Laundry—Wang Shurong, eBag Laundry—He Junliang, eBag Laundry—Wang Weijun
eBag Laundry—Li Yang, eBag Laundry—Zhao Yukun
-Alipay
750.00JellyBool 497***@qq.comSponsor untuk Swoole, hahahaAlipay
500.00Beijing PHP-iOS Xiao GuoSemoga Swoole semakin baikWeChat
500.00Zhou Chong 027***@gmail.com-Alipay
500.00nosun nos***@126.comTerima kasih Swoole, terima kasih Bro Feng!Alipay
307.20September nnn***@163.comMendukung proyek SwooleAlipay
300.00Xu Changlong xcl***@qq.com-Alipay
2.00Ghost Valley www.php91.net-OSChina
100.00Sunny 844700118@qq.comTulus berharap proyek open source Swoole go globalAlipay
10.00Fafa 131****1929Sedikit dukungan, sedang belajar, terima kasihAlipay
39.90Hong 198***@163.comTerima kasih SwooleAlipay
10.00Robert 158****2981Dukungan kecilAlipay
20.00Hongsheng 999***@sina.comDukung Swoole, dukung open source, dukung berbagi dari para ahliAlipay
50.00Fay 136****0965Kekaguman dari programmer cilikAlipay
100.00JACK tuy***@gmail.comTerima kasih atas apa yang kamu lakukan, sedikit tanda hatiAlipay
1.00Hundred Miles and Rivers fre***@126.comSwooleAlipay
100.00Xiao Jun ban***@126.comSemangatAlipay
200.00LinQ zzu***@foxmail.comDukung Swoole, dukung Tianfeng, semangatAlipay
10.00Weizi 809***@qq.comSangat suka proyek Swoole ini, dukungan kecilAlipay
50.00Jiang Qiang 991***@qq.comSedikit tanda hati, dukung Swoole.Alipay
100.00Liu Zhiming-WeChat
66.66Gui Su 275***@qq.comDonasi SwooleAlipay
20.00[Archangel]-WeChat
5.00Jin Ge-WeChat
10.00myersguo-WeChat
10.00Luo Dongju-WeChat
10.00Aku si kecil Ni Ma-WeChat
6.66Phoenix bir***@sina.comSemangat! Swoole kuasai dunia.Alipay
128.00Head North 134****4818Terus berusahaAlipay
100.00Yafang 135****5404Fangtooth Rescue—Yang YafangAlipay
100.00Yonghui 133****2017Fangtooth Rescue—Chen YonghuiAlipay
58.00Walker gej***@sohu.comDukung Swoole semangat!!Alipay
100.00Qian Da zhi***@163.comTerima kasih SwooleAlipay
100.00Qiang dre***@126.comSwooleAlipay
10.00True adm***@yyzg.ccDukung sedikitAlipay
100.00Wink hlm***@163.comSemangatAlipay
50.00Mingzhi 610***@qq.comSedang bersiap menggunakanAlipay
100.00Li Gaofeng gao***@126.comDukung SwooleAlipay
50.00Lobtao lob***@qq.comDukung SwooleAlipay
100.00Azhi pan***@126.comSemoga Swoole bertahanAlipay
50.00Yankang 657***@qq.com-Alipay
20.00LeesLee Feng lee***@163.comDukungAlipay
100.00Manxin gma***@163.com-Alipay
100.00Longbao che***@163.com37350968-Dukung SwooleAlipay
16.00Spicy hotpot 137****0761Sedikit hatiAlipay
100.00Peng 186****8597DukungAlipay
100.00Haotian 185****2081Semoga Swoole semakin baikAlipay
100.00Aibo aib***@vhost.com.cnDukungAlipay
10.00Bai Feng 133****8172Donasi SwooleAlipay
100.00Cuoyin Taishuai 189***@163.comSwoole semakin majuAlipay
18.00Wiping blood lsg***@126.comJempol untuk open sourceAlipay
100.00Old Horse chanyu@21cn.netSwoole membumi, selalu maju!Alipay
100.00Persi ipe***@sixsir.comSedikit hati, terima kasih tim Swoole.Alipay
50.00Lin 131****1019Harus dukungAlipay
10.00Singer lan***@qq.comPakai Swoole, proyek belum live, sedikit hatiAlipay
20.00Superbright 156****9731Terima kasih dewaAlipay
10.00Mu Xin yun***@qq.comHatiAlipay
100.00Qi xia***@gmail.comDewa terima kasih, Swoole benar-benar alat ajaibAlipay
10.00Chun lov***@qq.comSedikit hatiAlipay
100.00Cheng Yan 151***8370DukungAlipay
200.00Yihai ab1***@163.comTerima kasih Swoole dan bantuan Rango. RoarAlipay
100.00Dr 244***@qq.comTerima kasih SwooleAlipay
66.00Elarity 4677833@qq.comSemoga aku segera bisa berkontribusi untuk SwooleAlipay
99.00coldstar saf***@163.comOpen source adalah semangatAlipay
200.00Yan wud***@163.comDukung SwooleAlipay
10.00Xiao wux***@163.comTransferAlipay
100.00Pan Jun 315***@qq.comDukung open source!Alipay
100.00Dawei bet***@foxmail.comDonasi pengembanganAlipay
20.00Chen Xi cx1***@gmail.comDonasi SwooleAlipay
50.00Xiantao xsi***@163.comSwooleAlipay
25.00Lu 120***@qq.com-Alipay
50.00Haibin hea***@163.comSemangatAlipay
20.00Gaolu 187***@163.comDukung, semangatAlipay
10.00Lu Qingsong 131****0160-Alipay
50.00Jiazhen wnd***@126.comDukung, semangatAlipay
1.00Tan Jihai 121***@qq.com-Alipay
120.00Xu Hanchuan sun***@yahoo.com-Alipay
20.00Qu Yongchang 576***@qq.com-Alipay
188.00Wang Xuebing xue***@163.com-Alipay
10.00Zhou Zhenzhong as1***@tom.com-Alipay
1.00Hou Wei hw8***@126.com-Alipay
100.00Li Yunfei 158****5024-Alipay
100.00Zhang Xinhai zxh***@sina.com-Alipay
25.00Zhou Xi 336***@qq.comHasil carpooling pagi ini, disumbangkan ke Swoole.Alipay
10.00Liu Jie 150****4670-Alipay
100.00Huang Renyun 657***@qq.com-Alipay
27.00Zhou Xi 336***@qq.com-Alipay
100.00He Jingxin 671***@qq.com-Alipay
66.66twosee tw***@qq.comSudah mendonasikan diriku ke SwooleOSChina
diff --git a/public/id/other/errno.md b/public/id/other/errno.md new file mode 100644 index 0000000..fbde417 --- /dev/null +++ b/public/id/other/errno.md @@ -0,0 +1,289 @@ +# Kode Error + +Gunakan `swoole_last_error()` untuk mendapatkan kode error saat ini; + +Gunakan `swoole_strerror(int $errno, 9);` untuk mengonversi kode error dasar Swoole menjadi teks pesan error; + +```php +echo swoole_strerror(swoole_last_error(), 9) . PHP_EOL; +echo swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL, 9) . PHP_EOL; +``` + +## Daftar Kode Error Linux :id=linux + +| C Name | Value | Description | Arti | +| --------------- | ----- | -------------------------------------------- | ----------------------------- | +| Success | 0 | Success | Sukses | +| EPERM | 1 | Operation not permitted | Operasi tidak diizinkan | +| ENOENT | 2 | No such file or directory | File atau direktori tidak ada | +| ESRCH | 3 | No such process | Proses tidak ada | +| EINTR | 4 | Interrupted system call | Panggilan sistem terinterupsi | +| EIO | 5 | I/O error | Error I/O | +| ENXIO | 6 | No such device or address | Device atau alamat tidak ada | +| E2BIG | 7 | Arg list too long | Daftar argumen terlalu panjang| +| ENOEXEC | 8 | Exec format error | Error format eksekusi | +| EBADF | 9 | Bad file number | File descriptor rusak | +| ECHILD | 10 | No child processes | Tidak ada proses anak | +| EAGAIN | 11 | Try again | Resource sementara tidak tersedia | +| ENOMEM | 12 | Out of memory | Kehabisan memori | +| EACCES | 13 | Permission denied | Izin ditolak | +| EFAULT | 14 | Bad address | Alamat salah | +| ENOTBLK | 15 | Block device required | Device block diperlukan | +| EBUSY | 16 | Device or resource busy | Device atau resource sibuk | +| EEXIST | 17 | File exists | File sudah ada | +| EXDEV | 18 | Cross-device link | Tautan lintas device tidak valid | +| ENODEV | 19 | No such device | Device tidak ada | +| ENOTDIR | 20 | Not a directory | Bukan direktori | +| EISDIR | 21 | Is a directory | Adalah direktori | +| EINVAL | 22 | Invalid argument | Argumen tidak valid | +| ENFILE | 23 | File table overflow | Tabel file overflow | +| EMFILE | 24 | Too many open files | Terlalu banyak file terbuka | +| ENOTTY | 25 | Not a tty device | Bukan device tty | +| ETXTBSY | 26 | Text file busy | File teks sibuk | +| EFBIG | 27 | File too large | File terlalu besar | +| ENOSPC | 28 | No space left on device | Tidak ada ruang di device | +| ESPIPE | 29 | Illegal seek | Pencarian ilegal | +| EROFS | 30 | Read-only file system | Sistem file read-only | +| EMLINK | 31 | Too many links | Terlalu banyak tautan | +| EPIPE | 32 | Broken pipe | Pipe rusak | +| EDOM | 33 | Math argument out of domain | Argumen matematika di luar domain| +| ERANGE | 34 | Math result not representable | Hasil matematika tidak representatif | +| EDEADLK | 35 | Resource deadlock would occur | Deadlock resource | +| ENAMETOOLONG | 36 | Filename too long | Nama file terlalu panjang | +| ENOLCK | 37 | No record locks available | Tidak ada kunci tersedia | +| ENOSYS | 38 | Function not implemented | Fungsi tidak diimplementasikan| +| ENOTEMPTY | 39 | Directory not empty | Direktori tidak kosong | +| ELOOP | 40 | Too many symbolic links encountered | Terlalu banyak symbolic link | +| EWOULDBLOCK | 41 | Same as EAGAIN | Sama dengan EAGAIN | +| ENOMSG | 42 | No message of desired type | Tidak ada pesan tipe yang diinginkan| +| EIDRM | 43 | Identifier removed | Identifier dihapus | +| ECHRNG | 44 | Channel number out of range | Nomor saluran di luar rentang | +| EL2NSYNC | 45 | Level 2 not synchronized | Level 2 tidak sinkron | +| EL3HLT | 46 | Level 3 halted | Level 3 berhenti | +| EL3RST | 47 | Level 3 reset | Level 3 reset | +| ELNRNG | 48 | Link number out of range | Jumlah tautan di luar rentang | +| EUNATCH | 49 | Protocol driver not attached | Driver protokol tidak terpasang| +| ENOCSI | 50 | No CSI structure available | Tidak ada struktur CSI | +| EL2HLT | 51 | Level 2 halted | Level 2 berhenti | +| EBADE | 52 | Invalid exchange | Tukar tidak valid | +| EBADR | 53 | Invalid request descriptor | Deskriptor request tidak valid| +| EXFULL | 54 | Exchange full | Penuh | +| ENOANO | 55 | No anode | Tidak ada anode | +| EBADRQC | 56 | Invalid request code | Kode request tidak valid | +| EBADSLT | 57 | Invalid slot | Slot tidak valid | +| EDEADLOCK | 58 | Same as EDEADLK | Sama dengan EDEADLK | +| EBFONT | 59 | Bad font file format | Format file font salah | +| ENOSTR | 60 | Device not a stream | Bukan stream | +| ENODATA | 61 | No data available | Tidak ada data | +| ETIME | 62 | Timer expired | Timer kedaluwarsa | +| ENOSR | 63 | Out of streams resources | Resource stream habis | +| ENONET | 64 | Machine is not on the network | Mesin tidak terhubung jaringan| +| ENOPKG | 65 | Package not installed | Paket tidak terinstal | +| EREMOTE | 66 | Object is remote | Object jarak jauh | +| ENOLINK | 67 | Link has been severed | Tautan terputus | +| EADV | 68 | Advertise error | Error iklan | +| ESRMNT | 69 | Srmount error | Error srmount | +| ECOMM | 70 | Communication error on send | Error komunikasi saat kirim | +| EPROTO | 71 | Protocol error | Error protokol | +| EMULTIHOP | 72 | Multihop attempted | Percobaan multihop | +| EDOTDOT | 73 | RFS specific error | Error spesifik RFS | +| EBADMSG | 74 | Not a data message | Bukan pesan data | +| EOVERFLOW | 75 | Value too large for defined data type | Nilai terlalu besar untuk tipe data | +| ENOTUNIQ | 76 | Name not unique on network | Nama tidak unik di jaringan | +| EBADFD | 77 | File descriptor in bad state | File descriptor dalam keadaan rusak| +| EREMCHG | 78 | Remote address changed | Alamat jarak jauh berubah | +| ELIBACC | 79 | Cannot access a needed shared library | Tidak bisa akses shared library| +| ELIBBAD | 80 | Accessing a corrupted shared library | Shared library rusak | +| ELIBSCN | 81 | A .lib section in an .out is corrupted | Bagian .lib di .out rusak | +| ELIBMAX | 82 | Linking in too many shared libraries | Terlalu banyak shared library | +| ELIBEXEC | 83 | Cannot exec a shared library directly | Tidak bisa eksekusi shared library langsung| +| EILSEQ | 84 | Illegal byte sequence | Urutan byte ilegal | +| ERESTART | 85 | Interrupted system call should be restarted | Panggilan sistem harus di-restart| +| ESTRPIPE | 86 | Streams pipe error | Error pipe stream | +| EUSERS | 87 | Too many users | Terlalu banyak pengguna | +| ENOTSOCK | 88 | Socket operation on non-socket | Operasi socket di non-socket | +| EDESTADDRREQ | 89 | Destination address required | Alamat tujuan diperlukan | +| EMSGSIZE | 90 | Message too long | Pesan terlalu panjang | +| EPROTOTYPE | 91 | Protocol wrong type for socket | Tipe protokol socket salah | +| ENOPROTOOPT | 92 | Protocol not available | Protokol tidak tersedia | +| EPROTONOSUPPORT | 93 | Protocol not supported | Protokol tidak didukung | +| ESOCKTNOSUPPORT | 94 | Socket type not supported | Tipe socket tidak didukung | +| EOPNOTSUPP | 95 | Operation not supported on transport | Operasi tidak didukung | +| EPFNOSUPPORT | 96 | Protocol family not supported | Keluarga protokol tidak didukung| +| EAFNOSUPPORT | 97 | Address family not supported by protocol | Alamat tidak didukung protokol| +| EADDRINUSE | 98 | Address already in use | Alamat sudah digunakan | +| EADDRNOTAVAIL | 99 | Cannot assign requested address | Tidak bisa memberikan alamat | +| ENETDOWN | 100 | Network is down | Jaringan mati | +| ENETUNREACH | 101 | Network is unreachable | Jaringan tidak dapat dijangkau| +| ENETRESET | 102 | Network dropped | Koneksi jaringan hilang | +| ECONNABORTED | 103 | Software caused connection | Software menyebabkan koneksi terputus| +| ECONNRESET | 104 | Connection reset by | Koneksi di-reset | +| ENOBUFS | 105 | No buffer space available | Tidak ada buffer | +| EISCONN | 106 | Transport endpoint is already connected | Endpoint sudah terhubung | +| ENOTCONN | 107 | Transport endpoint is not connected | Endpoint tidak terhubung | +| ESHUTDOWN | 108 | Cannot send after transport endpoint shutdown| Tidak bisa kirim setelah shutdown| +| ETOOMANYREFS | 109 | Too many references: cannot splice | Terlalu banyak referensi | +| ETIMEDOUT | 110 | Connection timed | Koneksi timeout | +| ECONNREFUSED | 111 | Connection refused | Koneksi ditolak | +| EHOSTDOWN | 112 | Host is down | Host mati | +| EHOSTUNREACH | 113 | No route to host | Tidak ada rute ke host | +| EALREADY | 114 | Operation already | Operasi sudah berjalan | +| EINPROGRESS | 115 | Operation now in | Operasi sedang berlangsung | +| ESTALE | 116 | Stale NFS file handle | File handle NFS basi | +| EUCLEAN | 117 | Structure needs cleaning | Struktur perlu pembersihan | +| ENOTNAM | 118 | Not a XENIX-named | Bukan XENIX-named | +| ENAVAIL | 119 | No XENIX semaphores | Tidak ada semaphore XENIX | +| EISNAM | 120 | Is a named type file | File tipe bernama | +| EREMOTEIO | 121 | Remote I/O error | Error I/O jarak jauh | +| EDQUOT | 122 | Quota exceeded | Melebihi kuota disk | +| ENOMEDIUM | 123 | No medium found | Tidak ada media ditemukan | +| EMEDIUMTYPE | 124 | Wrong medium type | Tipe media salah | +| ECANCELED | 125 | Operation Canceled | Operasi dibatalkan | +| ENOKEY | 126 | Required key not available | Key diperlukan tidak tersedia | +| EKEYEXPIRED | 127 | Key has expired | Key kedaluwarsa | +| EKEYREVOKED | 128 | Key has been revoked | Key dicabut | +| EKEYREJECTED | 129 | Key was rejected by service | Key ditolak oleh service | +| EOWNERDEAD | 130 | Owner died | Pemilik mati | +| ENOTRECOVERABLE | 131 | State not recoverable | Status tidak bisa dipulihkan | +| ERFKILL | 132 | Operation not possible due to RF-kill | Operasi tidak bisa karena RF-kill| +| EHWPOISON | 133 | Memory page has hardware error | Halaman memori error hardware | + +## Daftar Kode Error Swoole :id=swoole + +| Constants Name | Value | Description | +| ---------------------------------------------- | ----- | --------------------------------- | +| SWOOLE_ERROR_MALLOC_FAIL | 501 | Malloc fail | +| SWOOLE_ERROR_SYSTEM_CALL_FAIL | 502 | System call fail | +| SWOOLE_ERROR_PHP_FATAL_ERROR | 503 | PHP fatal error | +| SWOOLE_ERROR_NAME_TOO_LONG | 504 | Name too long | +| SWOOLE_ERROR_INVALID_PARAMS | 505 | Invalid params | +| SWOOLE_ERROR_QUEUE_FULL | 506 | Queue full | +| SWOOLE_ERROR_OPERATION_NOT_SUPPORT | 507 | Operation not support | +| SWOOLE_ERROR_PROTOCOL_ERROR | 508 | Protocol error | +| SWOOLE_ERROR_WRONG_OPERATION | 509 | Wrong operation | +| SWOOLE_ERROR_PHP_RUNTIME_NOTICE | 510 | PHP runtime notice | +| SWOOLE_ERROR_FOR_TEST | 511 | For test | +| SWOOLE_ERROR_NO_PAYLOAD | 550 | No payload | +| - | | | +| SWOOLE_ERROR_UNDEFINED_BEHAVIOR | 600 | Undefined behavior | +| SWOOLE_ERROR_NOT_THREAD_SAFETY | 601 | Not thread safety | +| - | | | +| SWOOLE_ERROR_FILE_NOT_EXIST | 700 | File not exist | +| SWOOLE_ERROR_FILE_TOO_LARGE | 701 | File too large | +| SWOOLE_ERROR_FILE_EMPTY | 702 | File empty | +| SWOOLE_ERROR_DNSLOOKUP_DUPLICATE_REQUEST | 710 | DNS Lookup duplicate request | +| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED | 711 | DNS Lookup resolve failed | +| SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT | 712 | DNS Lookup resolve timeout | +| SWOOLE_ERROR_DNSLOOKUP_UNSUPPORTED | 713 | DNS Lookup unsupported | +| SWOOLE_ERROR_DNSLOOKUP_NO_SERVER | 714 | DNS Lookup no server | +| SWOOLE_ERROR_BAD_IPV6_ADDRESS | 720 | Bad ipv6 address | +| SWOOLE_ERROR_UNREGISTERED_SIGNAL | 721 | Unregistered signal | +| SWOOLE_ERROR_BAD_HOST_ADDR | 722 | Bad host addr | +| - | | | +| SWOOLE_ERROR_EVENT_SOCKET_REMOVED | 800 | Event socket removed | +| - | | | +| SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER | 1001 | Session closed by server | +| SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT | 1002 | Session closed by client | +| SWOOLE_ERROR_SESSION_CLOSING | 1003 | Session closing | +| SWOOLE_ERROR_SESSION_CLOSED | 1004 | Session closed | +| SWOOLE_ERROR_SESSION_NOT_EXIST | 1005 | Session not exist | +| SWOOLE_ERROR_SESSION_INVALID_ID | 1006 | Session invalid id | +| SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA | 1007 | Session discard timeout data | +| SWOOLE_ERROR_SESSION_DISCARD_DATA | 1008 | Session discard data | +| SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW | 1009 | Output buffer overflow | +| SWOOLE_ERROR_OUTPUT_SEND_YIELD | 1010 | Output send yield | +| SWOOLE_ERROR_SSL_NOT_READY | 1011 | SSL not ready | +| SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE | 1012 | SSL cannot use senfile | +| SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE | 1013 | SSL empty peer certificate | +| SWOOLE_ERROR_SSL_VERIFY_FAILED | 1014 | SSL verify failed | +| SWOOLE_ERROR_SSL_BAD_CLIENT | 1015 | SSL bad client | +| SWOOLE_ERROR_SSL_BAD_PROTOCOL | 1016 | SSL bad protocol | +| SWOOLE_ERROR_SSL_RESET | 1017 | SSL reset | +| SWOOLE_ERROR_SSL_HANDSHAKE_FAILED | 1018 | SSL handshake failed | +| - | | | +| SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE | 1201 | Package length too large | +| SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND | 1202 | Package length not found | +| SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE | 1203 | Data length too large | +| SWOOLE_ERROR_PACKAGE_MALFORMED_DATA | 1204 | Package malformed data | +| - | | | +| SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG | 2001 | Task package too big | +| SWOOLE_ERROR_TASK_DISPATCH_FAIL | 2002 | Task dispatch fail | +| SWOOLE_ERROR_TASK_TIMEOUT | 2003 | Task timeout | +| - | | | +| SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG | 3001 | Http2 stream id too big | +| SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER | 3002 | Http2 stream no header | +| SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND | 3003 | Http2 stream not found | +| SWOOLE_ERROR_HTTP2_STREAM_IGNORE | 3004 | Http2 stream ignore | +| SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED | 3005 | Http2 send control frame failed | +| - | | | +| SWOOLE_ERROR_AIO_BAD_REQUEST | 4001 | Aio bad request | +| SWOOLE_ERROR_AIO_CANCELED | 4002 | Aio canceled | +| SWOOLE_ERROR_AIO_TIMEOUT | 4003 | Aio timeout | +| - | | | +| SWOOLE_ERROR_CLIENT_NO_CONNECTION | 5001 | Client no connection | +| - | | | +| SWOOLE_ERROR_SOCKET_CLOSED | 6001 | Socket closed | +| SWOOLE_ERROR_SOCKET_POLL_TIMEOUT | 6002 | Socket poll timeout | +| - | | | +| SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION | 7001 | Socks5 unsupport version | +| SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD | 7002 | Socks5 unsupport method | +| SWOOLE_ERROR_SOCKS5_AUTH_FAILED | 7003 | Socks5 auth failed | +| SWOOLE_ERROR_SOCKS5_SERVER_ERROR | 7004 | Socks5 server error | +| SWOOLE_ERROR_SOCKS5_HANDSHAKE_FAILED | 7005 | Socks5 handshake failed | +| - | | | +| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR | 7101 | Http proxy handshake error | +| SWOOLE_ERROR_HTTP_INVALID_PROTOCOL | 7102 | Http invalid protocol | +| SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED | 7103 | Http proxy handshake failed | +| SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE | 7104 | Http proxy bad response | +| SWOOLE_ERROR_HTTP_CONFLICT_HEADER | 7105 | Http conflict header | +| SWOOLE_ERROR_HTTP_CONTEXT_UNAVAILABLE | 7106 | Http context unavailable | +| SWOOLE_ERROR_HTTP_COOKIE_UNAVAILABLE | 7107 | Http cookie unavailable | +| - | | | +| SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT | 8501 | Websocket bad client | +| SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE | 8502 | Websocket bad opcode | +| SWOOLE_ERROR_WEBSOCKET_UNCONNECTED | 8503 | Websocket unconnected | +| SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED | 8504 | Websocket handshake failed | +| SWOOLE_ERROR_WEBSOCKET_PACK_FAILED | 8505 | Websocket pack failed | +| SWOOLE_ERROR_WEBSOCKET_UNPACK_FAILED | 8506 | Websocket unpack failed | +| SWOOLE_ERROR_WEBSOCKET_INCOMPLETE_PACKET | 8507 | Websocket incomplete packet | +| - | | | +| SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT | 9001 | Server must created before client | +| SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET | 9002 | Server too many socket | +| SWOOLE_ERROR_SERVER_WORKER_TERMINATED | 9003 | Server worker terminated | +| SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT | 9004 | Server invalid listen port | +| SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT | 9005 | Server too many listen port | +| SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL | 9006 | Server pipe buffer full | +| SWOOLE_ERROR_SERVER_NO_IDLE_WORKER | 9007 | Server no idle worker | +| SWOOLE_ERROR_SERVER_ONLY_START_ONE | 9008 | Server only start one | +| SWOOLE_ERROR_SERVER_SEND_IN_MASTER | 9009 | Server send in master | +| SWOOLE_ERROR_SERVER_INVALID_REQUEST | 9010 | Server invalid request | +| SWOOLE_ERROR_SERVER_CONNECT_FAIL | 9011 | Server connect fail | +| SWOOLE_ERROR_SERVER_INVALID_COMMAND | 9012 | Server invalid command | +| SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE | 9013 | Server is not regular file | +| SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT | 9014 | Server send to woker timeout | +| SWOOLE_ERROR_SERVER_INVALID_CALLBACK | 9015 | Server invalid callback | +| SWOOLE_ERROR_SERVER_UNRELATED_THREAD | 9016 | Server unrelated thread | +| - | | | +| SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT | 9101 | Server worker exit timeout | +| SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA | 9102 | Server worker abnormal pipe data | +| SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA | 9103 | Server worker unprocessed data | +| - | | | +| SWOOLE_ERROR_CO_OUT_OF_COROUTINE | 10001 | Coroutine out of coroutine | +| SWOOLE_ERROR_CO_HAS_BEEN_BOUND | 10002 | Coroutine has been bound | +| SWOOLE_ERROR_CO_HAS_BEEN_DISCARDED | 10003 | Coroutine has been discarded | +| SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK | 10004 | Coroutine mutex double unlock | +| SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED | 10005 | Coroutine block object locked | +| SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING | 10006 | Coroutine block object waiting | +| SWOOLE_ERROR_CO_YIELD_FAILED | 10007 | Coroutine yield failed | +| SWOOLE_ERROR_CO_GETCONTEXT_FAILED | 10008 | Coroutine getcontext failed | +| SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED | 10009 | Coroutine swapcontext failed | +| SWOOLE_ERROR_CO_MAKECONTEXT_FAILED | 10010 | Coroutine makecontext failed | +| SWOOLE_ERROR_CO_IOCPINIT_FAILED | 10011 | Coroutine iocpinit failed | +| SWOOLE_ERROR_CO_PROTECT_STACK_FAILED | 10012 | Coroutine protect stack failed | +| SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR | 10013 | Coroutine std thread link error | +| SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD | 10014 | Coroutine disabled multi thread | +| SWOOLE_ERROR_CO_CANNOT_CANCEL | 10015 | Coroutine cannot cancel | +| SWOOLE_ERROR_CO_NOT_EXISTS | 10016 | Coroutine not exists | +| SWOOLE_ERROR_CO_CANCELED | 10017 | Coroutine canceled | +| SWOOLE_ERROR_CO_TIMEDOUT | 10018 | Coroutine timedout | +| SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT | 10019 | Coroutine socket close wait | diff --git a/public/id/other/issue.md b/public/id/other/issue.md new file mode 100644 index 0000000..67c4b27 --- /dev/null +++ b/public/id/other/issue.md @@ -0,0 +1,87 @@ +# Melaporkan Bug + +## Informasi + +Saat kamu merasa menemukan BUG di inti Swoole, harap laporkan. Pengembang inti Swoole mungkin belum tahu tentang masalah ini kecuali kamu melaporkannya. Kamu bisa melaporkan bug di [GitHub issue](https://github.com/swoole/swoole-src/issues) (klik tombol `New issue`). Laporan bug di sini akan diprioritaskan. + +Jangan kirim laporan bug melalui email atau pesan pribadi. Kamu juga bisa mengajukan permintaan atau saran untuk Swoole di GitHub issue. + +Sebelum melaporkan bug, harap baca **Cara Melaporkan Bug** di bawah. + +## Membuat Issue Baru + +Saat membuat issue, sistem akan memberikan template berikut. Harap isi dengan benar, jika tidak issue mungkin diabaikan karena kurang informasi: + +```markdown + +Please answer these questions before submitting your issue. Thanks! +> Silakan jawab pertanyaan berikut sebelum mengirim issue: + +1. What did you do? If possible, provide a simple script for reproducing the error. +> Jelaskan proses munculnya masalah, tempelkan kode terkait, idealnya berikan script sederhana yang bisa mereproduksi masalah secara stabil. + +2. What did you expect to see? +> Apa hasil yang diharapkan? + +3. What did you see instead? +> Apa hasil yang sebenarnya? + +4. What version of Swoole are you using (`php --ri swoole`)? +> Versi apa yang kamu gunakan? Tempelkan output dari `php --ri swoole` + +5. What is your machine environment used (including the version of kernel & php & gcc)? +> Lingkungan sistem apa yang kamu gunakan (termasuk kernel, PHP, versi compiler gcc)? +> Bisa menggunakan perintah `uname -a`, `php -v`, `gcc -v` + +``` + +Yang paling penting adalah menyediakan **script kode sederhana yang bisa mereproduksi masalah secara stabil**. Jika tidak, kamu harus memberikan informasi sebanyak mungkin untuk membantu pengembang menemukan penyebab error. + +## Analisis Memori (Sangat Direkomendasikan) + +Seringkali, Valgrind lebih efektif menemukan masalah memori daripada gdb. Jalankan programmu dengan perintah berikut sampai bug terpicu: + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* Saat terjadi error, bisa keluar dengan mengetik `ctrl+c`, lalu upload file `/tmp/valgrind.log` untuk membantu tim pengembang menemukan BUG. + +## Tentang Segmentation Fault (Core Dump) + +Dalam kasus khusus, kamu bisa menggunakan alat debugging untuk membantu pengembang menemukan masalah: + +```shell +WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 +``` + +Jika peringatan di atas muncul di log Swoole (signal 11), berarti program mengalami `core dump`. Kamu perlu menggunakan alat debugging untuk menentukan lokasi kejadian. + +> Sebelum menggunakan `gdb` untuk melacak `swoole`, perlu menambahkan parameter `--enable-debug` saat kompilasi untuk menyimpan lebih banyak informasi + +Aktifkan file core dump: +```shell +ulimit -c unlimited +``` + +Picu BUG, file core dump akan dihasilkan di direktori program, root direktori sistem, atau direktori `/cores` (tergantung konfigurasi sistem). + +Masukkan perintah berikut untuk masuk ke gdb: + +``` +gdb php core +gdb php /tmp/core.1234 +``` + +Lalu ketik `bt` dan enter untuk melihat call stack yang bermasalah: +``` +(gdb) bt +``` + +Bisa melihat stack frame tertentu dengan mengetik `f angka`: +``` +(gdb) f 1 +(gdb) f 0 +``` + +Tempelkan semua informasi di atas ke dalam issue. diff --git a/public/id/other/signal.md b/public/id/other/signal.md new file mode 100644 index 0000000..8bc1ae7 --- /dev/null +++ b/public/id/other/signal.md @@ -0,0 +1,117 @@ +# Daftar Sinyal Linux + +## Tabel Perbandingan Lengkap + +| Sinyal | Nilai | Aksi Default | Arti (Penyebab Sinyal) | +| ---------- | -------- | ------------ | --------------------------------------------- | +| SIGHUP | 1 | Term | Hangup terminal atau proses mati | +| SIGINT | 2 | Term | Sinyal interupsi dari keyboard | +| SIGQUIT | 3 | Core | Sinyal quit dari keyboard | +| SIGILL | 4 | Core | Instruksi ilegal | +| SIGABRT | 6 | Core | Sinyal abnormal dari abort | +| SIGFPE | 8 | Core | Eksepsi floating-point | +| SIGKILL | 9 | Term | Mematikan proses | +| SIGSEGV | 11 | Core | Segmentasi ilegal (referensi memori tidak valid)| +| SIGPIPE | 13 | Term | Pipe rusak: menulis ke pipe tanpa pembaca | +| SIGALRM | 14 | Term | Timer alarm kedaluwarsa | +| SIGTERM | 15 | Term | Terminasi | +| SIGUSR1 | 30,10,16 | Term | Sinyal kustom pengguna 1 | +| SIGUSR2 | 31,12,17 | Term | Sinyal kustom pengguna 2 | +| SIGCHLD | 20,17,18 | Ign | Proses anak berhenti atau terminasi | +| SIGCONT | 19,18,25 | Cont | Jika berhenti, lanjutkan eksekusi | +| SIGSTOP | 17,19,23 | Stop | Sinyal berhenti bukan dari terminal | +| SIGTSTP | 18,20,24 | Stop | Sinyal berhenti dari terminal | +| SIGTTIN | 21,21,26 | Stop | Proses latar belakang baca terminal | +| SIGTTOU | 22,22,27 | Stop | Proses latar belakang tulis terminal | +| | | | | +| SIGBUS | 10,7,10 | Core | Bus error (kesalahan akses memori) | +| SIGPOLL | | Term | Event Pollable terjadi (Sys V), sinonim SIGIO | +| SIGPROF | 27,27,29 | Term | Timer profiling kedaluwarsa | +| SIGSYS | 12,-,12 | Core | Panggilan sistem ilegal (SVr4) | +| SIGTRAP | 5 | Core | Trap/breakpoint | +| SIGURG | 16,23,21 | Ign | Sinyal urgent socket (4.2BSD) | +| SIGVTALRM | 26,26,28 | Term | Timer virtual kedaluwarsa (4.2BSD) | +| SIGXCPU | 24,24,30 | Core | Melebihi batas CPU (4.2BSD) | +| SIGXFSZ | 25,25,31 | Core | Melebihi batas ukuran file (4.2BSD) | +| | | | | +| SIGIOT | 6 | Core | IOT trap, sinonim dengan SIGABRT | +| SIGEMT | 7,-,7 | | Term | +| SIGSTKFLT | -,16,- | Term | Stack fault pada koprosesor (tidak digunakan) | +| SIGIO | 23,29,22 | Term | I/O dapat dilakukan pada deskriptor | +| SIGCLD | -,-,18 | Ign | Sinonim dengan SIGCHLD | +| SIGPWR | 29,30,19 | Term | Kegagalan daya (System V) | +| SIGINFO | 29,-,- | | Sinonim dengan SIGPWR | +| SIGLOST | -,-,- | Term | File lock hilang | +| SIGWINCH | 28,28,20 | Ign | Ukuran jendela berubah (4.3BSD, Sun) | +| SIGUNUSED | -,31,- | Term | Sinyal tidak digunakan (akan jadi SIGSYS) | + +## Sinyal Tidak Andal + +| Nama | Deskripsi | +| --------- | -------------------------------- | +| SIGHUP | Koneksi terputus | +| SIGINT | Karakter interupsi terminal | +| SIGQUIT | Karakter quit terminal | +| SIGILL | Instruksi hardware ilegal | +| SIGTRAP | Kerusakan hardware | +| SIGABRT | Terminasi abnormal (abort) | +| SIGBUS | Kerusakan hardware | +| SIGFPE | Eksepsi aritmatika | +| SIGKILL | Terminasi | +| SIGUSR1 | Sinyal defined pengguna | +| SIGUSR2 | Sinyal defined pengguna | +| SIGSEGV | Referensi memori tidak valid | +| SIGPIPE | Menulis ke pipe tanpa pembaca | +| SIGALRM | Timer kedaluwarsa (alarm) | +| SIGTERM | Terminasi | +| SIGCHLD | Status proses anak berubah | +| SIGCONT | Melanjutkan proses yang dijeda | +| SIGSTOP | Berhenti | +| SIGTSTP | Karakter stop terminal | +| SIGTTIN | Baca tty kontrol dari background | +| SIGTTOU | Tulis ke tty kontrol dari background | +| SIGURG | Kondisi darurat (socket) | +| SIGXCPU | Melebihi batas CPU (setrlimit) | +| SIGXFSZ | Melebihi batas ukuran file (setrlimit) | +| SIGVTALRM | Alarm waktu virtual (setitimer) | +| SIGPROF | Profiling time kedaluwarsa (setitimer) | +| SIGWINCH | Ukuran jendela terminal berubah | +| SIGIO | I/O asinkron | +| SIGPWR | Gagal daya/restart | +| SIGSYS | Panggilan sistem tidak valid | + +## Sinyal Andal + +| Nama | Defined Pengguna | +| ------------ | ---------------- | +| SIGRTMIN | | +| SIGRTMIN+1 | | +| SIGRTMIN+2 | | +| SIGRTMIN+3 | | +| SIGRTMIN+4 | | +| SIGRTMIN+5 | | +| SIGRTMIN+6 | | +| SIGRTMIN+7 | | +| SIGRTMIN+8 | | +| SIGRTMIN+9 | | +| SIGRTMIN+10 | | +| SIGRTMIN+11 | | +| SIGRTMIN+12 | | +| SIGRTMIN+13 | | +| SIGRTMIN+14 | | +| SIGRTMIN+15 | | +| SIGRTMAX-14 | | +| SIGRTMAX-13 | | +| SIGRTMAX-12 | | +| SIGRTMAX-11 | | +| SIGRTMAX-10 | | +| SIGRTMAX-9 | | +| SIGRTMAX-8 | | +| SIGRTMAX-7 | | +| SIGRTMAX-6 | | +| SIGRTMAX-5 | | +| SIGRTMAX-4 | | +| SIGRTMAX-3 | | +| SIGRTMAX-2 | | +| SIGRTMAX-1 | | +| SIGRTMAX | | diff --git a/public/id/other/sysctl.md b/public/id/other/sysctl.md new file mode 100644 index 0000000..333f324 --- /dev/null +++ b/public/id/other/sysctl.md @@ -0,0 +1,110 @@ +# Penyesuaian Parameter Kernel + +## Pengaturan ulimit + +`ulimit -n` harus disesuaikan menjadi 100000 atau lebih besar. Jalankan `ulimit -n 100000` di command line untuk mengubahnya. Jika tidak bisa diubah, perlu mengatur `/etc/security/limits.conf`: + +``` +* soft nofile 262140 +* hard nofile 262140 +root soft nofile 262140 +root hard nofile 262140 +* soft core unlimited +* hard core unlimited +root soft core unlimited +root hard core unlimited +``` + +Perhatikan, setelah mengubah file `limits.conf`, perlu restart sistem untuk berlaku. + +## Pengaturan Kernel + +Ada 3 cara mengubah parameter kernel di `Linux`: + +- Ubah file `/etc/sysctl.conf`, tambahkan opsi konfigurasi dengan format `key = value`, simpan lalu jalankan `sysctl -p` untuk memuat konfigurasi baru +- Gunakan perintah `sysctl` untuk perubahan sementara, misalnya: `sysctl -w net.ipv4.tcp_mem="379008 505344 758016"` +- Langsung ubah file di direktori `/proc/sys/`, misalnya: `echo "379008 505344 758016" > /proc/sys/net/ipv4/tcp_mem` + +> Cara pertama akan otomatis berlaku setelah restart OS, cara kedua dan ketiga akan hilang setelah restart + +### net.unix.max_dgram_qlen = 100 + +swoole menggunakan unix socket dgram untuk komunikasi antar proses. Jika volume request besar, perlu menyesuaikan parameter ini. Default sistem adalah 10, bisa diatur ke 100 atau lebih besar. Atau menambah jumlah worker process untuk mengurangi beban request per worker. + +### net.core.wmem_max + +Ubah parameter ini untuk menambah ukuran memori buffer socket. + +``` +net.ipv4.tcp_mem = 379008 505344 758016 +net.ipv4.tcp_wmem = 4096 16384 4194304 +net.ipv4.tcp_rmem = 4096 87380 4194304 +net.core.wmem_default = 8388608 +net.core.rmem_default = 8388608 +net.core.rmem_max = 16777216 +net.core.wmem_max = 16777216 +``` + +### net.ipv4.tcp_tw_reuse + +Apakah socket reuse diaktifkan. Fungsinya agar Server bisa cepat menggunakan kembali port yang didengarkan saat restart. Tanpa parameter ini, server bisa gagal start karena port belum dilepaskan tepat waktu. + +### net.ipv4.tcp_tw_recycle + +Mengaktifkan daur ulang socket cepat. Server koneksi pendek perlu mengaktifkan parameter ini. Parameter ini mengaktifkan daur ulang cepat socket TIME-WAIT di koneksi TCP. Default Linux adalah 0 (mati). Mengaktifkan ini bisa menyebabkan koneksi NAT tidak stabil, harap uji dengan hati-hati. + +## Pengaturan Message Queue + +Saat menggunakan message queue sebagai komunikasi antar proses, perlu menyesuaikan parameter kernel ini: + +- kernel.msgmnb = 4203520, ukuran maksimum message queue (bytes) +- kernel.msgmni = 64, jumlah maksimum message queue yang bisa dibuat +- kernel.msgmax = 8192, panjang maksimum satu data message queue + +## FreeBSD/MacOS + +- sysctl -w net.local.dgram.maxdgram=8192 +- sysctl -w net.local.dgram.recvspace=200000 + Ubah ukuran buffer Unix Socket + +## Mengaktifkan CoreDump + +Atur parameter kernel: + +``` +kernel.core_pattern = /data/core_files/core-%e-%p-%t +``` + +Periksa batas file coredump saat ini dengan perintah `ulimit -c`: + +```shell +ulimit -c +``` + +Jika 0, perlu mengubah `/etc/security/limits.conf` untuk pengaturan limit. + +> Setelah mengaktifkan core-dump, saat program mengalami abnormal, proses akan diekspor ke file. Sangat membantu untuk menyelidiki masalah program. + +## Konfigurasi Penting Lainnya + +- net.ipv4.tcp_syncookies=1 +- net.ipv4.tcp_max_syn_backlog=81920 +- net.ipv4.tcp_synack_retries=3 +- net.ipv4.tcp_syn_retries=3 +- net.ipv4.tcp_fin_timeout = 30 +- net.ipv4.tcp_keepalive_time = 300 +- net.ipv4.tcp_tw_reuse = 1 +- net.ipv4.tcp_tw_recycle = 1 +- net.ipv4.ip_local_port_range = 20000 65000 +- net.ipv4.tcp_max_tw_buckets = 200000 +- net.ipv4.route.max_size = 5242880 + +## Memeriksa Apakah Konfigurasi Berlaku + +Misalnya, setelah mengubah `net.unix.max_dgram_qlen = 100`, periksa dengan: + +```shell +cat /proc/sys/net/unix/max_dgram_qlen +``` + +Jika berhasil, akan menampilkan nilai yang baru. diff --git a/public/id/other/tools.md b/public/id/other/tools.md new file mode 100644 index 0000000..86bd622 --- /dev/null +++ b/public/id/other/tools.md @@ -0,0 +1,207 @@ +# Penggunaan Tools + +## yasd + +[yasd](https://github.com/swoole/yasd) + +Alat debugging step-by-step, bisa digunakan di lingkungan korutin `Swoole`, mendukung mode debugging `IDE` dan command line. + +## tcpdump + +tcpdump adalah alat penting saat men-debug program komunikasi jaringan. tcpdump sangat powerful, bisa melihat setiap detail komunikasi jaringan. Misalnya TCP, bisa melihat 3-way handshake, PUSH/ACK data push, close 4-way handshake, semua detailnya. Termasuk jumlah byte setiap paket yang diterima, waktu, dll. + +### Cara Penggunaan + +Contoh penggunaan paling sederhana: + +```shell +sudo tcpdump -i any tcp port 9501 +``` +* Parameter `-i` menentukan network interface, `any` berarti semua interface +* `tcp` menentukan hanya memonitor protokol TCP +* `port` menentukan port yang dimonitor + +!> tcpdump membutuhkan root privilege; untuk melihat konten data komunikasi, bisa tambahkan parameter `-Xnlps0`, parameter lain lihat artikel online + +### Hasil + +``` +13:29:07.788802 IP localhost.42333 > localhost.9501: Flags [S], seq 828582357, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 0,nop,wscale 7], length 0 +13:29:07.788815 IP localhost.9501 > localhost.42333: Flags [S.], seq 1242884615, ack 828582358, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 2207513,nop,wscale 7], length 0 +13:29:07.788830 IP localhost.42333 > localhost.9501: Flags [.], ack 1, win 342, options [nop,nop,TS val 2207513 ecr 2207513], length 0 +13:29:10.298686 IP localhost.42333 > localhost.9501: Flags [P.], seq 1:5, ack 1, win 342, options [nop,nop,TS val 2208141 ecr 2207513], length 4 +13:29:10.298708 IP localhost.9501 > localhost.42333: Flags [.], ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:10.298795 IP localhost.9501 > localhost.42333: Flags [P.], seq 1:13, ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 12 +13:29:10.298803 IP localhost.42333 > localhost.9501: Flags [.], ack 13, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:11.563361 IP localhost.42333 > localhost.9501: Flags [F.], seq 5, ack 13, win 342, options [nop,nop,TS val 2208457 ecr 2208141], length 0 +13:29:11.563450 IP localhost.9501 > localhost.42333: Flags [F.], seq 13, ack 6, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +13:29:11.563473 IP localhost.42333 > localhost.9501: Flags [.], ack 14, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +``` +* `13:29:11.563473` Waktu dengan presisi mikrodetik +* localhost.42333 > localhost.9501 menunjukkan arah komunikasi, 42333 adalah client, 9501 adalah server +* [S] menunjukkan ini adalah SYN request +* [.] menunjukkan ini adalah ACK packet, (client)SYN->(server)SYN->(client)ACK adalah proses 3-way handshake +* [P] menunjukkan ini adalah data push, bisa dari server ke client atau client ke server +* [F] menunjukkan ini adalah FIN packet, operasi tutup koneksi, client/server bisa memulai +* [R] menunjukkan ini adalah RST packet, fungsinya sama dengan F packet, tapi RST menandakan saat koneksi ditutup, masih ada data yang belum diproses. Bisa diartikan sebagai paksa putus koneksi +* win 342 adalah ukuran sliding window +* length 12 adalah ukuran paket data + +## strace + +strace bisa melacak eksekusi system call. Saat program bermasalah, bisa menggunakan strace untuk menganalisis dan melacak masalah. + +!> Di FreeBSD/MacOS bisa menggunakan truss + +### Cara Penggunaan + +```shell +strace -o /tmp/strace.log -f -p $PID +``` + +* -f berarti melacak multi-thread dan multi-proses, tanpa parameter ini tidak bisa menangkap sub-proses dan sub-thread +* -o berarti output ke file +* -p $PID, menentukan process ID yang dilacak, bisa dilihat melalui ps aux +* -tt mencetak waktu terjadinya system call, presisi mikrodetik +* -s membatasi panjang string yang dicetak, misalnya data yang diterima recvfrom, default hanya 32 byte +* -c statistik real-time waktu setiap system call +* -T mencetak durasi setiap system call + +## gdb + +GDB adalah alat debugging program yang powerful di UNIX, dirilis oleh GNU Open Source. Bisa digunakan untuk men-debug program C/C++. PHP dan Swoole dikembangkan dengan C, jadi GDB bisa digunakan untuk men-debug program PHP+Swoole. + +Debugging gdb bersifat interaktif command line, perlu menguasai perintah umum. + +### Cara Penggunaan + +```shell +gdb -p processID +gdb php +gdb php core +``` + +Ada 3 cara menggunakan gdb: + +* Melacak program PHP yang sedang berjalan, gunakan `gdb -p processID` +* Menjalankan dan men-debug program PHP dengan gdb, gunakan `gdb php -> run server.php` +* Setelah program PHP mengalami coredump, gunakan gdb untuk me-load core memory image `gdb php core` + +!> Jika php tidak ada di PATH environment variable, perlu menentukan path absolut saat menggunakan gdb, misalnya `gdb /usr/local/bin/php` + +### Perintah Umum + +* `p`: print, mencetak nilai variabel C +* `c`: continue, melanjutkan eksekusi program yang dihentikan +* `b`: breakpoint, memasang breakpoint, bisa berdasarkan nama fungsi seperti `b zif_php_function`, atau berdasarkan nomor baris source code seperti `b src/networker/Server.c:1000` +* `t`: thread, berpindah thread, jika process memiliki banyak thread, bisa menggunakan perintah t untuk berpindah +* `ctrl + c`: menginterupsi program yang sedang berjalan, digunakan bersama perintah c +* `n`: next, mengeksekusi baris berikutnya, debugging step-by-step +* `info threads`: melihat semua thread yang berjalan +* `l`: list, melihat source code, bisa menggunakan `l nama_fungsi` atau `l nomor_baris` +* `bt`: backtrace, melihat call stack fungsi saat runtime +* `finish`: menyelesaikan fungsi saat ini +* `f`: frame, digunakan dengan bt untuk berpindah ke level tertentu di call stack +* `r`: run, menjalankan program + +### zbacktrace + +zbacktrace adalah perintah gdb kustom yang disediakan oleh paket source PHP. Fungsinya mirip dengan bt, bedanya zbacktrace menampilkan call stack fungsi PHP, bukan C. + +Download php-src, extract, cari file `.gdbinit` di root directory, lalu di gdb shell: + +```shell +source .gdbinit +zbacktrace +``` +`.gdbinit` juga menyediakan perintah lain, lihat source code untuk info lebih detail. + +#### Melacak Infinite Loop dengan gdb+zbacktrace + +```shell +gdb -p processID +``` + +* Gunakan `ps aux` untuk mencari Worker process ID yang mengalami infinite loop +* `gdb -p` melacak process yang ditentukan +* Tekan `ctrl + c`, `zbacktrace`, `c` berulang kali untuk melihat di bagian kode PHP mana loop terjadi +* Temukan kode PHP yang sesuai dan perbaiki + +## lsof + +Platform Linux menyediakan `lsof` untuk melihat file handle yang dibuka oleh suatu process. Bisa digunakan untuk melacak semua socket, file, resource yang dibuka oleh worker process Swoole. + +### Cara Penggunaan + +```shell +lsof -p [processID] +``` + +### Hasil + +```shell +lsof -p 26821 +lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing + Output information may be incomplete. +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +php 26821 htf cwd DIR 8,4 4096 5375979 /home/htf/workspace/swoole/examples +php 26821 htf rtd DIR 8,4 4096 2 / +php 26821 htf txt REG 8,4 24192400 6160666 /opt/php/php-5.6/bin/php +php 26821 htf DEL REG 0,5 7204965 /dev/zero +php 26821 htf DEL REG 0,5 7204960 /dev/zero +php 26821 htf DEL REG 0,5 7204958 /dev/zero +php 26821 htf DEL REG 0,5 7204957 /dev/zero +php 26821 htf DEL REG 0,5 7204945 /dev/zero +php 26821 htf mem REG 8,4 761912 6160770 /opt/php/php-5.6/lib/php/extensions/debug-zts-20131226/gd.so +php 26821 htf mem REG 8,4 2769230 2757968 /usr/local/lib/libcrypto.so.1.1 +php 26821 htf mem REG 8,4 162632 6322346 /lib/x86_64-linux-gnu/ld-2.23.so +php 26821 htf DEL REG 0,5 7204959 /dev/zero +php 26821 htf 0u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 1u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 2u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 3r CHR 1,9 0t0 11 /dev/urandom +php 26821 htf 4u IPv4 7204948 0t0 TCP *:9501 (LISTEN) +php 26821 htf 5u IPv4 7204949 0t0 UDP *:9502 +php 26821 htf 6u IPv6 7204950 0t0 TCP *:9503 (LISTEN) +php 26821 htf 7u IPv6 7204951 0t0 UDP *:9504 +php 26821 htf 8u IPv4 7204952 0t0 TCP localhost:8000 (LISTEN) +php 26821 htf 9u unix 0x0000000000000000 0t0 7204953 type=DGRAM +php 26821 htf 10u unix 0x0000000000000000 0t0 7204954 type=DGRAM +php 26821 htf 11u unix 0x0000000000000000 0t0 7204955 type=DGRAM +php 26821 htf 12u unix 0x0000000000000000 0t0 7204956 type=DGRAM +php 26821 htf 13u a_inode 0,11 0 9043 [eventfd] +php 26821 htf 14u unix 0x0000000000000000 0t0 7204961 type=DGRAM +php 26821 htf 15u unix 0x0000000000000000 0t0 7204962 type=DGRAM +php 26821 htf 16u unix 0x0000000000000000 0t0 7204963 type=DGRAM +php 26821 htf 17u unix 0x0000000000000000 0t0 7204964 type=DGRAM +php 26821 htf 18u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 19u a_inode 0,11 0 9043 [signalfd] +php 26821 htf 20u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 22u IPv4 7452776 0t0 TCP localhost:9501->localhost:59056 (ESTABLISHED) +``` + +* File so adalah dynamic link library yang dimuat process +* IPv4/IPv6 TCP (LISTEN) adalah port yang didengarkan server +* UDP adalah port UDP yang didengarkan server +* `unix type=DGRAM` adalah [unixSocket](/learn?id=apa-itu-IPC) yang dibuat process +* IPv4 (ESTABLISHED) menunjukkan TCP client yang terhubung ke server, berisi IP dan PORT client, serta status (ESTABLISHED) +* 9u / 10u menunjukkan nilai fd (file descriptor) dari file handle tersebut +* Informasi lain lihat manual lsof + +## perf + +`perf` adalah alat dynamic tracing yang sangat powerful dari Linux kernel. Perintah `perf top` bisa digunakan untuk menganalisis masalah performa program yang sedang berjalan secara real-time. Berbeda dengan `callgrind`, `xdebug`, `xhprof`, `perf` tidak perlu mengubah kode untuk mengekspor file hasil profile. + +### Cara Penggunaan + +```shell +perf top -p [processID] +``` + +### Output + +![perf top output](/_images/other/perf.png) + +Hasil perf menunjukkan dengan jelas waktu eksekusi setiap fungsi C di proses saat ini, bisa mengetahui fungsi C mana yang menggunakan banyak resource CPU. + +Jika kamu familiar dengan Zend VM, terlalu banyak panggilan fungsi Zend tertentu bisa menunjukkan bahwa programmu banyak menggunakan fungsi tertentu, menyebabkan CPU usage tinggi. Lakukan optimasi yang sesuai. diff --git a/public/id/process/process.md b/public/id/process/process.md new file mode 100644 index 0000000..94d51ad --- /dev/null +++ b/public/id/process/process.md @@ -0,0 +1,896 @@ +# Swoole\Process + +Modul manajemen proses yang disediakan Swoole, sebagai pengganti `pcntl` PHP + +!> Modul ini cukup low-level, merupakan bungkus dari manajemen proses OS. Pengguna harus punya pengalaman pemrograman multi-proses di sistem `Linux`. + +`pcntl` bawaan `PHP` punya banyak kekurangan, seperti: + +* Tidak menyediakan fungsi komunikasi antar proses +* Tidak mendukung redirect standard input dan output +* Cuma nyediain interface primitif kayak `fork`, gampang salah pakai + +`Process` nyediain fungsionalitas yang lebih kuat dari `pcntl`, dengan `API` yang lebih gampang dipake, bikin PHP lebih ringan dalam pemrograman multi-proses. + +`Process` punya fitur-fitur berikut: + +* Bisa dengan mudah mengimplementasikan komunikasi antar proses +* Mendukung redirect standard input dan output, di child process `echo` nggak bakal cetak ke layar, tapi nulis ke pipe. Baca input keyboard bisa dialihkan jadi baca data dari pipe +* Nyediain interface [exec](/process/process?id=exec), proses yang dibuat bisa jalanin program lain, dan gampang komunikasi dengan parent process `PHP` +* Di lingkungan coroutine, modul `Process` nggak bisa dipake. Bisa pake `runtime hook` + `proc_open`, lihat [Manajemen Proses Coroutine](/coroutine/proc_open) + +### Contoh Penggunaan + + * Buat 3 child process, main process pake wait buat membersihkan + * Kalo main process exit abnormal, child process tetep jalan sampe selesai semua tugas baru exit + +```php +use Swoole\Process; + +for ($n = 1; $n <= 3; $n++) { + $process = new Process(function () use ($n) { + echo 'Child #' . getmypid() . " start and sleep {$n}s" . PHP_EOL; + sleep($n); + echo 'Child #' . getmypid() . ' exit' . PHP_EOL; + }); + $process->start(); +} +for ($n = 3; $n--;) { + $status = Process::wait(true); + echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL; +} +echo 'Parent #' . getmypid() . ' exit' . PHP_EOL; +``` + +## Properti + +### pipe + +Deskriptor file [unixSocket](/learn?id=apa-itu-IPC). + +```php +public int $pipe; +``` + +### msgQueueId + +`id` dari message queue. + +```php +public int $msgQueueId; +``` + +### msgQueueKey + +`key` dari message queue. + +```php +public string $msgQueueKey; +``` + +### pid + +`pid` dari proses saat ini. + +```php +public int $pid; +``` + +### id + +`id` dari proses saat ini. + +```php +public int $id; +``` + +## Konstanta +Parameter | Fungsi +---|--- +Swoole\Process::IPC_NOWAIT | Langsung balik kalo message queue kosong +Swoole\Process::PIPE_READ | Tutup read socket +Swoole\Process::PIPE_WRITE | Tutup write socket + +## Method + +### __construct() + +Method konstruktor. + +```php +Swoole\Process->__construct(callable $function, bool $redirect_stdin_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false) +``` + +* **Parameter** + + * **`callable $function`** + * **Fungsi**: Fungsi yang dijalankan setelah child process berhasil dibuat【level bawah otomatis simpen fungsi ke properti `callback` object】, catat, properti ini `private`. + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`bool $redirect_stdin_stdout`** + * **Fungsi**: Redirect standard input dan output child process.【Kalo diaktifkan, output di child process nggak dicetak ke layar, tapi ditulis ke pipe main process. Baca input keyboard jadi baca data dari pipe. Default blocking read. Lihat [exec()](/process/process?id=exec)】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $pipe_type`** + * **Fungsi**: Tipe [unixSocket](/learn?id=apa-itu-IPC)【Kalo `$redirect_stdin_stdout` diaktifkan, opsi ini akan abaikan parameter user, paksa jadi `SOCK_STREAM`. Kalo di child process nggak ada komunikasi antar proses, bisa diset `0`】 + * **Default**: `SOCK_DGRAM` + * **Nilai lain**: `0`, `SOCK_STREAM` + + * **`bool $enable_coroutine`** + * **Fungsi**: Aktifkan coroutine di `callback function`. Kalo diaktifkan, bisa langsung pake API coroutine di fungsi child process + * **Default**: `false` + * **Nilai lain**: `true` + * **Versi**: Swoole >= v4.3.0 + +* **Tipe [unixSocket](/learn?id=apa-itu-IPC)** + +Tipe unixSocket | Keterangan +---|--- +0 | Jangan dibuat +1 | Buat unixSocket tipe [SOCK_STREAM](/learn?id=apa-itu-IPC) +2 | Buat unixSocket tipe [SOCK_DGRAM](/learn?id=apa-itu-IPC) + + +### useQueue() + +Pake message queue untuk komunikasi antar proses. + +```php +Swoole\Process->useQueue(int $key = 0, int $mode = SWOOLE_MSGQUEUE_BALANCE, int $capacity = -1): bool +``` + +* **Parameter** + + * **`int $key`** + * **Fungsi**: Key message queue. Kalo masukin nilai <= 0, level bawah bakal pake fungsi `ftok` dengan nama file yang lagi dijalanin sebagai parameter buat generate key. + * **Default**: `0` + * **Nilai lain**: tidak ada + + * **`int $mode`** + * **Fungsi**: Mode komunikasi antar proses, + * **Default**: `SWOOLE_MSGQUEUE_BALANCE`, `Swoole\Process::pop()` bakal balikin pesan pertama di queue, `Swoole\Process::push()` nggak bakal nambahin tipe tertentu ke pesan. + * **Nilai lain**: `SWOOLE_MSGQUEUE_ORIENT`, `Swoole\Process::pop()` bakal balikin data spesifik dengan tipe pesan `process id + 1`, `Swoole\Process::push()` bakal nambahin tipe `process id + 1` ke pesan. + + * **`int $capacity`** + * **Fungsi**: Jumlah maksimum pesan yang bisa disimpan di message queue. + * **Default**: `-1` + * **Nilai lain**: tidak ada + +* **Catatan** + + * Kalo message queue kosong, `Swoole\Process->pop()` bakal blocking terus, atau kalo message queue penuh, `Swoole\Process->push()` juga bakal blocking terus. Kalo nggak mau blocking, nilai `$mode` harus `SWOOLE_MSGQUEUE_BALANCE|Swoole\Process::IPC_NOWAIT` atau `SWOOLE_MSGQUEUE_ORIENT|Swoole\Process::IPC_NOWAIT`. + +### statQueue() + +Dapetin status message queue + +```php +Swoole\Process->statQueue(): array|false +``` + +* **Return Value** + + * Balikin array kalo sukses, array berisi dua key-value: `queue_num` jumlah total pesan di queue, `queue_bytes` ukuran total pesan di queue. + * Gagal balik `false`. + +### freeQueue() + +Hancurin message queue. + +```php +Swoole\Process->freeQueue(): bool +``` + +* **Return Value** + + * Sukses balik `true`. + * Gagal balik `false`. + +### pop() + +Ambil data dari message queue. + +```php +Swoole\Process->pop(int $size = 65536): string|false +``` + +* **Parameter** + + * **`int $size`** + * **Fungsi**: Ukuran data yang diambil. + * **Default**: `65536` + * **Nilai lain**: tidak ada + + +* **Return Value** + + * Balik `string` kalo sukses. + * Gagal balik `false`. + +* **Catatan** + + * Kalo tipe message queue `SW_MSGQUEUE_BALANCE`, balikin pesan pertama di queue. + * Kalo tipe message queue `SW_MSGQUEUE_ORIENT`, balikin pesan pertama dengan tipe `process id saat ini + 1`. + +### push() + +Kirim data ke message queue. + +```php +Swoole\Process->push(string $data): bool +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang dikirim. + * **Default**: `` + * **Nilai lain**: tidak ada + + +* **Return Value** + + * Balik `true` kalo sukses. + * Gagal balik `false`. + +* **Catatan** + + * Kalo tipe message queue `SW_MSGQUEUE_BALANCE`, data langsung dimasukin ke message queue. + * Kalo tipe message queue `SW_MSGQUEUE_ORIENT`, data bakal ditambahin tipe, yaitu `process id saat ini + 1`. + +### setTimeout() + +Set timeout baca/tulis message queue. + +```php +Swoole\Process->setTimeout(float $seconds): bool +``` + +* **Parameter** + + * **`float $seconds`** + * **Fungsi**: Waktu timeout + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + +* **Return Value** + + * Sukses balik `true`. + * Gagal balik `false`. + +### setBlocking() + +Set apakah socket message queue blocking. + +```php +Swoole\Process->setBlocking(bool $$blocking): void +``` + +* **Parameter** + + * **`bool $blocking`** + * **Fungsi**: Apakah blocking, `true` blocking, `false` non-blocking + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +* **Catatan** + + * Socket proses yang baru dibuat default-nya blocking, jadi pas komunikasi UNIX domain socket, kirim atau baca pesan bakal bikin proses blocking. + +### write() + +Nulis pesan antar parent dan child process (UNIX domain socket). + +```php +Swoole\Process->write(string $data): false|int +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang mau ditulis + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + +* **Return Value** + + * Sukses balik `int`, jumlah byte yang berhasil ditulis. + * Gagal balik `false`. + +### read() + +Baca pesan antar parent dan child process (UNIX domain socket). + +```php +Swoole\Process->read(int $size = 8192): false|string +``` + +* **Parameter** + + * **`int $size`** + * **Fungsi**: Ukuran data yang mau dibaca + * **Default**: `8192` + * **Nilai lain**: tidak ada + + +* **Return Value** + + * Sukses balik `string`. + * Gagal balik `false`. + +### set() + +Set parameter. + +```php +Swoole\Process->set(array $settings): void +``` + +Bisa pake `enable_coroutine` buat ngontrol apakah mau aktifin coroutine, sama kayak parameter ke-empat konstruktor. + +```php +Swoole\Process->set(['enable_coroutine' => true]); +``` + +!> Tersedia di Swoole >= v4.4.4 + +### start() + +Jalanin system call `fork`, mulai child process. Di sistem `Linux` butuh ratusan mikrodetik buat bikin proses. + +```php +Swoole\Process->start(): int|false +``` + +* **Return Value** + + * Sukses balik `PID` child process + * Gagal balik `false`. Bisa pake [swoole_errno](/functions?id=swoole_errno) dan [swoole_strerror](/functions?id=swoole_strerror) buat dapetin kode error dan pesan error. + +* **Catatan** + + * Child process bakal mewarisi memory dan file handle dari parent process + * Child process pas mulai bakal bersihin [EventLoop](/learn?id=apa-itu-eventloop), [Signal](/process/process?id=signal), dan [Timer](/timer) yang diwarisi dari parent process + + !> Setelah dijalankan, child process bakal pertahankan memory dan resource parent process. Misalnya kalo di parent process dibuat koneksi redis, di child process object ini bakal dipertahankan, semua operasi pake koneksi yang sama. Contoh di bawah: + +```php +$redis = new Redis; +$redis->connect('127.0.0.1', 6379); + +function callback_function() { + swoole_timer_after(1000, function () { + echo "hello world\n"; + }); + global $redis;//koneksi yang sama +}; + +swoole_timer_tick(1000, function () { + echo "parent timer\n"; +});//nggak diwarisi + +Swoole\Process::signal(SIGCHLD, function ($sig) { + while ($ret = Swoole\Process::wait(false)) { + // buat child process baru + $p = new Swoole\Process('callback_function'); + $p->start(); + } +}); + +// buat child process baru +$p = new Swoole\Process('callback_function'); + +$p->start(); +``` + +!> 1. Child process pas mulai otomatis bersihin timer [Swoole\Timer::tick](/timer?id=tick), signal [Process::signal](/process/process?id=signal), dan event listener [Swoole\Event::add](/event?id=add) dari parent process; +2. Child process bakal mewarisi object koneksi `$redis` yang dibuat parent process, parent dan child pake koneksi yang sama. + +### exportSocket() + +Export `unixSocket` jadi object `Swoole\Coroutine\Socket`, lalu pake method `Swoole\Coroutine\socket` buat komunikasi antar proses. Detailnya lihat [Coroutine\socket](/coroutine_client/socket) dan [Komunikasi IPC](/learn?id=apa-itu-IPC). + +```php +Swoole\Process->exportSocket(): Swoole\Coroutine\Socket|false +``` + +!> Panggil method ini berkali-kali, object yang dibalikin sama; +`socket` yang di-export `exportSocket()` adalah `fd` baru, nutup socket yang di-export nggak bakal pengaruh ke pipeline asli proses. +Karena ini object `Swoole\Coroutine\Socket`, harus dipake di [coroutine container](/coroutine/scheduler), jadi parameter konstruktor Swoole\Process `$enable_coroutine` harus true. +Parent process juga kalo mau pake object `Swoole\Coroutine\Socket`, perlu manual `Coroutine\run()` buat bikin coroutine container. + +* **Return Value** + + * Sukses balik object `Coroutine\Socket` + * Proses belum bikin unixSocket, operasi gagal, balik `false` + +* **Contoh Penggunaan** + +Implementasi komunikasi parent-child sederhana: + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +$proc1 = new Process(function (Process $proc) { + $socket = $proc->exportSocket(); + echo $socket->recv(); + $socket->send("hello master\n"); + echo "proc1 stop\n"; +}, false, 1, true); + +$proc1->start(); + +//Parent process bikin coroutine container +run(function() use ($proc1) { + $socket = $proc1->exportSocket(); + $socket->send("hello pro1\n"); + var_dump($socket->recv()); +}); +Process::wait(true); +``` + +Contoh komunikasi yang lebih kompleks: + +```php +use Swoole\Process; +use Swoole\Timer; +use function Swoole\Coroutine\run; + +$process = new Process(function ($proc) { + Timer::tick(1000, function () use ($proc) { + $socket = $proc->exportSocket(); + $socket->send("hello master\n"); + echo "child timer\n"; + }); +}, false, 1, true); + +$process->start(); + +run(function() use ($process) { + Process::signal(SIGCHLD, static function ($sig) { + while ($ret = Swoole\Process::wait(false)) { + /* bersihin, event loop bakal exit */ + Process::signal(SIGCHLD, null); + Timer::clearAll(); + } + }); + /* bisa jalanin kode async atau coroutine lain di sini */ + Timer::tick(500, function () { + echo "parent timer\n"; + }); + + $socket = $process->exportSocket(); + while (1) { + var_dump($socket->recv()); + } +}); +``` +!> Catat default tipenya `SOCK_STREAM`, perlu handle masalah boundary paket TCP, lihat method `setProtocol()` di [Coroutine\socket](/coroutine_client/socket). + +Pake tipe `SOCK_DGRAM` buat komunikasi IPC bisa hindari masalah boundary paket TCP, lihat [Komunikasi IPC](/learn?id=apa-itu-IPC): + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +//Komunikasi IPC, meskipun socket tipe SOCK_DGRAM, nggak perlu pake fungsi sendto/recvfrom, send/recv aja cukup. +$proc1 = new Process(function (Process $proc) { + $socket = $proc->exportSocket(); + while (1) { + var_dump($socket->send("hello master\n")); + } + echo "proc1 stop\n"; +}, false, 2, 1);//pipe type diisi 2 yaitu SOCK_DGRAM + +$proc1->start(); + +run(function() use ($proc1) { + $socket = $proc1->exportSocket(); + Swoole\Coroutine::sleep(5); + var_dump(strlen($socket->recv()));//sekali recv cuma dapet satu string "hello master\n", nggak bakal dapet banyak +}); + +Process::wait(true); +``` + +### name() + +Ubah nama proses. Fungsi ini adalah alias dari [swoole_set_process_name](/functions?id=swoole_set_process_name). + +```php +Swoole\Process->name(string $name): bool +``` + +!> Setelah `exec` dijalankan, nama proses bakal diatur ulang sama program baru; method `name` harus dipake di callback child process setelah `start`. + +### exec() + +Jalanin program eksternal. Fungsi ini adalah bungkus dari system call `exec`. + +```php +Swoole\Process->exec(string $execfile, array $args); +``` + +* **Parameter** + + * **`string $execfile`** + * **Fungsi**: Path absolut file yang bisa dijalankan, contoh `"/usr/bin/python"` + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`array $args`** + * **Fungsi**: Daftar argumen `exec`【contoh `array('test.py', 123)`, sama kayak `python test.py 123`】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +Setelah sukses, segmen kode proses saat ini bakal diganti sama program baru. Child process berubah jadi program lain. Parent process dan proses saat ini tetep hubungan parent-child. + +Parent process dan proses baru bisa komunikasi lewat standard input output, redirect stdin/stdout harus diaktifkan. + +!> `$execfile` harus pake path absolut, kalo nggak bakal error file not found; +Karena system call `exec` bakal nimpa program saat ini dengan program yang ditentukan, child process perlu baca/tulis standard output buat komunikasi dengan parent process; +Kalo nggak diset `redirect_stdin_stdout = true`, setelah `exec` dijalankan, child process dan parent process nggak bisa komunikasi. + +* **Contoh Penggunaan** + +Contoh 1: Bisa pake [Swoole\Server](/server/init) di child process yang dibuat `Swoole\Process`, tapi demi keamanan harus panggil `$worker->exec()` setelah `$process->start`. Kodenya: + +```php +$process = new Swoole\Process('callback_function', true); + +$pid = $process->start(); + +function callback_function(Swoole\Process $worker) +{ + $worker->exec('/usr/local/bin/php', array(__DIR__.'/swoole_server.php')); +} + +Swoole\Process::wait(); +``` + +Contoh 2: Jalanin program Yii + +```php +$process = new \Swoole\Process(function (\Swoole\Process $childProcess) { + // cara nulis kayak gini nggak didukung + // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii t/index -m=123 abc xyz'); + + // Bungkus system call exec + // Path absolut + // Parameter harus dipisah-pisah di array + $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', 't/index', '-m=123', 'abc', 'xyz']); // system call exec +}); +$process->start(); // mulai child process +``` + +Contoh 3: Parent process komunikasi dengan child process `exec` pake standard input output: + +```php +// exec - komunikasi pipe dengan proses exec +use Swoole\Process; +use function Swoole\Coroutine\run; + +$process = new Process(function (Process $worker) { + $worker->exec('/bin/echo', ['hello']); +}, true, 1, true); // perlu aktifkan redirect stdin/stdout + +$process->start(); + +run(function() use($process) { + $socket = $process->exportSocket(); + echo "from exec: " . $socket->recv() . "\n"; +}); +``` + +Contoh 4: Jalanin perintah shell + +Method `exec` beda sama `shell_exec` yang disediakan `PHP`. Ini lebih low-level, bungkus system call. Kalo perlu jalanin perintah `shell`, pake cara ini: + +```php +$worker->exec('/bin/sh', array('-c', "cp -rf /data/test/* /tmp/test/")); +``` + +### close() + +Buat nutup [unixSocket](/learn?id=apa-itu-IPC) yang sudah dibuat. + +```php +Swoole\Process->close(int $which): bool +``` + +* **Parameter** + + * **`int $which`** + * **Fungsi**: Karena unixSocket full-duplex, tentuin sisi mana yang ditutup【default `0` nutup baca dan tulis, `1`: nutup tulis, `2` nutup baca】 + * **Default**: `0`, nutup read dan write socket. + * **Nilai lain**: `Swoole/Process::SW_PIPE_CLOSE_READ` nutup read socket, `Swoole/Process::SW_PIPE_CLOSE_WRITE` nutup write socket, + +!> Ada kasus khusus di mana object `Process` nggak bisa di-release. Kalo terus-terusan bikin proses, bisa bocor koneksi. Panggil fungsi ini bisa langsung nutup `unixSocket`, release resource. + +### exit() + +Keluar dari child process. + +```php +Swoole\Process->exit(int $status = 0); +``` + +* **Parameter** + + * **`int $status`** + * **Fungsi**: Status code keluar proses【Kalo `0` artinya normal, bakal lanjut execute cleanup】 + * **Default**: `0` + * **Nilai lain**: tidak ada + +!> Pekerjaan cleanup meliputi: + + * `shutdown_function` PHP + * Destructor object (`__destruct`) + * Fungsi `RSHUTDOWN` ekstensi lain + +Kalo `$status` bukan `0`, artinya exit abnormal, proses bakal langsung dihentikan, nggak jalanin cleanup terkait. + +Di parent process, jalanin `Process::wait` bisa dapet event exit dan status code child process. + +### kill() + +Kirim sinyal ke proses dengan `pid` tertentu. + +```php +Swoole\Process::kill(int $pid, int $signo = SIGTERM): bool +``` + +* **Parameter** + + * **`int $pid`** + * **Fungsi**: Process `pid` + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $signo`** + * **Fungsi**: Sinyal yang dikirim【`$signo=0`, bisa detek apakah proses ada, nggak kirim sinyal】 + * **Default**: `SIGTERM` + * **Nilai lain**: tidak ada + +### signal() + +Set mendengarkan sinyal asynchronous. + +```php +Swoole\Process::signal(int $signo, callable $callback): bool +``` + +Method ini berdasarkan `signalfd` dan [EventLoop](/learn?id=apa-itu-eventloop) yang merupakan `IO` asynchronous, nggak bisa dipake di program blocking, karena callback yang didaftar nggak bakal dijadwal; + +Program synchronous blocking bisa pake `pcntl_signal` dari ekstensi `pcntl`; + +Kalo sinyal ini udah diset callback-nya, set ulang bakal timpa history. + +* **Parameter** + + * **`int $signo`** + * **Fungsi**: Sinyal + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`callable $callback`** + * **Fungsi**: Callback function【Kalo `$callback` `null`, hapus mendengarkan sinyal】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +!> Di [Swoole\Server](/server/init) ada sinyal tertentu yang nggak bisa diset mendengarkan, kayak `SIGTERM` dan `SIGALAM` + +* **Contoh Penggunaan** + +```php +Swoole\Process::signal(SIGTERM, function($signo) { + echo "shutdown."; +}); +``` + +!> Di versi `v4.4.0`, kalo [EventLoop](/learn?id=apa-itu-eventloop) proses cuma punya event mendengarkan sinyal, tanpa event lain (misalnya Timer), proses bakal langsung exit. + +```php +Swoole\Process::signal(SIGTERM, function($signo) { + echo "shutdown."; +}); +Swoole\Event::wait(); +``` + +Program di atas nggak bakal masuk [EventLoop](/learn?id=apa-itu-eventloop), `Swoole\Event::wait()` bakal langsung balik, dan exit proses. + +### wait() + +Membersihkan child process yang udah selesai jalan. + +!> Swoole >= `v4.5.0` disarankan pake versi coroutine `wait()`, lihat [Swoole\Coroutine\System::wait()](/coroutine/system?id=wait) + +```php +Swoole\Process::wait(bool $blocking = true): array|false +``` + +* **Parameter** + + * **`bool $blocking`** + * **Fungsi**: Apakah blocking wait【default blocking】 + * **Default**: `true` + * **Nilai lain**: `false` + +* **Return Value** + + * Sukses balik array berisi `PID` child process, status code exit, sinyal `KILL` + * Gagal balik `false` + +!> Setiap child process selesai, parent process harus panggil `wait()` buat membersihkan, kalo nggak child process bakal jadi zombie process, boros resource proses OS. +Kalo parent process punya tugas lain dan nggak bisa blocking di `wait`, parent process harus daftar sinyal `SIGCHLD` buat `wait` proses yang exit. +Pas sinyal SIGCHLD terjadi, mungkin ada banyak child process yang exit barengan; `wait()` harus diset non-blocking, loop `wait` sampe balik `false`. + +* **Contoh** + +```php +Swoole\Process::signal(SIGCHLD, function ($sig) { + // harus false, mode non-blocking + while ($ret = Swoole\Process::wait(false)) { + echo "PID={$ret['pid']}\n"; + } +}); +``` + +### daemon() + +Ubah proses saat ini jadi daemon. + +```php +Swoole\Process::daemon(bool $nochdir = true, bool $noclose = true): bool +``` + +* **Parameter** + + * **`bool $nochdir`** + * **Fungsi**: Apakah pindah direktori saat ini ke root【`true` berarti jangan pindah direktori】 + * **Default**: `true` + * **Nilai lain**: `false` + + * **`bool $noclose`** + * **Fungsi**: Apakah nutup file descriptor stdin/stdout【`true` berarti jangan nutup】 + * **Default**: `true` + * **Nilai lain**: `false` + +!> Pas berubah jadi daemon, `PID` proses bakal berubah. Bisa pake `getmypid()` buat dapetin `PID` saat ini + +### alarm() + +Timer presisi tinggi, bungkus system call `setitimer` OS, bisa set timer level mikrodetik. Timer bakal picu sinyal, harus dipake bareng [Process::signal](/process/process?id=signal) atau `pcntl_signal`. + +!> `alarm` nggak bisa dipake bareng [Timer](/timer) + +```php +Swoole\Process->alarm(int $time, int $type = 0): bool +``` + +* **Parameter** + + * **`int $time`** + * **Fungsi**: Interval timer【kalo negatif, hapus timer】 + * **Satuan**: Mikrodetik + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $type`** + * **Fungsi**: Tipe timer + * **Default**: `0` + * **Nilai lain**: + +Tipe Timer | Keterangan +---|--- +0 | Waktu real, picu sinyal `SIGALAM` +1 | CPU time user mode, picu sinyal `SIGVTALAM` +2 | CPU time user + kernel mode, picu sinyal `SIGPROF` + +* **Return Value** + + * Set sukses balik `true` + * Gagal balik `false`, bisa pake `swoole_errno` dapetin kode error + +* **Contoh Penggunaan** + +```php +use Swoole\Process; +use function Swoole\Coroutine\run; + +run(function () { + Process::signal(SIGALRM, function () { + static $i = 0; + echo "#{$i}\talarm\n"; + $i++; + if ($i > 20) { + Process::alarm(-1); + Process::kill(getmypid()); + } + }); + + //100ms + Process::alarm(100 * 1000); + + while(true) { + sleep(0.5); + } +}); +``` + +### setAffinity() + +Set afinitas `CPU`, bisa bind proses ke core `CPU` tertentu. + +Fungsi ini biar proses cuma jalan di beberapa core `CPU`, kasih resource `CPU` tertentu buat program yang lebih penting. + +```php +Swoole\Process->setAffinity(array $cpus): bool +``` + +* **Parameter** + + * **`array $cpus`** + * **Fungsi**: Bind core `CPU`【contoh `array(0,2,3)` bind ke `CPU0/CPU2/CPU3`】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +!> - Elemen `$cpus` nggak boleh lebih dari jumlah core `CPU`; +- `CPU-ID` nggak boleh lebih dari (jumlah core `CPU` - `1`); +- Fungsi ini tetapi system operasi harus dukung bind `CPU`; +- Pake [swoole_cpu_num()](/functions?id=swoole_cpu_num) buat dapetin jumlah core `CPU` server. + +### getAffinity() +Dapetin afinitas `CPU` proses + +```php +Swoole\Process->getAffinity(): array +``` +Return value array, elemennya adalah core `CPU`, contoh: `[0, 1, 3, 4]` artinya proses ini bakal dijadwal ke core `CPU` `0/1/3/4` + +### setPriority() + +Set prioritas proses, process group, dan user process. + +!> Swoole >= `v4.5.9` + +```php +Swoole\Process->setPriority(int $which, int $priority): bool +``` + +* **Parameter** + + * **`int $which`** + * **Fungsi**: Nentuin tipe prioritas yang diubah + * **Default**: tidak ada + * **Nilai lain**: + +| Konstanta | Keterangan | +| ------------- | ---------- | +| PRIO_PROCESS | Proses | +| PRIO_PGRP | Process group | +| PRIO_USER | User process | + + * **`int $priority`** + * **Fungsi**: Prioritas. Makin kecil nilainya, makin tinggi prioritas + * **Default**: tidak ada + * **Nilai lain**: `[-20, 20]` + +* **Return Value** + + * Kalo balik `false`, bisa pake [swoole_errno](/functions?id=swoole_errno) dan [swoole_strerror](/functions?id=swoole_strerror) dapetin kode error dan pesan error. + +### getPriority() + +Dapetin prioritas proses. + +!> Swoole >= `v4.5.9` + +```php +Swoole\Process->getPriority(int $which): int +``` diff --git a/public/id/process/process_manager.md b/public/id/process/process_manager.md new file mode 100644 index 0000000..eb58a09 --- /dev/null +++ b/public/id/process/process_manager.md @@ -0,0 +1,149 @@ +# Process\Manager + +Manager proses, berbasis [Process\Pool](/process/process_pool). Bisa mengelola banyak proses. Dibanding `Process\Pool`, bisa dengan gampang bikin banyak proses yang jalanin tugas berbeda, dan bisa kontrol apakah setiap proses mau pake lingkungan coroutine. + +## Dukungan Versi + +| Versi | Nama Class | Catatan Update | +| ----- | ------------------------------ | --------------------------------------- | +| v4.5.3 | Swoole\Process\ProcessManager | - | +| v4.5.5 | Swoole\Process\Manager | Diganti nama, ProcessManager jadi alias Manager | + +!> Tersedia di versi `v4.5.3` ke atas. + +## Contoh Penggunaan + +```php +use Swoole\Process\Manager; +use Swoole\Process\Pool; + +$pm = new Manager(); + +for ($i = 0; $i < 2; $i++) { + $pm->add(function (Pool $pool, int $workerId) { + }); +} + +$pm->start(); +``` + +## Method + +### __construct() + +Konstruktor. + +```php +Swoole\Process\Manager::__construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0); +``` + +* **Parameter** + + * **`int $ipcType`** + * **Fungsi**: Mode komunikasi antar proses, sama kayak `$ipc_type` di `Process\Pool`【default `0` artinya nggak pake fitur IPC】 + * **Default**: `0` + * **Nilai lain**: tidak ada + + * **`int $msgQueueKey`** + * **Fungsi**: `key` message queue, sama kayak `$msgqueue_key` di `Process\Pool` + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### setIPCType() + +Set cara komunikasi antar worker process. + +```php +Swoole\Process\Manager->setIPCType(int $ipcType): self; +``` + +* **Parameter** + + * **`int $ipcType`** + * **Fungsi**: Mode komunikasi antar proses + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### getIPCType() + +Dapetin cara komunikasi antar worker process. + +```php +Swoole\Process\Manager->getIPCType(): int; +``` + +### setMsgQueueKey() + +Set `key` message queue. + +```php +Swoole\Process\Manager->setMsgQueueKey(int $msgQueueKey): self; +``` + +* **Parameter** + + * **`int $msgQueueKey`** + * **Fungsi**: `key` message queue + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### getMsgQueueKey() + +Dapetin `key` message queue. + +```php +Swoole\Process\Manager->getMsgQueueKey(): int; +``` + +### add() + +Nambahin worker process. + +```php +Swoole\Process\Manager->add(callable $func, bool $enableCoroutine = false): self; +``` + +* **Parameter** + + * **`callable $func`** + * **Fungsi**: Callback yang dijalanin proses saat ini + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`bool $enableCoroutine`** + * **Fungsi**: Apakah bikin coroutine buat proses ini buat jalanin callback + * **Default**: false + * **Nilai lain**: tidak ada + +### addBatch() + +Nambahin worker process secara batch. + +```php +Swoole\Process\Manager->addBatch(int $workerNum, callable $func, bool $enableCoroutine = false): self +``` + +* **Parameter** + + * **`int $workerNum`** + * **Fungsi**: Jumlah proses yang ditambahin + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`callable $func`** + * **Fungsi**: Callback yang dijalanin proses-proses ini + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`bool $enableCoroutine`** + * **Fungsi**: Apakah bikin coroutine buat proses-proses ini + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +### start() + +Mulai worker process. + +```php +Swoole\Process\Manager->start(): void +``` diff --git a/public/id/process/process_pool.md b/public/id/process/process_pool.md new file mode 100644 index 0000000..276c412 --- /dev/null +++ b/public/id/process/process_pool.md @@ -0,0 +1,543 @@ +# Swoole\Process\Pool + +Process pool, berdasarkan modul Manager [Swoole\Server](/server/init) untuk manajemen proses. Bisa mengelola banyak worker process. Fungsi inti modul ini adalah manajemen proses. Dibanding pake `Process` buat multi-proses, `Process\Pool` lebih sederhana, enkapsulasi lebih tinggi, developer nggak perlu nulis banyak kode buat manajemen proses. Bareng [Co\Server](/coroutine/server?id=contoh-lengkap) bisa bikin program server pure coroutine yang manfaatin multi-core CPU. + +## Komunikasi Antar Proses + +`Swoole\Process\Pool` nyediain tiga cara komunikasi antar proses: + +### Message Queue +Kalo parameter ke-2 `Swoole\Process\Pool->__construct` diset `SWOOLE_IPC_MSGQUEUE`, artinya pake message queue buat komunikasi antar proses. Bisa kirim pesan lewat ekstensi `php sysvmsg`, maksimum pesan `65536`. + +* **Catatan** + + * Kalo mau pake ekstensi `sysvmsg`, di konstruktor harus dikasih `msgqueue_key` + * `Swoole` level bawah nggak dukung parameter ke-2 `mtype` dari `msg_send` ekstensi `sysvmsg`, kasih nilai bukan `0` aja + +### Socket Communication +Kalo parameter ke-2 `Swoole\Process\Pool->__construct` diset `SWOOLE_IPC_SOCKET`, artinya pake `Socket Communication`. Kalo client dan server nggak di mesin yang sama, bisa pake cara ini. + +Levat method [Swoole\Process\Pool->listen()](/process/process_pool?id=listen) buat mendengarkan port, pake [Message event](/process/process_pool?id=on) buat nerima data dari client, pake [Swoole\Process\Pool->write()](/process/process_pool?id=write) buat balikin respon ke client. + +`Swoole` mengharuskan client pas kirim data pake cara ini, harus nambahin 4 byte panjang value dengan network byte order di depan data. +```php +$msg = 'Hello Swoole'; +$packet = pack('N', strlen($msg)) . $msg; +``` + +### UnixSocket +Kalo parameter ke-2 `Swoole\Process\Pool->__construct` diset `SWOOLE_IPC_UNIXSOCK`, artinya pake [UnixSocket](/learn?id=apa-itu-IPC), **sangat disarankan pake cara ini buat komunikasi antar proses**. + +Cara ini gampang, tinggal pake method [Swoole\Process\Pool->sendMessage()](/process/process_pool?id=sendMessage) dan [Message event](/process/process_pool?id=on) buat komunikasi antar proses. + +Atau kalo `mode coroutine` diaktifkan, bisa dapetin object `Swoole\Process` lewat [Swoole\Process\Pool->getProcess()](/process/process_pool?id=getProcess), pake [Swoole\Process->exportsocket()](/process/process?id=exportsocket) dapetin object `Swoole\Coroutine\Socket`, pake object ini buat komunikasi antar proses. Tapi kalo gitu [Message event](/process/process_pool?id=on) nggak bisa diset. + +!> Parameter dan konfigurasi lingkungan bisa liat [konstruktor](/process/process_pool?id=__construct) dan [parameter konfigurasi](/process/process_pool?id=set) + +## Konstanta + +Konstanta | Keterangan +---|--- +SWOOLE_IPC_MSGQUEUE | Komunikasi [message queue](/learn?id=apa-itu-IPC) sistem +SWOOLE_IPC_SOCKET | Komunikasi SOCKET +SWOOLE_IPC_UNIXSOCK | Komunikasi [UnixSocket](/learn?id=apa-itu-IPC) (v4.4+) + +## Dukungan Coroutine + +Di versi `v4.4.0` nambahin dukungan coroutine, lihat [Swoole\Process\Pool::__construct](/process/process_pool?id=__construct) + +## Contoh Penggunaan + +```php +use Swoole\Process; +use Swoole\Coroutine; + +$pool = new Process\Pool(5); +$pool->set(['enable_coroutine' => true]); +$pool->on('WorkerStart', function (Process\Pool $pool, $workerId) { + /** Ini adalah Worker process */ + static $running = true; + Process::signal(SIGTERM, function () use (&$running) { + $running = false; + echo "TERM\n"; + }); + echo("[Worker #{$workerId}] WorkerStart, pid: " . posix_getpid() . "\n"); + while ($running) { + Coroutine::sleep(1); + echo "sleep 1\n"; + } +}); +$pool->on('WorkerStop', function (\Swoole\Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStop\n"); +}); +$pool->start(); +``` + +## Method + +### __construct() + +Konstruktor. + +```php +Swoole\Process\Pool::__construct(int $worker_num, int $ipc_type = SWOOLE_IPC_NONE, int $msgqueue_key = 0, bool $enable_coroutine = false); +``` + +* **Parameter** + + * **`int $worker_num`** + * **Fungsi**: Jumlah worker process + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $ipc_type`** + * **Fungsi**: Mode komunikasi antar proses【default `SWOOLE_IPC_NONE` artinya nggak pake fitur IPC】 + * **Default**: `SWOOLE_IPC_NONE` + * **Nilai lain**: `SWOOLE_IPC_MSGQUEUE`, `SWOOLE_IPC_SOCKET`, `SWOOLE_IPC_UNIXSOCK` + + !> -Kalo `SWOOLE_IPC_NONE`, harus set callback `onWorkerStart`, dan harus implement logic loop di `onWorkerStart`. Pas `onWorkerStart` exit, worker process langsung exit, nanti Manager process bakal jalanin ulang; + -`SWOOLE_IPC_MSGQUEUE` artinya pake message queue sistem, bisa set `$msgqueue_key` buat specify `KEY` message queue; kalo nggak diset, bakal pake queue private; + -`SWOOLE_IPC_SOCKET` artinya pake `Socket`, perlu pake method [listen](/process/process_pool?id=listen) buat specify alamat dan port; + -`SWOOLE_IPC_UNIXSOCK` artinya pake [unixSocket](/learn?id=apa-itu-IPC), dipake di mode coroutine, **sangat disarankan pake cara ini**, detailnya liat di bawah; + -Kalo pake selain `SWOOLE_IPC_NONE`, harus set callback `onMessage`, `onWorkerStart` jadi opsional. + + * **`int $msgqueue_key`** + * **Fungsi**: `key` message queue + * **Default**: `0` + * **Nilai lain**: tidak ada + + * **`bool $enable_coroutine`** + * **Fungsi**: Aktifkan coroutine atau nggak【kalo pake coroutine, `onMessage` callback nggak bisa diset】 + * **Default**: `false` + * **Nilai lain**: `true` + +* **Mode Coroutine** + +Di versi `v4.4.0`, modul `Process\Pool` nambahin dukungan coroutine. Bisa aktifkan dengan set parameter ke-4 jadi `true`. Kalo coroutine diaktifkan, level bawah bakal otomatis bikin coroutine dan [coroutine container](/coroutine/scheduler) pas `onWorkerStart`, di callback bisa langsung pake `API` coroutine, contoh: + +```php +$pool = new Swoole\Process\Pool(1, SWOOLE_IPC_NONE, 0, true); + +$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + while (true) { + Co::sleep(0.5); + echo "hello world\n"; + } +}); + +$pool->start(); +``` + +Kalo coroutine diaktifkan, Swoole bakal larang set callback event `onMessage`. Kalo perlu komunikasi antar proses, set parameter ke-2 jadi `SWOOLE_IPC_UNIXSOCK` buat pake [unixSocket](/learn?id=apa-itu-IPC), lalu pake `$pool->getProcess()->exportSocket()` buat export object [Swoole\Coroutine\Socket](/coroutine_client/socket), implement komunikasi antar `Worker` process. Contoh: + + ```php +$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_UNIXSOCK, 0, true); + +$pool->on('workerStart', function (Swoole\Process\Pool $pool, int $workerId) { + $process = $pool->getProcess(0); + $socket = $process->exportSocket(); + if ($workerId == 0) { + echo $socket->recv(); + $socket->send("hello proc1\n"); + echo "proc0 stop\n"; + } else { + $socket->send("hello proc0\n"); + echo $socket->recv(); + echo "proc1 stop\n"; + $pool->shutdown(); + } +}); + +$pool->start(); + ``` + +!> Detailnya lihat [Swoole\Coroutine\Socket](/coroutine_client/socket) dan [Swoole\Process](/process/process?id=exportsocket). + +```php +$q = msg_get_queue($key); +foreach (range(1, 100) as $i) { + $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]); + msg_send($q, $i, $data, false); +} +``` + +### set() + +Set parameter. + +```php +Swoole\Process\Pool->set(array $settings): void +``` + +Parameter opsional|Tipe|Fungsi|Default +---|---|---|--- +enable_coroutine|bool|Aktifkan coroutine|false +enable_message_bus|bool|Aktifkan message bus. Kalo `true`, data besar bakal dipecah jadi potongan kecil, dikirim satu per satu|false +max_package_size|int|Maksimal data yang bisa diterima proses|2 * 1024 * 1024 + +* **Catatan** + + * Kalo `enable_message_bus` `true`, `max_package_size` nggak berfungsi, karena data bakal dipecah-potong. + * Di mode `SWOOLE_IPC_MSGQUEUE`, `max_package_size` juga nggak berfungsi, level bawah maksimal nerima `65536`. + * Di mode `SWOOLE_IPC_SOCKET`, kalo `enable_message_bus` `false` dan data yang diterima lebih besar dari `max_package_size`, level bawah bakal langsung putus koneksi. + * Di mode `SWOOLE_IPC_UNIXSOCK`, kalo `enable_message_bus` `false` dan data lebih besar dari `max_package_size`, data lebihan bakal dipotong. + * Kalo mode coroutine diaktifkan dan `enable_message_bus` `true`, `max_package_size` juga nggak berfungsi. Level bawah bakal ngatur pemecahan (kirim) dan penggabungan (terima) data. Kalo nggak, penerimaan data dibatasi `max_package_size` + +!> Swoole >= v4.4.4 + +### on() + +Set callback function process pool. + +```php +Swoole\Process\Pool->on(string $event, callable $function): bool; +``` + +* **Parameter** + + * **`string $event`** + * **Fungsi**: Tentukan event + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`callable $function`** + * **Fungsi**: Callback function + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +* **Event** + + * **onWorkerStart** Child process mulai + + ```php + /** + * @param \Swoole\Process\Pool $pool Object Pool + * @param int $workerId WorkerId nomor worker process saat ini, internal bakal kasih nomor + */ + $pool = new Swoole\Process\Pool(2); + $pool->on('WorkerStart', function(Swoole\Process\Pool $pool, int $workerId){ + echo "Worker#{$workerId} is started\n"; + }); + ``` + + * **onWorkerStop** Child process selesai + + ```php + /** + * @param \Swoole\Process\Pool $pool Object Pool + * @param int $workerId WorkerId nomor worker process saat ini, internal bakal kasih nomor + */ + $pool = new Swoole\Process\Pool(2); + $pool->on('WorkerStop', function(Swoole\Process\Pool $pool, int $workerId){ + echo "Worker#{$workerId} stop\n"; + }); + ``` + + * **onMessage** Penerimaan pesan + + !> Nerima pesan dari luar. Satu koneksi cuma bisa kirim satu pesan, mirip mekanisme short connection `PHP-FPM` + + ```php + /** + * @param \Swoole\Process\Pool $pool Object Pool + * @param string $data Isi data pesan + */ + $pool = new Swoole\Process\Pool(2); + $pool->on('Message', function(Swoole\Process\Pool $pool, string $data){ + var_dump($data); + }); + ``` + + !> Nama event nggak case-sensitive, `WorkerStart`, `workerStart` atau `workerstart` sama aja. + +### listen() + +Mendengarkan `SOCKET`, cuma bisa dipake kalo `$ipc_mode = SWOOLE_IPC_SOCKET`. + +```php +Swoole\Process\Pool->listen(string $host, int $port = 0, int $backlog = 2048): bool +``` + +* **Parameter** + + * **`string $host`** + * **Fungsi**: Alamat yang didengarkan【dukung dua tipe: `TCP` dan [unixSocket](/learn?id=apa-itu-IPC). `127.0.0.1` mendengarkan alamat `TCP`, perlu specify `$port`. `unix:/tmp/php.sock` mendengarkan [unixSocket](/learn?id=apa-itu-IPC)】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $port`** + * **Fungsi**: Port yang didengarkan【wajib di mode `TCP`】 + * **Default**: `0` + * **Nilai lain**: tidak ada + + * **`int $backlog`** + * **Fungsi**: Panjang antrian koneksi + * **Default**: `2048` + * **Nilai lain**: tidak ada + +* **Return Value** + + * Sukses mendengarkan, balik `true` + * Gagal mendengarkan, balik `false`, bisa panggil `swoole_errno` dapetin kode error. Kalo gagal mendengarkan, pas panggil `start` bakal langsung balik `false` + +* **Protokol Komunikasi** + + Pas kirim data ke port yang didengarkan, client harus nambahin 4 byte panjang value network byte order sebelum request. Format protokol: + +```php +// $msg data yang dikirim +$packet = pack('N', strlen($msg)) . $msg; +``` + +* **Contoh Penggunaan** + +```php +$pool->listen('127.0.0.1', 8089); +$pool->listen('unix:/tmp/php.sock'); +``` + +### write() + +Nulis data ke lawan bicara, cuma bisa dipake kalo `$ipc_mode` `SWOOLE_IPC_SOCKET`. + +```php +Swoole\Process\Pool->write(string $data): bool +``` + +!> Method ini operasi memory, tanpa `IO`, operasi kirim data adalah `IO` synchronous blocking + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang ditulis【bisa panggil `write` berkali-kali, level bawah bakal tulis semua data ke `socket` pas `onMessage` selesai, lalu `close` koneksi】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +* **Contoh Penggunaan** + + * **Server** + + ```php + $pool = new Swoole\Process\Pool(2, SWOOLE_IPC_SOCKET); + + $pool->on("Message", function ($pool, $message) { + echo "Message: {$message}\n"; + $pool->write("hello "); + $pool->write("world "); + $pool->write("\n"); + }); + + $pool->listen('127.0.0.1', 8089); + $pool->start(); + ``` + + * **Client** + + ```php + $fp = stream_socket_client("tcp://127.0.0.1:8089", $errno, $errstr) or die("error: $errstr\n"); + $msg = json_encode(['data' => 'hello', 'uid' => 1991]); + fwrite($fp, pack('N', strlen($msg)) . $msg); + sleep(1); + //bakal nampilin hello world\n + $data = fread($fp, 8192); + var_dump(substr($data, 4, unpack('N', substr($data, 0, 4))[1])); + fclose($fp); + ``` + +### sendMessage() + +Kirim data ke proses target, cuma bisa dipake kalo `$ipc_mode` `SWOOLE_IPC_UNIXSOCK`. + +```php +Swoole\Process\Pool->sendMessage(string $data, int $dst_worker_id): bool +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Data yang dikirim + * **Default**: tidak ada + * **Nilai lain**: tidak ada + + * **`int $dst_worker_id`** + * **Fungsi**: ID proses target + * **Default**: `0` + * **Nilai lain**: tidak ada + +* **Return Value** + + * Kirim sukses balik `true` + * Kirim gagal balik `false` + +* **Catatan** + + * Kalo data yang dikirim lebih besar dari `max_package_size` dan `enable_message_bus` `false`, proses target pas nerima data bakal memotong data + +```php +set(['enable_coroutine' => true, 'enable_message_bus' => false, 'max_package_size' => 2 * 1024]); + +$pool->on('WorkerStart', function (Swoole\Process\Pool $pool, int $workerId) { + if ($workerId == 0) { + $pool->sendMessage(str_repeat('a', 2 * 3000), 1); + } +}); + +$pool->on('Message', function (Swoole\Process\Pool $pool, string $data) { + var_dump(strlen($data)); +}); +$pool->start(); + +// int(2048) + + +$pool = new Process\Pool(2, SWOOLE_IPC_UNIXSOCK); +$pool->set(['enable_coroutine' => true, 'enable_message_bus' => true, 'max_package_size' => 2 * 1024]); + +$pool->on('WorkerStart', function (Swoole\Process\Pool $pool, int $workerId) { + if ($workerId == 0) { + $pool->sendMessage(str_repeat('a', 2 * 3000), 1); + } +}); + +$pool->on('Message', function (Swoole\Process\Pool $pool, string $data) { + var_dump(strlen($data)); +}); +$pool->start(); + +// int(6000) +``` + +### start() + +Mulai worker process. + +```php +Swoole\Process\Pool->start(): bool +``` + +!> Mulai sukses, proses saat ini masuk status `wait`, mengelola worker process; +Mulai gagal, balik `false`, bisa pake `swoole_errno` dapetin kode error. + +* **Contoh Penggunaan** + +```php +$workerNum = 10; +$pool = new Swoole\Process\Pool($workerNum); + +$pool->on("WorkerStart", function ($pool, $workerId) { + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while (true) { + $msg = $redis->brpop($key, 2); + if ( $msg == null) continue; + var_dump($msg); + } +}); + +$pool->on("WorkerStop", function ($pool, $workerId) { + echo "Worker#{$workerId} is stopped\n"; +}); + +$pool->start(); +``` + +* **Manajemen Proses** + + * Kalo worker process kena fatal error atau exit sendiri, manager bakal membersihkan, hindari zombie process + * Setelah worker process exit, manager bakal otomatis buat worker process baru + * Main process nerima sinyal `SIGTERM` bakal berhenti `fork` proses baru, dan `kill` semua worker process yang jalan + * Main process nerima sinyal `SIGUSR1` bakal `kill` satu-satu worker process yang jalan, lalu restart worker process baru + +* **Penanganan Sinyal** + + Level bawah cuma set penanganan sinyal di main process (manager process), nggak set sinyal untuk `Worker` process. Developer perlu implement sendiri mendengarkan sinyal. + + - Worker process mode asynchronous, pake [Swoole\Process::signal](/process/process?id=signal) buat mendengarkan sinyal + - Worker process mode synchronous, pake `pcntl_signal` dan `pcntl_signal_dispatch` buat mendengarkan sinyal + + Di worker process harus mendengarkan sinyal `SIGTERM`. Kalo main process perlu matiin proses itu, bakal kirim sinyal `SIGTERM`. Kalo worker process nggak mendengarkan `SIGTERM`, level bawah bakal paksa matiin proses, sebagian logic bisa ilang. + +```php +$pool->on("WorkerStart", function ($pool, $workerId) { + $running = true; + pcntl_signal(SIGTERM, function () use (&$running) { + $running = false; + }); + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while ($running) { + $msg = $redis->brpop($key); + pcntl_signal_dispatch(); + if ( $msg == null) continue; + var_dump($msg); + } +}); +``` + +### stop() + +Keluarin socket proses saat ini dari event loop. Fungsi ini cuma berguna kalo coroutine diaktifkan. + +```php +Swoole\Process\Pool->stop(): bool +``` + +### shutdown() + +Hentikan worker process. + +```php +Swoole\Process\Pool->shutdown(): bool +``` + +### getProcess() + +Dapetin object worker process saat ini. Balikin object [Swoole\Process](/process/process). + +!> Swoole >= `v4.2.0` + +```php +Swoole\Process\Pool->getProcess(int $worker_id): Swoole\Process +``` + +* **Parameter** + + * **`int $worker_id`** + * **Fungsi**: Tentukan `worker` yang mau diambil【opsional, default `worker` saat ini】 + * **Default**: tidak ada + * **Nilai lain**: tidak ada + +!> Harus dipanggil setelah `start`, di `onWorkerStart` worker process atau callback lain; +Object `Process` yang dibalikin adalah singleton, panggil `getProcess()` berkali-kali di worker process bakal balikin object yang sama. + +* **Contoh Penggunaan** + +```php +$pool = new Swoole\Process\Pool(3); + +$pool->on('WorkerStart', function ($pool, $workerId) { + $process = $pool->getProcess(); + $process->exec('/usr/local/bin/php', ['-r', 'var_dump(swoole_version());']); +}); + +$pool->start(); +``` + +### detach() + +Lepas Worker process saat ini dari mengelola pool. Level bawah bakal langsung buat proses baru, proses lama nggak bakal ngolah data lagi, lifecycle diurus kode aplikasi sendiri. + +!> Swoole >= `v4.7.0` + +```php +Swoole\Process\Pool->detach(): bool +``` diff --git a/public/id/question/install.md b/public/id/question/install.md new file mode 100644 index 0000000..c4c65e4 --- /dev/null +++ b/public/id/question/install.md @@ -0,0 +1,265 @@ +# Masalah Instalasi + +## Upgrade Versi Swoole + +Kamu bisa gunakan pecl untuk instalasi dan upgrade + +```shell +pecl upgrade swoole +``` + +Bisa juga download versi baru langsung dari github/gitee/pecl, lalu instal ulang dengan kompilasi. + +* Update versi Swoole tidak perlu menghapus versi lama, proses instalasi akan menimpa versi lama +* Setelah kompilasi Swoole, tidak ada file tambahan, hanya `swoole.so`. Kalau ada binary dari mesin lain, tinggal timpa `swoole.so` untuk ganti versi +* Kalau clone dari git, setelah `git pull`, jangan lupa jalankan `phpize`, `./configure`, `make clean`, `make install` +* Bisa juga pakai docker yang sesuai untuk upgrade versi Swoole + +## Ada di phpinfo tapi tidak ada di php -m + +Cek dulu apakah ada di mode CLI, jalankan `php --ri swoole` + +Kalau keluar info ekstensi Swoole, artinya instalasi berhasil! + +**99.999% orang di step ini sudah bisa langsung pakai Swoole** + +Tidak perlu peduliin `php -m` atau `phpinfo` di web ada atau tidak Swoole + +Karena Swoole jalan di mode cli, di mode fpm tradisional fungsinya sangat terbatas + +Di mode fpm, fitur utama seperti async/coroutine **tidak bisa dipakai**. 99.999% orang tidak akan mendapatkan yang diinginkan di mode fpm, tapi malah sibuk mikirin kenapa info ekstensi tidak muncul + +**Pastikan kamu benar-benar paham mode jalan Swoole, baru lanjut urus masalah instalasi!** + +### Penyebab + +Setelah kompilasi Swoole, muncul di halaman `phpinfo` `php-fpm/apache`, tapi tidak muncul di `php -m` command line. Penyebabnya mungkin `cli/php-fpm/apache` pakai konfigurasi php.ini yang berbeda + +### Solusi + +1. Cek lokasi php.ini + +Jalankan `php -i | grep php.ini` atau `php --ini` di `cli` untuk cari path absolut php.ini + +`php-fpm/apache` cek di halaman `phpinfo` untuk cari path absolut php.ini + +2. Cek apakah di php.ini ada `extension=swoole.so` + +```shell +cat /path/to/php.ini | grep swoole.so +``` + +## pcre.h: No such file or directory + +Kompilasi ekstensi Swoole muncul error + +```bash +fatal error: pcre.h: No such file or directory +``` + +Penyebabnya kurang pcre, perlu install libpcre + +### ubuntu/debian + +```shell +sudo apt-get install libpcre3 libpcre3-dev +``` +### centos/redhat + +```shell +sudo yum install pcre-devel +``` + +### Linux Lainnya + +Download source code dari [situs resmi PCRE](http://www.pcre.org/), kompilasi dan install library `pcre`. + +Setelah install library `PCRE`, kompilasi ulang `swoole`, lalu pakai `php --ri swoole` untuk cek apakah ada `pcre => enabled` + +## '__builtin_saddl_overflow' was not declared in this scope + + ``` +error: '__builtin_saddl_overflow' was not declared in this scope + if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), 1, &lresult))) { + +note: in definition of macro 'UNEXPECTED' + # define UNEXPECTED(condition) __builtin_expect(!!(condition), 0) +``` + +Ini masalah yang sudah diketahui. Masalahnya adalah gcc default di CentOS tidak punya definisi yang diperlukan, bahkan setelah upgrade gcc, PECL tetap pakai compiler lama. + +Untuk install driver, harus upgrade gcc dulu dengan menginstall devtoolset: + +```shell +sudo yum install centos-release-scl +sudo yum install devtoolset-7 +scl enable devtoolset-7 bash +``` + +## fatal error: 'openssl/ssl.h' file not found + +Waktu kompilasi, tambahkan parameter [--with-openssl-dir](/environment?id=parameter-umum) untuk menentukan path library openssl + +!> Kalau pakai [pecl](/environment?id=pecl) untuk install Swoole dan ingin mengaktifkan openssl, bisa tambah [--with-openssl-dir](/environment?id=parameter-umum), contoh: `enable openssl support? [no] : yes --with-openssl-dir=/opt/openssl/` + +## make atau make install gagal atau error kompilasi + +NOTICE: PHP message: PHP Warning: PHP Startup: swoole: Unable to initialize module +Module compiled with module API=20090626 +PHP compiled with module API=20121212 +These options need to match +in Unknown on line 0 + +Versi PHP tidak sesuai dengan `phpize` dan `php-config` yang dipakai waktu kompilasi. Harus pakai path absolut untuk kompilasi dan menjalankan PHP. + +```shell +/usr/local/php-5.4.17/bin/phpize +./configure --with-php-config=/usr/local/php-5.4.17/bin/php-config + +/usr/local/php-5.4.17/bin/php server.php +``` + +## Install xdebug + +```shell +git clone git@github.com:swoole/sdebug.git -b sdebug_2_9 --depth=1 + +cd sdebug + +phpize +./configure +make clean +make +make install + +#Kalau phpize, php-config dll sudah default, langsung jalankan +./rebuild.sh +``` + +Ubah php.ini untuk load ekstensi, tambahkan ini: + +```ini +zend_extension=xdebug.so + +xdebug.remote_enable=1 +xdebug.remote_autostart=1 +xdebug.remote_host=localhost +xdebug.remote_port=8000 +xdebug.idekey="xdebug" +``` + +Cek apakah berhasil di-load + +```shell +php --ri sdebug +``` + +## configure: error: C preprocessor "/lib/cpp" fails sanity check + +Kalau pas instalasi error + +```shell +configure: error: C preprocessor "/lib/cpp" fails sanity check +``` + +Artinya kurang library dependensi yang diperlukan, install dengan: + +```shell +yum install glibc-headers +yum install gcc-c++ +``` + +## Error kompilasi Swoole versi baru dengan PHP7.4.11+ :id=asm_goto + +Di MacOS, kompilasi Swoole versi baru dengan PHP7.4.11+ muncul error seperti: + +```shell +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:523:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:586:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:656:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h:766:10: error: 'asm goto' constructs are not supported yet + __asm__ goto( + ^ +4 errors generated. +make: *** [ext-src/php_swoole.lo] Error 1 +ERROR: `make' failed +``` + +Solusi: edit source code di `/usr/local/Cellar/php/7.4.12/include/php/Zend/zend_operators.h`, sesuaikan path header file masing-masing; + +Ubah `ZEND_USE_ASM_ARITHMETIC` jadi tetap `0`, yaitu pertahankan kode di `else` + +```c +#if defined(HAVE_ASM_GOTO) && !__has_feature(memory_sanitizer) +# define ZEND_USE_ASM_ARITHMETIC 1 +#else +# define ZEND_USE_ASM_ARITHMETIC 0 +#endif +``` + +## fatal error: curl/curl.h: No such file or directory :id=libcurl + +Setelah mengaktifkan opsi `--enable-swoole-curl`, kompilasi ekstensi Swoole muncul error + +```bash +fatal error: curl/curl.h: No such file or directory +``` + +Penyebabnya kurang dependensi curl, perlu install libcurl + +### ubuntu/debian + +```shell +sudo apt-get install libcurl4-openssl-dev +``` +### centos/redhat + +```shell +sudo yum install libcurl-devel +``` + +### alpine + +```shell +apk add curl-dev +``` + +## fatal error: ares.h: No such file or directory :id=libcares + +Setelah mengaktifkan opsi `--enable-cares`, kompilasi ekstensi Swoole muncul error + +```bash +fatal error: ares.h: No such file or directory +``` + +Penyebabnya kurang dependensi c-ares, perlu install libcares + +### ubuntu/debian + +```shell +sudo apt-get install libc-ares-dev +``` + +### centos/redhat + +```shell +sudo yum install c-ares-devel +``` + +### alpine + +```shell +apk add c-ares-dev +``` + +### MacOs + +```shell +brew install c-ares +``` diff --git a/public/id/question/swoole.md b/public/id/question/swoole.md new file mode 100644 index 0000000..f37db75 --- /dev/null +++ b/public/id/question/swoole.md @@ -0,0 +1,29 @@ +# Asal-usul Proyek Swoole dan Asal Nama + +!> Halaman ini ditulis oleh Rango, founder proyek open source Swoole. Hanya mewakili pandangan pribadinya. + +## Asal-usul Proyek + +Gagasan awal proyek Swoole berasal dari proyek software enterprise yang saya kerjakan sebelumnya. Sekitar akhir tahun 2010, produk perusahaan punya kebutuhan di mana pengguna bisa membuat alamat email secara bebas, lalu pengguna lain bisa kirim email ke alamat tersebut, dan backend bisa mengurai isi email jadi data secara real-time dan langsung notifikasi pengguna. Waktu itu proyek pakai PHP. Pas implementasi fitur ini, ketemu masalah: PHP cuma bisa andalkan SMTP server lain, dan harus polling email baru secara berkala lewat protokol pop3 — jadinya nggak real-time. Kalau mau sistem real-time, harus bikin sendiri `TCP Socket Server` yang implement protokol `SMTP` buat nerima data. Waktu itu `PHP` di bidang ini masih kosong, belum ada framework komunikasi network yang matang. Buat mewujudkan kebutuhan ini, saya belajar dari `socket` sampe `TCP/IP`, IO multiplexing, `libevent`, multiprocess, dan akhirnya berhasil bikin program ini. Setelah selesai, saya ingin buka source program ini, berharap bisa bantu developer PHP lain yang punya masalah serupa. Dengan adanya framework kayak gini, PHP nggak cuma bisa bikin website doang, tapi bisa meluas ke ranah yang lebih besar. + +## Masalah Performa + +Alasan penting lainnya adalah masalah performa program PHP. Awalnya saya belajar Java, baru jadi programmer PHP setelah kerja. Selama ngembangin pake PHP, saya terus mikir: apa sih kelebihan utama PHP dibanding Java? Simple dan efisien. Setelah request selesai, PHP melepaskan semua resource dan memory, nggak perlu khawatir memory leak. Kode berkualitas rendah pun jalan lancar. Tapi ini juga kelemahan fatal PHP. Begitu jumlah request naik dan concurrency tinggi, bikin resource cepet-cepet terus dilepas, efisiensi program PHP turun drastis. Selain itu, makin kompleks fitur dan makin banyak kode, itu bencana buat PHP. Makanya framework PHP nggak diterima luas sama programmer PHP, beda sama Java yang nggak punya masalah ini. Framework sebagus apapun bakal terbebani sama cara nggak efisien ini, bikin sistem lemot. Makanya saya mikir, gimana kalo pake PHP buat bikin application server PHP sendiri, biar kode PHP di-load ke memory dan punya umur lebih panjang. Dengan begitu, koneksi database dan object besar lain nggak dilepas. Setiap request cuma perlu ngolah sedikit kode, dan kode itu cuma dikompilasi sekali pas pertama jalan, lalu menetap di memory. Plus, yang tadinya nggak mungkin di PHP — object persistence, connection pool database, cache connection pool — semua bisa diwujudkan. Efisiensi sistem bakal naik drastis. + +Setelah riset beberapa waktu, akhirnya berhasil diwujudkan. Saya bikin HTTP server pake PHP sendiri, jalan sebagai server mandiri. Waktu eksekusi satu halaman program (ada object generation, koneksi database, operasi template smarty) turun dari 0.0x detik jadi 0.00x detik. Pake Apache AB concurrency 100, Request per Second lebih tinggi 10 kali lipat dari cara LAMP tradisional. Di mesin test saya (Ubuntu10.04 Intel Core E5300 + 2G RAM), Apache cuma 83 RPS. Swoole Server bisa sampe 1150+ RPS. + +Proyek ini adalah cikal bakal Swoole. Versi ini saya maintenance selama 2 tahun lebih. Selama itu, saya makin paham masalah dari pendekatan teknis ini, kayak performa jelek, banyak batasan yang nggak bisa langsung panggil interface OS, dan manajemen memory yang nggak efisien. + +## Masuk Tencent + +Akhir 2011 saya masuk Tencent, bertanggung jawab ngembangin platform PHP untuk Pengyou Wang (friendster). Kaget juga pas lihat rekan-rekan di Pengyou Wang bukan cuma mikirin hal yang sama, tapi mereka langsung implementasi. Tim Pengyou Wang udah pake solusi ini di production. Pengyou Wang punya tiga komponen utama: pertama PWS, WebServer murni PHP. Lebih dari 600 server di Pengyou Wang jalan di PWS, tanpa pake Apache, PHP-FPM, dll. Kedua SAPS, antrian distributed murni PHP, waktu itu sekitar 150 server. Banyak logic kayak image cropping, avatar processing, messaging, data sync — semuanya pake SAPS buat asinkronisasi. Ketiga PSF, framework server PHP. Banyak server logic layer di Pengyou Wang berbasis PSF. Sekitar 300 server jalanin program server PSF. Selama di Pengyou Wang, saya banyak belajar Linux internal dan komunikasi network, dapet banyak pengalaman tracking dan debugging komunikasi network di lingkungan cluster besar dengan concurrency tinggi. Ini jadi fondasi yang bagus buat ngembangin Swoole. + +## Ngembangin Swoole + +Selama periode itu juga saya belajar dan paham solusi teknis keren kayak Node.js, Golang, dapet banyak inspirasi. Tahun 2012, saya punya ide baru dan mutusin pake bahasa C buat implement ulang versi yang lebih kuat dan lebih kenceng. Inilah ekstensi Swoole yang sekarang. + +Sekarang Swoole udah dipake banyak tim teknis PHP buat proyek nyata, baik di China maupun luar. Di China yang terkenal: Baidu Order Center, Baidu Maps, Tencent QQ Official Account dan Enterprise QQ, Zhanqi live streaming, 360, Dangdang, Qyer, dll. Selain itu, banyak proyek IoT, hardware, dan game juga pake Swoole. Framework open source berbasis Swoole juga makin banyak, kayak TSF, Blink, swPromise, dll. Di GitHub juga banyak proyek dan kode terkait Swoole. + +## Asal Nama + +Nama Swoole bukan kata bahasa Inggris. Itu kata yang saya ciptakan, mirip bunyi. Awalnya saya mau namain `sword-server`, melambangkan pedang tajam buat developer PHP. Tapi kemudian inget Google juga nama ciptaan, jadinya saya kasih nama `swoole`. diff --git a/public/id/question/use.md b/public/id/question/use.md new file mode 100644 index 0000000..5fe4e90 --- /dev/null +++ b/public/id/question/use.md @@ -0,0 +1,507 @@ +# Masalah Penggunaan + +## Bagaimana Performa Swoole + +> Perbandingan QPS + +Pakai Apache-Bench (ab) untuk stress test Nginx static page, Golang HTTP program, dan PHP7+Swoole HTTP program. Di mesin yang sama, dengan koncurrency 100 dan total 1 juta request HTTP, perbandingan QPS: + +| Software | QPS | Versi Software | +| --- | --- | --- | +| Nginx | 164489.92 | nginx/1.4.6 (Ubuntu) | +| Golang | 166838.68 | go version go1.5.2 linux/amd64 | +| PHP7+Swoole | 287104.12 | Swoole-1.7.22-alpha | +| Nginx-1.9.9 | 245058.70 | nginx/1.9.9 | + +!> Catatan: Di test Nginx-1.9.9, access_log dimatikan, open_file_cache diaktifkan untuk cache file statis ke memory + +> Lingkungan Test + +* CPU: Intel Core i5-4590 CPU @ 3.30GHz x 4 +* Memory: 16G +* Disk: 128G SSD +* OS: Ubuntu 14.04 (Linux 3.16.0-55-generic) + +> Metode Stress Test + +```shell +ab -c 100 -n 1000000 -k http://127.0.0.1:8080/ +``` + +> Konfigurasi VHOST + +```nginx +server { + listen 80 default_server; + + location / { + default_type text/html; + return 200 "

Hello World!

"; + } +} +``` + +> Halaman Test + +```html +

Hello World!

+``` + +> Jumlah Process + +Nginx menjalankan 4 Worker process +```shell +htf@htf-All-Series:~/soft/php-7.0.0$ ps aux|grep nginx +root 1221 0.0 0.0 86300 3304 ? Ss 12Des07 0:00 nginx: master process /usr/sbin/nginx +www-data 1222 0.0 0.0 87316 5440 ? S 12Des07 0:44 nginx: worker process +www-data 1223 0.0 0.0 87184 5388 ? S 12Des07 0:36 nginx: worker process +www-data 1224 0.0 0.0 87000 5520 ? S 12Des07 0:40 nginx: worker process +www-data 1225 0.0 0.0 87524 5516 ? S 12Des07 0:45 nginx: worker process +``` + +> Golang + +Kode test + +```go +package main + +import ( + "log" + "net/http" + "runtime" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU() - 1) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Last-Modified", "Thu, 18 Jun 2015 10:24:27 GMT") + w.Header().Add("Accept-Ranges", "bytes") + w.Header().Add("E-Tag", "55829c5b-17") + w.Header().Add("Server", "golang-http-server") + w.Write([]byte("

\nHello world!\n

\n")) + }) + + log.Printf("Go http Server listen on :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} +``` + +> PHP7+Swoole + +PHP7 sudah mengaktifkan `OPcache`. + +Kode test + +```php +$http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); + +$http->set([ + 'worker_num' => 4, +]); + +$http->on('request', function ($request, Swoole\Http\Server $response) { + $response->header('Last-Modified', 'Thu, 18 Jun 2015 10:24:27 GMT'); + $response->header('E-Tag', '55829c5b-17'); + $response->header('Accept-Ranges', 'bytes'); + $response->end("

\nHello Swoole.\n

"); +}); + +$http->start(); +``` + +> **TechEmpower Web Framework Benchmarks — Tolok Ukur Performa Framework Web Global** + +Hasil benchmark terbaru: [techempower](https://www.techempower.com/benchmarks/#section=test&runid=9d5522a6-2917-467a-9d7a-8c0f6a8ed790) + +Swoole memimpin **peringkat pertama di bahasa dinamis** + +Test operasi IO database, pakai kode bisnis dasar tanpa optimasi khusus + +**Performa melebihi semua framework bahasa statis (pakai MySQL bukan PostgreSQL)** + +## Bagaimana Swoole Menjaga TCP Long Connection + +Ada 2 konfigurasi untuk menjaga TCP long connection: [tcp_keepalive](/server/setting?id=open_tcp_keepalive) dan [heartbeat](/server/setting?id=heartbeat_check_interval). + +## Cara Restart Service Swoole yang Benar + +Di pengembangan sehari-hari, setelah mengubah kode PHP sering perlu restart service biar kode berlaku. Server backend yang sibuk terus memproses request. Kalau admin matiin/restart server pake `kill`, bisa aja kode lagi jalan setengah, nggak bisa jamin integritas logika bisnis. + +`Swoole` punya mekanisme graceful termination/restart. Admin cukup kirim sinyal tertentu ke `Server` atau panggil method `reload`, worker process bakal berhenti dan dijalankan ulang. Detailnya lihat [reload()](/server/methods?id=reload) + +Tapi ada beberapa yang perlu diperhatikan: + +Pertama, kode yang baru diubah harus di-load ulang di event `OnWorkerStart` baru berlaku. Misalnya suatu class di-load lewat composer autoload sebelum `OnWorkerStart`, itu nggak bakal jalan. + +Kedua, `reload` perlu dipasangin sama dua parameter ini: [max_wait_time](/server/setting?id=max_wait_time) dan [reload_async](/server/setting?id=reload_async). Kalau udah diset, bisa mengimplementasikan `async safe restart`. + +Tanpa fitur ini, pas Worker process dapet sinyal restart atau mencapai [max_request](/server/setting?id=max_request), service langsung berhenti. Padahal di dalam `Worker` mungkin masih ada event listener, task async bakal ilang. Kalo set parameter di atas, `Worker` baru bakal dibuat dulu, `Worker` lama bakal keluar sendiri setelah semua event selesai, yaitu `reload_async`. + +Kalau `Worker` lama nggak keluar-keluar, level bawah nambahin timer. Dalam waktu yang ditentukan ([max_wait_time](/server/setting?id=max_wait_time) detik), kalo `Worker` lama nggak keluar, level bawah bakal paksa berhenti dan muncul [WARNING](/question/use?id=forced-to-terminate). + +Contoh: + +```php +set(array( + 'worker_num' => 1, + 'max_wait_time' => 60, + 'reload_async' => true, +)); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + + echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; + + Swoole\Timer::tick(5000, function () { + echo 'tick'; + }); +}); + +$serv->start(); +``` + +Misalnya kode di atas, kalo nggak ada `reload_async`, timer yang dibuat di `onReceive` bakal ilang, nggak ada kesempatan buat proses callback timer. + +### Event Exit Process + +Untuk dukung fitur async restart, level bawah nambahin event [onWorkerExit](/server/events?id=onWorkerExit). Pas `Worker` lama mau keluar, event `onWorkerExit` bakal dipicu. Di callback event ini, aplikasi bisa bersihin koneksi `Socket` yang umurnya panjang, sampai [event loop](/learn?id=apa-itu-eventloop) nggak ada fd atau udah mencapai [max_wait_time](/server/setting?id=max_wait_time) baru exit. + +```php +$serv->on('WorkerExit', function (Swoole\Server $serv, $worker_id) { + $redisState = $serv->redis->getState(); + if ($redisState == Swoole\Redis::STATE_READY or $redisState == Swoole\Redis::STATE_SUBSCRIBE) + { + $serv->redis->close(); + } +}); +``` + +Selain itu, di [Swoole Plus](https://www.swoole.com/swoole_plus) ada fitur deteksi perubahan file. Jadi nggak perlu manual reload atau kirim sinyal, worker bakal restart otomatis kalo file berubah. + +## Kenapa send lalu langsung close itu tidak aman + +Send lalu langsung close itu tidak aman, baik di sisi server maupun client. + +Operasi send berhasil cuma berarti data berhasil ditulis ke buffer socket OS, bukan berarti penerima beneran nerima data. Apakah OS berhasil ngirim, apakah server tujuan nerima, apakah program server ngolah — semua nggak ada jaminan. + +> Soal logika setelah close, lihat pengaturan linger di bawah + +Logikanya sama kayak telepon. A ngomong sesuatu ke B, A selesai ngomong langsung tutup telepon. B denger atau nggak, A nggak tau. Kalo A selesai ngomong, B bilang "siap", baru B tutup telepon, itu baru aman. + +Pengaturan linger + +Pas `socket` di-close, kalo buffer masih ada data, OS level bawah bakal menentukan berdasarkan pengaturan `linger`. + +```c +struct linger +{ + int l_onoff; + int l_linger; +}; +``` + +* l_onoff = 0, pas close langsung balik, level bawah bakal kirim dulu data yang belum terkirim baru lepas resource, yaitu graceful exit. +* l_onoff != 0, l_linger = 0, pas close langsung balik, tapi data yang belum terkirim nggak dikirim, malah paksa tutup socket descriptor pake paket RST, yaitu force exit. +* l_onoff != 0, l_linger > 0, pas close nggak langsung balik, kernel nunda beberapa saat sesuai nilai l_linger. Kalo sebelum timeout data berhasil dikirim (termasuk paket FIN) dan dikonfirmasi pihak lawan, close balik sukses, socket descriptor graceful exit. Kalo nggak, close balik error, data hilang, socket descriptor dipaksa keluar. Kalo socket descriptor di-set non-blocking, close langsung balik value. + +## client has already been bound to another coroutine + +Untuk satu koneksi `TCP`, level bawah Swoole cuma ngizinin satu coroutine buat baca dan satu coroutine buat tulis dalam satu waktu. Artinya nggak boleh ada banyak coroutine yang baca/tulis ke satu TCP yang sama, level bawah bakal lempar error binding: + +```shell +Fatal error: Uncaught Swoole\Error: Socket#6 has already been bound to another coroutine#2, reading or writing of the same socket in coroutine#3 at the same time is not allowed +``` + +Kode reproduksi: + +```php +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function() { + $cli = new Client('www.xinhuanet.com', 80); + Coroutine::create(function () use ($cli) { + $cli->get('/'); + }); + Coroutine::create(function () use ($cli) { + $cli->get('/'); + }); +}); +``` + +Solusi: https://wenda.swoole.com/detail/107474 + +!> Batasan ini berlaku di semua lingkungan multi-coroutine. Paling sering terjadi di callback [onReceive](/server/events?id=onreceive) yang pake satu koneksi TCP barengan, karena callback ini otomatis bikin coroutine. Kalau butuh connection pool, `Swoole` punya [connection pool](/coroutine/conn_pool) bawaan yang bisa langsung dipake, atau bikin sendiri pake `channel`. + +## Call to undefined function Co\run() + +Kebanyakan contoh di dokumen ini pake `Co\run()` untuk bikin coroutine container. [Pelajari apa itu coroutine container](/coroutine?id=apa-itu-coroutine-container) + +Kalo nemu error kayak gini: + +```bash +PHP Fatal error: Uncaught Error: Call to undefined function Co\run() + +PHP Fatal error: Uncaught Error: Call to undefined function go() +``` + +Artinya versi ekstensi `Swoole` kamu di bawah `v4.4.0` atau kamu matiin [coroutine short name](/other/alias?id=coroutine-short-name). Ini solusinya: + +* Kalo versi terlalu rendah, upgrade ke `>= v4.4.0` atau pake `go` ganti `Co\run` buat bikin coroutine; +* Kalo coroutine short name dimatiin, hidupkan [coroutine short name](/other/alias?id=coroutine-short-name); +* Pake [Coroutine::create](/coroutine/coroutine?id=create) ganti `Co\run` atau `go` buat bikin coroutine; +* Pake nama lengkap: `Swoole\Coroutine\run`; + +## Bolehkah pake 1 koneksi Redis atau MySQL barengan + +Sama sekali nggak boleh. Setiap process harus bikin koneksi `Redis`, `MySQL`, `PDO` sendiri-sendiri. Client storage lain juga sama. Soalnya kalo pake 1 koneksi barengan, nggak bisa dijamin proses mana yang bakal nerima hasilnya. Semua process yang megang koneksi secara teori bisa baca/tulis koneksi itu, jadinya data kacau. + +**Pokoknya antar process, jangan pernah pake koneksi barengan** + +* Di [Swoole\Server](/server/init), koneksi harus dibuat di [onWorkerStart](/server/events?id=onworkerstart) +* Di [Swoole\Process](/process/process), koneksi harus dibuat di callback child process setelah [Swoole\Process->start](/process/process?id=start) +* Info ini juga berlaku buat program yang pake `pcntl_fork` + +Contoh: + +```php +$server = new Swoole\Server('0.0.0.0', 9502); + +//Koneksi redis/mysql harus dibuat di callback onWorkerStart +$server->on('workerstart', function($server, $id) { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $server->redis = $redis; +}); + +$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + $value = $server->redis->get("key"); + $server->send($fd, "Swoole: ".$value); +}); + +$server->start(); +``` + +## Masalah Koneksi Sudah Ditutup + +Kayak pesan di bawah ini + +```bash +NOTICE swFactoryProcess_finish (ERRNO 1004): send 165 byte failed, because connection[fd=123] is closed + +NOTICE swFactoryProcess_finish (ERROR 1005): connection[fd=123] does not exists +``` + +Pas server mau ngirim respon, client udah nutup koneksi. + +Biasanya terjadi karena: + +* Browser di-refresh terus (belum selesai load udah ditutup) +* Stress test ab dibatalkan di tengah jalan +* Stress test wrk berdasarkan waktu (request yang belum selesai dibatalkan pas waktu habis) + +Semua kasus di atas itu wajar, bisa diabaikan. Makanya level error-nya NOTICE + +Kalo tiba-tiba ada banyak koneksi yang putus tanpa alasan jelas, baru perlu diperhatiin + +```bash +WARNING swWorker_discard_data (ERRNO 1007): [2] received the wrong data[21 bytes] from socket#75 + +WARNING Worker_discard_data (ERRNO 1007): [2] ignore data[5 bytes] received from session#2 +``` + +Sama, error ini juga artinya koneksi udah ditutup, data yang diterima bakal dibuang. Lihat [discard_timeout_request](/server/setting?id=discard_timeout_request) + +## Properti connected dan status koneksi tidak sinkron + +Sejak versi coroutine 4.x, properti `connected` nggak akan diupdate realtime lagi, method [isConnect](/client?id=isconnected) nggak bisa diandalkan. + +### Penyebab + +Tujuan coroutine adalah konsisten sama model programming synchronous blocking. Di model synchronous blocking, nggak ada konsep update status koneksi realtime. Kayak PDO, curl, dll — nggak ada konsep koneksi. Koneksi putus baru ketahuan pas operasi IO balikin error atau lempar exception. + +Pendekatan umum di level bawah Swoole adalah, pas terjadi error IO, balikin false (atau konten kosong) dan set error code serta error message di object client. + +### Catatan + +Meskipun versi asynchronous sebelumnya dukung update properti `connected` secara "real-time", sebenarnya itu nggak reliable. Koneksi bisa aja putus pas setelah kamu cek. + +## Connection refused itu apa + +Pas telnet 127.0.0.1 9501 muncul Connection refused, artinya server nggak mendengarkan port ini. + +* Cek apakah program jalan: ps aux +* Cek apakah port didengarkan: netstat -lp +* Cek proses komunikasi network: tcpdump traceroute + +## Resource temporarily unavailable [11] + +Client `swoole_client` pas `recv` error + +```shell +swoole_client::recv(): recv() failed. Error: Resource temporarily unavailable [11] +``` + +Error ini artinya server nggak balik data dalam waktu yang ditentukan, receive timeout. + +* Bisa pake tcpdump buat cek proses komunikasi network, periksa apakah server ngirim data +* Fungsi `$serv->send` di server perlu dicek apakah balikin true +* Komunikasi internet butuh waktu lebih, perlu perbesar timeout swoole_client + +## worker exit timeout, forced to terminate :id=forced-to-terminate + +Nemu error kayak gini: + +```bash +WARNING swWorker_reactor_try_to_exit (ERRNO 9012): worker exit timeout, forced to terminate +``` + +Artinya dalam waktu yang ditentukan ([max_wait_time](/server/setting?id=max_wait_time) detik) Worker ini nggak keluar, level bawah Swoole paksa matiin process ini. + +Bisa reproduksi pake kode ini: + +```php +use Swoole\Timer; + +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set( + [ + 'reload_async' => true, + 'max_wait_time' => 4, + ] +); + +$server->on('workerStart', function (Swoole\Server $server, int $wid) { + if ($wid === 0) { + Timer::tick(5000, function () { + echo 'tick'; + }); + Timer::after(500, function () use ($server) { + $server->shutdown(); + }); + } +}); + +$server->on('receive', function () { + +}); + +$server->start(); +``` + +## Unable to find callback function for signal Broken pipe: 13 + +Nemu error kayak gini: + +```bash +WARNING swSignalfd_onSignal (ERRNO 707): Unable to find callback function for signal Broken pipe: 13 +``` + +Artinya ngirim data ke koneksi yang udah putus. Biasanya karena nggak ngecek return value send, tetep kirim padahal gagal. + +## Pengetahuan Dasar yang Perlu Dikuasai buat Belajar Swoole + +### Multiprocess/Multithread + +* Paham konsep process dan thread di OS `Linux` +* Paham dasar-dasar switching dan scheduling process/thread di `Linux` +* Paham dasar-dasar komunikasi antar process, kayak pipe, `UnixSocket`, message queue, shared memory + +### SOCKET + +* Paham operasi dasar `SOCKET` kayak `accept/connect`, `send/recv`, `close`, `listen`, `bind` +* Paham konsep receive buffer, send buffer, blocking/non-blocking, timeout di `SOCKET` + +### IO Multiplexing + +* Paham `select`/`poll`/`epoll` +* Paham event loop berbasis `select`/`epoll`, model `Reactor` +* Paham readable event, writable event + +### Protokol Jaringan TCP/IP + +* Paham protokol `TCP/IP` +* Paham protokol transport `TCP`, `UDP` + +### Alat Debug + +* Pake [gdb](/other/tools?id=gdb) buat debug program `Linux` +* Pake [strace](/other/tools?id=strace) buat trace system call process +* Pake [tcpdump](/other/tools?id=tcpdump) buat trace proses komunikasi network +* Alat `Linux` lainnya kayak ps, [lsof](/other/tools?id=lsof), top, vmstat, netstat, sar, ss, dll + +## Object of class Swoole\Curl\Handler could not be converted to int + +Pas pake [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl), muncul error: + +```bash +PHP Notice: Object of class Swoole\Curl\Handler could not be converted to int + +PHP Warning: curl_multi_add_handle() expects parameter 2 to be resource, object given +``` + +Penyebabnya, curl setelah di-hook bukan tipe resource lagi, tapi object. Jadi nggak bisa di-convert ke int. + +!> Soal `int` disaranin hubungi pihak SDK buat ubah kode. Di PHP8, curl udah bukan tipe resource, tapi object. + +Ada tiga solusi: + +1. Jangan aktifkan [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl). Tapi mulai [v4.5.4](/version/log?id=v454), [SWOOLE_HOOK_ALL](/runtime?id=swoole_hook_all) udah termasuk [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) secara default. Bisa set `SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_CURL` buat matiin [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) + +2. Pake SDK Guzzle, bisa ganti Handler biar pake coroutine + +3. Mulai Swoole `v4.6.0`, bisa pake [SWOOLE_HOOK_NATIVE_CURL](/runtime?id=swoole_hook_native_curl) sebagai ganti [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_curl) + +## Pake one-click coroutine dan Guzzle 7.0+ bareng, hasil request langsung ke terminal :id=hook_guzzle + +Kode reproduksi: + +```php +// composer require guzzlehttp/guzzle +include __DIR__ . '/vendor/autoload.php'; + +use GuzzleHttp\Client; +use Swoole\Coroutine; + +// Sebelum v4.5.4 +//Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); +Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); +Coroutine\run(function () { + $client = new Client(); + $url = 'http://baidu.com'; + $res = $client->request('GET', $url); + var_dump($res->getBody()->getContents()); +}); + +// Hasil request langsung ke terminal, bukan di-print +// +// +// +//string(0) "" +``` + +!> Solusinya sama kayak masalah sebelumnya. Tapi masalah ini udah diperbaiki di Swoole >= `v4.5.8`. + +## Error: No buffer space available[55] + +Error ini bisa diabaikan. Error ini muncul karena opsi [socket_buffer_size](/server/setting?id=socket_buffer_size) kegedean, ada sistem yang nggak nerima. Nggak pengaruh ke jalan program. + +## Ukuran Maksimum Request GET/POST + +### GET request maksimal 8192 + +GET request cuma punya satu Http header. Level bawah Swoole pake buffer memori ukuran tetap 8K, nggak bisa diubah. Kalo request bukan Http yang benar, bakal error. Level bawah bakal lempar error ini: + +```bash +WARN swReactorThread_onReceive_http_request: http header is too long. +``` + +### Upload File POST + +Ukuran maksimum dibatasi oleh konfigurasi [package_max_length](/server/setting?id=package_max_length), default 2M. Bisa panggil [Server->set](/server/methods?id=set) buat ganti nilainya. Level bawah Swoole pake memori semua, jadi kalo kegedean bisa bikin resource server habis karena banyak request bersamaan. + +Rumus: `Maksimum pemakaian memory` = `Maksimum request bersamaan` * `package_max_length` diff --git a/public/id/redis_server.md b/public/id/redis_server.md new file mode 100644 index 0000000..2b91400 --- /dev/null +++ b/public/id/redis_server.md @@ -0,0 +1,222 @@ +# Redis\Server + +Kelas `Server` yang kompatibel dengan protokol sisi server `Redis`, dapat digunakan untuk mengimplementasikan program server berdasarkan protokol `Redis`. + +?> `Swoole\Redis\Server` mewarisi dari [Server](/server/tcp_init), jadi semua `API` dan opsi konfigurasi yang disediakan oleh `Server` dapat digunakan, dan model prosesnya juga sama. Silakan merujuk ke bagian [Server](/server/init). + +* **Klien yang Tersedia** + + * Klien `redis` dalam bahasa pemrograman apa pun, termasuk ekstensi `redis` PHP dan pustaka `phpredis` + * [Swoole\Coroutine\Redis](/coroutine_client/redis) klien korutin + * Alat baris perintah yang disediakan oleh `Redis`, termasuk `redis-cli`, `redis-benchmark` + +## Metode + +`Swoole\Redis\Server` mewarisi dari `Swoole\Server`, dapat menggunakan semua metode yang disediakan oleh kelas induk. + +### setHandler + +?> **Mengatur penangan untuk perintah `Redis`.** + +!> `Redis\Server` tidak perlu mengatur callback [onReceive](/server/events?id=onreceive). Cukup gunakan metode `setHandler` untuk mengatur fungsi penangan untuk perintah yang sesuai. Setelah menerima perintah yang tidak didukung, secara otomatis akan mengirimkan respons `ERROR` ke klien dengan pesan `ERR unknown command '$command'`. + +```php +Swoole\Redis\Server->setHandler(string $command, callable $callback); +``` + +* **Parameter** + + * **`string $command`** + * **Fungsi**: Nama perintah + * **Nilai default**: Tidak ada + * **Nilai lainnya**: Tidak ada + + * **`callable $callback`** + * **Fungsi**: Fungsi penangan untuk perintah [ketika fungsi callback mengembalikan tipe string, akan otomatis dikirim ke klien] + * **Nilai default**: Tidak ada + * **Nilai lainnya**: Tidak ada + + !> Data yang dikembalikan harus dalam format `Redis`, dapat menggunakan metode statis `format` untuk mengemasnya + +### format + +?> **Memformat data respons perintah.** + +```php +Swoole\Redis\Server::format(int $type, mixed $value = null); +``` + +* **Parameter** + + * **`int $type`** + * **Fungsi**: Tipe data, konstanta terkait dapat dilihat di [Konstanta Parameter Format](/redis_server?id=格式参数常量). + * **Nilai default**: Tidak ada + * **Nilai lainnya**: Tidak ada + + !> Ketika `$type` adalah tipe `NIL`, `$value` tidak diperlukan; `$value` opsional untuk tipe `ERROR` dan `STATUS`; wajib untuk `INT`, `STRING`, `SET`, `MAP`. + + * **`mixed $value`** + * **Fungsi**: Nilai + * **Nilai default**: Tidak ada + * **Nilai lainnya**: Tidak ada + +### send + +?> **Menggunakan metode `send()` dari [Swoole\Server](/server/methods?id=send) untuk mengirim data ke klien.** + +```php +Swoole\Server->send(int $fd, string $data): bool +``` + +## Konstanta + +### Konstanta Parameter Format + +Terutama digunakan untuk fungsi `format` dalam mengemas data respons `Redis` + +Konstanta | Keterangan +---|--- +Server::NIL | Mengembalikan data nil +Server::ERROR | Mengembalikan kode kesalahan +Server::STATUS | Mengembalikan status +Server::INT | Mengembalikan integer, `format` harus memasukkan nilai parameter, tipe harus integer +Server::STRING | Mengembalikan string, `format` harus memasukkan nilai parameter, tipe harus string +Server::SET | Mengembalikan daftar, `format` harus memasukkan nilai parameter, tipe harus array +Server::MAP | Mengembalikan Map, `format` harus memasukkan nilai parameter, tipe harus array asosiatif + +## Contoh Penggunaan + +### Server + +```php +use Swoole\Redis\Server; + +define('DB_FILE', __DIR__ . '/db'); + +$server = new Server("127.0.0.1", 9501, SWOOLE_BASE); + +if (is_file(DB_FILE)) { + $server->data = unserialize(file_get_contents(DB_FILE)); +} else { + $server->data = array(); +} + +$server->setHandler('GET', function ($fd, $data) use ($server) { + if (count($data) == 0) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); + } + + $key = $data[0]; + if (empty($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } else { + return $server->send($fd, Server::format(Server::STRING, $server->data[$key])); + } +}); + +$server->setHandler('SET', function ($fd, $data) use ($server) { + if (count($data) < 2) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); + } + + $key = $data[0]; + $server->data[$key] = $data[1]; + return $server->send($fd, Server::format(Server::STATUS, "OK")); +}); + +$server->setHandler('sAdd', function ($fd, $data) use ($server) { + if (count($data) < 2) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sAdd' command")); + } + + $key = $data[0]; + if (!isset($server->data[$key])) { + $array[$key] = array(); + } + + $count = 0; + for ($i = 1; $i < count($data); $i++) { + $value = $data[$i]; + if (!isset($server->data[$key][$value])) { + $server->data[$key][$value] = 1; + $count++; + } + } + + return $server->send($fd, Server::format(Server::INT, $count)); +}); + +$server->setHandler('sMembers', function ($fd, $data) use ($server) { + if (count($data) < 1) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sMembers' command")); + } + $key = $data[0]; + if (!isset($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } + return $server->send($fd, Server::format(Server::SET, array_keys($server->data[$key]))); +}); + +$server->setHandler('hSet', function ($fd, $data) use ($server) { + if (count($data) < 3) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hSet' command")); + } + + $key = $data[0]; + if (!isset($server->data[$key])) { + $array[$key] = array(); + } + $field = $data[1]; + $value = $data[2]; + $count = !isset($server->data[$key][$field]) ? 1 : 0; + $server->data[$key][$field] = $value; + return $server->send($fd, Server::format(Server::INT, $count)); +}); + +$server->setHandler('hGetAll', function ($fd, $data) use ($server) { + if (count($data) < 1) { + return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hGetAll' command")); + } + $key = $data[0]; + if (!isset($server->data[$key])) { + return $server->send($fd, Server::format(Server::NIL)); + } + return $server->send($fd, Server::format(Server::MAP, $server->data[$key])); +}); + +$server->on('WorkerStart', function ($server) { + $server->tick(10000, function () use ($server) { + file_put_contents(DB_FILE, serialize($server->data)); + }); +}); + +$server->start(); +``` + +### Klien + +```shell +$ redis-cli -h 127.0.0.1 -p 9501 +127.0.0.1:9501> set name swoole +OK +127.0.0.1:9501> get name +"swoole" +127.0.0.1:9501> sadd swooler rango +(integer) 1 +127.0.0.1:9501> sadd swooler twosee guoxinhua +(integer) 2 +127.0.0.1:9501> smembers swooler +1) "rango" +2) "twosee" +3) "guoxinhua" +127.0.0.1:9501> hset website swoole "www.swoole.com" +(integer) 1 +127.0.0.1:9501> hset website swoole "swoole.com" +(integer) 0 +127.0.0.1:9501> hgetall website +1) "swoole" +2) "swoole.com" +127.0.0.1:9501> test +(error) ERR unknown command 'test' +127.0.0.1:9501> +``` diff --git a/public/id/runtime.md b/public/id/runtime.md new file mode 100644 index 0000000..9bd3878 --- /dev/null +++ b/public/id/runtime.md @@ -0,0 +1,696 @@ +# Runtime + +Dibandingkan dengan `Swoole1.x`, `Swoole4+` menyediakan korutin sebagai senjata andalan, semua kode bisnis sinkron tetapi `IO` level bawah asinkron, menjamin konkurensi sambil menghindari logika kode yang tersebar akibat callback asinkron tradisional dan terjebak dalam banyak lapisan callback yang membuat kode tidak terawat. Untuk mencapai efek ini, semua request `IO` harus [IO asinkron](/learn?id=同步io异步io). Sedangkan klien `MySQL`, `Redis` dll yang disediakan di era `Swoole1.x` meskipun IO asinkron, tetapi menggunakan cara pemrograman callback asinkron, bukan korutin, sehingga di era `Swoole4` klien-klien ini dihapus. + +Untuk mengatasi masalah dukungan korutin pada klien-klien ini, tim pengembang Swoole melakukan banyak pekerjaan: + +- Awalnya, untuk setiap jenis klien dibuat klien korutin, lihat [klien korutin](/coroutine_client/init), tetapi cara ini memiliki 3 masalah: + * Implementasi rumit, protokol detail setiap klien sangat kompleks, ingin mendukung sempurna membutuhkan kerja besar. + * Pengguna perlu mengubah banyak kode, misalnya query `MySQL` asli menggunakan `PDO` asli PHP, sekarang perlu menggunakan method [Swoole\Coroutine\MySQL](/coroutine_client/mysql). + * Sulit mencakup semua operasi, misalnya `proc_open()`, `sleep()` dll juga bisa memblokir dan menyebabkan program menjadi sinkron blocking. + +- Mengatasi masalah di atas, tim pengembang Swoole mengubah pendekatan implementasi, menggunakan cara `Hook` fungsi PHP asli untuk mengimplementasikan klien korutin. Dengan satu baris kode, kode IO sinkron dapat diubah menjadi [IO asinkron](/learn?id=同步io异步io) yang dapat dijadwalkan korutin, yaitu `Satu-klik Korutinisasi`. + +!> Fitur ini stabil mulai versi `v4.3`. Fungsi yang bisa `dikorutinisasi` juga semakin banyak, sehingga beberapa klien korutin yang ditulis sebelumnya sudah tidak direkomendasikan lagi, detail lihat [klien korutin](/coroutine_client/init). Contoh: di `v4.3+` mendukung `korutinisasi` operasi file (`file_get_contents`, `fread` dll), jika menggunakan versi `v4.3+` bisa langsung menggunakan `korutinisasi` daripada menggunakan [operasi file korutin](/coroutine/system) yang disediakan Swoole. + +## Prototipe Fungsi + +Atur rentang fungsi yang akan `dikorutinisasi` melalui `flags` + +```php +Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); // v4.4+ gunakan method ini. +// atau +Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); +``` + +Untuk mengaktifkan beberapa `flags` sekaligus, gunakan operator `|` + +```php +Co::set(['hook_flags'=> SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP]); +``` + +!> Fungsi yang di-`Hook` harus digunakan di [wadah korutin](/coroutine/scheduler) + +#### Pertanyaan Umum :id=runtime-qa + +!> **Mana yang digunakan `Swoole\Runtime::enableCoroutine()` atau `Co::set(['hook_flags'])`** + +* `Swoole\Runtime::enableCoroutine()` dapat mengatur flags secara dinamis setelah layanan mulai (runtime). Setelah dipanggil, berlaku global dalam proses saat ini, harus diletakkan di awal proyek untuk mendapatkan cakupan 100%; +* `Co::set()` dapat dipahami seperti `ini_set()` PHP, perlu dipanggil sebelum [Server->start()](/server/methods?id=start) atau [Co\run()](/coroutine/scheduler), jika tidak `hook_flags` yang diatur tidak akan berlaku. Di versi `v4.4+` sebaiknya gunakan cara ini untuk mengatur `flags`; +* Baik `Co::set(['hook_flags'])` maupun `Swoole\Runtime::enableCoroutine()` sebaiknya hanya dipanggil sekali, panggilan berulang akan ditimpa. + +## Opsi + +Opsi yang didukung `flags`: + +### SWOOLE_HOOK_ALL + +Aktifkan semua jenis flags berikut (tidak termasuk CURL) + +!> Mulai v4.5.4, `SWOOLE_HOOK_ALL` termasuk `SWOOLE_HOOK_CURL` + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); // tidak termasuk CURL +Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); // benar-benar korutinisasi semua jenis, termasuk CURL +``` + +### SWOOLE_HOOK_TCP + +Mulai `v4.1` mendukung, stream tipe TCP Socket, termasuk yang paling umum seperti `Redis`, `PDO`, `Mysqli` dan operasi koneksi TCP menggunakan fungsi seri [streams](https://www.php.net/streams) PHP, semua bisa di-`Hook`. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); + +Co\run(function() { + for ($c = 100; $c--;) { + go(function () {//membuat 100 korutin + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379);//di sini terjadi penjadwalan korutin, cpu beralih ke korutin berikutnya, tidak memblokir proses + $redis->get('key');//di sini terjadi penjadwalan korutin, cpu beralih ke korutin berikutnya, tidak memblokir proses + }); + } +}); +``` + +Kode di atas menggunakan class `Redis` asli, tapi sebenarnya sudah menjadi `IO asinkron`. `Co\run()` adalah membuat [wadah korutin](/coroutine/scheduler), `go()` adalah membuat korutin. Kedua operasi ini sudah otomatis dilakukan di [kelas Swoole\Server](/server/init) yang disediakan Swoole, tidak perlu manual, lihat [enable_coroutine](/server/setting?id=enable_coroutine). + +Artinya programmer `PHP` tradisional dapat menulis program konkuren tinggi, performa tinggi dengan kode logika yang paling familiar, seperti berikut: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TCP]); + +$http = new Swoole\Http\Server("0.0.0.0", 9501); +$http->set(['enable_coroutine' => true]); + +$http->on('request', function ($request, $response) { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379);//di sini terjadi penjadwalan korutin, cpu beralih ke korutin berikutnya (request berikutnya), tidak memblokir proses + $redis->get('key');//di sini terjadi penjadwalan korutin, cpu beralih ke korutin berikutnya (request berikutnya), tidak memblokir proses +}); + +$http->start(); +``` + +### SWOOLE_HOOK_UNIX + +Mulai `v4.2` mendukung. Stream tipe `Unix Stream Socket`. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UNIX]); + +Co\run(function () { + $socket = stream_socket_server( + 'unix://swoole.sock', + $errno, + $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_accept($socket)) { + } +}); +``` + +### SWOOLE_HOOK_UDP + +Mulai `v4.2` mendukung. Stream tipe UDP Socket. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UDP]); + +Co\run(function () { + $socket = stream_socket_server( + 'udp://0.0.0.0:6666', + $errno, + $errstr, + STREAM_SERVER_BIND + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_recvfrom($socket, 1, 0)) { + } +}); +``` + +### SWOOLE_HOOK_UDG + +Mulai `v4.2` mendukung. Stream tipe Unix Dgram Socket. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_UDG]); + +Co\run(function () { + $socket = stream_socket_server( + 'udg://swoole.sock', + $errno, + $errstr, + STREAM_SERVER_BIND + ); + if (!$socket) { + echo "$errstr ($errno)" . PHP_EOL; + exit(1); + } + while (stream_socket_recvfrom($socket, 1, 0)) { + } +}); +``` + +### SWOOLE_HOOK_SSL + +Mulai `v4.2` mendukung. Stream tipe SSL Socket. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SSL]); + +Co\run(function () { + $host = 'host.domain.tld'; + $port = 1234; + $timeout = 10; + $cert = '/path/to/your/certchain/certchain.pem'; + $context = stream_context_create( + array( + 'ssl' => array( + 'local_cert' => $cert, + ) + ) + ); + if ($fp = stream_socket_client( + 'ssl://' . $host . ':' . $port, + $errno, + $errstr, + 30, + STREAM_CLIENT_CONNECT, + $context + )) { + echo "connected\n"; + } else { + echo "ERROR: $errno - $errstr \n"; + } +}); +``` + +### SWOOLE_HOOK_TLS + +Mulai `v4.2` mendukung. Stream tipe `TLS Socket`, [referensi](https://www.php.net/manual/en/context.ssl.php). + +Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_TLS]); +``` + +### SWOOLE_HOOK_SLEEP + +Mulai `v4.2` mendukung. `Hook` fungsi `sleep`, termasuk `sleep`, `usleep`, `time_nanosleep`, `time_sleep_until`. Karena granularitas minimum timer level bawah adalah `1ms`, saat menggunakan fungsi sleep presisi tinggi seperti `usleep`, jika diatur kurang dari `1ms`, akan langsung menggunakan panggilan sistem `sleep`. Mungkin menyebabkan blocking tidur yang sangat singkat. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SLEEP]); + +Co\run(function () { + go(function () { + sleep(1); + echo '1' . PHP_EOL; + }); + go(function () { + echo '2' . PHP_EOL; + }); +}); +``` +Output +``` +2 +1 +``` + +### SWOOLE_HOOK_FILE + +Mulai `v4.3` mendukung. + +#### `Penanganan korutinisasi` operasi file, fungsi yang didukung: + +* `fopen` +* `fread`, `fgets`, `fgetc` +* `fwrite`, `fputs` +* `file_get_contents`, `file_put_contents`, `readfile` +* `unlink`, `mkdir`, `rmdir` +* `opendir`, `readdir`, `closedir`, `scandir` +* Fungsi pustaka standar `PHP` lain tentang operasi file disk + +Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_FILE]); + +Co\run(function () { + $fp = fopen("test.log", "a+"); + fwrite($fp, str_repeat('A', 2048)); + fwrite($fp, str_repeat('B', 2048)); +}); +``` + +#### Menonaktifkan `HOOK` File + +Karena setelah mengaktifkan `HOOK` file, semua operasi file menjadi asinkron non-blocking, termasuk operasi `autoload` dan `include` dll. Jika operasi ini terjadi pada titik penjadwalan korutin, dapat menyebabkan masalah yang tidak terduga. Untuk itu, nonaktifkan opsi `hook` file: + +```php +# Aktifkan semua opsi korutin HOOK kecuali HOOK file +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_FILE); +``` + +Setelah menonaktifkan `HOOK` file, semua operasi file menjadi sinkron blocking, tidak terjadi peralihan korutin, dapat digunakan dengan aman untuk `autoload`, `include` atau logika analisis kode sumber `PHP` lainnya. +Dalam keadaan HOOK file dimatikan, dapat menggunakan cara berikut untuk membaca/menulis file asinkron: +- `System::readFile()`: Membaca konten file secara asinkron +- `System::writeFile()`: Menulis konten file secara asinkron + +Sejak versi `6.1`, juga dapat menggunakan `fopen('async.file://path/to/file', 'rw')` untuk membuka stream file asinkron. +```php +Co\run(function () { + # Ini adalah stream file asinkron, semua operasi baca/tulis pada resource ini adalah asinkron non-blocking + $fp = fopen("async.file:///tmp/test.txt", "w+"); + fwrite($fp, "Hello World\n"); + fdatasync($fp); + fclose($fp); +}); +``` + +#### Kunci File +Saat membaca/menulis file secara konkuren, perlu menggunakan kunci file untuk menjamin konsistensi data. Saat memanggil `flock()` akan menghasilkan penjadwalan korutin. Contoh: + +```php +Co\run(function () { + $fp = fopen("/tmp/test.txt", "w+"); + flock($fp, LOCK_EX); + fwrite($fp, "Hello World\n"); + flock($fp, LOCK_UN); + fclose($fp); +}); +``` + +`flock()` mendukung tiga jenis kunci: `LOCK_SH`, `LOCK_EX`, `LOCK_UN`. Gunakan `LOCK_SH` untuk operasi baca saja, `LOCK_EX` untuk operasi baca/tulis, `LOCK_UN` untuk melepaskan kunci. + +Perhatikan bahwa `flock()` tidak dikontrol oleh opsi `SWOOLE_HOOK_FILE`, melainkan `SWOOLE_HOOK_STDIO`. Jadi meskipun `SWOOLE_HOOK_FILE` dimatikan, `flock()` tetap dikorutinisasi. +Jika perlu menonaktifkan korutinisasi `flock()`, nonaktifkan opsi `SWOOLE_HOOK_STDIO`. + +```php +Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_FILE & ~SWOOLE_HOOK_STDIO); +``` + +### SWOOLE_HOOK_STREAM_FUNCTION + +Mulai `v4.4` mendukung. `Hook` `stream_select()`. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_STREAM_FUNCTION]); + +Co\run(function () { + $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); + $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); + if (!$fp1) { + echo "$errstr ($errno) \n"; + } else { + fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); + $r_array = [$fp1, $fp2]; + $w_array = $e_array = null; + $n = stream_select($r_array, $w_array, $e_array, 10); + $html = ''; + while (!feof($fp1)) { + $html .= fgets($fp1, 1024); + } + fclose($fp1); + } +}); +``` + +### SWOOLE_HOOK_BLOCKING_FUNCTION + +Mulai `v4.4` mendukung. `Blocking function` di sini termasuk: `gethostbyname`, `exec`, `shell_exec`. Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_BLOCKING_FUNCTION]); + +Co\run(function () { + echo shell_exec('ls'); +}); +``` + +### SWOOLE_HOOK_PROC + +Mulai `v4.4` mendukung. Korutinisasi fungsi `proc*`, termasuk: `proc_open`, `proc_close`, `proc_get_status`, `proc_terminate`. + +Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PROC]); + +Co\run(function () { + $descriptorspec = array( + 0 => array("pipe", "r"), // stdin, proses anak membaca darinya + 1 => array("pipe", "w"), // stdout, proses anak menulis ke sana + ); + $process = proc_open('php', $descriptorspec, $pipes); + if (is_resource($process)) { + fwrite($pipes[0], 'I am process'); + fclose($pipes[0]); + + while (true) { + echo fread($pipes[1], 1024); + } + + fclose($pipes[1]); + $return_value = proc_close($process); + echo "command returned $return_value" . PHP_EOL; + } +}); +``` + +### SWOOLE_HOOK_CURL + +Mulai [v4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x) atau `v4.5` resmi didukung. + +#### Fungsi `CURL` yang didukung: + +* `curl_init` +* `curl_setopt` +* `curl_exec` +* `curl_multi_getcontent` +* `curl_setopt_array` +* `curl_error` +* `curl_getinfo` +* `curl_errno` +* `curl_close` +* `curl_reset` + +Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_CURL]); + +Co\run(function () { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://www.xinhuanet.com/"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + var_dump($result); +}); +``` + +### SWOOLE_HOOK_NATIVE_CURL + +`Penanganan korutinisasi` untuk `CURL`, berbeda dengan `SWOOLE_HOOK_CURL`, `SWOOLE_HOOK_NATIVE_CURL` diimplementasikan berdasarkan library `libcurl`, mendukung semua fungsi `CURL`. + +- Sebelum digunakan, perlu mengaktifkan opsi [--enable-swoole-curl](/environment?id=通用参数) saat kompilasi +- Opsi ini bersifat mutual eksklusif dengan [SWOOLE_HOOK_CURL](/runtime?id=swoole_hook_all), tidak bisa diaktifkan bersamaan +- Saat menggunakan opsi `SWOOLE_HOOK_ALL`, `SWOOLE_HOOK_NATIVE_CURL` diprioritaskan + +!> Versi `Swoole` >= `v4.6.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_NATIVE_CURL]); + +Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL]); +``` + +Contoh: + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); + +Co\run(function () { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://httpbin.org/get"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + var_dump($result); +}); +``` + +### SWOOLE_HOOK_SOCKETS + +`Penanganan korutinisasi` untuk ekstensi `sockets`. + +!> Versi `Swoole` >= `v4.6.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_SOCKETS]); +``` + +### SWOOLE_HOOK_STDIO + +`Penanganan korutinisasi` untuk `STDIO`. + +!> Versi Swoole >= `v4.6.2` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_STDIO]); +``` + +Contoh: + +```php +use Swoole\Process; +Co::set(['socket_read_timeout' => -1, 'hook_flags' => SWOOLE_HOOK_STDIO]); +$proc = new Process(function ($p) { + Co\run(function () use($p) { + $p->write('start'.PHP_EOL); + go(function() { + co::sleep(0.05); + echo "sleep\n"; + }); + echo fread(STDIN, 1024); + }); +}, true, SOCK_STREAM); +$proc->start(); +echo $proc->read(); +usleep(100000); +$proc->write('hello world'.PHP_EOL); +echo $proc->read(); +echo $proc->read(); +Process::wait(); +``` + +### SWOOLE_HOOK_PDO_PGSQL + +`Penanganan korutinisasi` untuk `pdo_pgsql`. + +!> Versi `Swoole` >= `v5.1.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_PGSQL]); +``` + +Contoh: +```php +exec('create table test (id int)'); + $dbh->exec('insert into test values(1)'); + $dbh->exec('insert into test values(2)'); + $res = $dbh->query("select * from test"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['trace_flags' => SWOOLE_HOOK_PDO_PGSQL]); + +Co\run(function () { + test(); +}); +``` + +### SWOOLE_HOOK_PDO_ODBC + +`Penanganan korutinisasi` untuk `pdo_odbc`. + +!> Versi `Swoole` >= `v5.1.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ODBC]); +``` + +Contoh: +```php +query("select sleep(1) s"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['trace_flags' => SWOOLE_TRACE_CO_ODBC, 'log_level' => SWOOLE_LOG_DEBUG]); + +Co\run(function () { + test(); +}); +``` + +### SWOOLE_HOOK_PDO_ORACLE + +`Penanganan korutinisasi` untuk `pdo_oci`. + +!> Versi `Swoole` >= `v5.1.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]); +``` + +Contoh: +```php +exec('create table test (id int)'); + $dbh->exec('insert into test values(1)'); + $dbh->exec('insert into test values(2)'); + $res = $dbh->query("select * from test"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]); +Co\run(function () { + test(); +}); +``` + +### SWOOLE_HOOK_PDO_SQLITE +`Penanganan korutinisasi` untuk `pdo_sqlite`. + +!> Versi `Swoole` >= `v5.1.0` tersedia + +```php +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_SQLITE]); +``` + +* **Catatan** + +!> Saat `swoole` mengkorutinisasi database `sqlite`, menggunakan mode `serialisasi` untuk menjamin [thread safety](https://www.sqlite.org/threadsafe.html). +Jika mode thread yang ditentukan saat kompilasi database `sqlite` adalah mode single-thread, `swoole` tidak dapat mengkorutinisasi `sqlite` dan akan melempar peringatan, tetapi tidak mempengaruhi penggunaan, hanya saja tidak akan terjadi peralihan korutin selama proses tambah, hapus, ubah, dan query. Dalam situasi ini hanya bisa mengompilasi ulang `sqlite` dan menentukan mode thread menjadi `serialisasi` atau `multi-thread`, [alasan](https://www.sqlite.org/compile.html#threadsafe). +Semua koneksi `sqlite` yang dibuat di lingkungan korutin adalah `serialized`, koneksi `sqlite` yang dibuat di lingkungan non-korutin secara default sama dengan mode thread `sqlite`. +Jika mode thread `sqlite` adalah `multi-thread`, maka koneksi yang dibuat di lingkungan non-korutin tidak bisa dibagikan ke banyak korutin, karena koneksi database dalam mode `multi-thread`, dan saat digunakan di lingkungan korutin tidak akan ditingkatkan menjadi `serialisasi`. +Mode thread default `sqlite` adalah `serialisasi`, [penjelasan serialisasi](https://www.sqlite.org/c3ref/c_config_covering_index_scan.html#sqliteconfigserialized), [mode thread default](https://www.sqlite.org/compile.html#threadsafe). + +Contoh: +```php + SWOOLE_HOOK_PDO_SQLITE]); + +run(function() { + for($i = 0; $i <= 5; $i++) { + go(function() use ($i) { + $db = new PDO('sqlite::memory:'); + $db->query('select randomblob(99999999)'); + var_dump($i); + }); + } +}); +``` + +## Method + +### setHookFlags() + +Mengatur rentang fungsi yang akan di-`Hook` melalui `flags` + +!> Versi `Swoole` >= `v4.5.0` tersedia + +```php +Swoole\Runtime::setHookFlags(int $flags): bool +``` + +### getHookFlags() + +Mendapatkan `flags` dari konten yang telah di-`Hook`, mungkin tidak konsisten dengan `flags` yang dimasukkan saat mengaktifkan `Hook` (karena `flags` yang gagal di-`Hook` akan dibersihkan) + +!> Versi `Swoole` >= `v4.4.12` tersedia + +```php +Swoole\Runtime::getHookFlags(): int +``` + +## Daftar Hook Umum + +### Daftar Tersedia + +* Ekstensi `redis` +* Ekstensi `mysqli`, `pdo_mysql` (perlu mengaktifkan `mysqlnd`) +* Ekstensi `curl` +* `file_get_contents`, `fopen` +* `stream_socket_client` (`predis`, `php-amqplib`) +* `stream_socket_server` +* `stream_select` (perlu versi `4.3.2` ke atas) +* `fsockopen` +* `proc_open` (perlu versi `4.4.0` ke atas) +* Ekstensi `soap` +* `pdo_pgsql` (perlu versi `v5.1.0` ke atas) +* `pdo_oci` (perlu versi `v5.1.0` ke atas) +* `pdo_odbc` (perlu versi `v5.1.0` ke atas) + +### Daftar Tidak Tersedia + +!> **Tidak mendukung korutinisasi** berarti korutin akan diturunkan ke mode blocking, saat itu menggunakan korutin tidak ada artinya + +* Ekstensi `mysql`: level bawah menggunakan `libmysqlclient` +* Ekstensi `mongodb`: level bawah menggunakan `mongo-c-client` +* `pdo_firebird`, level bawah menggunakan library klien `C` `firebird`, hanya mendukung `IO` sinkron blocking +* `php-amqp`, level bawah menggunakan `librabbitmq`, hanya mendukung `IO` sinkron blocking +* `ftp`, level bawah menggunakan `poll()` menunggu `Socket`, tidak mendukung korutinisasi + +## Perubahan API + +Di `v4.3` dan sebelumnya, `API` `Runtime::enableCoroutine()` memerlukan `2` parameter + +```php +Swoole\Runtime::enableCoroutine(bool $enable = true, int $flags = SWOOLE_HOOK_ALL); +``` + +- `$enable`: Mengaktifkan atau menonaktifkan korutinisasi. +- `$flags`: Memilih tipe yang akan `dikorutinisasi`, bisa multi pilih, default semua. Hanya efektif saat `$enable = true`. + +!> `Runtime::enableCoroutine(false)` menonaktifkan semua pengaturan Hook korutin yang diatur sebelumnya. + +Setelah versi `v4.4`, `API` `Runtime::enableCoroutine()` berubah menjadi hanya perlu `1` parameter + +```php +Swoole\Runtime::enableCoroutine(int $flags = SWOOLE_HOOK_ALL); +``` +- Perubahan: parameter `$enable` dihapus, `$flags` = `0` berarti menonaktifkan semua pengaturan Hook korutin. diff --git a/public/id/server/co_init.md b/public/id/server/co_init.md new file mode 100644 index 0000000..8670ec0 --- /dev/null +++ b/public/id/server/co_init.md @@ -0,0 +1,44 @@ +# Server (Gaya Coroutine) + +Perbedaan antara `Swoole\Coroutine\Server` dengan server [gaya asynchronous](/server/init) adalah bahwa `Swoole\Coroutine\Server` adalah server yang diimplementasikan sepenuhnya dengan coroutine, lihat [contoh lengkap](/coroutine/server?id=contoh-lengkap). + +## Kelebihan: + +- Tidak perlu mengatur fungsi callback event. Membuat koneksi, menerima data, mengirim data, dan menutup koneksi berlangsung secara sekuensial tanpa masalah konkurensi seperti pada [gaya asynchronous](/server/init), contohnya: + +```php +$serv = new Swoole\Server("127.0.0.1", 9501); + +// Memantau event koneksi masuk +$serv->on('Connect', function ($serv, $fd) { + $redis = new Redis(); + $redis->connect("127.0.0.1",6379);// Coroutine di OnConnect akan tertunda di sini + Co::sleep(5);// Mensimulasikan koneksi yang lambat + $redis->set($fd,"fd $fd connected"); +}); + +// Memantau event penerimaan data +$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { + $redis = new Redis(); + $redis->connect("127.0.0.1",6379);// Coroutine di OnReceive akan tertunda di sini + var_dump($redis->get($fd));// Mungkin koneksi Redis dari coroutine OnReceive sudah terbentuk sebelum set di atas dieksekusi, menghasilkan get bernilai false, terjadi kesalahan logika +}); + +// Memantau event penutupan koneksi +$serv->on('Close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +// Menjalankan server +$serv->start(); +``` + +Server `gaya asynchronous` di atas tidak dapat menjamin urutan event, artinya tidak dapat memastikan `onConnect` selesai sebelum `onReceive` masuk. Karena setelah mengaktifkan coroutine, callback `onConnect` dan `onReceive` akan otomatis membuat coroutine, dan saat menemui IO akan terjadi [penjadwalan coroutine](/coroutine?id=penjadwalan-coroutine). Gaya asynchronous tidak dapat menjamin urutan penjadwalan, sedangkan server gaya coroutine tidak memiliki masalah ini. + +- Server gaya coroutine dapat memulai dan menghentikan layanan secara dinamis, sedangkan server gaya asynchronous tidak dapat melakukan apa pun setelah `start()` dipanggil. + +## Kekurangan: + +- Server gaya coroutine tidak otomatis membuat banyak proses, perlu menggunakan modul [Process\Pool](/process/process_pool) untuk memanfaatkan multi-core. +- Server gaya coroutine sebenarnya adalah enkapsulasi dari modul [Co\Socket](/coroutine_client/socket), jadi menggunakan gaya coroutine memerlukan pengalaman dalam pemrograman socket. +- Saat ini tingkat enkapsulasinya tidak setinggi server gaya asynchronous, beberapa hal perlu diimplementasikan manual, misalnya fungsi `reload` perlu memantau sinyal sendiri. diff --git a/public/id/server/event_class.md b/public/id/server/event_class.md new file mode 100644 index 0000000..7d1eb6d --- /dev/null +++ b/public/id/server/event_class.md @@ -0,0 +1,33 @@ +# Swoole\Server\Event + +Berikut adalah penjelasan detail tentang `Swoole\Server\Event`. + +## Properti + +### $reactor_id +Mengembalikan id thread `Reactor` tempatnya berada. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Event->reactor_id +``` + +### $fd +Mengembalikan file descriptor `fd` dari koneksi tersebut. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Event->fd +``` + +### $dispatch_time +Mengembalikan waktu kedatangan data permintaan `dispatch_time`. Properti ini adalah `double`. Properti ini hanya tidak bernilai `0` di event `onReceive`. + +```php +Swoole\Server\Event->dispatch_time +``` + +### $data +Mengembalikan data `data` yang dikirim oleh klien. Properti ini adalah string dengan tipe `string`. Properti ini tidak `null` hanya di event `onReceive`. + +```php +Swoole\Server\Event->data +``` diff --git a/public/id/server/events.md b/public/id/server/events.md new file mode 100644 index 0000000..3c4c981 --- /dev/null +++ b/public/id/server/events.md @@ -0,0 +1,672 @@ +# Event + +Bagian ini akan memperkenalkan semua fungsi callback Swoole. Setiap fungsi callback adalah fungsi PHP, yang sesuai dengan sebuah event. + +## onStart + +?> **Dipanggil setelah startup di thread utama proses master (master)** + +```php +function onStart(Swoole\Server $server); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Sebelum event ini `Server` telah melakukan operasi berikut:** + + * Membuat dan menyelesaikan [Manager process](/learn?id=manager-process) + * Membuat dan menyelesaikan [Worker child process](/learn?id=worker-process) + * Mendengarkan semua port TCP/UDP/[unixSocket](/learn?id=apa-itu-ipc), tetapi belum mulai Menerima koneksi dan permintaan + * Mendengarkan timer + +* **Selanjutnya yang akan dilakukan:** + + * [Reactor](/learn?id=reactor-thread) utama mulai menerima event, klien dapat `connect` ke `Server` + +**Di callback `onStart`, hanya diperbolehkan `echo`, mencetak `Log`, mengubah nama proses. Tidak boleh melakukan operasi lain (tidak boleh memanggil fungsi terkait `server`, dll., karena layanan belum siap). `onWorkerStart` dan `onStart` dieksekusi secara paralel di proses yang berbeda, tidak ada urutan.** + +Anda dapat menyimpan nilai `$server->master_pid` dan `$server->manager_pid` ke dalam file di callback `onStart`. Dengan begitu, Anda dapat menulis skrip untuk mengirim sinyal ke dua `PID` ini untuk melakukan operasi shutdown dan restart. + +Event `onStart` dipanggil di thread utama proses `Master`. + +!> Objek resource global yang dibuat di `onStart` tidak dapat digunakan di proses `Worker`, karena saat panggilan `onStart` terjadi, proses `worker` sudah dibuat +Objek yang baru dibuat berada di proses utama, proses `Worker` tidak dapat mengakses area memori ini +Oleh karena itu, kode pembuatan objek global harus ditempatkan sebelum `Server::start`, contoh tipikal adalah [Swoole\Table](/memory/table?id=contoh-lengkap) + +* **Peringatan Keamanan** + +Di callback `onStart`, API asynchronous dan coroutine dapat digunakan, tetapi perlu diperhatikan bahwa ini mungkin bertentangan dengan `dispatch_func` dan `package_length_func`, **jangan gunakan bersamaan**. + +Jangan memulai timer di `onStart`, jika kode menjalankan `Swoole\Server::shutdown()`, karena selalu ada timer yang berjalan, program tidak akan bisa keluar. + +Callback `onStart` sebelum `return`, program server tidak akan menerima koneksi klien mana pun, sehingga fungsi blocking synchronous dapat digunakan dengan aman. + +* **Mode BASE** + +Dalam mode [SWOOLE_BASE](/learn?id=swoole_base) tidak ada proses `master`, sehingga event `onStart` tidak ada, jangan gunakan callback `onStart` dalam mode `BASE`. + +``` +WARNING swReactorProcess_start: The onStart event with SWOOLE_BASE is deprecated +``` + +## onBeforeShutdown + +?> **Event ini terjadi sebelum `Server` berakhir secara normal** + +!> Tersedia di Swoole versi >= `v4.8.0`. API coroutine dapat digunakan dalam event ini. + +```php +function onBeforeShutdown(Swoole\Server $server); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +## onShutdown + +?> **Event ini terjadi saat `Server` berakhir secara normal** + +```php +function onShutdown(Swoole\Server $server); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Sebelum ini `Swoole\Server` telah melakukan operasi berikut:** + + * Telah menutup semua thread [Reactor](/learn?id=reactor-thread), thread `HeartbeatCheck`, thread `UdpRecv` + * Telah menutup semua proses `Worker`, [Task process](/learn?id=taskworker-process), [User process](/server/methods?id=addprocess) + * Telah `close` semua port listening `TCP/UDP/UnixSocket` + * Telah menutup [Reactor](/learn?id=reactor-thread) utama + + !> Memaksa `kill` proses tidak akan memanggil callback `onShutdown`, seperti `kill -9` + Perlu menggunakan `kill -15` untuk mengirim sinyal `SIGTERM` ke proses utama agar dapat menghentikan sesuai alur normal + Menggunakan `Ctrl+C` di command line untuk menginterupsi program akan segera berhenti, dan level bawah tidak akan memanggil callback `onShutdown` + +* **Hal yang Perlu Diperhatikan** + + !> Jangan memanggil API asynchronous atau coroutine apa pun di `onShutdown`, saat `onShutdown` dipicu, level bawah telah menghancurkan semua fasilitas event loop; + Saat itu lingkungan coroutine sudah tidak ada, jika pengembang perlu menggunakan API terkait coroutine, harus memanggil `Co\run` secara manual untuk membuat [coroutine container](/coroutine?id=apa-itu-coroutine-container). + +## onWorkerStart + +?> **Event ini terjadi saat proses Worker / [Task process](/learn?id=taskworker-process) dimulai. Objek yang dibuat di sini dapat digunakan dalam siklus hidup proses.** + +```php +function onWorkerStart(Swoole\Server $server, int $workerId); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $workerId`** + * **Fungsi**: `id` proses `Worker` (bukan PID proses) + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* `onWorkerStart/onStart` dieksekusi secara concurrent, tidak ada urutan +* Dapat menggunakan properti `$server->taskworker` untuk menentukan apakah saat ini proses `Worker` atau [Task process](/learn?id=taskworker-process) +* Jika `worker_num` dan `task_worker_num` diatur lebih dari `1`, setiap proses akan memicu event `onWorkerStart` sekali, dapat dibedakan dengan [$worker_id](/server/properties?id=worker_id) +* Proses `worker` mengirim tugas ke proses `task`, setelah proses `task` menyelesaikan semua tugas, proses `worker` diberitahu melalui fungsi callback [onFinish](/server/events?id=onfinish). Misalnya, di backend mengirim email pemberitahuan massal ke seratus ribu pengguna, setelah operasi selesai, status operasi ditampilkan sebagai "sedang dikirim", saat ini dapat melanjutkan operasi lain, dan setelah email selesai dikirim, status operasi secara otomatis berubah menjadi "terkirim". + +Contoh berikut digunakan untuk mengganti nama proses Worker / [Task process](/learn?id=taskworker-process). + +```php +$server->on('WorkerStart', function ($server, $worker_id){ + global $argv; + if($worker_id >= $server->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]} task worker"); + } else { + swoole_set_process_name("php {$argv[0]} event worker"); + } +}); +``` + +Jika ingin menggunakan mekanisme [Reload](/server/methods?id=reload) untuk memuat ulang kode, harus `require` file bisnis Anda di `onWorkerStart`, bukan di awal file. File yang sudah disertakan sebelum `onWorkerStart` dipanggil, tidak akan dimuat ulang. + +File php yang umum dan tidak sering berubah dapat ditempatkan sebelum `onWorkerStart`. Meskipun tidak dapat dimuat ulang, file tersebut dibagi oleh semua `Worker`, tanpa perlu memori tambahan untuk menyimpan data ini. +Kode setelah `onWorkerStart` perlu disimpan di memori oleh setiap proses. + +* `$worker_id` menunjukkan `ID` proses `Worker` ini, rentang referensi [$worker_id](/server/properties?id=worker_id) +* [$worker_id](/server/properties?id=worker_id) tidak ada hubungannya dengan `PID` proses, dapat menggunakan fungsi `posix_getpid` untuk mendapatkan `PID` + +* **Dukungan Coroutine** + + * Di fungsi callback `onWorkerStart`, coroutine akan dibuat secara otomatis, sehingga `onWorkerStart` dapat memanggil API coroutine + +* **Catatan** + + !> Jika terjadi fatal error atau panggilan `exit` secara aktif dalam kode, proses `Worker/Task` akan keluar, proses manajemen akan membuat proses baru. Ini dapat menyebabkan infinite loop, terus-menerus membuat dan menghancurkan proses. + +## onWorkerStop + +?> **Event ini terjadi saat proses `Worker` dihentikan. Di fungsi ini, berbagai resource yang diminta oleh proses `Worker` dapat dibebaskan.** + +```php +function onWorkerStop(Swoole\Server $server, int $workerId); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $workerId`** + * **Fungsi**: `id` proses `Worker` (bukan PID proses) + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Catatan** + + !> -Proses berakhir tidak normal, seperti dipaksa `kill`, fatal error, `core dump`, tidak dapat menjalankan callback `onWorkerStop`. + -Jangan memanggil API asynchronous atau coroutine apa pun di `onWorkerStop`, saat `onWorkerStop` dipicu, level bawah telah menghancurkan semua fasilitas [event loop](/learn?id=apa-itu-eventloop). + +## onWorkerExit + +?> **Hanya efektif setelah mengaktifkan fitur [reload_async](/server/setting?id=reload_async). Lihat [Cara me-restart service dengan benar](/question/use?id=cara-me-restart-service-swoole-dengan-benar)** + +```php +function onWorkerExit(Swoole\Server $server, int $workerId); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $workerId`** + * **Fungsi**: `id` proses `Worker` (bukan PID proses) + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Catatan** + + !> -Jika proses `Worker` tidak keluar, `onWorkerExit` akan terus terpicu + -`onWorkerExit` akan terpicu di dalam proses `Worker`, jika di [Task process](/learn?id=taskworker-process) terdapat [event loop](/learn?id=apa-itu-eventloop), juga akan terpicu + -Di `onWorkerExit`, sebisa mungkin hapus/tutup koneksi `Socket` asynchronous, akhirnya level bawah mendeteksi jumlah handle event listener di [event loop](/learn?id=apa-itu-eventloop) adalah `0`, maka proses akan keluar + -Jika proses tidak memiliki event handle yang sedang didengarkan, saat proses berakhir, fungsi ini tidak akan dipanggil + -Menunggu proses `Worker` keluar baru kemudian menjalankan callback event `onWorkerStop` + +## onConnect + +?> **Saat ada koneksi baru masuk, dipanggil di proses worker.** + +```php +function onConnect(Swoole\Server $server, int $fd, int $reactorId); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $fd`** + * **Fungsi**: File descriptor koneksi + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $reactorId`** + * **Fungsi**: `ID` thread [Reactor](/learn?id=reactor-thread) tempat koneksi berada + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Catatan** + + !> `onConnect/onClose` adalah `2` callback yang terjadi di dalam proses `Worker`, bukan di proses utama. + Protokol `UDP` hanya memiliki event [onReceive](/server/events?id=onreceive), tidak ada event `onConnect/onClose`. + + * **[dispatch_mode](/server/setting?id=dispatch_mode) = 1/3** + + * Dalam mode ini, `onConnect/onReceive/onClose` dapat dikirim ke proses yang berbeda. Data objek `PHP` terkait koneksi tidak dapat diinisialisasi di callback [onConnect](/server/events?id=onconnect), dan dibersihkan di [onClose](/server/events?id=onclose). + * `3` jenis event `onConnect/onReceive/onClose` dapat dieksekusi secara concurrent, yang dapat menyebabkan anomali. + +## onReceive + +?> **Saat menerima data, fungsi ini dipanggil, terjadi di proses `worker`.** + +```php +function onReceive(Swoole\Server $server, int $fd, int $reactorId, string $data); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $fd`** + * **Fungsi**: File descriptor koneksi + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $reactorId`** + * **Fungsi**: `ID` thread [Reactor](/learn?id=reactor-thread) tempat koneksi `TCP` berada + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $data`** + * **Fungsi**: Isi data yang diterima, bisa berupa teks atau konten biner + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Tentang integritas paket dalam protokol `TCP`, lihat [TCP Data Packet Boundary Issue](/learn?id=masalah-batas-paket-data-tcp)** + + * Menggunakan konfigurasi `open_eof_check/open_length_check/open_http_protocol` yang disediakan level bawah dapat menjamin integritas paket data. + * Jika tidak menggunakan pemrosesan protokol level bawah, lakukan analisis data sendiri dalam kode PHP setelah [onReceive](/server/events?id=onreceive), gabung/pisah paket data. + + Contoh: dapat menambahkan `$buffer = array()` dalam kode, menggunakan `$fd` sebagai `key`, untuk menyimpan data konteks. Setiap kali menerima data, lakukan penggabungan string, `$buffer[$fd] .= $data`, kemudian periksa apakah string `$buffer[$fd]` merupakan paket data yang lengkap. + + Secara default, `fd` yang sama akan dialokasikan ke `Worker` yang sama, sehingga data dapat digabungkan. Saat menggunakan [dispatch_mode](/server/setting?id=dispatch_mode) = 3, data permintaan bersifat preemptive, data dari `fd` yang sama dapat dibagi ke proses yang berbeda, sehingga metode penggabungan data di atas tidak dapat digunakan. + +* **Listening multi-port, lihat [bagian ini](/server/port)** + + Setelah server utama mengatur protokol, port tambahan yang didengarkan secara default akan mewarisi pengaturan server utama. Perlu secara eksplisit memanggil method `set` untuk mengatur ulang protokol port. + + ```php + $server = new Swoole\Http\Server("127.0.0.1", 9501); + $port2 = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); + $port2->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + echo "[#".$server->worker_id."]\tClient[$fd]: $data\n"; + }); + ``` + + Meskipun di sini memanggil method `on` untuk mendaftarkan callback [onReceive](/server/events?id=onreceive), tetapi karena tidak memanggil method `set` untuk menimpa protokol server utama, port `9502` yang baru tetap menggunakan protokol `HTTP`. Menggunakan klien `telnet` untuk terhubung ke port `9502` dan mengirim string tidak akan memicu [onReceive](/server/events?id=onreceive) di server. + +* **Catatan** + + !> Tanpa mengaktifkan opsi protokol otomatis, [onReceive](/server/events?id=onreceive) satu kali menerima data maksimal `64K` + Dengan mengaktifkan opsi pemrosesan protokol otomatis, [onReceive](/server/events?id=onreceive) akan menerima paket data lengkap, maksimal tidak melebihi [package_max_length](/server/setting?id=package_max_length) + Mendukung format biner, `$data` bisa berupa data biner. + +## onPacket + +?> **Saat menerima paket data `UDP`, fungsi ini dipanggil, terjadi di proses `worker`.** + +```php +function onPacket(Swoole\Server $server, string $data, array $clientInfo); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`string $data`** + * **Fungsi**: Isi data yang diterima, bisa berupa teks atau konten biner + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`array $clientInfo`** + * **Fungsi**: Informasi klien mencakup `address/port/server_socket` dan berbagai data informasi klien lainnya, [lihat Server UDP](/start/start_udp_server) + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Catatan** + + !> Saat server mendengarkan port `TCP/UDP` secara bersamaan, data protokol `TCP` yang diterima akan memanggil callback [onReceive](/server/events?id=onreceive), dan paket data `UDP` yang diterima akan memanggil callback `onPacket`. Pemrosesan protokol otomatis seperti `EOF` atau `Length` yang diatur server ([lihat Masalah Batas Paket Data TCP](/learn?id=masalah-batas-paket-data-tcp)), tidak berlaku untuk port `UDP`, karena paket `UDP` sendiri memiliki batas pesan, tidak memerlukan pemrosesan protokol tambahan. + +## onClose + +?> **Setelah koneksi klien `TCP` ditutup, fungsi ini dipanggil di proses `Worker`.** + +```php +function onClose(Swoole\Server $server, int $fd, int $reactorId); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $fd`** + * **Fungsi**: File descriptor koneksi + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $reactorId`** + * **Fungsi**: Berasal dari thread `reactor` mana, bernilai negatif saat `close` dilakukan secara aktif + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Tips** + + * **Penutupan Aktif** + + * Saat server secara aktif menutup koneksi, level bawah akan mengatur parameter ini menjadi `-1`, dapat dibedakan dengan memeriksa `$reactorId < 0` apakah penutupan dilakukan oleh server atau klien. + * Hanya jika secara aktif memanggil method `close` dalam kode `PHP` dianggap sebagai penutupan aktif + + * **Deteksi Heartbeat** + + * [Deteksi Heartbeat](/server/setting?id=heartbeat_check_interval) adalah pemberitahuan penutupan dari thread deteksi heartbeat, saat penutupan, parameter `$reactorId` dari [onClose](/server/events?id=onclose) bukan `-1` + +* **Catatan** + + !> -Jika terjadi fatal error di fungsi callback [onClose](/server/events?id=onclose), dapat menyebabkan kebocoran koneksi. Melalui perintah `netstat` akan terlihat banyak koneksi `TCP` dengan status `CLOSE_WAIT`. + -Baik klien yang memulai `close` maupun server yang secara aktif memanggil `$server->close()` untuk menutup koneksi, akan memicu event ini. Oleh karena itu, selama koneksi ditutup, pasti akan memanggil fungsi ini. + -Di [onClose](/server/events?id=onclose) masih dapat memanggil method [getClientInfo](/server/methods?id=getClientInfo) untuk mendapatkan informasi koneksi. Setelah eksekusi callback [onClose](/server/events?id=onclose) selesai, baru akan memanggil `close` untuk menutup koneksi `TCP`. + -Saat callback [onClose](/server/events?id=onclose) di sini dipanggil, itu berarti koneksi klien sudah ditutup, jadi tidak perlu menjalankan `$server->close($fd)`. Menjalankan `$server->close($fd)` dalam kode akan melemparkan peringatan error `PHP`. + +## onTask + +?> **Dipanggil di dalam proses `task`. Proses `worker` dapat menggunakan fungsi [task](/server/methods?id=task) untuk mengirim tugas baru ke proses `task_worker`. Proses [Task](/learn?id=taskworker-process) saat ini saat memanggil fungsi callback [onTask](/server/events?id=ontask) akan mengubah status proses menjadi sibuk, dan tidak akan menerima Task baru lagi. Saat fungsi [onTask](/server/events?id=ontask) kembali, status proses akan berubah kembali menjadi idle dan terus menerima `Task` baru.** + +```php +function onTask(Swoole\Server $server, int $task_id, int $src_worker_id, mixed $data); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $task_id`** + * **Fungsi**: `id` proses `task` yang menjalankan tugas【`$task_id` dan `$src_worker_id` digabungkan baru bersifat unik secara global, `ID` tugas yang dikirim oleh proses `worker` yang berbeda mungkin sama】 + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $src_worker_id`** + * **Fungsi**: `id` proses `worker` yang mengirim tugas【`$task_id` dan `$src_worker_id` digabungkan baru bersifat unik secara global, `ID` tugas yang dikirim oleh proses `worker` yang berbeda mungkin sama】 + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`mixed $data`** + * **Fungsi**: Konten data tugas + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Tips** + + * **Mulai v4.2.12, jika [task_enable_coroutine](/server/setting?id=task_enable_coroutine) diaktifkan, prototipe fungsi callback adalah** + + ```php + $server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { + var_dump($task); + $task->finish([123, 'hello']); //menyelesaikan tugas, selesai dan mengembalikan data + }); + ``` + + * **Mengembalikan hasil eksekusi ke proses `worker`** + + * **Dalam fungsi [onTask](/server/events?id=ontask), `return` string berarti mengembalikan konten ini ke proses `worker`. Proses `worker` akan memicu fungsi [onFinish](/server/events?id=onfinish), menandakan bahwa `task` yang dikirim telah selesai. Tentu saja, Anda juga dapat memicu fungsi [onFinish](/server/events?id=onfinish) melalui `Swoole\Server->finish()`, tanpa perlu `return`** + + * Variabel `return` bisa berupa variabel `PHP` apa pun yang bukan `null` + +* **Catatan** + + !> Saat fungsi [onTask](/server/events?id=ontask) dieksekusi dan mengalami fatal error, atau dipaksa `kill` oleh proses eksternal, tugas saat ini akan dibuang, tetapi tidak akan memengaruhi `Task` lain yang sedang mengantri. + +## onFinish + +?> **Fungsi callback ini dipanggil di proses worker. Saat tugas yang dikirim oleh proses `worker` selesai di proses `task`, [task process](/learn?id=taskworker-process) akan mengirimkan hasil pemrosesan tugas ke proses `worker` melalui method `Swoole\Server->finish()`.** + +```php +function onFinish(Swoole\Server $server, int $task_id, mixed $data) +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $task_id`** + * **Fungsi**: `id` proses `task` yang menjalankan tugas + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`mixed $data`** + * **Fungsi**: Konten hasil pemrosesan tugas + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Catatan** + + !> -Jika event [onTask](/server/events?id=ontask) dari [task process](/learn?id=taskworker-process) tidak memanggil method `finish` atau `return` hasil, maka proses `worker` tidak akan memicu [onFinish](/server/events?id=onfinish) + -Proses `worker` yang menjalankan logika [onFinish](/server/events?id=onfinish) adalah proses yang sama dengan proses `worker` yang mengirim tugas `task`. + +## onPipeMessage + +?> **Saat proses kerja menerima pesan [unixSocket](/learn?id=apa-itu-ipc) yang dikirim oleh `$server->sendMessage()`, event `onPipeMessage` akan terpicu. Proses `worker/task` mungkin memicu event `onPipeMessage`.** + +```php +function onPipeMessage(Swoole\Server $server, int $src_worker_id, mixed $message); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $src_worker_id`** + * **Fungsi**: Dari proses `Worker` mana pesan berasal + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`mixed $message`** + * **Fungsi**: Konten pesan, bisa berupa tipe PHP apa pun + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +## onWorkerError + +?> **Saat terjadi error pada proses `Worker/Task`, fungsi ini akan dipanggil di dalam proses `Manager`.** + +!> Fungsi ini terutama digunakan untuk alarm dan monitoring. Begitu menemukan proses Worker keluar secara abnormal, kemungkinan besar menemui fatal error atau proses Core Dump. Dengan mencatat log atau mengirim informasi alarm untuk memberi tahu pengembang agar melakukan penanganan yang sesuai. + +```php +function onWorkerError(Swoole\Server $server, int $worker_id, int $worker_pid, int $exit_code, int $signal); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $worker_id`** + * **Fungsi**: `id` proses `worker` yang mengalami error + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $worker_pid`** + * **Fungsi**: `pid` proses `worker` yang mengalami error + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $exit_code`** + * **Fungsi**: Kode status keluar, rentang `0~255` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $signal`** + * **Fungsi**: Sinyal keluar proses + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Kesalahan Umum** + + * `signal = 11`: Menunjukkan proses `Worker` mengalami `segment fault`, mungkin memicu `BUG` level bawah. Harap kumpulkan informasi `core dump` dan log deteksi memori `valgrind`, [laporkan masalah ini ke tim pengembang Swoole](/other/issue) + * `exit_code = 255`: Menunjukkan proses Worker mengalami `Fatal Error`. Harap periksa log error PHP, temukan kode PHP yang bermasalah, dan selesaikan. + * `signal = 9`: Menunjukkan `Worker` dipaksa `Kill` oleh sistem. Harap periksa apakah ada operasi `kill -9` yang dilakukan, periksa informasi `dmesg` apakah ada `OOM (Out of memory)`. + * Jika ada `OOM`, alokasi memori terlalu besar. 1. Periksa konfigurasi `setting` `Server`, apakah [socket_buffer_size](/server/setting?id=socket_buffer_size) dll. dialokasikan terlalu besar; 2. Apakah membuat modul memori [Swoole\Table](/memory/table) yang sangat besar. + +## onManagerStart + +?> **Saat proses manajemen dimulai, event ini terpicu.** + +```php +function onManagerStart(Swoole\Server $server); +``` + +* **Tips** + + * Di fungsi callback ini, nama proses manajemen dapat diubah. + * Di versi sebelum `4.2.12`, proses `manager` tidak dapat menambahkan timer, tidak dapat mengirim task, tidak dapat menggunakan coroutine. + * Di versi `4.2.12` atau yang lebih baru, proses `manager` dapat menggunakan timer mode synchronous berbasis sinyal. + * Di proses `manager` dapat memanggil antarmuka [sendMessage](/server/methods?id=sendMessage) untuk mengirim pesan ke proses kerja lain. + + * **Urutan Startup** + + * Proses `Task` dan `Worker` sudah dibuat + * Status proses `Master` tidak jelas, karena `Manager` dan `Master` berjalan paralel, saat callback `onManagerStart` terjadi, tidak dapat memastikan apakah proses `Master` sudah siap. + + * **Mode BASE** + + * Dalam mode [SWOOLE_BASE](/learn?id=swoole_base), jika parameter `worker_num`, `max_request`, `task_worker_num` diatur, level bawah akan membuat proses `manager` untuk mengelola proses kerja. Oleh karena itu, callback event `onManagerStart` dan `onManagerStop` akan terpicu. + +## onManagerStop + +?> **Saat proses manajemen berakhir, event ini terpicu.** + +```php +function onManagerStop(Swoole\Server $server); +``` + +* **Tips** + + * Saat `onManagerStop` terpicu, itu berarti proses `Task` dan `Worker` telah selesai berjalan dan telah dibebaskan oleh proses `Manager`. + +## onBeforeReload + +?> **Sebelum proses Worker di `Reload`, event ini terpicu, dipanggil di proses Manager.** + +```php +function onBeforeReload(Swoole\Server $server); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +## onAfterReload + +?> **Setelah proses Worker di `Reload`, event ini terpicu, dipanggil di proses Manager.** + +```php +function onAfterReload(Swoole\Server $server); +``` + +* **Parameter** + + * **`Swoole\Server $server`** + * **Fungsi**: Objek Swoole\Server + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +## Urutan Eksekusi Event + +* Semua callback event terjadi setelah `$server->start` +* Saat server ditutup dan program berakhir, event terakhir adalah `onShutdown` +* Setelah server berhasil dimulai, `onStart/onManagerStart/onWorkerStart` akan dieksekusi secara concurrent di proses yang berbeda +* `onReceive/onConnect/onClose` terpicu di proses `Worker` +* Saat proses `Worker/Task` dimulai/berakhir, `onWorkerStart/onWorkerStop` akan dipanggil masing-masing satu kali +* Event [onTask](/server/events?id=ontask) hanya terjadi di [task process](/learn?id=taskworker-process) +* Event [onFinish](/server/events?id=onfinish) hanya terjadi di proses `worker` +* Urutan eksekusi `onStart/onManagerStart/onWorkerStart` `3` event tidak dapat ditentukan + +## Gaya Berorientasi Objek + +Setelah mengaktifkan [event_object](/server/setting?id=event_object), parameter callback event berikut akan berubah. + +* Koneksi klien [onConnect](/server/events?id=onconnect) +```php +$server->on('Connect', function (Swoole\Server $serv, Swoole\Server\Event $object) { + var_dump($object); +}); +``` + +* Menerima data [onReceive](/server/events?id=onreceive) +```php +$server->on('Receive', function (Swoole\Server $serv, Swoole\Server\Event $object) { + var_dump($object); +}); +``` + +* Koneksi ditutup [onClose](/server/events?id=onclose) +```php +$server->on('Close', function (Swoole\Server $serv, Swoole\Server\Event $object) { + var_dump($object); +}); +``` + +* UDP menerima paket [onPacket](/server/events?id=onpacket) +```php +$server->on('Packet', function (Swoole\Server $serv, Swoole\Server\Packet $object) { + var_dump($object); +}); +``` + +* Komunikasi antar proses [onPipeMessage](/server/events?id=onpipemessage) +```php +$server->on('PipeMessage', function (Swoole\Server $serv, Swoole\Server\PipeMessage $msg) { + var_dump($msg); + $object = $msg->data; + $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); +}); +``` + +* Proses mengalami error [onWorkerError](/server/events?id=onworkererror) +```php +$serv->on('WorkerError', function (Swoole\Server $serv, Swoole\Server\StatusInfo $info) { + var_dump($info); +}); +``` + +* Proses task menerima tugas [onTask](/server/events?id=ontask) +```php +$server->on('Task', function (Swoole\Server $serv, Swoole\Server\Task $task) { + var_dump($task); +}); +``` + +* Proses worker menerima hasil pemrosesan dari proses task [onFinish](/server/events?id=onfinish) +```php +$server->on('Finish', function (Swoole\Server $serv, Swoole\Server\TaskResult $result) { + var_dump($result); +}); +``` + +* [Swoole\Server\Event](/server/event_class) +* [Swoole\Server\Packet](/server/packet_class) +* [Swoole\Server\PipeMessage](/server/pipemessage_class) +* [Swoole\Server\StatusInfo](/server/statusinfo_class) +* [Swoole\Server\Task](/server/task_class) +* [Swoole\Server\TaskResult](/server/taskresult_class) diff --git a/public/id/server/init.md b/public/id/server/init.md new file mode 100644 index 0000000..eb364f2 --- /dev/null +++ b/public/id/server/init.md @@ -0,0 +1,21 @@ +# Server (Gaya Async) + +Membuat program server asynchronous dengan mudah, mendukung 3 jenis socket: `TCP`, `UDP`, dan [unixSocket](/learn?id=apa-itu-ipc), dengan dukungan `IPv4` dan `IPv6`, serta enkripsi tunnel `SSL/TLS` satu arah maupun dua arah. Pengguna tidak perlu mempedulikan detail implementasi internal, cukup atur fungsi callback untuk [event](/server/events) jaringan saja, lihat contoh di [Mulai Cepat](/start/start_tcp_server). + +!> Gaya `Server` ini bersifat asynchronous (semua event perlu mengatur fungsi callback), tetapi juga mendukung coroutine. Dengan mengaktifkan [enable_coroutine](/server/setting?id=enable_coroutine) (default aktif), coroutine dapat digunakan. Semua kode bisnis di bawah [coroutine](/coroutine) ditulis secara sinkron. + +Pelajari lebih lanjut: + +[Pengantar tiga mode operasi Server](/learn?id=pengantar-tiga-mode-operasi-server ':target=_blank') +[Apa perbedaan antara Process, ProcessPool, dan UserProcess](/learn?id=apa-perbedaannya ':target=_blank') +[Perbedaan dan hubungan antara Master Process, Reactor Thread, Worker Process, Task Process, dan Manager Process](/learn?id=perbedaan-dan-hubungan ':target=_blank') + +### Diagram Alir Eksekusi + +![running_process](https://wiki.swoole.com/_images/server/running_process.png ':size=800xauto') + +### Diagram Struktur Proses/Thread + +![process_structure](https://wiki.swoole.com/_images/server/process_structure.png ':size=800xauto') + +![process_structure_2](https://wiki.swoole.com/_images/server/process_structure_2.png) diff --git a/public/id/server/methods.md b/public/id/server/methods.md new file mode 100644 index 0000000..280db86 --- /dev/null +++ b/public/id/server/methods.md @@ -0,0 +1,1996 @@ +# Method + +## __construct() + +Membuat objek TCP Server untuk [I/O asynchronous](/learn?id=sync-io-async-io). + +```php +Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): \Swoole\Server +``` + + * **Parameter** + + * `string $host` + + * Fungsi: menentukan alamat ip yang akan didengarkan. + * Default: tidak ada. + * Nilai lain: tidak ada. + + !> Untuk IPv4 gunakan `127.0.0.1` untuk mendengarkan localhost, `0.0.0.0` untuk mendengarkan semua alamat. + Untuk IPv6 gunakan `::1` untuk mendengarkan localhost, `::` (setara dengan `0:0:0:0:0:0:0:0`) untuk mendengarkan semua alamat. + + * `int $port` + + * Fungsi: menentukan port yang akan didengarkan, misalnya `9501`. + * Default: tidak ada. + * Nilai lain: tidak ada. + + !> Jika nilai `$sockType` adalah [UnixSocket Stream/Dgram](/learn?id=apa-itu-ipc), parameter ini akan diabaikan. + Mendengarkan port di bawah `1024` memerlukan hak akses `root`. + Jika port ini sudah digunakan, `server->start` akan gagal. + + * `int $mode` + + * Fungsi: menentukan mode operasi. + * Default: [SWOOLE_PROCESS](/learn?id=swoole_process) mode multi-proses (default). + * Nilai lain: [SWOOLE_BASE](/learn?id=swoole_base) mode dasar, [SWOOLE_THREAD](/learn?id=swoole_thread) mode multi-thread (tersedia di Swoole 6.0). + + ?> Dalam mode `SWOOLE_THREAD`, klik di sini [Thread + Server (gaya async)](/thread/thread?id=thread-server-gaya-async) untuk melihat cara membuat server dalam mode multi-thread. + + !> Mulai Swoole 5, nilai default mode operasi adalah `SWOOLE_BASE`. + + * `int $sockType` + + * Fungsi: menentukan tipe Server. + * Default: tidak ada. + * Nilai lain: + * `SWOOLE_TCP/SWOOLE_SOCK_TCP` tcp ipv4 socket + * `SWOOLE_TCP6/SWOOLE_SOCK_TCP6` tcp ipv6 socket + * `SWOOLE_UDP/SWOOLE_SOCK_UDP` udp ipv4 socket + * `SWOOLE_UDP6/SWOOLE_SOCK_UDP6` udp ipv6 socket + * [SWOOLE_UNIX_DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php) unix socket dgram + * [SWOOLE_UNIX_STREAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/stream_server.php) unix socket stream + + !> Menggunakan `$sock_type` | `SWOOLE_SSL` dapat mengaktifkan enkripsi tunnel `SSL`. Setelah mengaktifkan SSL, harus dikonfigurasi dengan [ssl_key_file](/server/setting?id=ssl_cert_file) dan [ssl_cert_file](/server/setting?id=ssl_cert_file) + + * **Contoh** + +```php +$server = new \Swoole\Server($host, $port = 0, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP); + +// Dapat mencampur UDP/TCP, mendengarkan port internal dan eksternal secara bersamaan, lihat bagian addlistener untuk multi-port. +$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); // Menambah TCP +$server->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); // Menambah Web Socket +$server->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); // UDP +$server->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); //UnixSocket Stream +$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); //TCP + SSL + +$port = $server->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); // Sistem memberikan port secara acak, nilai kembali adalah port yang diberikan +echo $port->port; +``` + +## set() + +Digunakan untuk mengatur berbagai parameter runtime. Setelah server mulai, akses array parameter yang diatur melalui method `Server->set` menggunakan `$serv->setting`. + +```php +Swoole\Server->set(array $setting): void +``` + +!> `Server->set` harus dipanggil sebelum `Server->start`. Untuk arti spesifik setiap konfigurasi, silakan lihat [bagian ini](/server/setting) + + * **Contoh** + +```php +$server->set(array( + 'reactor_num' => 2, // jumlah thread + 'worker_num' => 4, // jumlah proses + 'backlog' => 128, // mengatur panjang antrian Listen + 'max_request' => 50, // jumlah maksimum request per proses + 'dispatch_mode' => 1, // strategi distribusi paket data +)); +``` + +## on() + +Mendaftarkan fungsi callback event untuk `Server`. + +```php +Swoole\Server->on(string $event, callable $callback): bool +``` + +!> Memanggil method `on` berulang kali akan menimpa pengaturan sebelumnya + +!> Mulai `PHP 8.2`, tidak lagi mendukung pengaturan properti dinamis secara langsung. Jika `$event` bukan event yang ditentukan `Swoole`, akan memunculkan peringatan + + * **Parameter** + + * `string $event` + + * Fungsi: nama event callback + * Default: tidak ada + * Nilai lain: tidak ada + + !> Tidak case-sensitive. Untuk daftar event callback, lihat [bagian ini](/server/events). Jangan tambahkan `on` pada string nama event + + * `callable $callback` + + * Fungsi: fungsi callback + * Default: tidak ada + * Nilai lain: tidak ada + + !> Dapat berupa string nama fungsi, method statis kelas, array method objek, fungsi anonim. Lihat [bagian ini](/learn?id=berbagai-cara-mengatur-fungsi-callback). + + * **Return Value** + + * Mengembalikan `true` jika operasi berhasil, `false` jika gagal. + + * **Contoh** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('connect', function ($server, $fd){ + echo "Client:Connect.\n"; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->on('close', function ($server, $fd) { + echo "Client: Close.\n"; +}); +$server->start(); +``` + +## addListener() + +Menambah port yang didengarkan. Dalam kode bisnis, panggil [Swoole\Server->getClientInfo](/server/methods?id=getclientinfo) untuk mengetahui dari port mana suatu koneksi berasal. + +```php +Swoole\Server->addListener(string $host, int $port, int $sockType): bool|Swoole\Server\Port +``` + +!> Mendengarkan port di bawah `1024` memerlukan hak akses `root` +Jika server utama menggunakan protokol `WebSocket` atau `HTTP`, port `TCP` baru yang didengarkan secara default akan mewarisi pengaturan protokol dari server utama. Harus memanggil method `set` secara terpisah untuk mengatur protokol baru agar protokol baru digunakan [Lihat penjelasan detail](/server/port). +Klik [di sini](/server/server_port) untuk melihat penjelasan detail `Swoole\Server\Port`. + + * **Parameter** + + * `string $host` + + * Fungsi: Sama dengan `$host` di `__construct()` + * Default: Sama dengan `$host` di `__construct()` + * Nilai lain: Sama dengan `$host` di `__construct()` + + * `int $port` + + * Fungsi: Sama dengan `$port` di `__construct()` + * Default: Sama dengan `$port` di `__construct()` + * Nilai lain: Sama dengan `$port` di `__construct()` + + * `int $sockType` + + * Fungsi: Sama dengan `$sockType` di `__construct()` + * Default: Sama dengan `$sockType` di `__construct()` + * Nilai lain: Sama dengan `$sockType` di `__construct()` + + * **Return Value** + + * Mengembalikan `Swoole\Server\Port` jika berhasil, mengembalikan `false` jika gagal. + +!> -Dalam mode `Unix Socket`, parameter `$host` harus berupa path file yang dapat diakses, parameter `$port` diabaikan +-Dalam mode `Unix Socket`, `$fd` client bukan lagi angka, melainkan string path file +-Di sistem `Linux`, setelah mendengarkan port `IPv6`, koneksi menggunakan alamat `IPv4` juga dapat dilakukan + +## listen() + +Method ini adalah alias dari `addlistener`. + +```php +Swoole\Server->listen(string $host, int $port, int $type): bool|Swoole\Server\Port +``` + +## addProcess() + +Menambahkan proses kerja kustom. Fungsi ini biasanya digunakan untuk membuat proses kerja khusus untuk monitoring, pelaporan, atau tugas khusus lainnya. + +```php +Swoole\Server->addProcess(Swoole\Process $process): int +``` + +!> Tidak perlu menjalankan `start`. Proses akan otomatis dibuat saat `Server` mulai, dan akan menjalankan fungsi child process yang ditentukan + + * **Parameter** + + * [Swoole\Process](/process/process) + + * Fungsi: objek `Swoole\Process` + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan nomor id proses jika berhasil, jika tidak program akan melempar fatal error. + + * **Catatan** + + !> -Child process yang dibuat dapat memanggil berbagai method yang disediakan objek `$server`, seperti `getClientList/getClientInfo/stats`. + -Dalam proses `Worker/Task`, dapat memanggil method yang disediakan `$process` untuk berkomunikasi dengan child process. + -Dalam proses kustom, dapat menggunakan `$server->sendMessage` untuk berkomunikasi dengan proses `Worker/Task`. + -User process tidak dapat menggunakan interface `Server->task/taskwait`. + -User process dapat menggunakan interface `Server->send/close`. + -User process harus memiliki perulangan `while(true)` (seperti contoh di bawah) atau perulangan [EventLoop](/learn?id=apa-itu-eventloop) (misalnya membuat timer), jika tidak user process akan terus keluar dan restart. + -Harus menggunakan `addProcess` sebelum `Server->start()` dijalankan, jika tidak user process tidak akan berjalan. + + * **Siklus Hidup** + + ?> -Siklus hidup user process sama dengan `Master` dan [Manager](/learn?id=proses-manager), tidak terpengaruh oleh [reload](/server/methods?id=reload). + -User process tidak dikendalikan oleh perintah `reload`, saat reload tidak ada informasi yang dikirim ke user process. + -Saat shutdown server, sinyal `SIGTERM` akan dikirim ke user process untuk menutupnya. + -Proses kustom akan dikelola oleh proses `Manager`. Jika terjadi fatal error, proses `Manager` akan membuat ulang. + -Proses kustom juga tidak memicu event seperti `onWorkerStop`. + + * **Contoh** + + ```php + $server = new Swoole\Server('127.0.0.1', 9501); + + /** + * User process mengimplementasikan fungsi broadcast, menerima pesan unixSocket secara loop, dan mengirim ke semua koneksi server + */ + $process = new Swoole\Process(function ($process) use ($server) { + $socket = $process->exportSocket(); + while (true) { + $msg = $socket->recv(); + foreach ($server->connections as $conn) { + $server->send($conn, $msg); + } + } + }, false, 2, 1); + + $server->addProcess($process); + + $server->on('receive', function ($serv, $fd, $reactor_id, $data) use ($process) { + // Broadcast pesan yang diterima + $socket = $process->exportSocket(); + $socket->send($data); + }); + + $server->start(); + ``` + + Lihat bagian [Komunikasi Antar Proses](/process/process?id=exportsocket). + +## start() + +Memulai server, mendengarkan semua port `TCP/UDP`. + +```php +Swoole\Server->start(): bool +``` + +!> Tips: berikut contoh berdasarkan mode [SWOOLE_PROCESS](/learn?id=swoole_process) + + * **Tips** + + - Setelah berhasil mulai, akan membuat `worker_num+2` proses: proses `Master` + proses `Manager` + `serv->worker_num` proses `Worker`. + - Jika gagal mulai, akan langsung mengembalikan `false`. + - Setelah berhasil mulai, akan masuk ke event loop, menunggu permintaan koneksi client. Kode setelah method `start` tidak akan dijalankan. + - Saat server ditutup, fungsi `start` mengembalikan `true` dan melanjutkan eksekusi ke bawah. + - Menyetel `task_worker_num` akan menambah jumlah [Task proses](/learn?id=proses-taskworker) yang sesuai. + - Method sebelum `start` dalam daftar method hanya dapat digunakan sebelum `start` dipanggil. Method setelah `start` hanya dapat digunakan dalam fungsi callback event seperti [onWorkerStart](/server/events?id=onworkerstart), [onReceive](/server/events?id=onreceive), dll. + + * **Ekstensi** + + * Master Process + + * Dalam proses master terdapat beberapa thread [Reactor](/learn?id=thread-reactor), melakukan polling event jaringan berbasis `epoll/kqueue/select`. Setelah menerima data, meneruskannya ke proses `Worker` untuk diproses. + + * Manager Process + + * Mengelola semua proses `Worker`, secara otomatis mendaur ulang proses `Worker` yang siklus hidupnya berakhir atau mengalami error, dan membuat proses `Worker` baru. + + * Worker Process + + * Memproses data yang diterima, termasuk mengurai protokol dan merespons permintaan. Jika `worker_num` tidak diatur, level bawah akan menjalankan proses `Worker` sebanyak jumlah `CPU`. + * Jika gagal mulai, ekstensi akan melempar fatal error, periksa informasi terkait di `php error_log`. `errno={number}` adalah `Linux Errno` standar, lihat dokumentasi terkait. + * Jika pengaturan `log_file` diaktifkan, informasi akan dicetak ke file `Log` yang ditentukan. + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Kesalahan Umum Saat Gagal Mulai** + + * Gagal `bind` port, karena proses lain sudah menggunakan port tersebut. + * Fungsi callback wajib belum diatur, menyebabkan kegagalan mulai. + * Kode `PHP` mengandung fatal error, periksa informasi error PHP `php_errors.log`. + * Jalankan `ulimit -c unlimited`, aktifkan `core dump`, periksa apakah ada segment fault. + * Matikan `daemonize`, matikan `log`, agar pesan error dapat dicetak ke layar. + +## reload() + +Restart semua proses Worker/Task dengan aman. + +```php +Swoole\Server->reload(bool $only_reload_taskworker = false): bool +``` + +!> Contoh: server backend yang sibuk setiap saat memproses permintaan. Jika administrator menghentikan/restart program server melalui proses `kill`, dapat menyebabkan kode berhenti di tengah eksekusi. +Dalam situasi ini dapat terjadi inkonsistensi data. Dalam sistem transaksi, misalnya logika pembayaran dilanjutkan dengan pengiriman barang, jika proses dihentikan setelah logika pembayaran, menyebabkan pengguna membayar tetapi tidak mendapat barang, konsekuensinya sangat serius. +`Swoole` menyediakan mekanisme penghentian/restart yang aman. Administrator hanya perlu mengirim sinyal tertentu ke `Server`, proses `Worker` dapat berhenti dengan aman. Lihat [Cara merestart service dengan benar](/question/use?id=cara-merestart-service-dengan-benar-di-swoole). + + * **Parameter** + + * `bool $only_reload_taskworker` + + * Fungsi: hanya restart [Task proses](/learn?id=proses-taskworker) + * Default: false + * Nilai lain: true + +!> -`reload` memiliki mekanisme proteksi. Saat `reload` sedang berlangsung, sinyal restart baru yang diterima akan diabaikan. +-Jika `user/group` diatur, proses `Worker` mungkin tidak memiliki izin untuk mengirim informasi ke proses `master`. Dalam kasus ini, harus menggunakan akun `root` dan menjalankan perintah `kill` di `shell` untuk restart. +-Perintah `reload` tidak berlaku untuk user process yang ditambahkan melalui [addProcess](/server/methods?id=addProcess). + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Ekstensi** + + * **Mengirim Sinyal** + + * `SIGTERM`: Kirim sinyal ini ke proses utama/proses manajemen untuk menghentikan server dengan aman. + * Dalam kode PHP, panggil `$serv->shutdown()` untuk menyelesaikan operasi ini. + * `SIGUSR1`: Kirim sinyal `SIGUSR1` ke proses utama/proses manajemen untuk `restart` semua proses `Worker` dan `TaskWorker` secara lancar. + * `SIGUSR2`: Kirim sinyal `SIGUSR2` ke proses utama/proses manajemen untuk merestart semua proses `Task` secara lancar. + * Dalam kode PHP, panggil `$serv->reload()` untuk menyelesaikan operasi ini. + + ```shell + # Restart semua worker process + kill -USR1 PID_proses_utama + + # Hanya restart task process + kill -USR2 PID_proses_utama + ``` + + > [Referensi: Daftar Sinyal Linux](/other/signal) + + * **Mode Process** + + Dalam mode `Process`, koneksi `TCP` dari client dipertahankan dalam proses `Master`. Restart atau keluar error dari proses `worker` tidak memengaruhi koneksi itu sendiri. + + * **Mode Base** + + Dalam mode `Base`, koneksi client dipertahankan langsung dalam proses `Worker`, sehingga reload akan memutus semua koneksi. + + !> Mode `Base` tidak mendukung reload [Task proses](/learn?id=proses-taskworker) + + * **Lingkup Reload** + + Operasi `Reload` hanya dapat memuat ulang file PHP yang dimuat oleh proses `Worker` setelah mulai. Gunakan fungsi `get_included_files` untuk mendaftar file PHP mana yang dimuat sebelum `WorkerStart`. File PHP dalam daftar ini tidak dapat dimuat ulang meskipun dilakukan `reload`. Perlu menutup server dan restart agar perubahan berlaku. + + ```php + $serv->on('WorkerStart', function(Swoole\Server $server, int $workerId) { + var_dump(get_included_files()); // File dalam array ini dimuat sebelum proses mulai, jadi tidak bisa di-reload + }); + ``` + + * **APC/OPcache** + + Jika `PHP` mengaktifkan `APC/OPcache`, reload akan terpengaruh. Ada `2` solusi: + + * Aktifkan deteksi `stat` pada `APC/OPcache`. Jika file diperbarui, `APC/OPcache` akan otomatis memperbarui `OPCode`. + * Jalankan `apc_clear_cache` atau `opcache_reset` sebelum memuat file di `onWorkerStart` untuk menyegarkan cache `OPCode`. + + * **Catatan** + + !> -Restart lancar hanya berlaku untuk file PHP yang di-`include/require` dalam [onWorkerStart](/server/events?id=onworkerstart) atau [onReceive](/server/events?id=onreceive) dalam proses `Worker`. + -File PHP yang sudah di-`include/require` sebelum `Server` mulai tidak dapat dimuat ulang melalui restart lancar. + -Untuk konfigurasi `Server` yang diatur melalui `$serv->set()`, harus menutup/restart seluruh `Server` untuk memuat ulang. + -`Server` dapat mendengarkan port internal, lalu menerima perintah kontrol jarak jauh untuk merestart semua proses `Worker`. + +## stop() + +Menghentikan proses `Worker` saat ini dan segera memicu fungsi callback `onWorkerStop`. + +```php +Swoole\Server->stop(int $workerId = -1, bool $waitEvent = false): bool +``` + + * **Parameter** + + * `int $workerId` + + * Fungsi: menentukan `worker id` + * Default: -1, berarti proses saat ini + * Nilai lain: tidak ada + + * `bool $waitEvent` + + * Fungsi: mengontrol strategi keluar, `false` berarti keluar segera, `true` berarti menunggu event loop kosong baru keluar + * Default: false + * Nilai lain: true + * Setelah versi `v6.1.0`, parameter ini telah dihapus, logika keluar dikendalikan oleh opsi `reload_async` + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + !> -Server [I/O asynchronous](/learn?id=sync-io-async-io) saat memanggil `stop` untuk keluar dari proses, mungkin masih ada event yang menunggu. Misalnya menggunakan `Swoole\MySQL->query`, mengirim pernyataan `SQL`, tetapi masih menunggu hasil dari server MySQL. Jika proses dipaksa keluar, hasil eksekusi `SQL` akan hilang. + -Mengatur `$waitEvent = true` akan menggunakan strategi [restart aman asynchronous](/question/use?id=cara-merestart-service-dengan-benar-di-swoole). Pertama memberi tahu proses `Manager` untuk memulai `Worker` baru guna menangani permintaan baru. `Worker` lama akan menunggu event hingga event loop kosong atau melebihi `max_wait_time`, lalu keluar, memaksimalkan keamanan event asynchronous. + +## shutdown() + +Menutup server. + +```php +Swoole\Server->shutdown(): bool +``` + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + * Fungsi ini dapat digunakan dalam proses `Worker`. + * Mengirim `SIGTERM` ke proses utama juga dapat menutup server. + +```shell +kill -15 PID_proses_utama +``` + +## tick() + +Menambahkan timer `tick` dengan fungsi callback kustom. Fungsi ini adalah alias dari [Swoole\Timer::tick](/timer?id=tick). + +```php +Swoole\Server->tick(int $millisecond, callable $callback): void +``` + + * **Parameter** + + * `int $millisecond` + + * Fungsi: interval waktu [milidetik] + * Default: tidak ada + * Nilai lain: tidak ada + + * `callable $callback` + + * Fungsi: fungsi callback + * Default: tidak ada + * Nilai lain: tidak ada + + * **Catatan** + + !> -Setelah proses `Worker` berhenti, semua timer akan otomatis dihapus + -Timer `tick/after` tidak dapat digunakan sebelum `Server->start` + -Setelah `Swoole 5`, penggunaan alias ini telah dihapus; gunakan langsung `Swoole\Timer::tick()` + + * **Contoh** + + * Penggunaan dalam [onReceive](/server/events?id=onreceive) + + ```php + function onReceive(Swoole\Server $server, int $fd, int $reactorId, mixed $data) + { + $server->tick(1000, function () use ($server, $fd) { + $server->send($fd, "hello world"); + }); + } + ``` + + * Penggunaan dalam [onWorkerStart](/server/events?id=onworkerstart) + + ```php + function onWorkerStart(Swoole\Server $server, int $workerId) + { + if (!$server->taskworker) { + $server->tick(1000, function ($id) { + var_dump($id); + }); + } else { + //task + $server->tick(1000); + } + } + ``` + +## after() + +Menambahkan timer satu kali yang akan dihapus setelah dijalankan. Fungsi ini adalah alias dari [Swoole\Timer::after](/timer?id=after). + +```php +Swoole\Server->after(int $millisecond, callable $callback) +``` + + * **Parameter** + + * `int $millisecond` + + * Fungsi: waktu eksekusi [milidetik] + * Default: tidak ada + * Nilai lain: tidak ada + * Versi: maksimum tidak boleh melebihi `86400000` di versi di bawah `Swoole v4.2.10` + + * `callable $callback` + + * Fungsi: fungsi callback, harus dapat dipanggil, fungsi `callback` tidak menerima parameter apapun + * Default: tidak ada + * Nilai lain: tidak ada + + * **Catatan** + + !> -Siklus hidup timer adalah level proses. Saat menggunakan `reload` atau `kill` untuk merestart/mematikan proses, semua timer akan dihapus + -Jika ada timer dengan logika dan data penting, implementasikan dalam fungsi callback `onWorkerStop`, atau lihat [Cara merestart service dengan benar](/question/use?id=cara-merestart-service-dengan-benar-di-swoole) + -Setelah `Swoole 5`, penggunaan alias ini telah dihapus; gunakan langsung `Swoole\Timer::after()` + +## defer() + +Menunda eksekusi suatu fungsi, alias dari [Swoole\Event::defer](/event?id=defer). + +```php +Swoole\Server->defer(Callable $callback): void +``` + + * **Parameter** + + * `Callable $callback` + + * Fungsi: fungsi callback [wajib], dapat berupa variabel fungsi yang dapat dieksekusi, string, array, atau fungsi anonim + * Default: tidak ada + * Nilai lain: tidak ada + + * **Catatan** + + !> -Sistem level bawah akan menjalankan fungsi ini setelah [EventLoop](/learn?id=apa-itu-eventloop) selesai. Tujuan fungsi ini adalah untuk menunda eksekusi beberapa kode PHP, sehingga program dapat memprioritaskan penanganan event `IO` lainnya. Misalnya, jika suatu fungsi callback membutuhkan komputasi CPU intensif tetapi tidak mendesak, biarkan proses menangani event lain terlebih dahulu baru melakukan komputasi CPU intensif + -Sistem tidak menjamin fungsi `defer` akan segera dijalankan. Jika ini adalah logika kritis sistem yang perlu segera dijalankan, gunakan timer `after` + -Saat menjalankan `defer` dalam callback `onWorkerStart`, harus menunggu event terjadi baru akan dipanggil + -Setelah `Swoole 5`, penggunaan alias ini telah dihapus; gunakan langsung `Swoole\Event::defer()` + + * **Contoh** + +```php +function query($server, $db) { + $server->defer(function() use ($db) { + $db->close(); + }); +} +``` + +## clearTimer() + +Menghapus timer `tick/after`, fungsi ini adalah alias dari [Swoole\Timer::clear](/timer?id=clear). + +```php +Swoole\Server->clearTimer(int $timerId): bool +``` + + * **Parameter** + + * `int $timerId` + + * Fungsi: menentukan id timer + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Catatan** + + !> -`clearTimer` hanya dapat digunakan untuk menghapus timer di proses saat ini + -Setelah `Swoole 5`, penggunaan alias ini telah dihapus; gunakan langsung `Swoole\Timer::clear()` + + * **Contoh** + +```php +$timerId = $server->tick(1000, function ($timerId) use ($server) { + $server->clearTimer($timerId);//$id adalah id timer +}); +``` + +## close() + +Menutup koneksi client. + +```php +Swoole\Server->close(int $fd, bool $reset = false): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan `fd` (file descriptor) yang akan ditutup + * Default: tidak ada + * Nilai lain: tidak ada + + * `bool $reset` + + * Fungsi: jika diatur ke `true`, akan memaksa menutup koneksi dan membuang data dalam antrian pengiriman + * Default: false + * Nilai lain: true + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Catatan** + + !> -`Server` yang menutup koneksi secara aktif juga akan memicu event [onClose](/server/events?id=onclose) + -Jangan menulis logika pembersihan setelah `close`. Letakkan dalam callback [onClose](/server/events?id=onclose) + -`fd` dari `HTTP\Server` dapat diperoleh melalui `response` dalam method callback tingkat atas + + * **Contoh** + +```php +$server->on('request', function ($request, $response) use ($server) { + $server->close($response->fd); +}); +``` + +## send() + +Mengirim data ke client. + +```php +Swoole\Server->send(int|string $fd, string $data, int $serverSocket = -1): bool +``` + + * **Parameter** + + * `int|string $fd` + + * Fungsi: menentukan file descriptor client atau path unix socket + * Default: tidak ada + * Nilai lain: tidak ada + + * `string $data` + + * Fungsi: data yang dikirim, untuk protokol `TCP` maksimum tidak boleh melebihi `2M`, dapat diubah dengan [buffer_output_size](/server/setting?id=buffer_output_size) + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $serverSocket` + + * Fungsi: diperlukan saat mengirim data ke [UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php), client TCP tidak perlu mengisi + * Default: -1, berarti port udp yang sedang didengarkan + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + !> Proses pengiriman bersifat asynchronous; level bawah akan otomatis memantau penulisan dan mengirim data ke client secara bertahap, artinya client tidak langsung menerima data setelah `send` kembali. + + * Keamanan + * Operasi `send` bersifat atomik. Beberapa proses secara bersamaan memanggil `send` ke koneksi `TCP` yang sama tidak akan menyebabkan pencampuran data. + + * Batasan Panjang + * Jika perlu mengirim data lebih dari `2M`, tulis data ke file sementara, lalu kirim melalui interface `sendfile`. + * Dengan mengatur parameter [buffer_output_size](/server/setting?id=buffer_output_size) dapat mengubah batasan panjang pengiriman. + * Saat mengirim data lebih dari `8K`, level bawah akan mengaktifkan memori bersama proses `Worker`, perlu melakukan operasi `Mutex->lock`. + + * Buffer + * Saat buffer [unixSocket](/learn?id=apa-itu-ipc) proses `Worker` penuh, pengiriman data `8K` akan menggunakan penyimpanan file sementara. + * Jika terus mengirim data dalam jumlah besar ke client yang sama dan client tidak sempat menerima, buffer memori `Socket` akan penuh. Swoole level bawah akan segera mengembalikan `false`. Saat `false`, data dapat disimpan ke disk dan dikirim setelah client selesai menerima data yang sudah dikirim. + + * [Penjadwalan Coroutine](/coroutine?id=penjadwalan-coroutine) + * Dalam mode coroutine dengan [send_yield](/server/setting?id=send_yield) diaktifkan, `send` akan otomatis tertunda saat buffer penuh. Coroutine akan dilanjutkan setelah sebagian data dibaca oleh lawan, dan terus mengirim data. + + * [UnixSocket](/learn?id=apa-itu-ipc) + * Saat mendengarkan port [UnixSocket DGRAM](https://github.com/swoole/swoole-src/blob/master/examples/unixsock/dgram_server.php), dapat menggunakan `send` untuk mengirim data ke lawan. + + ```php + $server->on("packet", function (Swoole\Server $server, $data, $addr){ + $server->send($addr['address'], 'SUCCESS', $addr['server_socket']); + }); + ``` + +## sendfile() + +Mengirim file ke koneksi client `TCP`. + +```php +Swoole\Server->sendfile(int $fd, string $filename, int $offset = 0, int $length = 0): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan file descriptor client + * Default: tidak ada + * Nilai lain: tidak ada + + * `string $filename` + + * Fungsi: path file yang akan dikirim, mengembalikan `false` jika file tidak ada + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $offset` + + * Fungsi: menentukan offset file, dapat mengirim data mulai dari posisi tertentu dalam file + * Default: 0 [Default `0`, berarti mulai dari awal file] + * Nilai lain: tidak ada + + * `int $length` + + * Fungsi: menentukan panjang yang akan dikirim + * Default: ukuran file + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Catatan** + + !> Fungsi ini dan `Server->send` sama-sama mengirim data ke client, bedanya data `sendfile` berasal dari file yang ditentukan. + +## sendto() + +Mengirim paket `UDP` ke `IP:PORT` client mana pun. + +```php +Swoole\Server->sendto(string $ip, int $port, string $data, int $serverSocket = -1): bool +``` + + * **Parameter** + + * `string $ip` + + * Fungsi: menentukan `ip` client + * Default: tidak ada + * Nilai lain: tidak ada + + ?> `$ip` adalah string `IPv4` atau `IPv6`, misalnya `192.168.1.102`. Jika `IP` tidak valid, akan mengembalikan error. + + * `int $port` + + * Fungsi: menentukan `port` client + * Default: tidak ada + * Nilai lain: tidak ada + + ?> `$port` adalah nomor port jaringan `1-65535`, jika port salah, pengiriman akan gagal. + + * `string $data` + + * Fungsi: konten data yang akan dikirim, dapat berupa teks atau biner + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $serverSocket` + + * Fungsi: menentukan port mana yang digunakan untuk mengirim paket data, deskriptor `server_socket` [dapat diperoleh di `$clientInfo` pada event [onPacket](/server/events?id=onpacket)] + * Default: -1, berarti port udp yang sedang didengarkan + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + ?> Server mungkin mendengarkan beberapa port `UDP` secara bersamaan, lihat [Multi-port Listening](/server/port). Parameter ini dapat menentukan port mana yang digunakan untuk mengirim paket data. + + * **Catatan** + + !> Harus mendengarkan port `UDP` untuk dapat mengirim data ke alamat `IPv4` + Harus mendengarkan port `UDP6` untuk dapat mengirim data ke alamat `IPv6` + + * **Contoh** + +```php +// Mengirim string "hello world" ke host dengan IP 220.181.57.216 port 9502. +$server->sendto('220.181.57.216', 9502, "hello world"); +// Mengirim paket UDP ke server IPv6 +$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world"); +``` + +## sendwait() + +Mengirim data ke client secara sinkron. + +```php +Swoole\Server->sendwait(int $fd, string $data): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan file descriptor client + * Default: tidak ada + * Nilai lain: tidak ada + + * `string $data` + + * Fungsi: data yang akan dikirim + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + * Dalam beberapa skenario khusus, `Server` perlu terus mengirim data ke client, sedangkan interface pengiriman data `Server->send` murni asynchronous. Pengiriman data dalam jumlah besar dapat menyebabkan antrian pengiriman memori penuh. + + * Menggunakan `Server->sendwait` dapat mengatasi masalah ini. `Server->sendwait` akan menunggu koneksi dapat ditulis. Hanya akan kembali setelah data selesai dikirim. + + * **Catatan** + + !> `sendwait` saat ini hanya tersedia untuk mode [SWOOLE_BASE](/learn?id=swoole_base) + `sendwait` hanya digunakan untuk komunikasi lokal atau internal. Jangan gunakan `sendwait` untuk koneksi eksternal, dan saat `enable_coroutine` => true (default aktif) jangan gunakan fungsi ini karena akan memblokir coroutine lain. Hanya server blocking sinkron yang dapat menggunakannya. + +## sendMessage() + +Mengirim pesan ke proses `Worker` mana pun atau [Task proses](/learn?id=proses-taskworker). Dapat dipanggil di non-main process dan management process. Proses yang menerima pesan akan memicu event `onPipeMessage`. + +```php +Swoole\Server->sendMessage(mixed $message, int $workerId): bool +``` + + * **Parameter** + + * `mixed $message` + + * Fungsi: konten data pesan yang akan dikirim, tanpa batasan panjang, tetapi jika melebihi `8K` akan menggunakan file memori sementara + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $workerId` + + * Fungsi: `ID` proses target, rentang lihat [$worker_id](/server/properties?id=worker_id) + * Default: tidak ada + * Nilai lain: tidak ada + + * **Tips** + + * Memanggil `sendMessage` dalam proses `Worker` bersifat [I/O asynchronous](/learn?id=sync-io-async-io); pesan akan disimpan di buffer terlebih dahulu dan dikirim ke [unixSocket](/learn?id=apa-itu-ipc) saat bisa ditulis. + * Memanggil `sendMessage` dalam [Task proses](/learn?id=proses-taskworker) secara default bersifat [I/O sinkron](/learn?id=sync-io-async-io), tetapi dalam beberapa kasus dapat otomatis beralih ke I/O asynchronous, lihat [Mengubah I/O Sinkron menjadi I/O Asynchronous](/learn?id=sync-io-converted-to-async-io). + * Memanggil `sendMessage` dalam [User process](/server/methods?id=addprocess) sama seperti Task, defaultnya sinkron dan blocking, lihat [Mengubah I/O Sinkron menjadi I/O Asynchronous](/learn?id=sync-io-converted-to-async-io). + + * **Catatan** + + !> - Jika `sendMessage()` bersifat [I/O asynchronous](/learn?id=sync-io-converted-to-async-io), jangan terus memanggil `sendMessage()` jika proses lawan tidak menerima data karena berbagai alasan, karena akan menghabiskan banyak sumber daya memori. Dapat menambahkan mekanisme respons, jika lawan tidak merespons, hentikan pemanggilan. + -Di `MacOS/FreeBSD`, jika melebihi `2K` akan menggunakan penyimpanan file sementara. + -Menggunakan [sendMessage](/server/methods?id=sendMessage) harus mendaftarkan fungsi callback event `onPipeMessage`. + -Mengatur [task_ipc_mode](/server/setting?id=task_ipc_mode) = 3 akan mencegah penggunaan [sendMessage](/server/methods?id=sendMessage) untuk mengirim pesan ke task process tertentu. + + * **Contoh** + +```php +$server = new Swoole\Server('0.0.0.0', 9501); + +$server->set(array( + 'worker_num' => 2, + 'task_worker_num' => 2, +)); +$server->on('pipeMessage', function ($server, $src_worker_id, $data) { + echo "#{$server->worker_id} message from #$src_worker_id: $data\n"; +}); +$server->on('task', function ($server, $task_id, $src_worker_id, $data) { + var_dump($task_id, $src_worker_id, $data); +}); +$server->on('finish', function ($server, $task_id, $data) { + +}); +$server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + if (trim($data) == 'task') { + $server->task("async task coming"); + } else { + $worker_id = 1 - $server->worker_id; + $server->sendMessage("hello task process", $worker_id); + } +}); + +$server->start(); +``` + +## exist() + +Memeriksa apakah koneksi yang sesuai dengan `fd` ada. + +```php +Swoole\Server->exist(int $fd): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: file descriptor + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika ada, `false` jika tidak ada. + + * **Tips** + + * Interface ini berbasis komputasi memori bersama, tanpa operasi `IO` apapun. + +## pause() + +Berhenti menerima data. + +```php +Swoole\Server->pause(int $fd): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan file descriptor + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + * Setelah memanggil fungsi ini, koneksi akan dihapus dari [EventLoop](/learn?id=apa-itu-eventloop) dan tidak lagi menerima data client. + * Fungsi ini tidak memengaruhi pemrosesan antrian pengiriman. + * Hanya dalam mode `SWOOLE_PROCESS`, setelah memanggil `pause`, mungkin masih ada data yang sudah sampai di proses `Worker`, sehingga masih dapat memicu event [onReceive](/server/events?id=onreceive). + +## resume() + +Melanjutkan penerimaan data. Digunakan berpasangan dengan method `pause`. + +```php +Swoole\Server->resume(int $fd): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan file descriptor + * Default: tidak ada + * Nilai lain: tidak ada + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + * Setelah memanggil fungsi ini, koneksi akan ditambahkan kembali ke [EventLoop](/learn?id=apa-itu-eventloop) dan terus menerima data client. + +## getCallback() + +Mendapatkan fungsi callback untuk event tertentu pada Server. + +```php +Swoole\Server->getCallback(string $event_name): \Closure|string|null|array +``` + + * **Parameter** + + * `string $event_name` + + * Fungsi: nama event, tidak perlu menambahkan `on`, tidak case-sensitive + * Default: tidak ada + * Nilai lain: Lihat [Event](/server/events) + + * **Return Value** + + * Jika fungsi callback yang sesuai ada, mengembalikan `Closure` / `string` / `array` berdasarkan [cara pengaturan fungsi callback](/learn?id=empat-cara-mengatur-fungsi-callback). + * Jika fungsi callback yang sesuai tidak ada, mengembalikan `null`. + +## getClientInfo() + +Mendapatkan informasi koneksi, alias `Swoole\Server->connection_info()`. + +```php +Swoole\Server->getClientInfo(int $fd, int $reactorId = -1, bool $ignoreError = false): false|array +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan file descriptor + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $reactorId` + + * Fungsi: `ID` thread [Reactor](/learn?id=thread-reactor) tempat koneksi berada, saat ini tidak berpengaruh, hanya untuk kompatibilitas API + * Default: -1 + * Nilai lain: tidak ada + + * `bool $ignoreError` + + * Fungsi: apakah mengabaikan error. Jika diatur ke `true`, akan mengembalikan informasi koneksi meskipun koneksi ditutup. `false` berarti mengembalikan false saat koneksi ditutup. + * Default: false + * Nilai lain: tidak ada + + * **Tips** + + * Sertifikat client + + * Hanya dapat diperoleh dalam proses yang dipicu oleh [onConnect](/server/events?id=onconnect) + * Formatnya `x509`, informasi sertifikat dapat diperoleh menggunakan fungsi `openssl_x509_parse`. + + * Saat menggunakan konfigurasi [dispatch_mode](/server/setting?id=dispatch_mode) = 1/3, karena strategi distribusi paket data ini digunakan untuk layanan tanpa status, informasi terkait akan langsung dihapus dari memori setelah koneksi terputus, sehingga `Server->getClientInfo` tidak dapat memperoleh informasi koneksi terkait. + + * **Return Value** + + * Mengembalikan `false` jika gagal. + * Mengembalikan `array` berisi informasi client jika berhasil. + +```php +$fd_info = $server->getClientInfo($fd); +var_dump($fd_info); + +array(15) { + ["server_port"]=> + int(9501) + ["server_fd"]=> + int(4) + ["socket_fd"]=> + int(25) + ["socket_type"]=> + int(1) + ["remote_port"]=> + int(39136) + ["remote_ip"]=> + string(9) "127.0.0.1" + ["reactor_id"]=> + int(1) + ["connect_time"]=> + int(1677322106) + ["last_time"]=> + int(1677322106) + ["last_recv_time"]=> + float(1677322106.901918) + ["last_send_time"]=> + float(0) + ["last_dispatch_time"]=> + float(0) + ["close_errno"]=> + int(0) + ["recv_queued_bytes"]=> + int(78) + ["send_queued_bytes"]=> + int(0) +} +``` + +Parameter | Keterangan +---|--- +server_port | Port listening server +server_fd | Server fd +socket_fd | Client fd +socket_type | Tipe socket +remote_port | Port client +remote_ip | IP client +reactor_id | Dari thread Reactor mana +connect_time | Waktu client terhubung ke Server, dalam detik, diatur oleh master process +last_time | Waktu terakhir menerima data, dalam detik, diatur oleh master process +last_recv_time | Waktu terakhir menerima data, dalam detik, diatur oleh master process +last_send_time | Waktu terakhir mengirim data, dalam detik, diatur oleh master process +last_dispatch_time | Waktu worker process menerima data +close_errno | Kode error saat koneksi ditutup. Jika koneksi ditutup secara error, close_errno bernilai non-nol, lihat daftar informasi error Linux +recv_queued_bytes | Jumlah data yang menunggu diproses +send_queued_bytes | Jumlah data yang menunggu dikirim +websocket_status | [Opsional] Status koneksi WebSocket, ditambahkan saat server adalah Swoole\WebSocket\Server +uid | [Opsional] Ditambahkan saat user ID diikat menggunakan bind +ssl_client_cert | [Opsional] Ditambahkan saat enkripsi tunnel SSL digunakan dan client mengatur sertifikat + +## getClientList() + +Melintasi semua koneksi client dari `Server` saat ini. Method `Server::getClientList` berbasis memori bersama, tanpa `IOWait`, sehingga kecepatan lintasnya sangat cepat. Selain itu, `getClientList` mengembalikan semua koneksi `TCP`, bukan hanya koneksi `TCP` dari proses `Worker` saat ini. Alias `Swoole\Server->connection_list()`. + +```php +Swoole\Server->getClientList(int $start_fd = 0, int $pageSize = 10): false|array +``` + + * **Parameter** + + * `int $start_fd` + + * Fungsi: menentukan `fd` awal + * Default: 0 + * Nilai lain: tidak ada + + * `int $pageSize` + + * Fungsi: jumlah data per halaman, maksimum tidak boleh melebihi `100` + * Default: 10 + * Nilai lain: tidak ada + + * **Return Value** + + * Jika berhasil, mengembalikan array indeks numerik, dengan elemen adalah `$fd` yang diperoleh. Array akan diurutkan dari kecil ke besar. `$fd` terakhir digunakan sebagai `start_fd` baru untuk pengambilan selanjutnya. + * Mengembalikan `false` jika gagal. + + * **Tips** + + * Disarankan menggunakan iterator [Server::$connections](/server/properties?id=connections) untuk melintasi koneksi. + * `getClientList` hanya tersedia untuk client `TCP`. Server `UDP` perlu menyimpan informasi client sendiri. + * Dalam mode [SWOOLE_BASE](/learn?id=swoole_base), hanya dapat memperoleh koneksi dari proses saat ini. + + * **Contoh** + +```php +$start_fd = 0; +while (true) { + $conn_list = $server->getClientList($start_fd, 10); + if ($conn_list === false || count($conn_list) === 0) { + echo "finish\n"; + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + foreach ($conn_list as $fd) { + $server->send($fd, "broadcast"); + } +} +``` + +## bind() + +Mengikat koneksi dengan `UID` yang ditentukan pengguna. Mengatur [dispatch_mode](/server/setting?id=dispatch_mode)=5 akan menggunakan nilai ini untuk alokasi tetap `hash`. Ini memastikan bahwa semua koneksi dari `UID` tertentu akan dialokasikan ke proses `Worker` yang sama. + +```php +Swoole\Server->bind(int $fd, int $uid): bool +``` + + * **Parameter** + + * `int $fd` + + * Fungsi: menentukan `fd` koneksi + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $uid` + + * Fungsi: `UID` yang akan diikat, harus berupa angka bukan `0` + * Default: tidak ada + * Nilai lain: `UID` maksimum tidak boleh melebihi `4294967295`, minimum tidak boleh kurang dari `-2147483648` + + * **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal. + + * **Tips** + + * Dapat menggunakan `$serv->getClientInfo($fd)` untuk melihat nilai `UID` yang diikat pada koneksi. + * Dalam pengaturan default [dispatch_mode](/server/setting?id=dispatch_mode)=2, `Server` akan mendistribusikan data koneksi ke proses `Worker` yang berbeda berdasarkan `socket fd`. Karena `fd` tidak stabil (berubah saat client reconnect setelah disconnect), data client ini dapat dialokasikan ke `Worker` lain. Dengan menggunakan `bind`, distribusi dapat dilakukan berdasarkan `UID` yang ditentukan pengguna. Bahkan jika disconnect dan reconnect, data koneksi `TCP` dengan `UID` yang sama akan dialokasikan ke proses `Worker` yang sama. + + * Masalah Urutan + + * Setelah client terhubung ke server dan mengirim beberapa paket secara berurutan, mungkin ada masalah urutan. Dalam operasi `bind`, paket berikutnya mungkin sudah di-dispatch, dan paket data ini masih akan dialokasikan ke proses saat ini berdasarkan modulo `fd`. Hanya paket data yang diterima setelah `bind` yang akan dialokasikan berdasarkan modulo `UID`. + * Oleh karena itu, jika ingin menggunakan mekanisme `bind`, protokol komunikasi jaringan perlu merancang langkah handshake. Setelah client berhasil terhubung, kirim permintaan handshake terlebih dahulu, lalu client jangan mengirim paket apapun. Setelah server selesai `bind` dan merespons, client baru dapat mengirim permintaan baru. + + * Pengikatan Ulang + + * Dalam beberapa kasus, logika bisnis mungkin memerlukan koneksi pengguna untuk diikat ulang dengan `UID` baru. Dalam hal ini, dapat memutus koneksi, membuat koneksi `TCP` baru dan handshake, lalu mengikat ke `UID` baru. + + * Mengikat `UID` Negatif + + * Jika `UID` yang diikat adalah negatif, akan diubah menjadi `unsigned 32-bit integer` di level bawah. Di lapisan PHP, perlu diubah menjadi `signed 32-bit integer`. Dapat menggunakan: + + ```php + $uid = -10; + $server->bind($fd, $uid); + $bindUid = $server->connection_info($fd)['uid']; + $bindUid = $bindUid >> 31 ? (~($bindUid - 1) & 0xFFFFFFFF) * -1 : $bindUid; + var_dump($bindUid === $uid); + ``` + + * **Catatan** + +!> -Hanya efektif saat mengatur `dispatch_mode=5` +-Saat `UID` belum diikat, distribusi default menggunakan modulo `fd` +-Satu koneksi hanya dapat di-`bind` sekali. Jika sudah terikat `UID`, memanggil `bind` lagi akan mengembalikan `false` + + * **Contoh** + +```php +$serv = new Swoole\Server('0.0.0.0', 9501); + +$serv->fdlist = []; + +$serv->set([ + 'worker_num' => 4, + 'dispatch_mode' => 5, //uid dispatch +]); + +$serv->on('connect', function ($serv, $fd, $reactor_id) { + echo "{$fd} connect, worker:" . $serv->worker_id . PHP_EOL; +}); + +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + $conn = $serv->connection_info($fd); + print_r($conn); + echo "worker_id: " . $serv->worker_id . PHP_EOL; + if (empty($conn['uid'])) { + $uid = $fd + 1; + if ($serv->bind($fd, $uid)) { + $serv->send($fd, "bind {$uid} success"); + } + } else { + if (!isset($serv->fdlist[$fd])) { + $serv->fdlist[$fd] = $conn['uid']; + } + print_r($serv->fdlist); + foreach ($serv->fdlist as $_fd => $uid) { + $serv->send($_fd, "{$fd} say:" . $data); + } + } +}); + +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "{$fd} Close". PHP_EOL; + unset($serv->fdlist[$fd]); +}); + +$serv->start(); +``` + +## stats() + +Mendapatkan informasi seperti jumlah koneksi `TCP` aktif `Server` saat ini, waktu mulai, total jumlah `accept/close` (membangun/menutup koneksi), dll. + +```php +Swoole\Server->stats(): array +``` + + * **Contoh** + +```php +array(25) { + ["start_time"]=> + int(1677310656) + ["connection_num"]=> + int(1) + ["abort_count"]=> + int(0) + ["accept_count"]=> + int(1) + ["close_count"]=> + int(0) + ["worker_num"]=> + int(2) + ["task_worker_num"]=> + int(4) + ["user_worker_num"]=> + int(0) + ["idle_worker_num"]=> + int(1) + ["dispatch_count"]=> + int(1) + ["request_count"]=> + int(0) + ["response_count"]=> + int(1) + ["total_recv_bytes"]=> + int(78) + ["total_send_bytes"]=> + int(165) + ["pipe_packet_msg_id"]=> + int(3) + ["session_round"]=> + int(1) + ["min_fd"]=> + int(4) + ["max_fd"]=> + int(25) + ["worker_request_count"]=> + int(0) + ["worker_response_count"]=> + int(1) + ["worker_dispatch_count"]=> + int(1) + ["task_idle_worker_num"]=> + int(4) + ["tasking_num"]=> + int(0) + ["coroutine_num"]=> + int(1) + ["coroutine_peek_num"]=> + int(1) + ["task_queue_num"]=> + int(1) + ["task_queue_bytes"]=> + int(1) +} +``` + +Parameter | Keterangan +---|--- +start_time | Waktu server mulai +connection_num | Jumlah koneksi saat ini +abort_count | Jumlah koneksi yang ditolak +accept_count | Jumlah koneksi yang diterima +close_count | Jumlah koneksi yang ditutup +worker_num | Jumlah worker process yang berjalan +task_worker_num | Jumlah task worker process yang berjalan [Tersedia sejak `v4.5.7`] +user_worker_num | Jumlah task worker process kustom +idle_worker_num | Jumlah worker process idle +dispatch_count | Jumlah paket yang dikirim Server ke Worker [Tersedia sejak `v4.5.7`, hanya efektif dalam mode [SWOOLE_PROCESS](/learn?id=swoole_process)] +request_count | Jumlah permintaan yang diterima Server [Hanya data request yang ditangani oleh onReceive, onMessage, onRequest, onPacket yang dihitung] +response_count | Jumlah respons yang dikirim Server +total_recv_bytes| Total data yang diterima +total_send_bytes | Total data yang dikirim +pipe_packet_msg_id | ID komunikasi antar proses +session_round | Session id awal +min_fd | File descriptor koneksi terkecil +max_fd | File descriptor koneksi terbesar +worker_request_count | Jumlah permintaan yang diterima Worker process saat ini [Worker process akan keluar jika worker_request_count melebihi max_request] +worker_response_count | Jumlah respons Worker process saat ini +worker_dispatch_count | Hitungan tugas yang dikirim dari master process ke Worker process saat ini, bertambah saat dispatch oleh [master process](/learn?id=thread-reactor) +task_idle_worker_num | Jumlah task process idle +tasking_num | Jumlah task process yang sedang bekerja +coroutine_num | Jumlah coroutine saat ini [Untuk Coroutine], informasi lebih lanjut lihat [bagian ini](/coroutine/gdb) +coroutine_peek_num | Jumlah total coroutine +task_queue_num | Jumlah task dalam antrian pesan [Untuk Task] +task_queue_bytes | Penggunaan memori antrian task dalam byte [Untuk Task] + +## task() + +Mengirim tugas asynchronous ke pool `task_worker`. Fungsi ini non-blocking, akan kembali segera setelah eksekusi. Proses `Worker` dapat terus menangani permintaan baru. Untuk menggunakan fitur `Task`, harus mengatur `task_worker_num` terlebih dahulu, dan harus mengatur fungsi callback event [onTask](/server/events?id=ontask) dan [onFinish](/server/events?id=onfinish) pada `Server`. + +```php +Swoole\Server->task(mixed $data, int $dstWorkerId = -1, callable $finishCallback): int +``` + + * **Parameter** + + * `mixed $data` + + * Fungsi: data tugas yang akan dikirim, harus berupa variabel PHP yang dapat diserialisasi + * Default: tidak ada + * Nilai lain: tidak ada + + * `int $dstWorkerId` + + * Fungsi: dapat menentukan [Task proses](/learn?id=proses-taskworker) tujuan, masukkan `ID` Task proses saja, rentangnya `[0, $server->setting['task_worker_num']-1]` + * Default: -1 [Default `-1` berarti pengiriman acak, level bawah akan otomatis memilih [Task proses](/learn?id=proses-taskworker) yang idle] + * Nilai lain: `[0, $server->setting['task_worker_num']-1]` + + * `callable $finishCallback` + + * Fungsi: fungsi callback `finish`. Jika tugas mengatur fungsi callback, saat Task mengembalikan hasil, fungsi callback yang ditentukan akan langsung dijalankan, dan callback [onFinish](/server/events?id=onfinish) dari `Server` tidak akan dijalankan. Hanya dapat dipicu saat mengirim tugas dari proses `Worker`. + * Default: `null` + * Nilai lain: tidak ada + + * **Return Value** + + * Jika berhasil, mengembalikan integer `$task_id`, yang merupakan `ID` tugas ini. Jika ada callback finish, [onFinish](/server/events?id=onfinish) akan membawa parameter `$task_id`. + * Jika gagal, mengembalikan `false`. `$task_id` mungkin `0`, jadi harus menggunakan `===` untuk memeriksa kegagalan. + + * **Tips** + + * Fitur ini digunakan untuk menjalankan tugas lambat secara asynchronous, misalnya server chat room dapat menggunakannya untuk mengirim broadcast. Saat tugas selesai, panggil `$serv->finish("finish")` dalam [task process](/learn?id=proses-taskworker) untuk memberi tahu worker process bahwa tugas telah selesai. Tentu saja `Swoole\Server->finish` bersifat opsional. + * `task` menggunakan komunikasi [unixSocket](/learn?id=apa-itu-ipc) di level bawah, sepenuhnya berbasis memori, tanpa konsumsi `IO`. Kinerja baca/tulis proses tunggal dapat mencapai `1 juta/s`. Proses yang berbeda menggunakan unixSocket yang berbeda untuk komunikasi, memaksimalkan pemanfaatan multi-core. + * Jika target [Task proses](/learn?id=proses-taskworker) tidak ditentukan, memanggil method `task` akan memeriksa status sibuk/idle [Task proses](/learn?id=proses-taskworker). Level bawah hanya akan mengirim tugas ke [Task proses](/learn?id=proses-taskworker) yang sedang idle. Jika semua [Task proses](/learn?id=proses-taskworker) sibuk, level bawah akan melakukan polling untuk mengirim tugas ke setiap proses. Gunakan method [server->stats](/server/methods?id=stats) untuk mendapatkan jumlah tugas yang sedang mengantri. + * Parameter ketiga dapat langsung mengatur fungsi [onFinish](/server/events?id=onfinish). Jika tugas mengatur fungsi callback, saat Task mengembalikan hasil, fungsi callback yang ditentukan akan langsung dijalankan, dan callback [onFinish](/server/events?id=onfinish) dari `Server` tidak akan dijalankan. Hanya dapat dipicu saat mengirim tugas dari proses `Worker`. + + ```php + $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + ``` + + * `$task_id` adalah integer dari `0-4,2 milyar`, unik dalam proses saat ini. + * Secara default, fitur `task` tidak aktif. Perlu mengatur `task_worker_num` secara manual untuk mengaktifkan fitur ini. + * Jumlah `TaskWorker` diatur dalam parameter [Server->set()](/server/methods?id=set), misalnya `task_worker_num => 64` berarti menjalankan `64` proses untuk menerima tugas asynchronous. + + * **Parameter Konfigurasi** + + * `Server->task/taskwait/finish` `3` method ini, jika data `$data` yang dikirim melebihi `8K` akan menggunakan file sementara untuk menyimpan. Jika konten file sementara melebihi [server->package_max_length](/server/setting?id=package_max_length), level bawah akan memunculkan peringatan. Peringatan ini tidak memengaruhi pengiriman data, tetapi Task yang terlalu besar mungkin memiliki masalah kinerja. + + ```shell + WARN: task package is too big. + ``` + + * **Tugas Satu Arah** + + * Tugas yang dikirim dari proses `Master`, `Manager`, `UserProcess` bersifat satu arah. Dalam `TaskWorker` process, tidak dapat menggunakan `return` atau `Server->finish()` untuk mengembalikan data hasil. + + * **Catatan** + + !> -Method `task` tidak dapat dipanggil dalam [task process](/learn?id=proses-taskworker) + -Menggunakan `task` harus mengatur callback [onTask](/server/events?id=ontask) dan [onFinish](/server/events?id=onfinish) untuk `Server`, jika tidak `Server->start` akan gagal + -Jumlah operasi `task` harus kurang dari kecepatan pemrosesan [onTask](/server/events?id=ontask). Jika kapasitas pengiriman melebihi kapasitas pemrosesan, data `task` akan memenuhi buffer, menyebabkan proses `Worker` terblokir. Proses `Worker` tidak akan dapat menerima permintaan baru + -Dalam user process yang ditambahkan dengan [addProcess](/server/method?id=addProcess), dapat menggunakan `task` untuk mengirim tugas satu arah, tetapi tidak dapat mengembalikan data hasil. Gunakan interface [sendMessage](/server/methods?id=sendMessage) untuk berkomunikasi dengan proses `Worker/Task` + + * **Contoh** + +```php +$server = new Swoole\Server("127.0.0.1", 9501, SWOOLE_BASE); + +$server->set(array( + 'worker_num' => 2, + 'task_worker_num' => 4, +)); + +$server->on('Receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + echo "Menerima data" . $data . "\n"; + $data = trim($data); + $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + $task_id = $server->task($data, 0); + $server->send($fd, "Mendistribusikan tugas, id tugas adalah $task_id\n"); +}); +``` +## taskwait() + +`taskwait` adalah method dengan tujuan yang sama seperti method `task`, digunakan untuk mengirimkan tugas asynchronous ke pool [task worker](/learn?id=taskworker-process) untuk dieksekusi. Tidak seperti `task`, `taskwait` adalah fungsi synchronous, ia menunggu sampai tugas selesai atau timeout. `$result` adalah hasil eksekusi tugas, dikirim oleh fungsi `$server->finish`. Jika tugas timeout, ia akan mengembalikan `false`. + +```php +Swoole\Server->taskwait(mixed $data, float $timeout = 0.5, int $dstWorkerId = -1): mixed +``` + +* **Parameter** + + * `mixed $data` + + * Fungsi: Data tugas yang akan dikirim, bisa berupa tipe apa pun, tipe non-string akan diserialisasi secara otomatis oleh sistem. + * Default: Tidak ada + * Nilai Lain: Tidak ada + + * `float $timeout` + + * Fungsi: Durasi timeout, tipe float, dalam detik, mendukung granularitas minimal `1ms`. Jika tidak ada data yang dikembalikan dari [task worker](/learn?id=taskworker-process) yang ditentukan dalam waktu yang ditentukan, `taskwait` akan mengembalikan `false` dan tidak akan memproses data hasil tugas berikutnya. + * Default: 0.5 + * Nilai Lain: Tidak ada + + * `int $dstWorkerId` + + * Fungsi: Tentukan [task worker](/learn?id=taskworker-process) yang akan menerima tugas. Cukup berikan ID task worker, dalam rentang `[0, $server->setting['task_worker_num']-1]`. + * Default: -1 (Nilai default `-1` berarti kirim secara acak, sistem akan secara otomatis memilih [task worker](/learn?id=taskworker-process) yang sedang tidak sibuk.) + * Nilai Lain: `[0, $server->setting['task_worker_num']-1]` + +* **Return Value** + + * Mengembalikan `false` menunjukkan kegagalan pengiriman tugas. + * Jika method `finish` dijalankan dalam event `onTask` atau melakukan `return`, maka `taskwait` akan mengembalikan hasil yang dikirim oleh event `onTask`. + +* **Tips** + + * **Mode Coroutine** + + * Mulai dari versi `4.0.4`, method `taskwait` akan mendukung [penjadwalan coroutine](/coroutine?id=coroutine-scheduling). Ketika `Server->taskwait()` dipanggil dalam coroutine, ia akan secara otomatis menjalani [penjadwalan coroutine](/coroutine?id=coroutine-scheduling), menghindari blocking wait. + * Memanfaatkan [penjadwal coroutine](/coroutine?id=coroutine-scheduler), `taskwait` dapat mencapai panggilan concurrent. + * Hanya boleh ada satu return atau satu `Server->finish` dalam event `onTask`, jika tidak, peringatan expired task[1] akan dihasilkan setelah return atau `Server->finish` berlebih dijalankan. + + * **Mode Synchronous** + + * Dalam mode blocking synchronous, `taskwait` memerlukan komunikasi [UnixSocket](/learn?id=what-is-ipc) dan shared memory untuk mengembalikan data ke proses `Worker`, dan proses ini bersifat synchronous dan blocking. + + * **Kasus Khusus** + + * Jika tidak ada operasi [synchronous I/O](/learn?id=synchronous-io-asynchronous-io) dalam event [onTask](/server/events?id=ontask), dengan hanya `2` kali perpindahan proses di level bawah, dan tidak ada wait `IO` yang dihasilkan. Dalam kasus ini, `taskwait` dapat dianggap non-blocking. Dalam pengujian aktual, hanya membaca dan menulis array `PHP` dalam event [onTask](/server/events?id=ontask), dengan `100.000` operasi `taskwait` hanya memakan waktu `1` detik, rata-rata `10` mikrodetik per operasi. + +* **Perhatian** + + !> - Jangan gunakan `Swoole\Server::finish` dengan `taskwait`. + - Method `taskwait` tidak dapat dipanggil di [task worker](/learn?id=taskworker-process). +## taskWaitMulti() + +Menjalankan beberapa tugas asynchronous secara concurrent. Method ini tidak mendukung penjadwalan coroutine dan dapat menyebabkan coroutine lain ikut berjalan. Di lingkungan coroutine, Anda perlu menggunakan `taskCo` seperti yang dijelaskan di bawah. + +```php +Swoole\Server->taskWaitMulti(array $tasks, float $timeout = 0.5): false|array +``` + +* **Parameter** + + * `array $tasks` + + * Fungsi: Harus berupa array dengan indeks numerik, tidak mendukung array asosiatif. Sistem akan melakukan iterasi melalui `$tasks` dan mengirimkan setiap tugas ke [Task Worker Process](/learn?id=taskworker-process) satu per satu. + * Default: Tidak ada + * Nilai Lain: Tidak ada + + * `float $timeout` + + * Fungsi: Angka floating point yang menunjukkan waktu dalam detik. + * Default: 0.5 detik + * Nilai Lain: Tidak ada + +* **Return Value** + + * Ketika tugas selesai atau timeout, ia mengembalikan array hasil. Urutan hasil dalam array sesuai dengan urutan tugas dalam `$tasks`, contoh: `$result[2]` sesuai dengan `$tasks[2]`. + * Timeout dari tugas tertentu tidak akan memengaruhi tugas lain. Array hasil tidak akan menyertakan tugas yang timeout. + +* **Catatan** + + !> -Jumlah maksimum tugas concurrent tidak boleh melebihi `1024`. + +* **Contoh** + +```php +$tasks[] = mt_rand(1000, 9999); // Tugas 1 +$tasks[] = mt_rand(1000, 9999); // Tugas 2 +$tasks[] = mt_rand(1000, 9999); // Tugas 3 +var_dump($tasks); + +// Tunggu semua hasil tugas kembali, timeout diatur ke 10 detik +$results = $server->taskWaitMulti($tasks, 10.0); + +if (!isset($results[0])) { + echo "Tugas 1 timeout\n"; +} +if (isset($results[1])) { + echo "Hasil Tugas 2 adalah {$results[1]}\n"; +} +if (isset($results[2])) { + echo "Hasil Tugas 3 adalah {$results[2]}\n"; +} +``` +## taskCo() + +Menjalankan `Task` secara concurrent dan melakukan penjadwalan coroutine, digunakan untuk mendukung fungsionalitas `taskWaitMulti` di lingkungan coroutine. + +```php +Swoole\Server->taskCo(array $tasks, float $timeout = 0.5): false|array +``` + +* `$tasks`: Daftar tugas, harus berupa array. Sistem akan melakukan iterasi melalui array, mengirimkan setiap elemen sebagai `task` ke pool proses `Task`. +* `$timeout`: Waktu timeout, default `0.5` detik. Jika tidak semua tugas selesai dalam waktu yang ditentukan, proses akan segera berhenti dan mengembalikan hasil. +* Setelah tugas selesai atau timeout, mengembalikan array hasil. Urutan setiap hasil tugas dalam array hasil sesuai dengan `$tasks`, contoh: hasil yang sesuai dengan `$tasks[2]` adalah `$result[2]`. +* Jika tugas tertentu gagal atau timeout, item yang sesuai dalam array hasil akan menjadi `false`, contoh: jika `$tasks[2]` gagal, maka nilai `$result[2]` akan menjadi `false`. + +!> Jumlah maksimum tugas concurrent tidak boleh melebihi `1024` + +* **Proses Penjadwalan** + + * Setiap tugas dalam daftar `$tasks` akan dikirim secara acak ke proses `Task` worker. Setelah dikirim, `yield` menangguhkan coroutine saat ini dan mengatur timer untuk `$timeout` detik. + * Di `onFinish`, kumpulkan hasil tugas yang sesuai dan simpan ke array hasil. Periksa apakah semua tugas telah mengembalikan hasil. Jika belum, lanjutkan menunggu. Jika sudah, `resume` untuk melanjutkan eksekusi coroutine yang sesuai dan hapus timer timeout. + * Jika tidak semua tugas selesai dalam waktu yang ditentukan, timer akan terpicu terlebih dahulu, sistem akan menghapus status waiting. Hasil tugas yang belum selesai akan ditandai sebagai `false`, dan coroutine yang sesuai akan segera dilanjutkan. + +* **Contoh** + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9502, SWOOLE_BASE); + +$server->set([ + 'worker_num' => 1, + 'task_worker_num' => 2, +]); + +$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { + echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; + if ($serv->worker_id == 1) { + sleep(1); + } + return $data; +}); + +$server->on('Request', function ($request, $response) use ($server) { + $tasks[0] = "hello world"; + $tasks[1] = ['data' => 1234, 'code' => 200]; + $result = $server->taskCo($tasks, 0.5); + $response->end('Test End, Result: ' . var_export($result, true)); +}); + +$server->start(); +``` +## finish() + +Digunakan dalam proses [Task Worker](/learn?id=taskworker-process) untuk memberi tahu proses `Worker` bahwa tugas yang dikirim telah selesai. Fungsi ini dapat mengirimkan data hasil ke proses `Worker`. + +```php +Swoole\Server->finish(mixed $data): bool +``` + +* **Parameter** + + * `mixed $data` + + * Fungsi: Konten hasil dari pemrosesan tugas + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Return Value** + + * Mengembalikan `true` jika berhasil, `false` jika gagal + +* **Tips** + * Method `finish` dapat dipanggil beberapa kali secara berurutan, memicu event [onFinish](/server/events?id=onfinish) beberapa kali di proses `Worker` + * Setelah memanggil method `finish` dalam fungsi callback [onTask](/server/events?id=ontask), data `return` tetap akan memicu event [onFinish](/server/events?id=onfinish) + * `Server->finish` bersifat opsional. Jika proses `Worker` tidak perlu peduli dengan hasil eksekusi tugas, fungsi ini tidak perlu dipanggil + * Mengembalikan string dalam fungsi callback [onTask](/server/events?id=ontask) sama dengan memanggil `finish` + +* **Catatan** + + !> Saat menggunakan fungsi `Server->finish`, fungsi callback [onFinish](/server/events?id=onfinish) harus diatur untuk `Server`. Fungsi ini hanya dapat digunakan dalam proses [Task Worker](/learn?id=taskworker-process) di callback [onTask](/server/events?id=ontask). +## heartbeat() + +Berbeda dengan deteksi pasif [heartbeat_check_interval](/server/setting?id=heartbeat_check_interval), method ini secara aktif memeriksa semua koneksi server dan mengidentifikasi koneksi yang telah melebihi waktu yang disepakati. Jika `if_close_connection` ditentukan, ia akan secara otomatis menutup koneksi yang timeout. Jika tidak ditentukan, ia hanya akan mengembalikan array fd koneksi. + +```php +Swoole\Server->heartbeat(bool $ifCloseConnection = true): bool|array +``` + +* **Parameter** + + * `bool $ifCloseConnection` + + * Fungsi: Apakah akan menutup koneksi yang timeout + * Default: true + * Nilai Lain: false + +* **Return Value** + + * Jika berhasil, akan mengembalikan array kontinu yang berisi `$fd` yang ditutup + * Jika tidak berhasil, akan mengembalikan `false` + +* **Contoh** + +```php +$closeFdArrary = $server->heartbeat(); +``` +## getLastError() + +Mendapatkan kode error dari kesalahan operasi terbaru. Dalam kode bisnis, logika yang berbeda dapat dijalankan berdasarkan tipe kode error. + +```php +Swoole\Server->getLastError(): int +``` + +* **Return Value** + +Kode Error | Penjelasan +---|--- +1001 | Koneksi telah ditutup oleh `Server`. Error ini biasanya terjadi ketika kode sudah menjalankan `$server->close()` untuk menutup koneksi tetapi masih memanggil `$server->send()` untuk mengirim data ke koneksi tersebut. +1002 | Koneksi telah ditutup oleh `Client`, dan `Socket` ditutup, tidak dapat mengirim data ke lawan bicara. +1003 | Sedang dalam proses penutupan, `send()` tidak dapat digunakan dalam fungsi callback [onClose](/server/events?id=onclose). +1004 | Koneksi telah ditutup. +1005 | Koneksi tidak ada; `$fd` yang diberikan mungkin salah. +1007 | Menerima data yang timeout. Setelah `TCP` menutup koneksi, beberapa data mungkin tersisa di buffer [unixSocket](/learn?id=apa-itu-ipc), dan data tersebut akan dibuang. +1008 | Buffer pengiriman penuh dan tidak dapat melakukan operasi `send`. Error ini menunjukkan bahwa lawan bicara dari koneksi ini tidak dapat menerima data dengan cepat, menyebabkan buffer pengiriman penuh. +1202 | Data yang dikirim melebihi pengaturan [server->buffer_output_size](/server/setting?id=buffer_output_size). +9007 | Hanya terjadi saat menggunakan [dispatch_mode](/server/setting?id=dispatch_mode)=3, menunjukkan bahwa saat ini tidak ada proses yang tersedia. Anda dapat meningkatkan jumlah proses `worker_num`. +## getSocket() + +Memanggil method ini dapat memperoleh handle `socket` yang mendasarinya, dan objek yang dikembalikan adalah resource handle `sockets`. + +```php +Swoole\Server->getSocket(): false|\Socket +``` + +!> Method ini memerlukan ekstensi `sockets` PHP dan opsi `--enable-sockets` saat mengkompilasi `Swoole`. + +* **Port Listening** + + * Port yang ditambahkan menggunakan method `listen` dapat menggunakan method `getSocket` yang disediakan oleh objek `Swoole\Server\Port`. + + ```php + $port = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); + $socket = $port->getSocket(); + ``` + + * Menggunakan fungsi `socket_set_option` dapat mengatur beberapa parameter `socket` level rendah. + + ```php + $socket = $server->getSocket(); + if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; + } + ``` + +* **Dukungan Multicast** + + * Menggunakan `socket_set_option` untuk mengatur parameter `MCAST_JOIN_GROUP` dapat menggabungkan `Socket` ke multicast dan mendengarkan paket jaringan multicast. + +```php +$server = new Swoole\Server('0.0.0.0', 9905, SWOOLE_BASE, SWOOLE_SOCK_UDP); +$server->set(['worker_num' => 1]); +$socket = $server->getSocket(); + +$ret = socket_set_option( + $socket, + IPPROTO_IP, + MCAST_JOIN_GROUP, + array( + 'group' => '224.10.20.30', // Menunjukkan alamat multicast + 'interface' => 'eth0' // Menunjukkan nama interface jaringan, bisa berupa angka atau string, seperti eth0, wlan0 + ) +); + +if ($ret === false) { + throw new RuntimeException('Unable to join multicast group'); +} + +$server->on('Packet', function (Swoole\Server $server, $data, $addr) { + $server->sendto($addr['address'], $addr['port'], "Swoole: $data"); + var_dump($addr, strlen($data)); +}); + +$server->start(); +``` +## protect() + +Mengatur koneksi klien ke status terlindungi, agar tidak diputus oleh thread heartbeat. + +```php +Swoole\Server->protect(int $fd, bool $is_protected = true): bool +``` + +* **Parameter** + + * `int $fd` + + * Fungsi: menentukan `fd` koneksi klien + * Default: tidak ada + * Nilai Lain: tidak ada + + * `bool $is_protected` + + * Fungsi: status yang akan diatur + * Default: true (menunjukkan status terlindungi) + * Nilai Lain: false (menunjukkan status tidak terlindungi) + +* **Return Value** + + * Mengembalikan `true` menunjukkan operasi berhasil, mengembalikan `false` menunjukkan operasi gagal. +## confirm() + +Konfirmasi koneksi, digunakan bersama dengan [enable_delay_receive](/server/setting?id=enable_delay_receive). Ketika klien membuat koneksi, ia tidak mendengarkan event yang dapat dibaca, hanya memicu callback event [onConnect](/server/events?id=onconnect). Di callback [onConnect](/server/events?id=onconnect), jalankan `confirm` untuk mengonfirmasi koneksi. Pada saat itu, server akan mulai mendengarkan event yang dapat dibaca untuk menerima data dari klien yang terhubung. + +!> Tersedia untuk Swoole versi >= `v4.5.0` + +```php +Swoole\Server->confirm(int $fd): bool +``` + +* **Parameter** + + * `int $fd` + + * Fungsi: Pengidentifikasi unik koneksi + * Default: Tidak ada + * Lainnya: Tidak ada + +* **Return Value** + + * Mengembalikan `true` jika konfirmasi berhasil + * Mengembalikan `false` jika koneksi yang sesuai dengan `$fd` tidak ada, ditutup, atau sudah dalam status mendengarkan; menunjukkan kegagalan konfirmasi + +* **Tujuan** + + Method ini umumnya digunakan untuk melindungi server dari serangan lalu lintas berlebih. Ketika koneksi klien diterima, fungsi [onConnect](/server/events?id=onconnect) dipicu. Ini dapat digunakan untuk memeriksa `IP` sumber dan memutuskan apakah akan mengizinkan pengiriman data ke server. + +* **Contoh** + +```php +// Membuat objek Server, mendengarkan di 127.0.0.1:9501 +$serv = new Swoole\Server("127.0.0.1", 9501); +$serv->set([ + 'enable_delay_receive' => true, +]); + +// Mendengarkan event koneksi +$serv->on('Connect', function ($serv, $fd) { + // Periksa $fd di sini dan konfirmasi jika ok + $serv->confirm($fd); +}); + +// Mendengarkan event penerimaan data +$serv->on('Receive', function ($serv, $fd, $reactor_id, $data) { + $serv->send($fd, "Server: " . $data); +}); + +// Mendengarkan event penutupan koneksi +$serv->on('Close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +// Memulai server +$serv->start(); +``` +## getWorkerId() + +Mendapatkan `id` dari proses `Worker` saat ini (bukan `PID` proses), sesuai dengan `$workerId` di [onWorkerStart](/server/events?id=onworkerstart). + +```php +Swoole\Server->getWorkerId(): int|false +``` + +!> Tersedia untuk Swoole versi >= `v4.5.0RC1` +## getWorkerPid() + +Mendapatkan `PID` dari proses `Worker` yang ditentukan + +```php +Swoole\Server->getWorkerPid(int $worker_id = -1): int|false +``` + +* **Parameter** + + * `int $worker_id` + + * Fungsi: mendapatkan `pid` dari proses yang ditentukan + * Default: -1 (mewakili proses saat ini) + * Nilai Lain: N/A + +!> Tersedia sejak Swoole versi >= `v4.5.0RC1` +## getWorkerStatus() + +Mendapatkan status proses `Worker` + +```php +Swoole\Server->getWorkerStatus(int $worker_id = -1): int|false +``` + +!> Tersedia di Swoole versi >= `v4.5.0RC1` + +* **Parameter** + + * `int $worker_id` + + * Fungsi: Mendapatkan status proses + * Default: -1, [-1 mewakili proses saat ini] + * Nilai Lain: Tidak ada + +* **Return Value** + + * Mengembalikan status proses `Worker`, lihat nilai status proses + * Mengembalikan `false` jika bukan proses `Worker` atau proses tidak ada + +* **Nilai Status Proses** + + Konstanta | Nilai | Deskripsi | Ketergantungan Versi + ---|---|---|--- + SWOOLE_WORKER_BUSY | 1 | Sibuk | v4.5.0RC1 + SWOOLE_WORKER_IDLE | 2 | Diam | v4.5.0RC1 + SWOOLE_WORKER_EXIT | 3 | Dalam kasus di mana [reload_async](/server/setting?id=reload_async) diaktifkan, mungkin ada 2 proses untuk worker_id yang sama, satu baru dan satu lama. Proses lama akan membaca kode status EXIT. | v4.5.5 +## getManagerPid() + +Mendapatkan `PID` dari proses `Manager` untuk layanan saat ini. + +```php +Swoole\Server->getManagerPid(): int +``` + +!> Tersedia di Swoole versi `v4.5.0RC1` atau lebih tinggi +## getMasterPid() + +Mendapatkan `PID` dari proses `Master` layanan saat ini. + +```php +Swoole\Server->getMasterPid(): int +``` + +!> Tersedia di Swoole versi >= `v4.5.0RC1` +## addCommand() + +Menambahkan perintah kustom `command` + +```php +Swoole\Server->addCommand(string $name, int $accepted_process_types, Callable $callback): bool +``` + +!> -Tersedia di Swoole versi >= `v4.8.0` + -Fungsi ini hanya dapat dipanggil sebelum layanan dimulai. Jika sudah ada perintah dengan nama yang sama, akan mengembalikan `false` langsung. + +* **Parameter** + + * `string $name` + + * Fungsi: Nama `command` + * Default: Tidak ada + * Nilai Lain: Tidak ada + + * `int $accepted_process_types` + + * Fungsi: Tipe proses yang menerima permintaan. Jika Anda ingin mendukung beberapa tipe proses, Anda dapat menghubungkannya dengan `|`, contoh, `SWOOLE_SERVER_COMMAND_MASTER | SWOOLE_SERVER_COMMAND_MANAGER` + * Default: Tidak ada + * Nilai Lain: + * `SWOOLE_SERVER_COMMAND_MASTER` proses master + * `SWOOLE_SERVER_COMMAND_MANAGER` proses manager + * `SWOOLE_SERVER_COMMAND_EVENT_WORKER` process worker + * `SWOOLE_SERVER_COMMAND_TASK_WORKER` proses task + + * `callable $callback` + + * Fungsi: Fungsi callback. Memiliki dua parameter, satu adalah kelas `Swoole\Server`, dan yang lainnya adalah variabel yang ditentukan pengguna. Variabel ini dilewatkan melalui parameter keempat dari `Swoole\Server::command()`. + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Return Value** + + * Mengembalikan `true` menunjukkan penambahan perintah kustom berhasil, mengembalikan `false` menunjukkan kegagalan +## command() + +Memanggil perintah kustom `command` yang telah ditentukan + +```php +Swoole\Server->command(string $name, int $process_id, int $process_type, mixed $data, bool $json_decode = true): false|string|array +``` + +!> Tersedia di Swoole versi >= `v4.8.0`. Dalam mode `SWOOLE_PROCESS` dan `SWOOLE_BASE`, fungsi ini hanya dapat digunakan di proses `master`. + +* **Parameter** + + * `string $name` + + * Deskripsi: Nama `command` + * Default: Tidak ada + * Lainnya: Tidak ada + + * `int $process_id` + + * Deskripsi: ID Proses + * Default: Tidak ada + * Lainnya: Tidak ada + + * `int $process_type` + + * Deskripsi: Tipe permintaan proses, hanya salah satu dari nilai berikut yang dapat dipilih. + * Default: Tidak ada + * Nilai Lain: + * `SWOOLE_SERVER_COMMAND_MASTER` proses master + * `SWOOLE_SERVER_COMMAND_MANAGER` proses manager + * `SWOOLE_SERVER_COMMAND_EVENT_WORKER` process worker + * `SWOOLE_SERVER_COMMAND_TASK_WORKER` proses task + + * `mixed $data` + + * Deskripsi: Data permintaan, data ini harus dapat diserialisasi + * Default: Tidak ada + * Lainnya: Tidak ada + + * `bool $json_decode` + + * Deskripsi: Apakah akan mendekode menggunakan `json_decode` + * Default: true + * Nilai Lain: false + +* **Contoh Penggunaan** + ```php + addCommand('test_getpid', SWOOLE_SERVER_COMMAND_MASTER | SWOOLE_SERVER_COMMAND_EVENT_WORKER, + function ($server, $data) { + var_dump($data); + return json_encode(['pid' => posix_getpid()]); + }); + $server->set([ + 'log_file' => '/dev/null', + 'worker_num' => 2, + ]); + + $server->on('start', function (Server $serv) { + $result = $serv->command('test_getpid', 0, SWOOLE_SERVER_COMMAND_MASTER, ['type' => 'master']); + Assert::eq($result['pid'], $serv->getMasterPid()); + $result = $serv->command('test_getpid', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']); + Assert::eq($result['pid'], $serv->getWorkerPid(1)); + $result = $serv->command('test_not_found', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']); + Assert::false($result); + + $serv->shutdown(); + }); + + $server->on('request', function (Request $request, Response $response) { + }); + $server->start(); + ``` diff --git a/public/id/server/packet_class.md b/public/id/server/packet_class.md new file mode 100644 index 0000000..35b912e --- /dev/null +++ b/public/id/server/packet_class.md @@ -0,0 +1,47 @@ +# Swoole\Server\Packet + +Berikut adalah penjelasan detail tentang `Swoole\Server\Packet`. + +## Properti + +### $server_socket +Mengembalikan file descriptor `fd` server. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Packet->server_socket +``` + +### $server_port +Mengembalikan port listening `server_port` server. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Packet->server_port +``` + +### $dispatch_time +Mengembalikan waktu kedatangan data permintaan `dispatch_time`. Properti ini adalah `double`. + +```php +Swoole\Server\Packet->dispatch_time +``` + +### $address +Mengembalikan alamat klien `address`. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\Packet->address +``` + +### $port +Mengembalikan port listening `port` klien. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Packet->port +``` + +### $data +Mengembalikan data `data` yang dikirimkan oleh klien. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\Packet->data +``` diff --git a/public/id/server/pipemessage_class.md b/public/id/server/pipemessage_class.md new file mode 100644 index 0000000..d6caddb --- /dev/null +++ b/public/id/server/pipemessage_class.md @@ -0,0 +1,26 @@ +# Swoole\Server\PipeMessage + +Berikut adalah penjelasan detail tentang `Swoole\Server\PipeMessage`. + +## Properti + +### $source_worker_id +Mengembalikan id proses `worker` dari sumber data. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\PipeMessage->source_worker_id +``` + +### $dispatch_time +Mengembalikan waktu kedatangan data permintaan `dispatch_time`. Properti ini adalah `double`. + +```php +Swoole\Server\PipeMessage->dispatch_time +``` + +### $data +Mengembalikan data `data` yang dibawa oleh koneksi. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\PipeMessage->data +``` diff --git a/public/id/server/port.md b/public/id/server/port.md new file mode 100644 index 0000000..81d224e --- /dev/null +++ b/public/id/server/port.md @@ -0,0 +1,176 @@ +# Mendengarkan Banyak Port + +`Swoole\Server` dapat mendengarkan banyak port, dan setiap port dapat mengatur cara penanganan protokol yang berbeda, misalnya port 80 menangani protokol HTTP, port 9507 menangani protokol TCP. Enkripsi transmisi `SSL/TLS` juga dapat diaktifkan hanya untuk port tertentu. + +!> Misalnya server utama adalah protokol WebSocket atau HTTP, port TCP baru yang didengarkan (nilai kembalian dari [listen](/server/methods?id=listen), yaitu objek [Swoole\Server\Port](server/server_port.md), selanjutnya disebut port) secara default akan mewarisi pengaturan protokol dari Server utama. Harus memanggil method `set` dan `on` dari objek `port` secara terpisah untuk mengatur protokol baru agar protokol baru diaktifkan. + +## Mendengarkan Port Baru + +```php +//mengembalikan objek port +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port2 = $server->listen("127.0.0.1", 9502, SWOOLE_SOCK_UDP); +$port3 = $server->listen("127.0.0.1", 9503, SWOOLE_SOCK_TCP | SWOOLE_SSL); +``` + +## Mengatur Protokol Jaringan + +```php +//memanggil method set pada objek port +$port1->set([ + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_max_length' => 800000, +]); + +$port3->set([ + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'ssl_cert_file' => 'ssl.cert', + 'ssl_key_file' => 'ssl.key', +]); +``` + +## Mengatur Fungsi Callback + +```php +//mengatur fungsi callback untuk setiap port +$port1->on('connect', function ($serv, $fd){ + echo "Client:Connect.\n"; +}); + +$port1->on('receive', function ($serv, $fd, $reactor_id, $data) { + $serv->send($fd, 'Swoole: '.$data); + $serv->close($fd); +}); + +$port1->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +$port2->on('packet', function ($serv, $data, $addr) { + var_dump($data, $addr); +}); +``` + +## Http/WebSocket + +`Swoole\Http\Server` dan `Swoole\WebSocket\Server` karena diimplementasikan menggunakan subclass, tidak dapat membuat server HTTP atau WebSocket melalui method `listen` pada instance `Swoole\Server`. + +Jika fungsi utama server adalah `RPC`, tetapi ingin menyediakan antarmuka manajemen Web sederhana. Dalam skenario seperti itu, Anda dapat membuat server `HTTP/WebSocket` terlebih dahulu, kemudian melakukan `listen` untuk port TCP asli. + +### Contoh + +```php +$http_server = new Swoole\Http\Server('0.0.0.0',9998); +$http_server->set(['daemonize'=> false]); +$http_server->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); + +//mendengarkan port TCP tambahan, membuka layanan TCP eksternal, dan mengatur callback server TCP +$tcp_server = $http_server->listen('0.0.0.0', 9999, SWOOLE_SOCK_TCP); +//secara default port baru 9999 akan mewarisi pengaturan server utama, yaitu protokol HTTP +//perlu memanggil method set untuk menimpa pengaturan server utama +$tcp_server->set([]); +$tcp_server->on('receive', function ($server, $fd, $threadId, $data) { + echo $data; +}); + +$http_server->start(); +``` + +Dengan kode seperti ini, Anda dapat membuat Server yang menyediakan layanan HTTP sekaligus layanan TCP. Kombinasi kode yang lebih elegan terserah Anda. + +## Pengaturan Gabungan Port Multi-Protokol TCP, HTTP, WebSocket + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_websocket_protocol' => true, // mengatur agar port ini mendukung protokol WebSocket +]); +``` + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_http_protocol' => false, // mengatur port ini untuk menonaktifkan fungsi protokol HTTP +]); +``` + +Begitu juga dengan: `open_http_protocol`, `open_http2_protocol`, `open_mqtt_protocol`, dll. + +## Parameter Opsional + +* Port listening yang tidak memanggil method `set` untuk mengatur opsi pemrosesan protokol, akan mewarisi konfigurasi terkait dari server utama +* Server utama adalah server `HTTP/WebSocket`, jika parameter protokol tidak diatur, port yang didengarkan tetap akan diatur sebagai protokol `HTTP` atau `WebSocket`, dan tidak akan menjalankan callback [onReceive](/server/events?id=onreceive) yang diatur untuk port tersebut +* Server utama adalah server `HTTP/WebSocket`, port listening memanggil `set` untuk mengatur parameter konfigurasi, akan menghapus pengaturan protokol server utama. Port listening akan berubah menjadi protokol `TCP`. Jika port listening ingin tetap menggunakan protokol `HTTP/WebSocket`, perlu menambahkan `open_http_protocol => true` dan `open_websocket_protocol => true` dalam konfigurasi + +**`port` dapat mengatur parameter melalui `set`:** + +* Parameter socket: seperti `backlog`, `open_tcp_keepalive`, `open_tcp_nodelay`, `tcp_defer_accept`, dll. +* Terkait protokol: seperti `open_length_check`, `open_eof_check`, `package_length_type`, dll. +* Terkait sertifikat SSL: seperti `ssl_cert_file`, `ssl_key_file`, dll. + +Detail lebih lanjut dapat merujuk ke [bab Konfigurasi](/server/setting) + +## Callback Opsional + +`port` yang tidak memanggil method `on` untuk mengatur fungsi callback, secara default menggunakan fungsi callback server utama. `port` dapat mengatur callback berikut melalui method `on`: + +### Server TCP + +* onConnect +* onClose +* onReceive + +### Server UDP + +* onPacket +* onReceive + +### Server HTTP + +* onRequest + +### Server WebSocket + +* onMessage +* onOpen +* onHandshake + +!> Fungsi callback dari port listening yang berbeda tetap dieksekusi dalam ruang proses `Worker` yang sama + +## Iterasi Koneksi di Banyak Port + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9514, SWOOLE_BASE); + +$tcp = $server->listen("0.0.0.0", 9515, SWOOLE_SOCK_TCP); +$tcp->set([]); + +$server->on("open", function ($serv, $req) { + echo "new WebSocket Client, fd={$req->fd}\n"; +}); + +$server->on("message", function ($serv, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $serv->push($frame->fd, "this is server OnMessage"); +}); + +$tcp->on('receive', function ($server, $fd, $reactor_id, $data) { + //hanya mengiterasi koneksi port 9514, karena menggunakan $server, bukan $tcp + $websocket = $server->ports[0]; + foreach ($websocket->connections as $_fd) { + var_dump($_fd); + if ($server->exist($_fd)) { + $server->push($_fd, "this is server onReceive"); + } + } + $server->send($fd, 'receive: '.$data); +}); + +$server->start(); +``` diff --git a/public/id/server/properties.md b/public/id/server/properties.md new file mode 100644 index 0000000..cad5551 --- /dev/null +++ b/public/id/server/properties.md @@ -0,0 +1,220 @@ +# Properti + +### $setting + +Parameter yang diatur oleh fungsi [Server->set()](/server/methods?id=set) akan disimpan ke properti `Server->$setting`. Di dalam fungsi callback, nilai dari parameter operasional dapat diakses. Properti ini adalah array dengan tipe `array`. + +```php +Swoole\Server->setting +``` + +* **Contoh** + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set(array('worker_num' => 4)); + +echo $server->setting['worker_num']; +``` + +### $connections + +Iterator koneksi `TCP`, dapat menggunakan `foreach` untuk melintasi semua koneksi server saat ini. Fungsionalitas properti ini sama dengan [Server->getClientList](/server/methods?id=getclientlist), namun lebih mudah digunakan. + +Elemen yang dilintasi adalah `fd` dari masing-masing koneksi. + +```php +Swoole\Server->connections +``` + +!> Properti `$connections` adalah objek iterator, bukan array PHP, jadi tidak bisa diakses dengan `var_dump` atau indeks array, hanya bisa dilintasi dengan `foreach` + +* **Mode Base** + + * Dalam mode [SWOOLE_BASE](/learn?id=swoole_base), operasi lintas proses pada koneksi `TCP` tidak didukung. Oleh karena itu, dalam mode `BASE`, iterator `$connections` hanya dapat digunakan di dalam proses saat ini. + +* **Contoh** + +```php +foreach ($server->connections as $fd) { + var_dump($fd); +} +echo "Server saat ini memiliki " . count($server->connections) . " koneksi\n"; +``` + +### $host + +Mengembalikan `host` dari alamat host yang didengarkan server saat ini. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server->host +``` + +### $port + +Mengembalikan `port` dari port yang didengarkan server saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->port +``` + +### $type + +Mengembalikan `type` dari Server saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->type +``` + +!> Properti ini mengembalikan salah satu dari nilai berikut: +- `SWOOLE_SOCK_TCP` tcp ipv4 socket +- `SWOOLE_SOCK_TCP6` tcp ipv6 socket +- `SWOOLE_SOCK_UDP` udp ipv4 socket +- `SWOOLE_SOCK_UDP6` udp ipv6 socket +- `SWOOLE_SOCK_UNIX_DGRAM` unix socket dgram +- `SWOOLE_SOCK_UNIX_STREAM` unix socket stream + +### $ssl + +Mengembalikan apakah server saat ini mengaktifkan `ssl`. Properti ini adalah `bool`. + +```php +Swoole\Server->ssl +``` + +### $mode + +Mengembalikan mode proses `mode` dari server saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->mode +``` + +!> Properti ini mengembalikan salah satu dari nilai berikut: +- `SWOOLE_BASE` mode proses tunggal +- `SWOOLE_PROCESS` mode multi-proses + +### $ports + +Array port yang didengarkan. Jika server mendengarkan beberapa port, dapat melintasi `Server::$ports` untuk mendapatkan semua objek `Swoole\Server\Port`. + +`swoole_server::$ports[0]` adalah port server utama yang diatur oleh method konstruktor. + +* **Contoh** + +```php +$ports = $server->ports; +$ports[0]->set($settings); +$ports[1]->on('Receive', function () { + //callback +}); +``` + +### $master_pid + +Mengembalikan `PID` dari proses utama server saat ini. + +```php +Swoole\Server->master_pid +``` + +!> Hanya bisa didapatkan setelah `onStart/onWorkerStart` + +* **Contoh** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('start', function ($server){ + echo $server->master_pid; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->start(); +``` + +### $manager_pid + +Mengembalikan `PID` dari proses manajemen server saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->manager_pid +``` + +!> Hanya bisa didapatkan setelah `onStart/onWorkerStart` + +* **Contoh** + +```php +$server = new Swoole\Server("127.0.0.1", 9501); +$server->on('start', function ($server){ + echo $server->manager_pid; +}); +$server->on('receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, 'Swoole: '.$data); + $server->close($fd); +}); +$server->start(); +``` + +### $worker_id + +Mendapatkan nomor proses `Worker` saat ini, termasuk [Task process](/learn?id=taskworker-process). Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->worker_id +``` + +* **Contoh** + +```php +$server = new Swoole\Server('127.0.0.1', 9501); +$server->set([ + 'worker_num' => 8, + 'task_worker_num' => 4, +]); +$server->on('WorkerStart', function ($server, int $workerId) { + if ($server->taskworker) { + echo "task workerId:{$workerId}\n"; + echo "task worker_id:{$server->worker_id}\n"; + } else { + echo "workerId:{$workerId}\n"; + echo "worker_id:{$server->worker_id}\n"; + } +}); +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { +}); +$server->on('Task', function ($serv, $task_id, $reactor_id, $data) { +}); +$server->start(); +``` + +* **Tips** + + * Properti ini sama dengan `$workerId` saat [onWorkerStart](/server/events?id=onworkerstart). + * Rentang nomor proses `Worker` adalah `[0, $server->setting['worker_num'] - 1]` + * Rentang nomor [Task process](/learn?id=taskworker-process) adalah `[$server->setting['worker_num'], $server->setting['worker_num'] + $server->setting['task_worker_num'] - 1]` + +!> Nilai `worker_id` tidak berubah setelah proses worker di-restart + +### $taskworker + +Apakah proses saat ini adalah proses `Task`. Properti ini adalah `bool`. + +```php +Swoole\Server->taskworker +``` + +* **Return Value** + + * `true` berarti proses saat ini adalah proses kerja `Task` + * `false` berarti proses saat ini adalah proses `Worker` + +### $worker_pid + +Mendapatkan `ID` proses sistem operasi dari proses `Worker` saat ini. Sama dengan nilai kembalian `posix_getpid()`. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server->worker_pid +``` diff --git a/public/id/server/server_port.md b/public/id/server/server_port.md new file mode 100644 index 0000000..3285ebe --- /dev/null +++ b/public/id/server/server_port.md @@ -0,0 +1,106 @@ +# Swoole\Server\Port + +Berikut adalah penjelasan detail tentang `Swoole\Server\Port`. + +## Properti + +### $host +Mengembalikan alamat host yang didengarkan. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\Port->host +``` + +### $port +Mengembalikan port host yang didengarkan. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Port->port +``` + +### $type +Mengembalikan tipe `server` ini. Properti ini adalah enumerasi, mengembalikan salah satu dari `SWOOLE_TCP`, `SWOOLE_TCP6`, `SWOOLE_UDP`, `SWOOLE_UDP6`, `SWOOLE_UNIX_DGRAM`, `SWOOLE_UNIX_STREAM`. + +```php +Swoole\Server\Port->type +``` + +### $sock +Mengembalikan socket yang didengarkan. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Port->sock +``` + +### $ssl +Mengembalikan apakah enkripsi `ssl` diaktifkan. Properti ini adalah `bool`. + +```php +Swoole\Server\Port->ssl +``` + +### $setting +Mengembalikan pengaturan untuk port ini. Properti ini adalah array `array`. + +```php +Swoole\Server\Port->setting +``` + +### $connections +Mengembalikan semua koneksi ke port ini. Properti ini adalah iterator. + +```php +Swoole\Server\Port->connections +``` + +## Method + +### set() + +Digunakan untuk mengatur berbagai parameter `Swoole\Server\Port` saat runtime, cara penggunaannya sama dengan [Swoole\Server->set()](/server/methods?id=set). + +```php +Swoole\Server\Port->set(array $setting): void +``` + +### on() + +Digunakan untuk mengatur fungsi callback `Swoole\Server\Port`, cara penggunaannya sama dengan [Swoole\Server->on()](/server/methods?id=on). + +```php +Swoole\Server\Port->on(string $event, callable $callback): bool +``` + +### getCallback() + +Mengembalikan fungsi callback yang telah diatur. + +```php +Swoole\Server\Port->getCallback(string $name): ?callback +``` + +* **Parameter** + + * `string $name` + + * Fungsi: Nama event callback + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Return Value** + + * Mengembalikan fungsi callback jika berhasil, mengembalikan `null` jika fungsi callback tidak ada. + +### getSocket() + +Mengubah `fd` socket saat ini menjadi objek `Socket` PHP. + +```php +Swoole\Server\Port->getSocket(): Socket|false +``` + +* **Return Value** + + * Mengembalikan objek `Socket` jika berhasil, mengembalikan `false` jika gagal. + +!> Perhatikan, fungsi ini hanya dapat digunakan jika `--enable-sockets` diaktifkan saat kompilasi Swoole. diff --git a/public/id/server/setting.md b/public/id/server/setting.md new file mode 100644 index 0000000..34e72c9 --- /dev/null +++ b/public/id/server/setting.md @@ -0,0 +1,1775 @@ +# Konfigurasi + +Fungsi [Swoole\Server->set()](/server/methods?id=set) digunakan untuk mengatur berbagai parameter runtime `Server`. Semua sub-halaman di bagian ini adalah elemen dari array konfigurasi. + +!> Mulai dari versi [v4.5.5](/version/log?id=v455), level bawah akan mendeteksi apakah item konfigurasi yang diatur benar. Jika mengatur item konfigurasi yang bukan disediakan oleh `Swoole`, akan menghasilkan Warning. + +```shell +PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php +``` + +### debug_mode + +?> Mengatur mode log ke mode debug `debug`, hanya berfungsi jika kompilasi menggunakan `--enable-debug`. + +```php +$server->set([ + 'debug_mode' => true +]) +``` + +### trace_flags + +?> Mengatur label log trace, hanya mencetak sebagian log trace. `trace_flags` mendukung penggunaan operator `|` atau untuk mengatur beberapa item trace. Hanya berfungsi jika kompilasi menggunakan `--enable-trace-log`. + +Level bawah mendukung item trace berikut, dapat menggunakan `SWOOLE_TRACE_ALL` untuk melacak semua item: + +* `SWOOLE_TRACE_SERVER` +* `SWOOLE_TRACE_CLIENT` +* `SWOOLE_TRACE_BUFFER` +* `SWOOLE_TRACE_CONN` +* `SWOOLE_TRACE_EVENT` +* `SWOOLE_TRACE_WORKER` +* `SWOOLE_TRACE_REACTOR` +* `SWOOLE_TRACE_PHP` +* `SWOOLE_TRACE_HTTP2` +* `SWOOLE_TRACE_EOF_PROTOCOL` +* `SWOOLE_TRACE_LENGTH_PROTOCOL` +* `SWOOLE_TRACE_CLOSE` +* `SWOOLE_TRACE_HTTP_CLIENT` +* `SWOOLE_TRACE_COROUTINE` +* `SWOOLE_TRACE_REDIS_CLIENT` +* `SWOOLE_TRACE_MYSQL_CLIENT` +* `SWOOLE_TRACE_AIO` +* `SWOOLE_TRACE_ALL` + +### log_file + +?> **Menentukan file log error `Swoole`** + +?> Informasi error yang terjadi selama runtime `Swoole` akan dicatat ke file ini, secara default akan dicetak ke layar. +Setelah mengaktifkan mode daemon `(daemonize => true)`, output standar akan dialihkan ke `log_file`. Konten yang dicetak ke layar seperti `echo/var_dump/print` dalam kode PHP akan ditulis ke file `log_file`. + +* **Tips** + + * Log di `log_file` hanya untuk mencatat error runtime, tidak perlu disimpan dalam jangka panjang. + + * **Label Log** + + ?> Dalam informasi log, sebelum process ID akan ditambahkan beberapa label, yang menunjukkan tipe thread/proses tempat log dihasilkan. + + * `#` Proses Master + * `$` Proses Manager + * `*` Proses Worker + * `^` Proses Task + + * **Membuka Ulang File Log** + + ?> Selama program server berjalan, jika file log dipindahkan (`mv`) atau dihapus (`unlink`), informasi log tidak dapat ditulis dengan normal. Pada saat ini, dapat mengirim sinyal `SIGRTMIN` ke `Server` untuk membuka ulang file log. + + * Hanya mendukung platform `Linux` + * Tidak mendukung proses [UserProcess](/server/methods?id=addProcess) + +* **Catatan** + + !> `log_file` tidak akan membagi file secara otomatis, jadi perlu membersihkan file ini secara berkala. Dengan mengamati output `log_file`, dapat memperoleh berbagai informasi error dan peringatan server. + +### log_level + +?> **Mengatur level pencetakan log error `Server`, rentang `0-6`. Informasi log di bawah level `log_level` tidak akan dikeluarkan.** 【Default:`SWOOLE_LOG_INFO`】 + +Konstanta level terkait lihat [Tingkat Log](/consts?id=tingkat-log) + +* **Catatan** + + !> `SWOOLE_LOG_DEBUG` dan `SWOOLE_LOG_TRACE` hanya tersedia saat dikompilasi dengan [--enable-debug-log](/environment?id=parameter-debug) dan [--enable-trace-log](/environment?id=parameter-debug); + Saat mengaktifkan daemon `daemonize`, level bawah akan menulis semua output cetak layar dalam program ke [log_file](/server/setting?id=log_file), konten ini tidak dikontrol oleh `log_level`. + +### log_date_format + +?> **Mengatur format waktu log `Server`**, format referensi [strftime](https://www.php.net/manual/en/function.strftime.php) `format` + +```php +$server->set([ + 'log_date_format' => '%Y-%m-%d %H:%M:%S', +]); +``` + +### log_date_with_microseconds + +?> **Mengatur presisi log `Server`, apakah menyertakan mikrodetik**【Default:`false`】 + +### log_rotation + +?> **Mengatur rotasi log `Server`**【Default:`SWOOLE_LOG_ROTATION_SINGLE`】 + +| Konstanta | Keterangan | Informasi Versi | +| -------------------------------- | ---------- | --------------- | +| SWOOLE_LOG_ROTATION_SINGLE | Tidak aktif | - | +| SWOOLE_LOG_ROTATION_MONTHLY | Bulanan | v4.5.8 | +| SWOOLE_LOG_ROTATION_DAILY | Harian | v4.5.2 | +| SWOOLE_LOG_ROTATION_HOURLY | Per jam | v4.5.8 | +| SWOOLE_LOG_ROTATION_EVERY_MINUTE | Per menit | v4.5.8 | + +### display_errors + +?> Mengaktifkan / menonaktifkan informasi error `Swoole`. + +```php +$server->set([ + 'display_errors' => true +]) +``` + +### dns_server + +?> Mengatur alamat `ip` untuk query `dns`. + +### socket_dns_timeout + +?> Waktu timeout resolusi nama domain. Jika klien coroutine diaktifkan di sisi server, parameter ini dapat mengontrol waktu timeout resolusi nama domain klien, dalam satuan detik. + +### socket_connect_timeout + +?> Waktu timeout koneksi klien. Jika klien coroutine diaktifkan di sisi server, parameter ini dapat mengontrol waktu timeout koneksi klien, dalam satuan detik. + +### socket_write_timeout / socket_send_timeout + +?> Waktu timeout tulis klien. Jika klien coroutine diaktifkan di sisi server, parameter ini dapat mengontrol waktu timeout tulis klien, dalam satuan detik. +Konfigurasi ini juga dapat digunakan untuk mengontrol waktu timeout eksekusi `shell_exec` atau [Swoole\Coroutine\System::exec()](/coroutine/system?id=exec) setelah `coroutine`. + +### socket_read_timeout / socket_recv_timeout + +?> Waktu timeout baca klien. Jika klien coroutine diaktifkan di sisi server, parameter ini dapat mengontrol waktu timeout baca klien, dalam satuan detik. + +### max_coroutine / max_coro_num :id=max_coroutine + +?> **Mengatur jumlah maksimum coroutine dalam proses kerja saat ini.**【Default:`100000`,untuk versi Swoole lebih kecil dari `v4.4.0-beta` nilai default adalah `3000`】 + +?> Melebihi `max_coroutine`, level bawah tidak dapat membuat coroutine baru. Swoole di sisi server akan melempar error `exceed max number of coroutine`, `TCP Server` akan langsung menutup koneksi, `Http Server` akan mengembalikan kode status HTTP 503. + +?> Dalam program `Server`, jumlah maksimum coroutine yang sebenarnya dapat dibuat sama dengan `worker_num * max_coroutine`. Jumlah coroutine proses task dan UserProcess dihitung secara terpisah. + +```php +$server->set(array( + 'max_coroutine' => 3000, +)); +``` + +### enable_deadlock_check + +?> Mengaktifkan deteksi deadlock coroutine. + +```php +$server->set([ + 'enable_deadlock_check' => true +]); +``` + +### enable_preemptive_scheduler + +?> Mengatur untuk mengaktifkan penjadwalan preemptive coroutine, untuk menghindari salah satu coroutine yang berjalan terlalu lama menyebabkan coroutine lain kelaparan. Waktu eksekusi maksimum coroutine adalah `10ms`. + +```php +$server->set([ + 'enable_preemptive_scheduler' => true +]); +``` + +### c_stack_size / stack_size + +?> Mengatur ukuran memori stack C awal untuk satu coroutine, default 2M. + +### aio_core_worker_num + +?> Mengatur jumlah minimum thread kerja `AIO`, nilai default adalah jumlah inti `cpu`. + +### aio_worker_num + +?> Mengatur jumlah maksimum thread kerja `AIO`, nilai default adalah jumlah inti `cpu` * 8. + +### aio_max_wait_time + +?> Waktu maksimum thread kerja menunggu tugas, dalam satuan detik. + +### aio_max_idle_time + +?> Waktu idle maksimum thread kerja, dalam satuan detik. + +### iouring_entries + +?> Mengatur ukuran antrian `io_uring`, default `8192`. Jika nilai yang diberikan bukan `pangkat 2`, kernel akan mengubahnya ke pangkat 2 terdekat yang lebih besar dari nilai tersebut. + +!> Jika nilai yang diberikan terlalu besar, kernel akan melempar error dan menghentikan program. + +!> Hanya dapat digunakan jika sistem memiliki `liburing` dan kompilasi `Swoole` menggunakan `--enable-iouring`. + +### iouring_workers + +?> Mengatur jumlah thread kerja `io_uring`, nilai default adalah `Jumlah inti CPU * 4`. + +!> Jika nilai yang diberikan terlalu besar, kernel akan melempar error dan menghentikan program. + +!> Hanya dapat digunakan jika sistem memiliki `liburing` dan kompilasi `Swoole` menggunakan `--enable-iouring`. + +### iouring_flag + +?> Mengatur mode kerja `io_uring`, nilai default adalah `SWOOLE_IOURING_DEFAULT`. + +- `SWOOLE_IOURING_DEFAULT`, mode interrupt-driven. Kirim permintaan `I/O` melalui panggilan sistem `io_uring_enter`, lalu periksa langsung status antrian completion untuk menentukan apakah sudah selesai. +- `SWOOLE_IOURING_SQPOLL`, mode polling kernel. Kernel akan membuat thread kernel untuk mengirim dan memanen permintaan `I/O`, hampir sepenuhnya menghilangkan context switching antara user-mode dan kernel-mode, kinerja lebih baik. + +!> Jika mode yang diberikan salah, kernel akan menggunakan `SWOOLE_IOURING_DEFAULT` mode interrupt-driven. + +### reactor_num + +?> **Mengatur jumlah thread [Reactor](/learn?id=reactor-thread) yang akan dimulai.**【Default:jumlah inti `CPU`】 + +?> Melalui parameter ini, jumlah thread pemrosesan event dalam proses utama dapat diatur untuk memanfaatkan multi-core secara penuh. Default akan mengaktifkan jumlah yang sama dengan jumlah inti `CPU`. +Thread `Reactor` dapat memanfaatkan multi-core, misalnya: mesin memiliki `128` core, maka level bawah akan memulai `128` thread. +Setiap thread akan mempertahankan satu [EventLoop](/learn?id=apa-itu-eventloop). Antar thread tidak ada lock, instruksi dapat dieksekusi secara paralel oleh `128` core `CPU`. +Mengingat penjadwalan sistem operasi memiliki tingkat penurunan kinerja tertentu, dapat diatur ke CPU core * 2, untuk memaksimalkan penggunaan setiap core CPU. + +* **Tips** + + * `reactor_num` disarankan diatur ke `1-4` kali jumlah inti `CPU` + * `reactor_num` maksimum tidak boleh melebihi [swoole_cpu_num()](/functions?id=swoole_cpu_num) * 4 + +* **Catatan** + + !> -`reactor_num` harus kurang dari atau sama dengan `worker_num`; + -Jika `reactor_num` yang diatur lebih besar dari `worker_num`, akan otomatis disesuaikan sehingga `reactor_num` sama dengan `worker_num`; + -Pada mesin dengan lebih dari `8` core, `reactor_num` default diatur ke `8`. + +### worker_num + +?> **Mengatur jumlah proses `Worker` yang akan dimulai.**【Default:jumlah inti `CPU`】 + +?> Jika `1` permintaan memakan waktu `100ms`, untuk menyediakan kapasitas pemrosesan `1000QPS`, harus mengkonfigurasi `100` proses atau lebih. +Tetapi semakin banyak proses yang dibuka, memori yang digunakan akan meningkat secara signifikan, dan overhead perpindahan antar proses akan semakin besar. Jadi atur secukupnya saja. Jangan terlalu besar. + +* **Tips** + + * Jika kode bisnis sepenuhnya [asynchronous IO](/learn?id=synchronous-io-asynchronous-io), atur ke `1-4` kali jumlah inti `CPU` adalah yang paling masuk akal + * Jika kode bisnis [synchronous IO](/learn?id=synchronous-io-asynchronous-io), perlu disesuaikan berdasarkan waktu respons permintaan dan beban sistem, misalnya: `100-500` + * Default diatur ke [swoole_cpu_num()](/functions?id=swoole_cpu_num), maksimum tidak boleh melebihi [swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 + * Asumsikan setiap proses memakan `40M` memori, `100` proses perlu memakan `4G` memori. + +### max_request + +?> **Mengatur jumlah maksimum tugas untuk proses `worker`.**【Default:`0` yaitu proses tidak akan keluar】 + +?> Setelah proses `worker` selesai memproses melebihi jumlah tugas ini, akan otomatis keluar. Setelah keluar, proses akan membebaskan semua memori dan resource. + +!> Parameter ini terutama digunakan untuk mengatasi masalah kebocoran memori proses PHP akibat kode program yang tidak sesuai standar. Aplikasi PHP memiliki kebocoran memori lambat, tetapi tidak dapat menemukan penyebab spesifik atau tidak dapat diperbaiki. Dapat diatasi sementara dengan mengatur `max_request`. Namun perlu menemukan kode yang bocor memori dan memperbaikinya, bukan melalui solusi ini. Dapat menggunakan Swoole Tracker untuk menemukan kode yang bocor. + +* **Tips** + + * Mencapai max_request belum tentu segera menutup proses, lihat [max_wait_time](/server/setting?id=max_wait_time). + * Di [SWOOLE_BASE](/learn?id=swoole_base), mencapai max_request dan me-restart proses akan menyebabkan koneksi klien terputus. + + !> Ketika terjadi fatal error dalam proses `worker` atau `exit` dijalankan secara manual, proses akan otomatis keluar. Proses `master` akan memulai proses `worker` baru untuk terus memproses permintaan. + +### max_conn / max_connection + +?> **Jumlah maksimum koneksi yang diizinkan untuk program server.**【Default:`ulimit -n`】 + +?> Misalnya `max_connection => 10000`, parameter ini digunakan untuk mengatur berapa banyak koneksi `TCP` maksimum yang dapat dipertahankan oleh `Server`. Setelah melebihi jumlah ini, koneksi yang baru masuk akan ditolak. + +* **Tips** + + * **Pengaturan Default** + + * Jika lapisan aplikasi tidak mengatur `max_connection`, level bawah akan menggunakan nilai `ulimit -n` sebagai pengaturan default + * Di versi `4.2.9` atau yang lebih baru, ketika level bawah mendeteksi `ulimit -n` melebihi `100000`, akan default diatur ke `100000`. Alasannya adalah beberapa sistem mengatur `ulimit -n` menjadi `1 juta`, perlu mengalokasikan memori dalam jumlah besar, yang menyebabkan kegagalan startup. + + * **Batas Maksimum** + + * Jangan mengatur `max_connection` melebihi `1M` + + * **Pengaturan Minimum** + + * Jika opsi ini diatur terlalu kecil, level bawah akan melempar error dan mengaturnya ke nilai `ulimit -n`. + * Nilai minimum adalah `(worker_num + task_worker_num) * 2 + 32` + + ```shell + serv->max_connection is too small. + ``` + + * **Penggunaan Memori** + + * Parameter `max_connection` jangan diatur terlalu besar, atur sesuai dengan kondisi memori mesin yang sebenarnya. `Swoole` akan mengalokasikan satu blok memori besar sekaligus berdasarkan nilai ini untuk menyimpan informasi `Connection`. Informasi `Connection` untuk satu koneksi `TCP` membutuhkan `224` byte. + +* **Catatan** + + !> `max_connection` maksimum tidak boleh melebihi nilai `ulimit -n` sistem operasi, jika tidak akan melaporkan peringatan, dan direset ke nilai `ulimit -n`. + + ```shell + WARN swServer_start_check: serv->max_conn is exceed the maximum value[100000]. + + WARNING set_max_connection: max_connection is exceed the maximum value, it's reset to 10240 + ``` + +### task_worker_num + +?> **Mengkonfigurasi jumlah [Task process](/learn?id=taskworker-process).** + +?> Setelah mengkonfigurasi parameter ini, fungsi `task` akan diaktifkan. Oleh karena itu, `Server` harus mendaftarkan `2` fungsi callback event [onTask](/server/events?id=ontask) dan [onFinish](/server/events?id=onfinish). Jika tidak mendaftar, program server tidak akan dapat dimulai. + +* **Tips** + + * [Task process](/learn?id=taskworker-process) bersifat synchronous blocking + + * Nilai maksimum tidak boleh melebihi [swoole_cpu_num()](/functions?id=swoole_cpu_num) * 1000 + + * **Metode Perhitungan** + * Waktu pemrosesan satu `task`, misalnya `100ms`, maka satu proses dalam 1 detik dapat menangani `1/0.1=10` task + * Kecepatan pengiriman `task`, misalnya menghasilkan `2000` `task` per detik + * `2000/10=200`, perlu mengatur `task_worker_num => 200`, mengaktifkan `200` proses Task + +* **Catatan** + + !> -Method `Swoole\Server->task` tidak dapat digunakan di dalam [Task process](/learn?id=taskworker-process) + +### task_ipc_mode + +?> **Mengatur cara komunikasi antara [Task process](/learn?id=taskworker-process) dan proses `Worker`.**【Default:`1`】 + +?> Silakan baca terlebih dahulu [Komunikasi IPC di Swoole](/learn?id=apa-itu-ipc). + +Mode | Fungsi +---|--- +1 | Menggunakan komunikasi `Unix Socket`【Mode default】 +2 | Menggunakan komunikasi antrian `sysvmsg` +3 | Menggunakan komunikasi antrian `sysvmsg`, dan diatur ke mode rebutan + +* **Tips** + + * **Mode `1`** + * Menggunakan mode `1`, mendukung pengiriman terarah, dapat menggunakan `dst_worker_id` di method [task](/server/methods?id=task) dan [taskwait](/server/methods?id=taskwait) untuk menentukan target `Task process`. + * Saat `dst_worker_id` diatur ke `-1`, level bawah akan memeriksa status setiap [Task process](/learn?id=taskworker-process), dan mengirim tugas ke proses yang sedang idle. + + * **Mode `2`、`3`** + * Mode antrian pesan menggunakan antrian memori yang disediakan sistem operasi untuk menyimpan data. Jika tidak menentukan `mssage_queue_key` antrian pesan `Key`, akan menggunakan antrian privat, dan antrian pesan akan dihapus setelah program `Server` berhenti. + * Setelah menentukan antrian pesan `Key`, data dalam antrian pesan tidak akan dihapus setelah program `Server` berhenti, sehingga setelah proses di-restart, data masih dapat diambil. + * Dapat menggunakan `ipcrm -q` ID antrian pesan untuk menghapus data antrian pesan secara manual. + * Perbedaan antara `Mode 2` dan `Mode 3` adalah, `Mode 2` mendukung pengiriman terarah, `$serv->task($data, $task_worker_id)` dapat menentukan [task process](/learn?id=taskworker-process) mana yang akan dikirim. `Mode 3` adalah mode rebutan penuh, [task process](/learn?id=taskworker-process) akan merebut antrian, pengiriman terarah tidak dapat digunakan, `task/taskwait` tidak dapat menentukan target process `ID`, meskipun `$task_worker_id` ditentukan, itu tidak akan valid dalam `mode 3`. + +* **Catatan** + + !> -`Mode 3` akan mempengaruhi method [sendMessage](/server/methods?id=sendMessage), sehingga pesan yang dikirim oleh [sendMessage](/server/methods?id=sendMessage) akan diambil secara acak oleh salah satu [task process](/learn?id=taskworker-process). + -Menggunakan komunikasi antrian pesan, jika kemampuan pemrosesan `Task process` lebih rendah dari kecepatan pengiriman, dapat menyebabkan proses `Worker` terblokir. + -Setelah menggunakan komunikasi antrian pesan, proses task tidak dapat mendukung coroutine (mengaktifkan [task_enable_coroutine](/server/setting?id=task_enable_coroutine)). + +### task_max_request + +?> **Mengatur jumlah maksimum tugas untuk [task process](/learn?id=taskworker-process).**【Default:`0`】 + +Mengatur jumlah maksimum tugas untuk proses task. Setelah proses task selesai memproses melebihi jumlah tugas ini, akan otomatis keluar. Parameter ini untuk mencegah overflow memori proses PHP. Jika tidak ingin proses keluar otomatis, dapat diatur ke 0. + +### task_tmpdir + +?> **Mengatur direktori sementara data task.**【Default:direktori Linux `/tmp`】 + +?> Di `Server`, jika data yang dikirim melebihi `8180` byte, file sementara akan digunakan untuk menyimpan data. `task_tmpdir` di sini digunakan untuk mengatur lokasi penyimpanan file sementara. + +* **Tips** + + * Level bawah default akan menggunakan direktori `/tmp` untuk menyimpan data `task`. Jika versi kernel `Linux` Anda terlalu rendah, direktori `/tmp` bukan filesystem memori, dapat diatur ke `/dev/shm/` + * Jika direktori `task_tmpdir` tidak ada, level bawah akan mencoba membuatnya secara otomatis + +* **Catatan** + + !> -Jika pembuatan gagal, `Server->start` akan gagal + +### task_enable_coroutine + +?> **Mengaktifkan dukungan coroutine `Task`.**【Default:`false`】,didukung sejak v4.2.12 + +?> Setelah diaktifkan, secara otomatis akan membuat coroutine dan [coroutine container](/coroutine/scheduler) dalam callback [onTask](/server/events?id=ontask). Kode `PHP` dapat langsung menggunakan `API` coroutine. + +* **Contoh** + +```php +$server->on('Task', function ($serv, Swoole\Server\Task $task) { + //Dari proses Worker mana + $task->worker_id; + //Nomor tugas + $task->id; + //Tipe tugas, taskwait, task, taskCo, taskWaitMulti mungkin menggunakan flags yang berbeda + $task->flags; + //Data tugas + $task->data; + //Waktu pengiriman, ditambahkan di v4.6.0 + $task->dispatch_time; + //API Coroutine + co::sleep(0.2); + //Menyelesaikan tugas, selesai dan mengembalikan data + $task->finish([123, 'hello']); +}); +``` + +* **Catatan** + + !> -`task_enable_coroutine` hanya dapat digunakan jika [enable_coroutine](/server/setting?id=enable_coroutine) adalah `true` + -Mengaktifkan `task_enable_coroutine`, proses kerja `Task` mendukung coroutine + -Tidak mengaktifkan `task_enable_coroutine`, hanya mendukung synchronous blocking + +### task_use_object/task_object :id=task_use_object + +?> **Menggunakan format callback Task berorientasi objek.**【Default:`false`】 + +?> Jika diatur ke `true`, callback [onTask](/server/events?id=ontask) akan berubah menjadi mode objek. + +* **Contoh** + +```php +set([ + 'worker_num' => 1, + 'task_worker_num' => 3, + 'task_use_object' => true, +// 'task_object' => true, // alias yang ditambahkan di v4.6.0 +]); +$server->on('receive', function (Swoole\Server $server, $fd, $tid, $data) { + $server->task(['fd' => $fd,]); +}); +$server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { + //$task di sini adalah objek Swoole\Server\Task + $server->send($task->data['fd'], json_encode($server->stats())); +}); +$server->start(); +``` + +### dispatch_mode + +?> **Strategi distribusi paket data.**【Default:`2`】 + +Nilai Mode | Mode | Fungsi +---|---|--- +1 | Round Robin | Setelah menerima, akan mendistribusikan secara round robin ke setiap proses `Worker` +2 | Mode Tetap | Mendistribusikan `Worker` berdasarkan file descriptor koneksi. Dengan cara ini, data dari koneksi yang sama hanya akan diproses oleh `Worker` yang sama +3 | Mode Preemptive | Proses utama akan memilih berdasarkan status sibuk/idle `Worker`, hanya akan mengirim ke `Worker` yang sedang idle +4 | Distribusi IP | Berdasarkan `IP` klien melakukan modulo `hash`, dialokasikan ke proses `Worker` tetap.
Dapat memastikan data koneksi dari sumber IP yang sama selalu dialokasikan ke proses `Worker` yang sama. Algoritma `inet_addr_mod(ClientIP, worker_num)` +5 | Distribusi UID | Perlu memanggil [Server->bind()](/server/methods?id=bind) dalam kode pengguna untuk mengikat `1` `uid` ke koneksi. Kemudian level bawah mendistribusikan ke proses `Worker` yang berbeda berdasarkan nilai `UID`.
Algoritma `UID % worker_num`, jika perlu menggunakan string sebagai `UID`, dapat menggunakan `crc32(UID_STRING)` +7 | Mode stream | `Worker` yang idle akan `accept` koneksi, dan menerima permintaan baru dari [Reactor](/learn?id=reactor-thread) + +* **Tips** + + * **Saran Penggunaan** + + * `Server` tanpa status dapat menggunakan `1` atau `3`, `Server` synchronous blocking menggunakan `3`, asynchronous non-blocking menggunakan `1` + * Dengan status menggunakan `2`、`4`、`5` + + * **Protokol UDP** + + * `dispatch_mode=2/4/5` adalah alokasi tetap, level bawah menggunakan modulo `IP` klien untuk hash ke proses `Worker` yang berbeda + * `dispatch_mode=1/3` dialokasikan secara acak ke proses `Worker` yang berbeda + * Fungsi `inet_addr_mod` + +``` + function inet_addr_mod($ip, $worker_num) { + $ip_parts = explode('.', $ip); + if (count($ip_parts) != 4) { + return false; + } + $ip_parts = array_reverse($ip_parts); + + $ip_long = 0; + foreach ($ip_parts as $part) { + $ip_long <<= 8; + $ip_long |= (int) $part; + } + + return $ip_long % $worker_num; + } +``` + +* **Mode Base** + * Konfigurasi `dispatch_mode` tidak valid dalam mode [SWOOLE_BASE](/learn?id=swoole_base), karena `BASE` tidak ada pengiriman tugas. Saat menerima data dari klien, akan langsung memanggil callback [onReceive](/server/events?id=onreceive) di thread/proses saat ini, tidak perlu mengirim ke proses `Worker`. + +* **Catatan** + + !> -`dispatch_mode=1/3`, level bawah akan menonaktifkan event `onConnect/onClose`, karena dalam 2 mode ini tidak dapat menjamin urutan `onConnect/onClose/onReceive`; + -Program server yang non-request-response, jangan gunakan mode `1` atau `3`. Contoh: layanan http bersifat response, dapat menggunakan `1` atau `3`, yang memiliki status koneksi TCP panjang tidak boleh menggunakan `1` atau `3`. + +### dispatch_func + +?> Mengatur fungsi `dispatch`. `Swoole` level bawah memiliki `6` jenis [dispatch_mode](/server/setting?id=dispatch_mode) bawaan. Jika masih belum memenuhi kebutuhan, dapat menulis fungsi `C++` atau `PHP` untuk mengimplementasikan logika `dispatch`. + +* **Cara Penggunaan** + +```php +$server->set(array( + 'dispatch_func' => 'my_dispatch_function', +)); +``` + +* **Tips** + + * Setelah mengatur `dispatch_func`, level bawah secara otomatis akan mengabaikan konfigurasi `dispatch_mode` + * Jika fungsi yang sesuai dengan `dispatch_func` tidak ada, level bawah akan melempar fatal error + * Jika perlu `dispatch` paket yang melebihi 8K, `dispatch_func` hanya dapat memperoleh konten `0-8180` byte + +* **Menulis Fungsi PHP** + + ?> Karena `ZendVM` tidak mendukung lingkungan multi-thread, meskipun beberapa thread [Reactor](/learn?id=reactor-thread) diatur, hanya satu `dispatch_func` yang dapat dieksekusi pada satu waktu. Oleh karena itu, level bawah akan melakukan operasi penguncian saat mengeksekusi fungsi PHP ini, mungkin ada masalah perebutan kunci. Jangan melakukan operasi blocking apa pun di `dispatch_func`, karena akan menyebabkan grup thread `Reactor` berhenti bekerja. + + ```php + $server->set(array( + 'dispatch_func' => function ($server, $fd, $type, $data) { + var_dump($fd, $type, $data); + return intval($data[0]); + }, + )); + ``` + + * `$fd` adalah pengidentifikasi unik koneksi klien, dapat menggunakan `Server::getClientInfo` untuk mendapatkan informasi koneksi + * `$type` tipe data, `0` menunjukkan data dari klien, `4` menunjukkan koneksi klien dibuat, `3` menunjukkan koneksi klien ditutup + * `$data` konten data, perlu diperhatikan: jika mengaktifkan parameter pemrosesan protokol seperti `HTTP`, `EOF`, `Length` dll., level bawah akan melakukan penggabungan paket. Tetapi di fungsi `dispatch_func`, hanya dapat meneruskan 8K konten pertama dari paket data, tidak dapat mendapatkan konten paket lengkap. + * **Harus** mengembalikan angka `0 - (server->worker_num - 1)`, menunjukkan proses kerja target pengiriman paket data `ID` + * Kurang dari `0` atau lebih besar atau sama dengan `server->worker_num` adalah target `ID` yang error, data `dispatch` akan dibuang + +* **Menulis Fungsi C++** + + **Di ekstensi PHP lainnya, gunakan swoole_add_function untuk mendaftarkan fungsi panjang ke engine Swoole.** + + ?> Saat fungsi C++ dipanggil, level bawah tidak akan mengunci, pemanggil harus menjamin keamanan thread sendiri + + ```c++ + int dispatch_function(swServer *serv, swConnection *conn, swEventData *data); + + int dispatch_function(swServer *serv, swConnection *conn, swEventData *data) + { + printf("cpp, type=%d, size=%d\n", data->info.type, data->info.len); + return data->info.len % serv->worker_num; + } + + int register_dispatch_function(swModule *module) + { + swoole_add_function("my_dispatch_function", (void *) dispatch_function); + } + ``` + + * Fungsi `dispatch` harus mengembalikan target `worker` process `id` tujuan pengiriman + * `worker_id` yang dikembalikan tidak boleh melebihi `server->worker_num`, jika tidak level bawah akan melempar segment error + * Mengembalikan negatif `(return -1)` berarti membuang paket data ini + * `data` dapat membaca tipe dan panjang event + * `conn` adalah informasi koneksi, jika paket `UDP`, `conn` adalah `NULL` + +* **Catatan** + + !> -`dispatch_func` hanya valid dalam mode [SWOOLE_PROCESS](/learn?id=swoole_process), server tipe [UDP/TCP/UnixSocket](/server/methods?id=__construct) semuanya valid + -`worker_id` yang dikembalikan tidak boleh melebihi `server->worker_num`, jika tidak level bawah akan melempar segment error + +### message_queue_key + +?> **Mengatur `KEY` antrian pesan.**【Default:`ftok($php_script_file, 1)`】 + +?> Hanya digunakan saat [task_ipc_mode](/server/setting?id=task_ipc_mode) = 2/3. `Key` yang diatur hanya sebagai `KEY` antrian tugas `Task`, lihat [Komunikasi IPC di Swoole](/learn?id=apa-itu-ipc). + +?> Antrian `task` tidak akan dihapus setelah `server` berakhir. Setelah memulai ulang program, [task process](/learn?id=taskworker-process) akan terus memproses tugas dalam antrian. Jika tidak ingin menjalankan tugas `Task` lama setelah restart program, dapat menghapus antrian pesan ini secara manual. + +```shell +ipcs -q +ipcrm -Q [msgkey] +``` + +### daemonize + +?> **Menjalankan sebagai daemon**【Default:`false`】 + +?> Saat mengatur `daemonize => true`, program akan berjalan di latar belakang sebagai proses daemon. Program server yang berjalan lama harus mengaktifkan ini. +Jika tidak mengaktifkan daemon, saat terminal ssh keluar, program akan dihentikan. + +* **Tips** + + * Setelah mengaktifkan daemon, input dan output standar akan dialihkan ke `log_file` + * Jika tidak mengatur `log_file`, akan dialihkan ke `/dev/null`, semua informasi cetakan layar akan dibuang + * Setelah mengaktifkan daemon, nilai variabel lingkungan `CWD` (direktori saat ini) akan berubah, pembacaan/penulisan file dengan path relatif akan error. Dalam program `PHP`, harus menggunakan path absolut + + * **systemd** + + * Saat menggunakan `systemd` atau `supervisord` untuk mengelola layanan `Swoole`, jangan atur `daemonize => true`. Alasan utamanya adalah mekanisme `systemd` berbeda dengan `init`. `PID` dari proses `init` adalah `1`. Setelah program menggunakan `daemonize`, akan terlepas dari terminal, akhirnya dikelola oleh proses `init`, hubungan menjadi hubungan parent-child. + * Tetapi `systemd` memulai proses latar belakang terpisah, melakukan `fork` sendiri untuk mengelola proses layanan lainnya, sehingga tidak perlu `daemonize`. Sebaliknya, menggunakan `daemonize => true` akan membuat program `Swoole` kehilangan hubungan parent-child dengan proses manajemen tersebut. + +### backlog + +?> **Mengatur panjang antrian `Listen`** + +?> Misalnya `backlog => 128`, parameter ini akan menentukan berapa banyak koneksi yang menunggu `accept` secara bersamaan. + +* **Tentang `backlog` `TCP`** + + ?> `TCP` memiliki proses three-way handshake, klien `syn=>server` `syn+ack=>klien` `ack`, saat server menerima `ack` dari klien, akan menempatkan koneksi ke dalam antrian yang disebut `accept queue` (catatan 1), + Ukuran antrian ditentukan oleh minimum antara parameter `backlog` dan konfigurasi `somaxconn`, dapat menggunakan perintah `ss -lt` untuk melihat ukuran akhir antrian `accept queue`, proses utama `Swoole` memanggil `accept` (catatan 2) + untuk mengambil dari `accept queue`. Saat `accept queue` penuh, koneksi mungkin berhasil (catatan 4), + atau mungkin gagal, setelah gagal, gejala yang terlihat klien adalah koneksi direset (catatan 3) + atau koneksi timeout, sementara server akan mencatat catatan kegagalan, dapat dilihat melalui `netstat -s|grep 'times the listen queue of a socket overflowed`. Jika fenomena di atas muncul, Anda harus memperbesar nilai ini. Untungnya, mode SWOOLE_PROCESS dari `Swoole` berbeda dengan perangkat lunak seperti `PHP-FPM/Apache`, tidak bergantung pada `backlog` untuk mengatasi masalah antrian koneksi. Jadi pada dasarnya tidak akan mengalami fenomena di atas. + + * Catatan 1: Setelah `linux2.2`, proses handshake dibagi menjadi dua antrian: `syn queue` dan `accept queue`. Panjang `syn queue` ditentukan oleh `tcp_max_syn_backlog`. + * Catatan 2: Kernel versi tinggi memanggil `accept4`, untuk menghemat satu panggilan sistem `set no block`. + * Catatan 3: Klien setelah menerima paket `syn+back` menganggap koneksi berhasil, sebenarnya server masih dalam status setengah terhubung, mungkin mengirim paket `rst` ke klien, yang terlihat oleh klien sebagai `Connection reset by peer`. + * Catatan 4: Keberhasilan melalui mekanisme retransmisi TCP, konfigurasi terkait adalah `tcp_synack_retries` dan `tcp_abort_on_overflow`. + +### open_tcp_keepalive + +?> Dalam `TCP` ada mekanisme `Keep-Alive` yang dapat mendeteksi koneksi mati. Jika lapisan aplikasi tidak sensitif terhadap siklus koneksi mati atau tidak mengimplementasikan mekanisme heartbeat, dapat menggunakan mekanisme `keepalive` yang disediakan sistem operasi untuk memutus koneksi mati. +Dalam konfigurasi [Server->set()](/server/methods?id=set), tambahkan `open_tcp_keepalive => true` untuk mengaktifkan `TCP keepalive`. +Selain itu, ada `3` opsi untuk menyesuaikan detail `keepalive`. + +* **Opsi** + + * **tcp_keepidle** + + Satuan detik, jika koneksi tidak ada permintaan data dalam `n` detik, akan mulai melakukan probe pada koneksi ini. + + * **tcp_keepcount** + + Jumlah probe, setelah melebihi jumlah akan `close` koneksi ini. + + * **tcp_keepinterval** + + Interval waktu probe, satuan detik. + +* **Contoh** + +```php +$serv = new Swoole\Server("192.168.2.194", 6666, SWOOLE_PROCESS); +$serv->set(array( + 'worker_num' => 1, + 'open_tcp_keepalive' => true, + 'tcp_keepidle' => 4, //4 detik tanpa transmisi data, lakukan deteksi + 'tcp_keepinterval' => 1, //probe setiap 1 detik + 'tcp_keepcount' => 5, //jumlah probe, setelah 5 kali tanpa balasan, tutup koneksi ini +)); + +$serv->on('connect', function ($serv, $fd) { + var_dump("Client:Connect $fd"); +}); + +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + var_dump($data); +}); + +$serv->on('close', function ($serv, $fd) { + var_dump("close fd $fd"); +}); + +$serv->start(); +``` + +### heartbeat_check_interval + +?> **Mengaktifkan deteksi heartbeat**【Default:`false`】 + +?> Opsi ini menunjukkan seberapa sering melakukan polling, satuan detik. Misalnya `heartbeat_check_interval => 60`, berarti setiap `60` detik, melintasi semua koneksi. Jika koneksi dalam `120` detik (saat `heartbeat_idle_time` tidak diatur, default adalah dua kali interval), tidak mengirim data apa pun ke server, koneksi ini akan dipaksa ditutup. Jika tidak dikonfigurasi, heartbeat tidak akan diaktifkan. Konfigurasi ini default dimatikan. + +* **Tips** + * `Server` tidak secara aktif mengirim paket heartbeat ke klien, tetapi secara pasif menunggu klien mengirim heartbeat. `heartbeat_check` di sisi server hanya mendeteksi waktu pengiriman data terakhir dari koneksi. Jika melebihi batas, koneksi akan diputus. + * Koneksi yang diputus oleh deteksi heartbeat tetap akan memicu callback event [onClose](/server/events?id=onclose) + +* **Catatan** + + !> `heartbeat_check` hanya mendukung koneksi `TCP` + +### heartbeat_idle_time + +?> **Waktu idle maksimum yang diizinkan untuk koneksi** + +?> Perlu digunakan bersama dengan `heartbeat_check_interval` + +```php +array( + 'heartbeat_idle_time' => 600, // menunjukkan bahwa jika koneksi tidak mengirim data apa pun ke server dalam 600 detik, koneksi ini akan dipaksa ditutup + 'heartbeat_check_interval' => 60, // menunjukkan polling setiap 60 detik +); +``` + +* **Tips** + + * Setelah mengaktifkan `heartbeat_idle_time`, server tidak akan secara aktif mengirim paket data ke klien + * Jika hanya mengatur `heartbeat_idle_time` tanpa mengatur `heartbeat_check_interval`, level bawah tidak akan membuat thread deteksi heartbeat. Dalam kode `PHP` dapat memanggil method `heartbeat` untuk menangani koneksi yang timeout secara manual. + +### open_eof_check + +?> **Mengaktifkan deteksi `EOF`**【Default:`false`】,lihat [Masalah Batas Paket Data TCP](/learn?id=masalah-batas-paket-data-tcp) + +?> Opsi ini akan mendeteksi data yang dikirim dari koneksi klien. Hanya saat akhir paket data adalah string yang ditentukan, data akan dikirim ke proses `Worker`. Jika tidak, data akan terus digabungkan, sampai melebihi buffer atau timeout baru akan dihentikan. Saat terjadi error, level bawah akan menganggapnya sebagai koneksi berbahaya, membuang data dan memaksa menutup koneksi. +Protokol umum seperti `Memcache/SMTP/POP` semuanya diakhiri dengan `\r\n`, dapat menggunakan konfigurasi ini. Setelah diaktifkan, dapat memastikan proses `Worker` selalu menerima satu atau lebih paket data lengkap sekaligus. + +```php +array( + 'open_eof_check' => true, //mengaktifkan deteksi EOF + 'package_eof' => "\r\n", //mengatur EOF +) +``` + +* **Catatan** + + !> Konfigurasi ini hanya valid untuk `Socket` tipe `STREAM` (stream), seperti [TCP, Unix Socket Stream](/server/methods?id=__construct) + Deteksi `EOF` tidak mencari string `eof` dari tengah data, sehingga proses `Worker` mungkin menerima beberapa paket data sekaligus, perlu melakukan `explode("\r\n", $data)` sendiri dalam kode lapisan aplikasi untuk memisahkan paket data. + +### open_eof_split + +?> **Mengaktifkan pemisahan paket otomatis `EOF`** + +?> Setelah mengatur `open_eof_check`, mungkin terjadi beberapa data digabung dalam satu paket. Parameter `open_eof_split` dapat mengatasi masalah ini, lihat [Masalah Batas Paket Data TCP](/learn?id=masalah-batas-paket-data-tcp). + +?> Mengatur parameter ini perlu melintasi seluruh konten paket data, mencari `EOF`, sehingga akan menghabiskan banyak resource `CPU`. Asumsikan setiap paket data `2M`, 10.000 permintaan per detik, ini dapat menghasilkan `20G` instruksi pencocokan karakter `CPU`. + +```php +array( + 'open_eof_split' => true, //mengaktifkan deteksi EOF_SPLIT + 'package_eof' => "\r\n", //mengatur EOF +) +``` + +* **Tips** + + * Setelah mengaktifkan parameter `open_eof_split`, level bawah akan mencari `EOF` dari tengah paket data, dan memisahkan paket data. [onReceive](/server/events?id=onreceive) setiap kali hanya menerima satu paket data yang diakhiri dengan string `EOF`. + * Setelah mengaktifkan parameter `open_eof_split`, terlepas dari apakah parameter `open_eof_check` diatur atau tidak, `open_eof_split` akan tetap berlaku. + + * **Perbedaan dengan `open_eof_check`** + + * `open_eof_check` hanya memeriksa apakah akhir data yang diterima adalah `EOF`, sehingga kinerjanya paling baik, hampir tanpa konsumsi + * `open_eof_check` tidak dapat mengatasi masalah penggabungan beberapa paket data, misalnya mengirim dua data dengan `EOF` secara bersamaan, level bawah mungkin mengembalikan semuanya sekaligus + * `open_eof_split` akan membandingkan data byte per byte dari kiri ke kanan, mencari `EOF` dalam data untuk memisahkan paket, kinerjanya lebih buruk. Tetapi setiap kali hanya mengembalikan satu paket data + +### package_eof + +?> **Mengatur string `EOF`.** Lihat [Masalah Batas Paket Data TCP](/learn?id=masalah-batas-paket-data-tcp) + +?> Perlu digunakan bersama dengan `open_eof_check` atau `open_eof_split`. + +* **Catatan** + + !> `package_eof` maksimum hanya diizinkan memasukkan string `8` byte + +### open_length_check + +?> **Mengaktifkan fitur deteksi panjang paket**【Default:`false`】,lihat [Masalah Batas Paket Data TCP](/learn?id=masalah-batas-paket-data-tcp) + +?> Deteksi panjang paket menyediakan parsing format protokol header tetap + body. Setelah diaktifkan, dapat memastikan proses `Worker` [onReceive](/server/events?id=onreceive) setiap kali menerima satu paket data lengkap. +Protokol deteksi panjang hanya perlu menghitung panjang sekali, pemrosesan data hanya melakukan offset pointer, kinerja sangat tinggi, **disarankan digunakan**. + +* **Tips** + + * **Protokol panjang menyediakan 3 opsi untuk mengontrol detail protokol.** + + ?> Konfigurasi ini hanya valid untuk `Socket` tipe `STREAM`, seperti [TCP, Unix Socket Stream](/server/methods?id=__construct) + + * **package_length_type** + + ?> Beberapa field dalam header paket sebagai nilai panjang paket, level bawah mendukung 10 jenis panjang. Silakan lihat [package_length_type](/server/setting?id=package_length_type) + + * **package_body_offset** + + ?> Mulai dari byte ke berapa untuk menghitung panjang, umumnya ada 2 situasi: + + * Nilai `length` mencakup seluruh paket (header + body), `package_body_offset` adalah `0` + * Panjang header adalah `N` byte, nilai `length` tidak termasuk header, hanya mencakup body, `package_body_offset` diatur ke `N` + + * **package_length_offset** + + ?> Nilai panjang `length` berada di byte ke berapa dalam header. + + * Contoh: + + ```c + struct + { + uint32_t type; + uint32_t uid; + uint32_t length; + uint32_t serid; + char body[0]; + } + ``` + + ?> Dalam desain protokol komunikasi di atas, panjang header adalah `4` integer, `16` byte, nilai `length` berada pada integer ke-`3`. Oleh karena itu `package_length_offset` diatur ke `8`, `0-3` byte adalah `type`, `4-7` byte adalah `uid`, `8-11` byte adalah `length`, `12-15` byte adalah `serid`. + + ```php + $server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'N', + 'package_length_offset' => 8, + 'package_body_offset' => 16, + )); + ``` + +### package_length_type + +?> **Tipe nilai panjang**, menerima satu parameter karakter, konsisten dengan fungsi [pack](http://php.net/manual/en/function.pack.php) `PHP`. + +Saat ini `Swoole` mendukung `10` jenis: + +Parameter Karakter | Fungsi +---|--- +c | Signed, 1 byte +C | Unsigned, 1 byte +s | Signed, host byte order, 2 byte +S | Unsigned, host byte order, 2 byte +n | Unsigned, network byte order, 2 byte +N | Unsigned, network byte order, 4 byte +l | Signed, host byte order, 4 byte (huruf L kecil) +L | Unsigned, host byte order, 4 byte (huruf L besar) +v | Unsigned, little-endian byte order, 2 byte +V | Unsigned, little-endian byte order, 4 byte + +### package_length_func + +?> **Mengatur fungsi parsing panjang** + +?> Mendukung `2` jenis fungsi: `C++` atau `PHP`. Fungsi panjang harus mengembalikan integer. + +Angka Kembalian | Fungsi +---|--- +Mengembalikan 0 | Data panjang tidak cukup, perlu menerima lebih banyak data +Mengembalikan -1 | Error data, level bawah akan otomatis menutup koneksi +Mengembalikan nilai panjang paket (termasuk total panjang header dan body) | Level bawah akan otomatis menggabungkan paket dan mengembalikannya ke fungsi callback + +* **Tips** + + * **Cara Penggunaan** + + ?> Prinsip implementasi adalah membaca sebagian kecil data terlebih dahulu, di dalam data ini terdapat nilai panjang. Kemudian mengembalikan panjang ini ke level bawah. Kemudian level bawah menyelesaikan penerimaan data sisanya dan menggabungkannya menjadi satu paket untuk di `dispatch`. + + * **Fungsi Parsing Panjang PHP** + + ?> Karena `ZendVM` tidak mendukung berjalan di lingkungan multi-thread, level bawah akan secara otomatis menggunakan `Mutex` untuk mengunci fungsi panjang `PHP`, menghindari eksekusi fungsi `PHP` secara concurrent. Tersedia di versi `1.9.3` atau yang lebih baru. + + !> Jangan melakukan operasi blocking `IO` dalam fungsi parsing panjang, dapat menyebabkan semua thread [Reactor](/learn?id=reactor-thread) terblokir. + + ```php + $server = new Swoole\Server("127.0.0.1", 9501); + + $server->set(array( + 'open_length_check' => true, + 'dispatch_mode' => 1, + 'package_length_func' => function ($data) { + if (strlen($data) < 8) { + return 0; + } + $length = intval(trim(substr($data, 0, 8))); + if ($length <= 0) { + return -1; + } + return $length + 8; + }, + 'package_max_length' => 2000000, //panjang maksimum protokol + )); + + $server->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { + var_dump($data); + echo "#{$server->worker_id}>> received length=" . strlen($data) . "\n"; + }); + + $server->start(); + ``` + + * **Fungsi Parsing Panjang C++** + + ?> Di ekstensi PHP lainnya, gunakan `swoole_add_function` untuk mendaftarkan fungsi panjang ke engine `Swoole`. + + !> Saat fungsi panjang C++ dipanggil, level bawah tidak akan mengunci, pemanggil harus menjamin keamanan thread sendiri. + + ```c++ + #include + #include + #include "swoole.h" + + using namespace std; + + int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length); + + void register_length_function(void) + { + swoole_add_function((char *) "test_get_length", (void *) test_get_length); + return SW_OK; + } + + int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length) + { + printf("cpp, size=%d\n", length); + return 100; + } + ``` + +### package_max_length + +?> **Mengatur ukuran maksimum paket data, dalam satuan byte.**【Default:`2M` yaitu `2 * 1024 * 1024`, minimum `64K`】 + +?> Setelah mengaktifkan parsing protokol [open_length_check](/server/setting?id=open_length_check)/[open_eof_check](/server/setting?id=open_eof_check)/[open_eof_split](/server/setting?id=open_eof_split)/[open_http_protocol](/server/setting?id=open_http_protocol)/[open_http2_protocol](/http_server?id=open_http2_protocol)/[open_websocket_protocol](/server/setting?id=open_websocket_protocol)/[open_mqtt_protocol](/server/setting?id=open_mqtt_protocol) dll., level bawah `Swoole` akan melakukan penggabungan paket data. Saat paket data belum diterima lengkap, semua data disimpan di memori. +Oleh karena itu perlu mengatur `package_max_length`, ukuran memori maksimum yang diizinkan untuk satu paket data. Jika ada 10.000 koneksi `TCP` yang mengirim data secara bersamaan, setiap paket data `2M`, maka dalam kondisi paling ekstrem, akan memakan `20G` ruang memori. + +* **Tips** + + * `open_length_check`: Saat menemukan panjang paket melebihi `package_max_length`, akan langsung membuang data ini, dan menutup koneksi, tidak akan memakan memori apa pun; + * `open_eof_check`: Karena tidak dapat mengetahui panjang paket terlebih dahulu, data yang diterima tetap akan disimpan ke memori, terus bertambah. Saat memori yang digunakan melebihi `package_max_length`, akan langsung membuang data ini, dan menutup koneksi; + * `open_http_protocol`: Permintaan `GET` maksimum diizinkan `8K`, dan tidak dapat mengubah konfigurasi. Permintaan `POST` akan mendeteksi `Content-Length`, jika `Content-Length` melebihi `package_max_length`, akan langsung membuang data ini, mengirim error `http 400`, dan menutup koneksi; + +* **Catatan** + + !> Parameter ini jangan diatur terlalu besar, karena akan memakan memori yang besar + +### open_http_protocol + +?> **Mengaktifkan pemrosesan protokol `HTTP`.**【Default:`false`】 + +?> Mengaktifkan pemrosesan protokol `HTTP`, [Swoole\Http\Server](/http_server) akan otomatis mengaktifkan opsi ini. Diatur ke `false` berarti menonaktifkan pemrosesan protokol `HTTP`. + +### open_mqtt_protocol + +?> **Mengaktifkan pemrosesan protokol `MQTT`.**【Default:`false`】 + +?> Setelah diaktifkan, akan mem-parsing header `MQTT`, proses `worker` [onReceive](/server/events?id=onreceive) setiap kali akan mengembalikan satu paket data `MQTT` lengkap. + +```php +$server->set(array( + 'open_mqtt_protocol' => true +)); +``` + +### open_redis_protocol + +?> **Mengaktifkan pemrosesan protokol `Redis`.**【Default:`false`】 + +?> Setelah diaktifkan, akan mem-parsing protokol `Redis`, proses `worker` [onReceive](/server/events?id=onreceive) setiap kali akan mengembalikan satu paket `Redis` lengkap. Disarankan langsung menggunakan [Redis\Server](/redis_server) + +```php +$server->set(array( + 'open_redis_protocol' => true +)); +``` + +### open_websocket_protocol + +?> **Mengaktifkan pemrosesan protokol `WebSocket`.**【Default:`false`】 + +?> Mengaktifkan pemrosesan protokol `WebSocket`, [Swoole\WebSocket\Server](websocket_server) akan otomatis mengaktifkan opsi ini. Diatur ke `false` berarti menonaktifkan pemrosesan protokol `websocket`. +Mengatur opsi `open_websocket_protocol` ke `true` setelahnya, akan otomatis mengatur protokol `open_http_protocol` juga menjadi `true`. + +### open_websocket_close_frame + +?> **Mengaktifkan frame penutup dalam protokol websocket.**【Default:`false`】 + +?> (Frame dengan `opcode` `0x08`) diterima dalam callback `onMessage` + +?> Setelah diaktifkan, dapat menerima frame penutup yang dikirim oleh klien atau server dalam callback `onMessage` di `WebSocketServer`. Pengembang dapat memprosesnya sendiri. + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + +$server->set(array("open_websocket_close_frame" => true)); + +$server->on('open', function (Swoole\WebSocket\Server $server, $request) {}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x08) { + echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) {}); + +$server->start(); +``` + +### open_tcp_nodelay + +?> **Mengaktifkan `open_tcp_nodelay`.**【Default:`false`】 + +?> Setelah diaktifkan, saat koneksi `TCP` mengirim data, algoritma penggabungan `Nagle` akan dimatikan, data akan segera dikirim ke koneksi TCP lawan. Dalam beberapa skenario, seperti terminal command line, mengetik perintah perlu segera dikirim ke server, dapat meningkatkan kecepatan respons. Silakan Google algoritma Nagle sendiri. + +### open_cpu_affinity + +?> **Mengaktifkan pengaturan CPU affinity.**【Default `false`】 + +?> Di platform hardware multi-core, mengaktifkan fitur ini akan mengikat `reactor thread`/`worker process` `Swoole` ke core yang tetap. Ini dapat menghindari proses/thread berpindah-pindah antar core saat runtime, meningkatkan tingkat hit `CPU` `Cache`. + +* **Tips** + + * **Menggunakan perintah taskset untuk melihat pengaturan CPU affinity proses:** + + ```bash + taskset -p ProcessID + pid 24666's current affinity mask: f + pid 24901's current affinity mask: 8 + ``` + + > mask adalah angka mask, dihitung per `bit`, setiap `bit` sesuai dengan satu core `CPU`. Jika suatu bit adalah `0` berarti mengikat core ini, proses akan dijadwalkan ke `CPU` ini. Contohnya, proses dengan `pid` `24666` memiliki `mask = f` yang berarti tidak terikat ke `CPU`, sistem operasi akan menjadwalkan proses ini ke core `CPU` mana pun. Proses dengan `pid` `24901` memiliki `mask = 8`, `8` dalam biner adalah `1000`, berarti proses ini terikat ke core `CPU` ke-`4`. + +### cpu_affinity_ignore + +?> **Dalam program intensif IO, semua interupsi jaringan ditangani oleh CPU0. Jika jaringan IO sangat berat, beban CPU0 yang terlalu tinggi dapat menyebabkan interupsi jaringan tidak dapat ditangani tepat waktu, sehingga kemampuan mengirim dan menerima paket jaringan akan menurun.** + +?> Jika tidak mengatur opsi ini, swoole akan menggunakan semua core CPU. Level bawah akan mengatur pengikatan CPU berdasarkan reactor_id atau worker_id modulo jumlah core CPU. +Jika kernel dan kartu jaringan memiliki fitur multi-queue, interupsi jaringan akan didistribusikan ke multi-core, dapat mengurangi tekanan interupsi jaringan. + +```php +array('cpu_affinity_ignore' => array(0, 1)) // Menerima array sebagai parameter, array(0, 1) berarti tidak menggunakan CPU0, CPU1, khusus dikosongkan untuk menangani interupsi jaringan. +``` + +* **Tips** + + * **Melihat interupsi jaringan** + +```shell +[~]$ cat /proc/interrupts + CPU0 CPU1 CPU2 CPU3 + 0: 1383283707 0 0 0 IO-APIC-edge timer + 1: 3 0 0 0 IO-APIC-edge i8042 + 3: 11 0 0 0 IO-APIC-edge serial + 8: 1 0 0 0 IO-APIC-edge rtc + 9: 0 0 0 0 IO-APIC-level acpi + 12: 4 0 0 0 IO-APIC-edge i8042 + 14: 25 0 0 0 IO-APIC-edge ide0 + 82: 85 0 0 0 IO-APIC-level uhci_hcd:usb5 + 90: 96 0 0 0 IO-APIC-level uhci_hcd:usb6 +114: 1067499 0 0 0 PCI-MSI-X cciss0 +130: 96508322 0 0 0 PCI-MSI eth0 +138: 384295 0 0 0 PCI-MSI eth1 +169: 0 0 0 0 IO-APIC-level ehci_hcd:usb1, uhci_hcd:usb2 +177: 0 0 0 0 IO-APIC-level uhci_hcd:usb3 +185: 0 0 0 0 IO-APIC-level uhci_hcd:usb4 +NMI: 11370 6399 6845 6300 +LOC: 1383174675 1383278112 1383174810 1383277705 +ERR: 0 +MIS: 0 +``` + +`eth0/eth1` adalah jumlah interupsi jaringan. Jika `CPU0 - CPU3` terdistribusi secara merata, itu membuktikan kartu jaringan memiliki fitur multi-queue. Jika semuanya terpusat pada satu core, itu berarti semua interupsi jaringan diproses oleh `CPU` ini. Setelah `CPU` ini melebihi `100%`, sistem tidak akan dapat memproses permintaan jaringan. Pada saat ini, perlu menggunakan `cpu_affinity_ignore` untuk mengosongkan `CPU` ini, khusus untuk menangani interupsi jaringan. + +Seperti pada gambar di atas, harus diatur `cpu_affinity_ignore => array(0)` + +?> Dapat menggunakan perintah `top` `->` masukkan `1`, untuk melihat penggunaan setiap core + +* **Catatan** + + !> Opsi ini harus diatur bersamaan dengan `open_cpu_affinity` agar berlaku + +### tcp_defer_accept + +?> **Mengaktifkan fitur `tcp_defer_accept`**【Default:`false`】 + +?> Dapat diatur ke nilai numerik, menunjukkan bahwa `accept` hanya dipicu saat koneksi `TCP` memiliki data yang dikirim. + +```php +$server->set(array( + 'tcp_defer_accept' => 5 +)); +``` + +* **Tips** + + * **Setelah mengaktifkan fitur `tcp_defer_accept`, waktu yang sesuai untuk `accept` dan [onConnect](/server/events?id=onconnect) akan berubah. Jika diatur ke `5` detik:** + + * Klien terhubung ke server tidak akan segera memicu `accept` + * Dalam `5` detik, klien mengirim data, saat ini akan memicu `accept/onConnect/onReceive` secara berurutan + * Dalam `5` detik, klien tidak mengirim data apa pun, saat ini akan memicu `accept/onConnect` + +### ssl_cert_file / ssl_key_file :id=ssl_cert_file + +?> **Mengatur enkripsi tunnel SSL.** + +?> Nilai yang diatur adalah string nama file, menentukan path cert certificate dan key private key. + +* **Tips** + + * **Konversi format `PEM` ke `DER`** + + ```shell + openssl x509 -in cert.crt -outform der -out cert.der + ``` + + * **Konversi format `DER` ke `PEM`** + + ```shell + openssl x509 -in cert.crt -inform der -outform pem -out cert.pem + ``` + +* **Catatan** + + !> -Aplikasi `HTTPS` browser harus mempercayai certificate untuk dapat browsing web; + -Dalam aplikasi `wss`, halaman yang memulai koneksi `WebSocket` harus menggunakan `HTTPS`; + -Jika browser tidak mempercayai `SSL` certificate, tidak dapat menggunakan `wss`; + -File harus dalam format `PEM`, tidak mendukung format `DER`, dapat menggunakan alat `openssl` untuk konversi. + + !> Menggunakan `SSL` harus menambahkan opsi [--enable-openssl](/environment?id=opsi-kompilasi) saat mengkompilasi `Swoole` + + ```php + $server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $server->set(array( + 'ssl_cert_file' => __DIR__.'/config/ssl.crt', + 'ssl_key_file' => __DIR__.'/config/ssl.key', + )); + ``` + +### ssl_method + +!> Parameter ini telah dihapus di versi [v4.5.4](/version/bc?id=_454), gunakan `ssl_protocols` + +?> **Mengatur algoritma enkripsi tunnel OpenSSL.**【Default:`SWOOLE_SSLv23_METHOD`], jenis yang didukung lihat [Metode Enkripsi SSL](/consts?id=metode-enkripsi-ssl) + +?> Algoritma yang digunakan `Server` dan `Client` harus konsisten, jika tidak, handshake `SSL/TLS` akan gagal, koneksi akan diputus. + +```php +$server->set(array( + 'ssl_method' => SWOOLE_SSLv3_CLIENT_METHOD, +)); +``` + +### ssl_protocols + +?> **Mengatur protokol enkripsi tunnel OpenSSL.**【Default:`0`, mendukung semua protokol], jenis yang didukung lihat [Protokol SSL](/consts?id=protokol-ssl) + +!> Tersedia di Swoole versi >= `v4.5.4` + +```php +$server->set(array( + 'ssl_protocols' => 0, +)); +``` + +### ssl_sni_certs + +?> **Mengatur certificate SNI (Server Name Identification)** + +!> Tersedia di Swoole versi >= `v4.6.0` + +```php +$server->set([ + 'ssl_cert_file' => __DIR__ . '/server.crt', + 'ssl_key_file' => __DIR__ . '/server.key', + 'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2, + 'ssl_sni_certs' => [ + 'cs.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_cs_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_cs_key.pem', + ], + 'uk.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_uk_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_uk_key.pem', + ], + 'us.php.net' => [ + 'ssl_cert_file' => __DIR__ . '/sni_server_us_cert.pem', + 'ssl_key_file' => __DIR__ . '/sni_server_us_key.pem', + ], + ] +]); +``` + +### ssl_ciphers + +?> **Mengatur algoritma enkripsi openssl.**【Default:`EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH`】 + +```php +$server->set(array( + 'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP', +)); +``` + +* **Tips** + + * Jika `ssl_ciphers` diatur ke string kosong, `openssl` akan memilih algoritma enkripsi sendiri + +### ssl_verify_peer + +?> **Pengaturan SSL server untuk memverifikasi certificate lawan.**【Default:`false`】 + +?> Default dimatikan, yaitu tidak memverifikasi certificate klien. Jika diaktifkan, harus mengatur opsi `ssl_client_cert_file` secara bersamaan. + +### ssl_allow_self_signed + +?> **Mengizinkan self-signed certificate.**【Default:`false`】 + +### ssl_client_cert_file + +?> **Root certificate, digunakan untuk memverifikasi certificate klien.** + +```php +$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set(array( + 'ssl_cert_file' => __DIR__ . '/config/ssl.crt', + 'ssl_key_file' => __DIR__ . '/config/ssl.key', + 'ssl_verify_peer' => true, + 'ssl_allow_self_signed' => true, + 'ssl_client_cert_file' => __DIR__ . '/config/ca.crt', +)); +``` + +!> Jika verifikasi `TCP` gagal, level bawah akan secara otomatis menutup koneksi. + +### ssl_compress + +?> **Mengatur apakah akan mengaktifkan kompresi `SSL/TLS`.** Saat digunakan di [Co\Client](/coroutine_client/client), memiliki alias `ssl_disable_compression` + +### ssl_verify_depth + +?> **Jika rantai certificate terlalu dalam, melebihi nilai yang ditetapkan opsi ini, maka verifikasi akan dihentikan.** + +### ssl_prefer_server_ciphers + +?> **Mengaktifkan perlindungan sisi server, mencegah serangan BEAST.** + +### ssl_dhparam + +?> **Menentukan parameter `Diffie-Hellman` untuk cipher DHE.** + +### ssl_ecdh_curve + +?> **Menentukan `curve` yang digunakan dalam pertukaran kunci ECDH.** + +```php +$server = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$server->set([ + 'ssl_compress' => true, + 'ssl_verify_depth' => 10, + 'ssl_prefer_server_ciphers' => true, + 'ssl_dhparam' => '', + 'ssl_ecdh_curve' => '', +]); +``` + +### user + +?> **Mengatur pengguna yang memiliki proses `Worker/TaskWorker`.**【Default:pengguna yang menjalankan script】 + +?> Jika server perlu mendengarkan port di bawah `1024`, harus memiliki hak `root`. Tetapi jika program berjalan di bawah pengguna `root`, begitu ada celah dalam kode, penyerang dapat menjalankan perintah jarak jauh sebagai `root`, risikonya besar. Setelah mengonfigurasi item `user`, proses utama dapat berjalan dengan hak `root`, sementara proses anak berjalan dengan hak pengguna biasa. + +```php +$server->set(array( + 'user' => 'Apache' +)); +``` + +* **Catatan** + + !> -Hanya efektif saat dijalankan dengan pengguna `root` + -Setelah menggunakan item konfigurasi `user/group` untuk mengatur proses kerja sebagai pengguna biasa, tidak akan dapat memanggil method `shutdown`/[reload](/server/methods?id=reload) dalam proses kerja untuk menutup atau me-restart layanan. Hanya dapat menggunakan akun `root` di terminal `shell` untuk menjalankan perintah `kill`. + +### group + +?> **Mengatur grup pengguna proses `Worker/TaskWorker`.**【Default:grup pengguna yang menjalankan script】 + +?> Sama seperti konfigurasi `user`, konfigurasi ini mengubah grup pengguna proses, meningkatkan keamanan program server. + +```php +$server->set(array( + 'group' => 'www-data' +)); +``` + +* **Catatan** + + !> Hanya efektif saat dijalankan dengan pengguna `root` + +### chroot + +?> **Mengarahkan ulang root filesystem proses `Worker`.** + +?> Pengaturan ini dapat mengisolasi proses baca/tulis filesystem dari filesystem sistem operasi yang sebenarnya. Meningkatkan keamanan. + +```php +$server->set(array( + 'chroot' => '/data/server/' +)); +``` + +### pid_file + +?> **Mengatur alamat file pid.** + +?> Saat `Server` dimulai, secara otomatis menulis `PID` proses `master` ke file. Saat `Server` ditutup, secara otomatis menghapus file `PID`. + +```php +$server->set(array( + 'pid_file' => __DIR__.'/server.pid', +)); +``` + +* **Catatan** + + !> Perlu diperhatikan bahwa jika `Server` berakhir tidak normal, file `PID` tidak akan dihapus. Perlu menggunakan [Swoole\Process::kill($pid, 0)](/process/process?id=kill) untuk mendeteksi apakah proses benar-benar ada. + +### buffer_input_size / input_buffer_size :id=buffer_input_size + +?> **Mengatur ukuran memori buffer input.**【Default:`2M`】 + +```php +$server->set([ + 'buffer_input_size' => 2 * 1024 * 1024, +]); +``` + +### buffer_output_size / output_buffer_size :id=buffer_output_size + +?> **Mengatur ukuran memori buffer output pengiriman.**【Default:`2M`】 + +```php +$server->set([ + 'buffer_output_size' => 32 * 1024 * 1024, //harus berupa angka +]); +``` + +* **Tips** + + !> Swoole versi >= `v4.6.7`, nilai default adalah nilai maksimum unsigned INT `UINT_MAX` + + * Satuan byte, default `2M`, misalnya mengatur `32 * 1024 * 1024` berarti satu kali `Server->send` maksimum diizinkan mengirim `32M` byte data + * Saat memanggil `Server->send`, `Http\Server->end/write`, `WebSocket\Server->push` dan instruksi pengiriman data lainnya, data maksimum yang dikirim `sekali` tidak boleh melebihi konfigurasi `buffer_output_size`. + + !> Parameter ini hanya berlaku untuk mode [SWOOLE_PROCESS](/learn?id=swoole_process), karena dalam mode PROCESS, data proses Worker harus dikirim ke proses utama kemudian ke klien, sehingga setiap proses Worker dan proses utama membuka buffer. [Lihat](/learn?id=reactor-thread) + +### socket_buffer_size + +?> **Mengatur panjang buffer koneksi klien.**【Default:`2M`】 + +?> Berbeda dengan `buffer_output_size`, `buffer_output_size` adalah batasan ukuran `sekali` send dari proses worker. `socket_buffer_size` digunakan untuk mengatur total ukuran buffer komunikasi antara proses `Worker` dan `Master`, lihat mode [SWOOLE_PROCESS](/learn?id=swoole_process). + +```php +$server->set([ + 'socket_buffer_size' => 128 * 1024 *1024, //harus berupa angka, satuan byte, misalnya 128 * 1024 *1024 berarti setiap koneksi klien TCP maksimum diizinkan memiliki 128M data yang akan dikirim +]); +``` + +- **Buffer Pengiriman Data** + + - Saat proses Master mengirim data dalam jumlah besar ke klien, data tidak dapat segera dikirim. Saat ini, data yang dikirim akan disimpan di buffer memori sisi server. Parameter ini dapat menyesuaikan ukuran buffer memori. + + - Jika data yang dikirim terlalu banyak, setelah buffer penuh, `Server` akan melaporkan informasi error berikut: + + ```bash + swFactoryProcess_finish: send failed, session#1 output buffer has been overflowed. + ``` + + ?> Kegagalan `send` akibat buffer penuh hanya memengaruhi klien saat ini, klien lain tidak terpengaruh. + Saat server memiliki banyak koneksi `TCP`, dalam kondisi terburuk, akan memakan memori sebesar `serv->max_connection * socket_buffer_size` byte. + + - Terutama program server komunikasi eksternal, komunikasi jaringan lambat. Jika terus-menerus mengirim data, buffer akan cepat penuh. Data yang dikirim akan menumpuk di memori `Server`. Oleh karena itu, aplikasi semacam ini harus mempertimbangkan kapasitas transmisi jaringan dari segi desain, simpan pesan ke disk terlebih dahulu, dan kirim data baru setelah klien memberi tahu server bahwa penerimaan selesai. + + - Seperti layanan live streaming video, bandwidth pengguna `A` adalah `100M`, mengirim `10M` data dalam `1` detik sangat mungkin. Bandwidth pengguna `B` hanya `1M`, jika mengirim `10M` data dalam `1` detik, pengguna `B` mungkin memerlukan `100` detik untuk menerima selesai. Pada saat ini, semua data akan menumpuk di memori server. + + - Dapat melakukan pemrosesan yang berbeda berdasarkan jenis konten data. Jika konten dapat dibuang, seperti layanan live streaming video, membuang beberapa frame data dalam kondisi jaringan buruk sepenuhnya bisa diterima. Jika konten tidak boleh hilang, seperti pesan WeChat, dapat disimpan ke disk server terlebih dahulu, dengan grup `100` pesan. Setelah pengguna menerima grup pesan ini, ambil grup pesan berikutnya dari disk dan kirim ke klien. + +### enable_unsafe_event + +?> **Mengaktifkan event `onConnect/onClose`.**【Default:`false`】 + +?> Setelah `Swoole` mengkonfigurasi [dispatch_mode](/server/setting?id=dispatch_mode)=1 atau `3`, karena sistem tidak dapat menjamin urutan `onConnect/onReceive/onClose`, event `onConnect/onClose` dimatikan secara default. +Jika aplikasi memerlukan event `onConnect/onClose`, dan dapat menerima risiko keamanan yang mungkin ditimbulkan oleh masalah urutan, dapat mengatur `enable_unsafe_event` menjadi `true` untuk mengaktifkan event `onConnect/onClose`. + +### discard_timeout_request + +?> **Membuang permintaan data dari koneksi yang sudah ditutup.**【Default:`true`】 + +?> Setelah `Swoole` mengkonfigurasi [dispatch_mode](/server/setting?id=dispatch_mode)=`1` atau `3`, sistem tidak dapat menjamin urutan `onConnect/onReceive/onClose`, sehingga mungkin ada beberapa data permintaan yang sampai ke proses `Worker` setelah koneksi ditutup. + +* **Tips** + + * Konfigurasi `discard_timeout_request` default `true`, berarti jika proses `worker` menerima data permintaan dari koneksi yang sudah ditutup, akan otomatis dibuang. + * Jika `discard_timeout_request` diatur ke `false`, berarti terlepas dari apakah koneksi ditutup, proses `Worker` akan tetap memproses permintaan data. + +### enable_reuse_port + +?> **Mengatur reuse port.**【Default:`false`】 + +?> Setelah mengaktifkan reuse port, dapat memulai ulang program Server yang mendengarkan port yang sama. + +* **Tips** + + * `enable_reuse_port = true` mengaktifkan reuse port + * `enable_reuse_port = false` menonaktifkan reuse port + +!> Hanya tersedia di kernel `Linux-3.9.0` atau yang lebih baru, dan `Swoole4.5` atau yang lebih baru. + +### enable_delay_receive + +?> **Mengatur agar setelah `accept` koneksi klien, tidak akan otomatis bergabung ke [EventLoop](/learn?id=apa-itu-eventloop).**【Default:`false`】 + +?> Setelah mengatur opsi ini ke `true`, setelah `accept` koneksi klien, tidak akan otomatis bergabung ke [EventLoop](/learn?id=apa-itu-eventloop), hanya memicu callback [onConnect](/server/events?id=onconnect). Proses `worker` dapat memanggil [$server->confirm($fd)](/server/methods?id=confirm) untuk mengonfirmasi koneksi. Saat itu, `fd` akan ditambahkan ke [EventLoop](/learn?id=apa-itu-eventloop) untuk mulai melakukan pengiriman dan penerimaan data. Juga dapat memanggil `$server->close($fd)` untuk menutup koneksi ini. + +```php +//mengaktifkan opsi enable_delay_receive +$server->set(array( + 'enable_delay_receive' => true, +)); + +$server->on("Connect", function ($server, $fd, $reactorId) { + $server->after(2000, function() use ($server, $fd) { + //mengonfirmasi koneksi, mulai menerima data + $server->confirm($fd); + }); +}); +``` + +### reload_async + +?> **Mengatur sakelar restart asynchronous.**【Default:`true`】 + +?> Mengatur sakelar restart asynchronous. Jika diatur ke `true`, fitur restart aman asynchronous akan diaktifkan. Proses `Worker` akan menunggu event asynchronous selesai sebelum keluar. Informasi detail lihat [Cara me-restart service dengan benar](/question/use?id=cara-me-restart-service-swoole-dengan-benar) + +?> Tujuan utama mengaktifkan `reload_async` adalah untuk memastikan bahwa coroutine atau tugas asynchronous dapat berakhir secara normal saat service di-reload. + +```php +$server->set([ + 'reload_async' => true +]); +``` + +* **Mode Coroutine** + + * Di versi `4.x`, saat [enable_coroutine](/server/setting?id=enable_coroutine) diaktifkan, level bawah akan menambahkan deteksi jumlah coroutine. Saat tidak ada coroutine sama sekali, proses baru akan keluar. Saat diaktifkan, meskipun `reload_async => false`, `reload_async` akan tetap dipaksa aktif. + +### max_wait_time + +?> **Mengatur waktu tunggu maksimum setelah proses `Worker` menerima notifikasi penghentian service**【Default:`3`】 + +?> Sering ditemui masalah karena `worker` terblokir sehingga `worker` tidak dapat `reload` secara normal, tidak dapat memenuhi beberapa skenario produksi, seperti merilis pembaruan kode panas yang perlu `reload` proses. Oleh karena itu, Swoole menambahkan opsi waktu timeout restart proses. Informasi detail lihat [Cara me-restart service dengan benar](/question/use?id=cara-me-restart-service-swoole-dengan-benar) + +* **Tips** + + * **Setelah proses manajemen menerima sinyal restart/tutup atau mencapai `max_request`, proses manajemen akan me-restart proses `worker`. Terdiri dari beberapa langkah berikut:** + + * Level bawah akan menambahkan timer (`max_wait_time`) detik. Setelah timer terpicu, periksa apakah proses masih ada. Jika ya, akan dipaksa dimatikan, dan process baru akan diambil. + * Perlu melakukan pekerjaan penutupan di callback `onWorkerStop`, harus selesai dalam `max_wait_time` detik. + * Kirim sinyal `SIGTERM` ke proses target secara berurutan, matikan proses. + +* **Catatan** + + !> Sebelum `v4.4.x`, default adalah `30` detik + +### tcp_fastopen + +?> **Mengaktifkan fitur TCP fast open.**【Default:`false`】 + +?> Fitur ini dapat meningkatkan kecepatan respons koneksi pendek `TCP`. Saat klien menyelesaikan langkah ketiga handshake, mengirim paket `SYN` sambil membawa data. + +```php +$server->set([ + 'tcp_fastopen' => true +]); +``` + +* **Tips** + + * Parameter ini dapat diatur ke port listening. Untuk pemahaman lebih dalam, lihat [google paper](http://conferences.sigcomm.org/co-next/2011/papers/1569470463.pdf) + +### request_slowlog_file + +?> **Mengaktifkan log permintaan lambat.** Mulai dari versi `v4.4.8` [telah dihapus](https://github.com/swoole/swoole-src/commit/b1a400f6cb2fba25efd2bd5142f403d0ae303366) + +!> Karena solusi slow log ini hanya efektif dalam proses blocking synchronous, tidak dapat digunakan di lingkungan coroutine. Sementara Swoole4 default mengaktifkan coroutine, kecuali menonaktifkan `enable_coroutine`. Jadi jangan gunakan ini, gunakan alat deteksi blocking [Swoole Tracker](https://business.swoole.com/tracker/index). + +?> Setelah diaktifkan, proses `Manager` akan mengatur sinyal clock, secara berkala mendeteksi semua proses `Task` dan `Worker`. Jika proses terblokir menyebabkan permintaan melebihi waktu yang ditentukan, akan secara otomatis mencetak stack panggilan fungsi `PHP` dari proses. + +?> Level bawah diimplementasikan berdasarkan panggilan sistem `ptrace`. Beberapa sistem mungkin menonaktifkan `ptrace`, sehingga tidak dapat melacak permintaan lambat. Pastikan parameter kernel `kernel.yama.ptrace_scope` adalah `0`. + +```php +$server->set([ + 'request_slowlog_file' => '/tmp/trace.log', +]); +``` + +* **Waktu Timeout** + +```php +$server->set([ + 'request_slowlog_timeout' => 2, // mengatur waktu timeout permintaan menjadi 2 detik + 'request_slowlog_file' => '/tmp/trace.log', +]); +``` + +!> File harus memiliki izin writable, jika tidak, level bawah akan melempar fatal error saat pembuatan file gagal. + +### enable_coroutine + +?> **Apakah akan mengaktifkan dukungan coroutine untuk server gaya asynchronous** + +?> Saat `enable_coroutine` dimatikan, coroutine tidak akan dibuat secara otomatis dalam [fungsi callback event](/server/events). Jika tidak perlu menggunakan coroutine, mematikan ini akan meningkatkan kinerja. Lihat [Apa itu Swoole Coroutine](/coroutine). + +* **Metode Konfigurasi** + + * Di `php.ini` konfigurasi `swoole.enable_coroutine = 'Off'` (lihat [dokumentasi ini](/other/config.md)) + * `$server->set(['enable_coroutine' => false]);` prioritasnya lebih tinggi dari ini + +* **Lingkup Pengaruh Opsi `enable_coroutine`** + + * onWorkerStart + * onConnect + * onOpen + * onReceive + * [setHandler](/redis_server?id=sethandler) + * onPacket + * onRequest + * onMessage + * onPipeMessage + * onFinish + * onClose + * tick/after timer + +!> Setelah mengaktifkan `enable_coroutine`, coroutine akan dibuat secara otomatis dalam fungsi callback di atas. + +* Saat `enable_coroutine` diatur ke `true`, level bawah secara otomatis membuat coroutine dalam callback [onRequest](/http_server?id=on). Pengembang tidak perlu menggunakan fungsi `go` sendiri untuk [membuat coroutine](/coroutine/coroutine?id=create). +* Saat `enable_coroutine` diatur ke `false`, level bawah tidak akan membuat coroutine secara otomatis. Jika pengembang ingin menggunakan coroutine, harus menggunakan `go` sendiri untuk membuat coroutine. Jika tidak perlu menggunakan fitur coroutine, cara pemrosesannya 100% konsisten dengan `Swoole1.x`. +* Perhatikan, mengaktifkan ini hanya berarti Swoole akan memproses permintaan melalui coroutine. Jika event berisi fungsi blocking, perlu mengonfigurasi `hook_flags` terlebih dahulu atau mengaktifkan [one-click coroutine](/runtime), untuk mengaktifkan coroutine pada fungsi blocking seperti `sleep`, `mysqlnd` atau ekstensi. + +```php +$server = new Swoole\Http\Server("127.0.0.1", 9501); + +$server->set([ + //menonaktifkan coroutine bawaan + 'enable_coroutine' => false, +]); + +$server->on("request", function ($request, $response) { + if ($request->server['request_uri'] == '/coro') { + go(function () use ($response) { + co::sleep(0.2); + $response->header("Content-Type", "text/plain"); + $response->end("Hello World\n"); + }); + } else { + $response->header("Content-Type", "text/plain"); + $response->end("Hello World\n"); + } +}); + +$server->start(); +``` + +### hook_flags + +?> **Mengatur ruang lingkup fungsi Hook `one-click coroutine`.**【Default:tidak hook】 + +!> Tersedia di Swoole versi `v4.5+` atau [4.4LTS](https://github.com/swoole/swoole-src/tree/v4.4.x). Detail lihat [One-click Coroutine](/runtime) + +```php +$server->set([ + 'hook_flags' => SWOOLE_HOOK_SLEEP, +]); +``` + +Level bawah mendukung item coroutine berikut, dapat menggunakan `SWOOLE_HOOK_ALL` untuk coroutine semua: + +* `SWOOLE_HOOK_TCP` +* `SWOOLE_HOOK_UNIX` +* `SWOOLE_HOOK_UDP` +* `SWOOLE_HOOK_UDG` +* `SWOOLE_HOOK_SSL` +* `SWOOLE_HOOK_TLS` +* `SWOOLE_HOOK_SLEEP` +* `SWOOLE_HOOK_FILE` +* `SWOOLE_HOOK_STREAM_FUNCTION` +* `SWOOLE_HOOK_BLOCKING_FUNCTION` +* `SWOOLE_HOOK_PROC` +* `SWOOLE_HOOK_CURL` +* `SWOOLE_HOOK_NATIVE_CURL` +* `SWOOLE_HOOK_SOCKETS` +* `SWOOLE_HOOK_STDIO` +* `SWOOLE_HOOK_PDO_PGSQL` +* `SWOOLE_HOOK_PDO_ODBC` +* `SWOOLE_HOOK_PDO_ORACLE` +* `SWOOLE_HOOK_PDO_SQLITE` +* `SWOOLE_HOOK_ALL` + +### send_yield + +?> **Saat memori buffer tidak mencukupi saat mengirim data, langsung [yield](/coroutine?id=penjadwalan-coroutine) dalam coroutine saat ini, menunggu pengiriman data selesai. Saat buffer kosong, secara otomatis [resume](/coroutine?id=penjadwalan-coroutine) coroutine saat ini, melanjutkan `send` data.**【Default: tersedia saat [dispatch_mode](/server/setting?id=dispatch_mode) 2/4, dan default aktif】 + +* Jika `Server/Client->send` mengembalikan `false` dan kode error adalah `SW_ERROR_OUTPUT_BUFFER_OVERFLOW`, tidak mengembalikan `false` ke lapisan `PHP`, tetapi [yield](/coroutine?id=penjadwalan-coroutine) untuk menangguhkan coroutine saat ini. +* `Server/Client` memonitor apakah event buffer kosong. Setelah event ini terpicu, data dalam buffer telah dikirim selesai. Pada saat ini, [resume](/coroutine?id=penjadwalan-coroutine) coroutine yang sesuai. +* Setelah coroutine pulih, lanjutkan memanggil `Server/Client->send` untuk menulis data ke buffer. Karena buffer sudah kosong, pengiriman pasti berhasil. + +Sebelum perbaikan + +```php +for ($i = 0; $i < 100; $i++) { + //Saat buffer penuh, akan langsung mengembalikan false, dan melaporkan error output buffer overflow + $server->send($fd, $data_2m); +} +``` + +Setelah perbaikan + +```php +for ($i = 0; $i < 100; $i++) { + //Saat buffer penuh, akan yield coroutine saat ini, setelah pengiriman selesai resume untuk melanjutkan eksekusi + $server->send($fd, $data_2m); +} +``` + +!> Fitur ini akan mengubah perilaku default level bawah, dapat dimatikan secara manual. + +```php +$server->set([ + 'send_yield' => false, +]); +``` + +* **Lingkup Pengaruh** + + * [Swoole\Server::send](/server/methods?id=send) + * [Swoole\Http\Response::write](/http_server?id=write) + * [Swoole\WebSocket\Server::push](/websocket_server?id=push) + * [Swoole\Coroutine\Client::send](/coroutine_client/client?id=send) + * [Swoole\Coroutine\Http\Client::push](/coroutine_client/http_client?id=push) + +### send_timeout + +Mengatur timeout pengiriman, digunakan bersama dengan `send_yield`. Jika data tidak berhasil dikirim ke buffer dalam waktu yang ditentukan, level bawah mengembalikan `false`, dan mengatur kode error menjadi `ETIMEDOUT`. Kode error dapat diperoleh dengan method [getLastError()](/server/methods?id=getlasterror). + +> Tipe float, satuan detik, granularitas minimal milidetik + +```php +$server->set([ + 'send_yield' => true, + 'send_timeout' => 1.5, // 1.5 detik +]); + +for ($i = 0; $i < 100; $i++) { + if ($server->send($fd, $data_2m) === false and $server->getLastError() == SOCKET_ETIMEDOUT) { + echo "Timeout pengiriman\n"; + } +} +``` + +### buffer_high_watermark + +?> **Mengatur batas atas buffer, dalam satuan byte.** + +```php +$server->set([ + 'buffer_high_watermark' => 8 * 1024 * 1024, +]); +``` + +### buffer_low_watermark + +?> **Mengatur batas bawah buffer, dalam satuan byte.** + +```php +$server->set([ + 'buffer_low_watermark' => 1 * 1024 * 1024, +]); +``` + +### tcp_user_timeout + +?> Opsi TCP_USER_TIMEOUT adalah opsi socket level TCP. Nilainya adalah waktu maksimum setelah paket data dikirim tanpa menerima konfirmasi ACK, dalam satuan milidetik. Silakan lihat dokumentasi man untuk detailnya. + +```php +$server->set([ + 'tcp_user_timeout' => 10 * 1000, // 10 detik +]); +``` + +!> Tersedia di Swoole versi >= `v4.5.3-alpha` + +### stats_file + +?> **Menentukan path file untuk menulis konten [stats()](/server/methods?id=stats). Setelah diatur, secara otomatis akan mengatur timer di [onWorkerStart](/server/events?id=onworkerstart) untuk secara berkala menulis konten [stats()](/server/methods?id=stats) ke file yang ditentukan.** + +```php +$server->set([ + 'stats_file' => __DIR__ . '/stats.log', +]); +``` + +!> Tersedia di Swoole versi >= `v4.5.5` + +### event_object + +?> **Setelah mengatur opsi ini, callback event akan menggunakan [gaya objek](/server/events?id=callback-object).**【Default:`false`】 + +```php +$server->set([ + 'event_object' => true, +]); +``` + +!> Tersedia di Swoole versi >= `v4.6.0` + +### start_session_id + +?> **Mengatur session ID awal** + +```php +$server->set([ + 'start_session_id' => 10, +]); +``` + +!> Tersedia di Swoole versi >= `v4.6.0` + +### single_thread + +?> **Mengatur ke thread tunggal.** Setelah diaktifkan, thread Reactor akan bergabung dengan thread Master dalam proses Master, diproses oleh thread Master. Di PHP ZTS, jika menggunakan mode `SWOOLE_PROCESS`, pasti perlu mengatur nilai ini ke `true`. + +```php +$server->set([ + 'single_thread' => true, +]); +``` + +!> Tersedia di Swoole versi >= `v4.2.13` + +### max_queued_bytes + +?> **Mengatur panjang maksimum antrian buffer penerima.** Jika melebihi, akan berhenti menerima. + +```php +$server->set([ + 'max_queued_bytes' => 1024 * 1024, +]); +``` + +!> Tersedia di Swoole versi >= `v4.5.0` + +### admin_server + +?> **Mengatur layanan admin_server, digunakan untuk melihat informasi layanan di [Swoole Dashboard](http://dashboard.swoole.com/).** + +```php +$server->set([ + 'admin_server' => '0.0.0.0:9502', +]); +``` + +!> Tersedia di Swoole versi >= `v4.8.0` + +### bootstrap + +?> **File entry program dalam mode multi-thread, default adalah nama file script yang sedang dijalankan.** + +!> Tersedia di Swoole versi >= `v6.0`, `PHP` dalam mode `ZTS`, kompilasi `Swoole` dengan `--enable-swoole-thread` + +```php +$server->set([ + 'bootstrap' => __FILE__, +]); +``` + +### init_arguments + +?> **Mengatur data berbagi data multi-thread. Konfigurasi ini memerlukan fungsi callback. Server akan secara otomatis menjalankan fungsi ini saat startup.** + +!> Swoole memiliki banyak container thread-safe, [Concurrent Map](/thread/map), [Concurrent List](/thread/arraylist), [Concurrent Queue](/thread/queue). Jangan mengembalikan variabel yang tidak aman dalam fungsi. + +!> Tersedia di Swoole versi >= `v6.0`, `PHP` dalam mode `ZTS`, kompilasi `Swoole` dengan `--enable-swoole-thread` + +```php +$server->set([ + 'init_arguments' => function() { return new Swoole\Thread\Map(); }, +]); + +$server->on('request', function($request, $response) { + $map = Swoole\Thread::getArguments(); +}); +``` diff --git a/public/id/server/statusinfo_class.md b/public/id/server/statusinfo_class.md new file mode 100644 index 0000000..243d8fc --- /dev/null +++ b/public/id/server/statusinfo_class.md @@ -0,0 +1,40 @@ +# Swoole\Server\StatusInfo + +Berikut adalah penjelasan detail tentang `Swoole\Server\StatusInfo`. + +## Properti + +### $worker_id +Mengembalikan id proses `worker` saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\StatusInfo->worker_id +``` + +### $worker_pid +Mengembalikan id proses induk dari proses `worker` saat ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\StatusInfo->worker_pid +``` + +### $status +Mengembalikan status proses `status`. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\StatusInfo->status +``` + +### $exit_code +Mengembalikan kode status keluar proses `exit_code`. Properti ini adalah integer dengan tipe `int`, rentang `0-255`. + +```php +Swoole\Server\StatusInfo->exit_code +``` + +### $signal +Sinyal keluar proses `signal`. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\StatusInfo->signal +``` diff --git a/public/id/server/task_class.md b/public/id/server/task_class.md new file mode 100644 index 0000000..4a2d6be --- /dev/null +++ b/public/id/server/task_class.md @@ -0,0 +1,124 @@ +# Swoole\Server\Task + +Berikut adalah penjelasan detail tentang `Swoole\Server\Task`. Class ini sangat sederhana, tetapi Anda tidak bisa mendapatkan objek `Task` dengan menggunakan `new Swoole\Server\Task()`. Objek semacam itu sama sekali tidak mengandung informasi server apa pun, dan menjalankan method `Swoole\Server\Task` apa pun akan menghasilkan fatal error. + +```shell +Invalid instance of Swoole\Server\Task in /home/task.php on line 3 +``` + +## Properti + +### $data +Data `data` yang dikirimkan dari proses `worker` ke proses `task`. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\Task->data +``` + +### $dispatch_time +Mengembalikan waktu `dispatch_time` saat data mencapai proses `task`. Properti ini adalah `double`. + +```php +Swoole\Server\Task->dispatch_time +``` + +### $id +Mengembalikan id tugas. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Task->id +``` + +### $worker_id +Mengembalikan dari proses `worker` mana data berasal. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Task->worker_id +``` + +### $flags +Beberapa informasi flag `flags` dari tugas asynchronous ini. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\Task->flags +``` + +?> Hasil yang dikembalikan oleh `flags` adalah salah satu dari tipe berikut: + - SWOOLE_TASK_NOREPLY | SWOOLE_TASK_NONBLOCK menunjukkan bahwa ini bukan dikirim oleh proses `Worker` ke proses `task`. Jika `Swoole\Server::finish()` dipanggil dalam event `onTask`, akan ada peringatan. + - SWOOLE_TASK_CALLBACK | SWOOLE_TASK_NONBLOCK menunjukkan bahwa fungsi callback terakhir dalam `Swoole\Server::finish()` bukan null, event `onFinish` tidak akan dieksekusi, dan hanya fungsi callback ini yang akan dieksekusi. + - SWOOLE_TASK_COROUTINE | SWOOLE_TASK_NONBLOCK menunjukkan bahwa tugas akan diproses dalam bentuk coroutine. + - SW_TASK_NONBLOCK nilai default, ketika ketiga kondisi di atas tidak terpenuhi. + +## Method + +### finish() + +Digunakan dalam [Task process](/learn?id=taskworker-process) untuk memberi tahu proses `Worker` bahwa tugas yang dikirim telah selesai. Fungsi ini dapat mengirimkan data hasil ke proses `Worker`. + +```php +Swoole\Server\Task->finish(mixed $data): bool +``` + +* **Parameter** + + * `mixed $data` + + * Fungsi: Konten hasil dari pemrosesan tugas + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Tips** + * Method `finish` dapat dipanggil beberapa kali secara berurutan, proses `Worker` akan memicu event [onFinish](/server/events?id=onfinish) beberapa kali + * Setelah memanggil method `finish` dalam fungsi callback [onTask](/server/events?id=ontask), data `return` tetap akan memicu event [onFinish](/server/events?id=onfinish) + * `Swoole\Server\Task->finish` bersifat opsional. Jika proses `Worker` tidak peduli dengan hasil eksekusi tugas, fungsi ini tidak perlu dipanggil + * Dalam fungsi callback [onTask](/server/events?id=ontask), `return` string sama dengan memanggil `finish` + +* **Catatan** + + !> Menggunakan fungsi `Swoole\Server\Task->finish` harus mengatur callback [onFinish](/server/events?id=onfinish) untuk `Server`. Fungsi ini hanya dapat digunakan dalam callback [onTask](/server/events?id=ontask) dari [Task process](/learn?id=taskworker-process) + +### pack() + +Menserialisasi data yang diberikan. + +```php +Swoole\Server\Task->pack(mixed $data): string|false +``` + +* **Parameter** + + * `mixed $data` + + * Fungsi: Konten hasil dari pemrosesan tugas + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Return Value** + * Mengembalikan hasil serialisasi jika berhasil. + +### unpack() + +Mendeserialisasi data yang diberikan. + +```php +Swoole\Server\Task->unpack(string $data): mixed +``` + +* **Parameter** + + * `string $data` + + * Fungsi: Data yang akan dideserialisasi + * Default: Tidak ada + * Nilai Lain: Tidak ada + +* **Return Value** + * Mengembalikan hasil deserialisasi jika berhasil. + +## Contoh Penggunaan +```php +on('task', function(Swoole\Server $serv, Swoole\Server\Task $task) { + $task->finish(['result' => true]); +}); +``` diff --git a/public/id/server/taskresult_class.md b/public/id/server/taskresult_class.md new file mode 100644 index 0000000..a068b03 --- /dev/null +++ b/public/id/server/taskresult_class.md @@ -0,0 +1,33 @@ +# Swoole\Server\TaskResult + +Berikut adalah penjelasan detail tentang `Swoole\Server\TaskResult`. + +## Properti + +### $task_id +Mengembalikan id thread `Reactor` tempat tugas berada. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\TaskResult->task_id +``` + +### $task_worker_id +Mengembalikan dari proses `task` mana hasil eksekusi berasal. Properti ini adalah integer dengan tipe `int`. + +```php +Swoole\Server\TaskResult->task_worker_id +``` + +### $dispatch_time +Mengembalikan data `data` yang dibawa oleh koneksi. Properti ini adalah `?string`. + +```php +Swoole\Server\TaskResult->dispatch_time +``` + +### $data +Mengembalikan data `data` yang dibawa oleh koneksi. Properti ini adalah string dengan tipe `string`. + +```php +Swoole\Server\StatusInfo->data +``` diff --git a/public/id/server/tcp_init.md b/public/id/server/tcp_init.md new file mode 100644 index 0000000..9d6fb2d --- /dev/null +++ b/public/id/server/tcp_init.md @@ -0,0 +1,3 @@ +# Swoole\Server + +Bagian ini mencakup semua method, properti, item konfigurasi, dan event dari kelas `Swoole\Server`. Kelas `Swoole\Server` adalah kelas dasar untuk semua server gaya asynchronous, dengan `Swoole\Http\Server`, `Swoole\WebSocket\Server`, dan `Swoole\Redis\Server` di bab-bab selanjutnya sebagai subclass-nya. diff --git a/public/id/start/coroutine.md b/public/id/start/coroutine.md new file mode 100644 index 0000000..510c8dd --- /dev/null +++ b/public/id/start/coroutine.md @@ -0,0 +1,157 @@ +# Eksplorasi Coroutine + +!> Sebaiknya baca [Coroutine](/coroutine) dulu buat paham konsep dasar coroutine sebelum lanjut ke artikel ini. + +Swoole4 pake mesin kernel coroutine yang benar-benar baru. Sekarang Swoole punya tim developer khusus, jadi ini adalah masa yang belum pernah terjadi sebelumnya dalam sejarah PHP—memberi kita peluang unik buat ngebut performa secara signifikan. + +Swoole4 atau versi di atasnya punya built-in coroutine dengan ketersediaan tinggi. Kamu bisa pake kode yang sepenuhnya sinkron buat mencapai [asynchronous I/O](/learn?id=同步io异步io). Kode PHP nggak perlu keyword tambahan apa-apa, sistem bakal ngatur penjadwalan coroutine secara otomatis. + +### Berapa Banyak yang Bisa Kamu Lakukan dalam Satu Detik Pake Coroutine? + +Tidur 10.000 kali, baca, tulis, cek, dan hapus file 10.000 kali, komunikasi dengan database pake PDO dan MySQLi 10.000 kali, bikin TCP server dan komunikasi dengan banyak client 10.000 kali, bikin UDP server dan komunikasi dengan banyak client 10.000 kali... Semua selesai dengan sempurna dalam satu proses! + +```php +use Swoole\Runtime; +use Swoole\Coroutine; +use function Swoole\Coroutine\run; + +// Setelah baris ini, operasi file, sleep, Mysqli, PDO, streams, dll jadi asynchronous IO. Lihat bab 'One-Key Coroutine'. +Runtime::enableCoroutine(); +$s = microtime(true); + +// Lihat bab 'Coroutine Container' buat Swoole\Coroutine\run(). +run(function() { + // i just want to sleep... + for ($c = 100; $c--;) { + Coroutine::create(function () { + for ($n = 100; $n--;) { + usleep(1000); + } + }); + } + + // 10k file read and write + for ($c = 100; $c--;) { + Coroutine::create(function () use ($c) { + $tmp_filename = "/tmp/test-{$c}.php"; + for ($n = 100; $n--;) { + $self = file_get_contents(__FILE__); + file_put_contents($tmp_filename, $self); + assert(file_get_contents($tmp_filename) === $self); + } + unlink($tmp_filename); + }); + } + + // 10k pdo and mysqli read + for ($c = 50; $c--;) { + Coroutine::create(function () { + $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); + $statement = $pdo->prepare('SELECT * FROM `user`'); + for ($n = 100; $n--;) { + $statement->execute(); + assert(count($statement->fetchAll()) > 0); + } + }); + } + for ($c = 50; $c--;) { + Coroutine::create(function () { + $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); + $statement = $mysqli->prepare('SELECT `id` FROM `user`'); + for ($n = 100; $n--;) { + $statement->bind_result($id); + $statement->execute(); + $statement->fetch(); + assert($id > 0); + } + }); + } + + // php_stream tcp server & client with 12.8k requests in single process + function tcp_pack(string $data): string + { + return pack('n', strlen($data)) . $data; + } + + function tcp_length(string $head): int + { + return unpack('n', $head)[1]; + } + + Coroutine::create(function () { + $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); + $socket = stream_socket_server( + 'tcp://0.0.0.0:9502', + $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx + ); + if (!$socket) { + echo "{$errstr} ({$errno})\n"; + } else { + $i = 0; + while ($conn = stream_socket_accept($socket, 1)) { + stream_set_timeout($conn, 5); + for ($n = 100; $n--;) { + $data = fread($conn, tcp_length(fread($conn, 2))); + assert($data === "Hello Swoole Server #{$n}!"); + fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); + } + if (++$i === 128) { + fclose($socket); + break; + } + } + } + }); + for ($c = 128; $c--;) { + Coroutine::create(function () { + $fp = stream_socket_client('tcp://127.0.0.1:9502', $errno, $errstr, 1); + if (!$fp) { + echo "{$errstr} ({$errno})\n"; + } else { + stream_set_timeout($fp, 5); + for ($n = 100; $n--;) { + fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); + $data = fread($fp, tcp_length(fread($fp, 2))); + assert($data === "Hello Swoole Client #{$n}!"); + } + fclose($fp); + } + }); + } + + // udp server & client with 12.8k requests in single process + Coroutine::create(function () { + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $socket->bind('127.0.0.1', 9503); + $client_map = []; + for ($c = 128; $c--;) { + for ($n = 0; $n < 100; $n++) { + $recv = $socket->recvfrom($peer); + $client_uid = "{$peer['address']}:{$peer['port']}"; + $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; + assert($recv === "Client: Hello #{$id}!"); + $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); + } + } + $socket->close(); + }); + for ($c = 128; $c--;) { + Coroutine::create(function () { + $fp = stream_socket_client('udp://127.0.0.1:9503', $errno, $errstr, 1); + if (!$fp) { + echo "$errstr ($errno)\n"; + } else { + for ($n = 0; $n < 100; $n++) { + fwrite($fp, "Client: Hello #{$n}!"); + $recv = fread($fp, 1024); + list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); + assert($address === '127.0.0.1' && (int)$port === 9503); + assert($recv === "Server: Hello #{$n}!"); + } + fclose($fp); + } + }); + } +}); +echo 'use ' . (microtime(true) - $s) . ' s'; +``` diff --git a/public/id/start/start_http_server.md b/public/id/start/start_http_server.md new file mode 100644 index 0000000..effddf9 --- /dev/null +++ b/public/id/start/start_http_server.md @@ -0,0 +1,58 @@ +# HTTP Server + +## Kode Program + +Tulis kode berikut ke dalam httpServer.php. + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->on('Request', function ($request, $response) { + $response->header('Content-Type', 'text/html; charset=utf-8'); + $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); +}); + +$http->start(); +``` + +Server `HTTP` cuma perlu fokus ke request dan response, jadi cukup listening ke event [onRequest](/http_server?id=on) aja. Event ini bakal kepanggil setiap ada request `HTTP` baru masuk. Callback function-nya punya `2` parameter: object `$request` yang isinya informasi request kayak data `GET/POST`, dan object `$response` yang bisa dipake buat ngirim response balik. `$response->end()` gunanya buat ngeluarin konten `HTML` dan sekaligus ngakhirin request. + +* `0.0.0.0` artinya listening di semua alamat `IP`. Sebuah server bisa punya beberapa `IP`, misalnya `127.0.0.1` buat local loopback, `192.168.1.100` buat LAN, `210.127.20.2` buat WAN. Kamu juga bisa specify cuma satu `IP` tertentu. +* `9501` adalah port yang di-dengarkan. Kalo port-nya udah dipake program lain, bakal muncul fatal error dan eksekusi berhenti. + +## Menjalankan Service + +```shell +php httpServer.php +``` + +* Buka browser dan akses `http://127.0.0.1:9501` buat liat hasilnya. +* Bisa juga pake tool `ab` dari Apache buat stress test server. + +## Masalah Chrome Request Dobel + +Pas pake browser `Chrome`, bakal ada request tambahan ke `/favicon.ico`. Ini bisa dihandle dengan ngembaliin response `404` di kode. + +```php +$http->on('Request', function ($request, $response) { + if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') { + $response->end(); + return; + } + var_dump($request->get, $request->post); + $response->header('Content-Type', 'text/html; charset=utf-8'); + $response->end('

Hello Swoole. #' . rand(1000, 9999) . '

'); +}); +``` + +## URL Routing + +Aplikasi bisa implementasi URL routing berdasarkan `$request->server['request_uri']`. Contoh: `http://127.0.0.1:9501/test/index/?a=1`, di kode bisa kayak gini. + +```php +$http->on('Request', function ($request, $response) { + list($controller, $action) = explode('/', trim($request->server['request_uri'], '/')); + // Map ke controller dan method yang berbeda berdasarkan $controller, $action. + (new $controller)->$action($request, $response); +}); +``` diff --git a/public/id/start/start_mqtt.md b/public/id/start/start_mqtt.md new file mode 100644 index 0000000..ca6e2bb --- /dev/null +++ b/public/id/start/start_mqtt.md @@ -0,0 +1,93 @@ +# Server MQTT (Internet of Things) + +Dengan mengaktifkan opsi [open_mqtt_protocol](/server/setting?id=open_mqtt_protocol), sistem bakal parse header `MQTT`, dan event [onReceive](/server/events?id=onreceive) di Worker process bakal ngembaliin satu paket data `MQTT` yang utuh tiap kali dipanggil. + +Kamu bisa pake Swoole sebagai server atau client MQTT buat implementasi solusi Internet of Things (IOT) yang lengkap. + +> Buat parsing protokol MQTT yang lengkap dan coroutine client, bisa pake [simps/mqtt](https://github.com/simps/mqtt) + +## Kode Program + +Tulis kode berikut ke dalam mqttServer.php + +```php +function decodeValue($data) +{ + return 256 * ord($data[0]) + ord($data[1]); +} + +function decodeString($data) +{ + $length = decodeValue($data); + return substr($data, 2, $length); +} + +function mqttGetHeader($data) +{ + $byte = ord($data[0]); + + $header['type'] = ($byte & 0xF0) >> 4; + $header['dup'] = ($byte & 0x08) >> 3; + $header['qos'] = ($byte & 0x06) >> 1; + $header['retain'] = $byte & 0x01; + + return $header; +} + +function eventConnect($header, $data) +{ + $connect_info['protocol_name'] = decodeString($data); + $offset = strlen($connect_info['protocol_name']) + 2; + + $connect_info['version'] = ord(substr($data, $offset, 1)); + $offset += 1; + + $byte = ord($data[$offset]); + $connect_info['willRetain'] = ($byte & 0x20 == 0x20); + $connect_info['willQos'] = ($byte & 0x18 >> 3); + $connect_info['willFlag'] = ($byte & 0x04 == 0x04); + $connect_info['cleanStart'] = ($byte & 0x02 == 0x02); + $offset += 1; + + $connect_info['keepalive'] = decodeValue(substr($data, $offset, 2)); + $offset += 2; + $connect_info['clientId'] = decodeString(substr($data, $offset)); + return $connect_info; +} + +$server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_BASE); + +$server->set([ + 'open_mqtt_protocol' => true, // Aktifkan protokol MQTT + 'worker_num' => 1, +]); + +$server->on('Connect', function ($server, $fd) { + echo "Client:Connect.\n"; +}); + +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + $header = mqttGetHeader($data); + var_dump($header); + + if ($header['type'] == 1) { + $resp = chr(32) . chr(2) . chr(0) . chr(0); + eventConnect($header, substr($data, 2)); + $server->send($fd, $resp); + } elseif ($header['type'] == 3) { + $offset = 2; + $topic = decodeString(substr($data, $offset)); + $offset += strlen($topic) + 2; + $msg = substr($data, $offset); + echo "client msg: {$topic}\n----------\n{$msg}\n"; + //file_put_contents(__DIR__.'/data.log', $data); + } + echo "received length=" . strlen($data) . "\n"; +}); + +$server->on('Close', function ($server, $fd) { + echo "Client: Close.\n"; +}); + +$server->start(); +``` diff --git a/public/id/start/start_server.md b/public/id/start/start_server.md new file mode 100644 index 0000000..56f203b --- /dev/null +++ b/public/id/start/start_server.md @@ -0,0 +1,17 @@ +# Contoh Sederhana + +Sebagian besar fitur `Swoole` cuma bisa dipakai di lingkungan `cli` command line. Pastikan kamu sudah punya lingkungan `Linux Shell`. Kamu bisa pakai editor kayak `Vim`, `Emacs`, `PhpStorm`, atau editor lainnya buat nulis kode, lalu jalankan programnya lewat command line dengan perintah berikut. + +```shell +php /path/to/your_file.php +``` + +Setelah program server `Swoole` berhasil dijalankan, kalo kode kamu nggak ada perintah `echo`, layar nggak bakal nampilin apa-apa. Tapi sebenarnya sistem udah mulai listening di port jaringan, siap nunggu koneksi dari client. Kamu bisa pake tool dan program client yang sesuai buat connect ke server dan melakukan testing. + +#### Manajemen Proses + +Secara default, setelah kamu jalanin service `Swoole`, kamu bisa langsung matiin pake `CTRL+C` di terminal. Tapi kalo terminal-nya ditutup, bakal timbul masalah. Makanya perlu dijalankan sebagai background process. Detailnya lihat [Daemonize](/server/setting?id=daemonize). + +!> Contoh-contoh di sini kebanyakan pake gaya pemrograman asynchronous. Kamu juga bisa mencapai fungsionalitas yang sama pake gaya coroutine. Lihat [Server (Coroutine Style)](coroutine/server.md). + +!> Sebagian besar modul yang disediakan `Swoole` cuma bisa dipake di terminal `cli`. Saat ini cuma [Synchronous Blocking Client](/client) yang bisa dipake di lingkungan `PHP-FPM`. diff --git a/public/id/start/start_task.md b/public/id/start/start_task.md new file mode 100644 index 0000000..8665e24 --- /dev/null +++ b/public/id/start/start_task.md @@ -0,0 +1,45 @@ +# Menjalankan Tugas Asinkron (Task) + +Di program server, kalo ada operasi yang butuh waktu lama—misalnya chat server yang broadcast atau web server yang ngirim email—kalo langsung dipanggil, prosesnya bakal keblokir dan server jadi lemot. + +Swoole nyediain fitur pemrosesan asynchronous task. Kamu bisa ngirim task ke pool process TaskWorker biar dieksekusi di latar belakang, nggak ganggu kecepatan proses request yang lagi berjalan. + +## Kode Program + +Berdasarkan contoh TCP server pertama, tinggal nambahin 2 event callback function: [onTask](/server/events?id=ontask) dan [onFinish](/server/events?id=onfinish). Selain itu, perlu ngatur jumlah process task sesuai dengan lama dan banyaknya task. + +Tulis kode berikut ke dalam task.php. + +```php +$serv = new Swoole\Server('127.0.0.1', 9501); + +// Set jumlah process worker untuk task. +$serv->set([ + 'task_worker_num' => 4 +]); + +// Callback ini jalan di worker process. +$serv->on('Receive', function($serv, $fd, $reactor_id, $data) { + // Kirim asynchronous task + $task_id = $serv->task($data); + echo "Dispatch AsyncTask: id={$task_id}\n"; +}); + +// Proses asynchronous task (callback ini jalan di task process). +$serv->on('Task', function ($serv, $task_id, $reactor_id, $data) { + echo "New AsyncTask[id={$task_id}]".PHP_EOL; + // Kembaliin hasil eksekusi task + $serv->finish("{$data} -> OK"); +}); + +// Proses hasil asynchronous task (callback ini jalan di worker process). +$serv->on('Finish', function ($serv, $task_id, $data) { + echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL; +}); + +$serv->start(); +``` + +Setelah manggil `$serv->task()`, program langsung balik dan lanjut jalan ke kode selanjutnya. Callback onTask bakal dieksekusi secara asynchronous di pool process Task. Begitu selesai, `$serv->finish()` dipanggil buat ngembaliin hasil. + +!> Operasi finish itu opsional—kamu bisa nggak ngembaliin hasil apa-apa. Kalo di event `onTask` kamu pake `return` buat ngembaliin hasil, itu sama aja kayak panggil `Swoole\Server::finish()`. diff --git a/public/id/start/start_tcp_server.md b/public/id/start/start_tcp_server.md new file mode 100644 index 0000000..5159c76 --- /dev/null +++ b/public/id/start/start_tcp_server.md @@ -0,0 +1,64 @@ +# TCP Server + +## Kode Program + +Tulis kode berikut ke dalam tcpServer.php. + +```php +// Buat object Server, listen di 127.0.0.1:9501. +$server = new Swoole\Server('127.0.0.1', 9501); + +// Listen event Connect. +$server->on('Connect', function ($server, $fd) { + echo "Client: Connect.\n"; +}); + +// Listen event Receive (ada data masuk). +$server->on('Receive', function ($server, $fd, $reactor_id, $data) { + $server->send($fd, "Server: {$data}"); +}); + +// Listen event Close (koneksi ditutup). +$server->on('Close', function ($server, $fd) { + echo "Client: Close.\n"; +}); + +// Jalankan server +$server->start(); +``` + +Ini bakal bikin server `TCP` yang listening di port `9501` di lokal. Logikanya sederhana: pas client ngirim string `hello` lewat socket, server bakal balas `Server: hello`. + +Karena `Server` bersifat asynchronous, kita nulis program dengan cara listening ke event. Pas event tertentu terjadi, sistem di bawahan bakal manggil function yang udah ditentukan. Misalnya pas ada koneksi `TCP` baru masuk, bakal ngejalanin callback [onConnect](/server/events?id=onconnect); pas ada koneksi yang ngirim data ke server, bakal nge-panggil function [onReceive](/server/events?id=onreceive). + +* Server bisa nanganin ribuan client sekaligus, `$fd` itu identifier unik buat setiap koneksi client. +* Panggil `$server->send()` buat ngirim data ke client tertentu, parameter `$fd` adalah identifier client-nya. +* Panggil `$server->close()` buat maksa nutup koneksi client tertentu. +* Client juga bisa nutup koneksi sendiri, nah ini bakal picu event callback [onClose](/server/events?id=onclose). + +## Menjalankan Program + +```shell +php tcpServer.php +``` + +Jalankan `server.php` di command line. Kalo udah berhasil, kamu bisa pake tool `netstat` buat liat port `9501` udah mulai listening. + +Sekarang kamu bisa pake `telnet` atau `netcat` buat connect ke server. + +```shell +telnet 127.0.0.1 9501 +hello +Server: hello +``` + +## Cara Sederhana Ngecek Kalo Gagal Connect + +* Di `Linux`, pake `netstat -an | grep ` buat liat apakah port-nya udah terbuka dan dalam status `Listening`. +* Kalo udah cek, cek firewall. +* Perhatikan IP address yang dipake server. Kalo pake `127.0.0.1` (loopback), client cuma bisa connect lewat `127.0.0.1` juga. +* Kalo pake server Alibaba Cloud atau Tencent Cloud, pastiin port-nya udah dibuka di security group. + +## Masalah Batasan Paket Data TCP + +Lihat [TCP Data Packet Boundary Issue](/learn?id=tcp数据包边界问题). diff --git a/public/id/start/start_udp_server.md b/public/id/start/start_udp_server.md new file mode 100644 index 0000000..4208960 --- /dev/null +++ b/public/id/start/start_udp_server.md @@ -0,0 +1,42 @@ +# UDP Server + +## Kode Program + +Tulis kode berikut ke dalam udpServer.php. + +```php +$server = new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + +// Listen event Packet (ada paket data masuk). +$server->on('Packet', function ($server, $data, $clientInfo) { + var_dump($clientInfo); + $server->sendto($clientInfo['address'], $clientInfo['port'], "Server: {$data}"); +}); + +// Jalankan server +$server->start(); +``` + +UDP Server beda sama TCP Server. UDP nggak punya konsep koneksi. Setelah server jalan, client nggak perlu Connect dulu, langsung bisa ngirim paket data ke port 9502 yang di-dengarkan server. Event yang dipake adalah `onPacket`. + +* `$clientInfo` berisi informasi client, berupa array yang punya IP dan port client. +* Panggil `$server->sendto` buat ngirim data balik ke client. + +!> Secara default Docker pake protokol TCP buat komunikasi. Kalo mau pake UDP, kamu harus konfigurasi network Docker-nya. +```shell +docker run -p 9502:9502/udp +``` + +## Menjalankan Service + +```shell +php udpServer.php +``` + +Kamu bisa pake `netcat -u` buat connect dan test UDP server. + +```shell +netcat -u 127.0.0.1 9502 +hello +Server: hello +``` diff --git a/public/id/start/start_ws_server.md b/public/id/start/start_ws_server.md new file mode 100644 index 0000000..225814e --- /dev/null +++ b/public/id/start/start_ws_server.md @@ -0,0 +1,65 @@ +# WebSocket Server + +## Kode Program + +Tulis kode berikut ke dalam websocketServer.php. + +```php +// Buat object WebSocket Server, listen di 0.0.0.0:9502. +$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); + +// Listen event Open (koneksi WebSocket terbuka). +$ws->on('Open', function ($ws, $request) { + $ws->push($request->fd, "hello, welcome\n"); +}); + +// Listen event Message (ada pesan masuk). +$ws->on('Message', function ($ws, $frame) { + echo "Message: {$frame->data}\n"; + $ws->push($frame->fd, "server: {$frame->data}"); +}); + +// Listen event Close (koneksi WebSocket ditutup). +$ws->on('Close', function ($ws, $fd) { + echo "client-{$fd} is closed\n"; +}); + +$ws->start(); +``` + +* Pas client ngirim pesan ke server, server bakal picu event callback `onMessage`. +* Server bisa panggil `$server->push()` buat ngirim pesan ke client tertentu (pake identifier `$fd`). + +## Menjalankan Program + +```shell +php websocketServer.php +``` + +Kamu bisa test pake Chrome browser, kode JS-nya: + +```javascript +var wsServer = 'ws://127.0.0.1:9502'; +var websocket = new WebSocket(wsServer); +websocket.onopen = function (evt) { + console.log("Connected to WebSocket server."); +}; + +websocket.onclose = function (evt) { + console.log("Disconnected"); +}; + +websocket.onmessage = function (evt) { + console.log('Retrieved data from server: ' + evt.data); +}; + +websocket.onerror = function (evt, e) { + console.log('Error occured: ' + evt.data); +}; +``` + +## Comet + +Selain WebSocket, server WebSocket juga bisa nanganin HTTP long connection. Tinggal tambahin event listener [onRequest](/http_server?id=on) buat implementasi solusi Comet (HTTP long polling). + +!> Buat detail pemakaian, lihat [Swoole\WebSocket](/websocket_server). diff --git a/public/id/stdext/array.md b/public/id/stdext/array.md new file mode 100644 index 0000000..b7e82cc --- /dev/null +++ b/public/id/stdext/array.md @@ -0,0 +1,78 @@ +# Array Method + +## Mapping Table +Misalnya `$array = [1, 2, 3]`, gunakan `$array->count()` untuk mendapatkan panjang array, setara dengan memanggil `count($array)`. + +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|---------------------|----------------------------------------------------|---------------------------------| +| all() | Mengecek apakah semua elemen memenuhi kondisi callback | array_all() | +| any() | Mengecek apakah ada elemen yang memenuhi kondisi callback | array_any() | +| changeKeyCase() | Mengubah key array ke huruf besar/kecil tertentu | array_change_key_case() | +| chunk() | Memisahkan array menjadi beberapa bagian kecil | array_chunk() | +| column() | Mengembalikan nilai kolom tertentu dari array | array_column() | +| countValues() | Menghitung frekuensi setiap nilai dalam array | array_count_values() | +| diff() | Menghitung selisih array | array_diff() | +| diffAssoc() | Menghitung selisih array asosiatif | array_diff_assoc() | +| diffKey() | Menghitung selisih key array | array_diff_key() | +| filter() | Menyaring elemen array menggunakan callback | array_filter() | +| find() | Mencari elemen pertama yang memenuhi kondisi callback | array_find() | +| flip() | Menukar key dan value array | array_flip() | +| intersect() | Menghitung irisan array | array_intersect() | +| intersectAssoc() | Menghitung irisan array asosiatif | array_intersect_assoc() | +| isList() | Mengecek apakah array adalah list | array_is_list() | +| keyExists() | Mengecek apakah key tertentu ada dalam array | array_key_exists() | +| keyFirst() | Mendapatkan key pertama array | array_key_first() | +| keyLast() | Mendapatkan key terakhir array | array_key_last() | +| keys() | Mengembalikan semua key array | array_keys() | +| map() | Memproses setiap elemen array dengan callback | array_map() | +| pad() | Mengisi array ke panjang tertentu dengan nilai tertentu | array_pad() | +| product() | Menghitung produk semua nilai dalam array | array_product() | +| rand() | Mengambil satu atau lebih elemen secara acak | array_rand() | +| reduce() | Mereduksi array menjadi nilai tunggal dengan callback | array_reduce() | +| replace() | Mengganti elemen array dengan satu atau lebih array | array_replace() | +| reverse() | Membalik urutan elemen array | array_reverse() | +| search() | Mencari nilai dalam array dan mengembalikan key-nya | array_search() | +| slice() | Mengambil sebagian array | array_slice() | +| sum() | Menghitung jumlah semua nilai dalam array | array_sum() | +| unique() | Menghapus nilai duplikat dalam array | array_unique() | +| values() | Mengembalikan semua nilai array | array_values() | +| count() | Mendapatkan jumlah elemen array | count() | +| merge() | Menggabungkan satu atau lebih array | array_merge() | +| contains() | Mengecek apakah array mengandung nilai tertentu | in_array() | +| join() | Menggabungkan elemen array menjadi string | implode() | +| isEmpty() | Mengecek apakah array kosong | empty() | + +## Array Write Method +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|------------------|----------------------------------------------------|------------------------| +| sort() | Mengurutkan array | sort() | +| pop() | Mengeluarkan elemen terakhir array | array_pop() | +| push() | Menambahkan satu atau lebih elemen ke akhir array | array_push() | +| shift() | Mengeluarkan elemen pertama array | array_shift() | +| unshift() | Menambahkan satu atau lebih elemen ke awal array | array_unshift() | +| splice() | Menghapus elemen di posisi tertentu dan bisa menyisipkan baru | array_splice() | +| walk() | Memproses setiap elemen array dengan callback | array_walk() | +| replaceStr() | Mengganti substring dalam elemen string array | str_replace() | +| iReplaceStr() | Mengganti substring (case-insensitive) dalam elemen string array | str_ireplace() | + +Perhatikan karena fungsi write array `PHP` parameter pertamanya adalah reference, tidak bisa langsung menggunakan method di atas pada array. Perlu diubah ke reference variable terlebih dahulu. + +```php +$array = [ 'apple', 'banana', 'cherry' ]; +$ref = &$array; +$ref->push('orange'); +var_dump($ref, $array); // Keduanya: [ 'apple', 'banana', 'cherry', 'orange' ] +``` + +## Array Serialization Method +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|-----------------|-----------------------------------------------|------------------------| +| serialize() | Menyerialiasi array ke string | serialize() | +| marshal() | Menyerialiasi array ke string (alias) | serialize() | +| jsonEncode() | Meng-encode array ke string `JSON` | json_encode() | + + +## Perbedaan +- `keyExists`, `map`, `replaceStr` dan `iReplaceStr` menyesuaikan urutan parameter fungsi PHP, menjadikan array yang dioperasikan sebagai parameter pertama +- Nama method `join` menggantikan fungsi PHP `implode` +- Nama method `contains` menggantikan fungsi PHP `in_array` diff --git a/public/id/stdext/index.md b/public/id/stdext/index.md new file mode 100644 index 0000000..68d5501 --- /dev/null +++ b/public/id/stdext/index.md @@ -0,0 +1,55 @@ +# Ekstensi Standard Library PHP (`stdext`) + +Mulai dari versi `6.1.0`, `Swoole` menambahkan modul `stdext` untuk memperluas standard library `PHP`, memberikan ekstensi dan perbaikan pada sintaks dasar yang lama tidak diperbaiki oleh `PHP` official tetapi sangat diinginkan oleh komunitas. + +## Instalasi +Saat kompilasi perlu menambahkan opsi `--enable-swoole-stdext` untuk mengaktifkan modul ini. + +> Modul ini mendukung mode `php-fpm` dan `cli` + +## Array dan String Sebagai Object + +Di bahasa pemrograman lain, array dan string ada sebagai object, misalnya `C++`, `Java`, `JS`, `Python`, `Golang`, dll. Array dan string memiliki built-in method untuk berbagai operasi. Sementara di `PHP`, perlu menggunakan fungsi untuk mengoperasikan array dan string, yang lebih mirip dengan bahasa `C` jaman dulu. + +Ekstensi sintaks ini menjadikan `string` dan `array` sebagai `final class` bawaan, sehingga bisa langsung menggunakan object method untuk beroperasi. +Built-in method tipe dasar pada dasarnya tetap memanggil implementasi `PHP` standard library. Built-in method memiliki korespondensi satu-satu dengan fungsi `str_` atau `array_` di `PHP`, +misalnya `$text->replace()` setara dengan fungsi `str_replace`. +Sistem hanya menyesuaikan nama fungsi, beberapa method menyesuaikan urutan parameter, dan beberapa method menyesuaikan tipe parameter dan return value. Detail lihat daftar method string, daftar method array, daftar method Stream. + +### Array Method + +```php +$array = [1, 2, 3, 99]; + +$array->contains(99); +$index = $array->indexOf(3); +$string = $array->join(';'); +``` + +### String Object +```php +$str = "hello world!"; + +$true = $str->startsWith('hello'); +$true = $str->endsWith('!'); +$array = $str->split(' '); +``` + +## Strongly Typed Array +Bisa memaksa tipe `key` dan `value` array, mengatasi masalah type lubang hitam yang disebabkan oleh penggunaan array. Sintaks mirip dengan penulisan generic `C++`, mendukung multi-level nesting. + +### List Syntax + +```php +$array = typed_array('', [1, 2, 3]); +$array = typed_array('', ['hello', 'world']; +$array = typed_array('', [new TestObject]; +$array = typed_array('<>'); +``` + +### Map Syntax +```php +$array = typed_array('', [1 => 'a', 2 => 'b', 3 => 'c']; +$array = typed_array('', [ 'first' => new TestObject]; +$array = typed_array('>'); +``` diff --git a/public/id/stdext/stream.md b/public/id/stdext/stream.md new file mode 100644 index 0000000..083985d --- /dev/null +++ b/public/id/stdext/stream.md @@ -0,0 +1,31 @@ +# Stream Method + +## Mapping Table +Misalnya `$fp = fopen($file, 'r+')`, gunakan `$fp->read(1024)` untuk membaca data dari stream, setara dengan memanggil `fread($fp, 1024)`. + +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|---------------|----------------------------------------------------|------------------------| +| write() | Menulis data ke stream | fwrite() | +| read() | Membaca data dari stream | fread() | +| close() | Menutup stream | fclose() | +| dataSync() | Menyinkronkan data stream ke storage device | fdatasync() | +| sync() | Menyinkronkan data dan metadata stream ke storage | fsync() | +| truncate() | Memotong stream ke panjang tertentu | ftruncate() | +| stat() | Mendapatkan informasi status stream | fstat() | +| seek() | Memindahkan posisi stream ke lokasi tertentu | fseek() | +| tell() | Mendapatkan posisi saat ini di stream | ftell() | +| lock() | Mengunci atau membuka kunci stream | flock() | +| eof() | Mengecek apakah sudah mencapai akhir stream | feof() | +| getChar() | Mendapatkan satu karakter dari stream | fgetc() | +| getLine() | Mendapatkan satu baris data dari stream | fgets() | + +## Contoh +```php +$fp = fopen('example.txt', 'r+'); +$fp->write("Hello, Swoole!\n"); +$fp->seek(0); +while (!$fp->eof()) { + $line = $fp->getLine(); + echo $line; +} +``` diff --git a/public/id/stdext/string.md b/public/id/stdext/string.md new file mode 100644 index 0000000..e83b713 --- /dev/null +++ b/public/id/stdext/string.md @@ -0,0 +1,118 @@ +# String Method + +## Mapping Table +Misalnya `$str = "hello world"`, gunakan `$str->length()` untuk mendapatkan panjang string, setara dengan memanggil `strlen($str)`. + +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|---------------------------|----------------------------------------------|------------------------------| +| length() | Mendapatkan panjang string | strlen() | +| isEmpty() | Mengecek apakah string kosong | empty() | +| lower() | Mengubah string ke huruf kecil | strtolower() | +| upper() | Mengubah string ke huruf besar | strtoupper() | +| lowerFirst() | Mengubah huruf pertama string ke kecil | lcfirst() | +| upperFirst() | Mengubah huruf pertama string ke besar | ucfirst() | +| upperWords() | Mengubah huruf pertama setiap kata ke besar | ucwords() | +| addCSlashes() | Menambahkan backslash sebelum karakter tertentu| addcslashes() | +| addSlashes() | Menambahkan backslash | addslashes() | +| chunkSplit() | Memisahkan string menjadi potongan kecil | chunk_split() | +| countChars() | Menghitung frekuensi setiap karakter | count_chars() | +| htmlEntityDecode() | Mengubah HTML entity ke karakter | html_entity_decode() | +| htmlEntityEncode() | Mengubah karakter ke HTML entity | htmlentities() | +| htmlSpecialCharsEncode() | Mengubah karakter khusus ke HTML entity | htmlspecialchars() | +| htmlSpecialCharsDecode() | Mengubah HTML entity ke karakter khusus | htmlspecialchars_decode() | +| trim() | Menghapus spasi di kedua ujung string | trim() | +| lTrim() | Menghapus spasi di kiri string | ltrim() | +| rTrim() | Menghapus spasi di kanan string | rtrim() | +| parseStr() | Mem-parsing query string menjadi variabel | parse_str() | +| parseUrl() | Mem-parsing URL dan mengembalikan komponennya | parse_url() | +| contains() | Mengecek apakah string mengandung substring | str_contains() | +| incr() | Increment bagian numerik dalam string | str_increment() | +| decr() | Decrement bagian numerik dalam string | str_decrement() | +| pad() | Mengisi string ke panjang tertentu | str_pad() | +| repeat() | Mengulang string sejumlah kali | str_repeat() | +| replace() | Mengganti substring dalam string | str_replace() | +| iReplace() | Mengganti substring (case-insensitive) | str_ireplace() | +| shuffle() | Mengacak urutan karakter string | str_shuffle() | +| split() | Memisahkan string menjadi array | explode() | +| startsWith() | Mengecek apakah string diawali substring | str_starts_with() | +| endsWith() | Mengecek apakah string diakhiri substring | str_ends_with() | +| wordCount() | Menghitung jumlah kata dalam string | str_word_count() | +| iCompare() | Membandingkan string (case-insensitive) | strcasecmp() | +| compare() | Membandingkan string (case-sensitive) | strcmp() | +| find() | Mencari posisi pertama substring | strstr() | +| iFind() | Mencari posisi pertama substring (case-insensitive)| stristr() | +| stripTags() | Menghapus tag HTML dan PHP | strip_tags() | +| stripCSlashes() | Menghapus backslash | stripcslashes() | +| stripSlashes() | Menghapus backslash | stripslashes() | +| iIndexOf() | Mencari posisi pertama substring (case-insensitive)| stripos() | +| indexOf() | Mencari posisi pertama substring | strpos() | +| lastIndexOf() | Mencari posisi terakhir substring | strrpos() | +| iLastIndexOf() | Mencari posisi terakhir substring (case-insensitive)| strripos() | +| lastCharIndexOf() | Mencari posisi terakhir karakter | strrchr() | +| substr() | Mengembalikan substring string | substr() | +| substrCompare() | Membandingkan substring string | substr_compare() | +| substrCount() | Menghitung jumlah kemunculan substring | substr_count() | +| substrReplace() | Mengganti substring dengan string lain | substr_replace() | +| reverse() | Membalik string | strrev() | +| md5() | Menghitung hash MD5 string | md5() | +| sha1() | Menghitung hash SHA1 string | sha1() | +| crc32() | Menghitung nilai CRC32 string | crc32() | +| hash() | Menghitung hash string | hash() | +| hashCode() | Menghitung kode hash string | swoole_hashcode() | +| base64Decode() | Melakukan Base64 decode pada string | base64_decode() | +| base64Encode() | Melakukan Base64 encode pada string | base64_encode() | +| urlDecode() | Melakukan URL decode pada string | urldecode() | +| urlEncode() | Melakukan URL encode pada string | urlencode() | +| rawUrlEncode() | Melakukan raw URL encode pada string | rawurlencode() | +| rawUrlDecode() | Melakukan raw URL decode pada string | rawurldecode() | +| match() | Mencocokkan string dengan regex | preg_match() | +| matchAll() | Mencocokkan semua substring dengan regex | preg_match_all() | +| isNumeric() | Mengecek apakah string numerik | is_numeric() | + +## Multibyte String Method Mapping +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|---------------------------|----------------------------------------------|------------------------------| +| mbUpperFirst() | Mengubah huruf pertama multibyte ke besar | mb_ucfirst() | +| mbLowerFirst() | Mengubah huruf pertama multibyte ke kecil | mb_lcfirst() | +| mbTrim() | Menghapus spasi di kedua ujung multibyte | mb_trim() | +| mbSubstrCount() | Menghitung jumlah substring multibyte | mb_substr_count() | +| mbSubstr() | Mengembalikan substring multibyte | mb_substr() | +| mbUpper() | Mengubah multibyte ke huruf besar | mb_strtoupper() | +| mbLower() | Mengubah multibyte ke huruf kecil | mb_strtolower() | +| mbFind() | Mencari posisi pertama substring multibyte | mb_strstr() | +| mbIndexOf() | Mencari posisi pertama substring multibyte | mb_strpos() | +| mbLastIndexOf() | Mencari posisi terakhir substring multibyte | mb_strrpos() | +| mbILastIndexOf() | Mencari posisi terakhir substring (case-insensitive)| mb_strripos() | +| mbLastCharIndexOf() | Mencari posisi terakhir karakter multibyte | mb_strrchr() | +| mbILastCharIndex() | Mencari posisi terakhir karakter (case-insensitive)| mb_strrichr() | +| mbLength() | Mendapatkan panjang multibyte string | mb_strlen() | +| mbIFind() | Mencari posisi pertama substring (case-insensitive)| mb_stristr() | +| mbIIndexOf() | Mencari posisi pertama substring (case-insensitive)| mb_stripos() | +| mbCut() | Mengembalikan substring multibyte (by byte) | mb_strcut() | +| mbRTrim() | Menghapus spasi di kanan multibyte | mb_rtrim() | +| mbLTrim() | Menghapus spasi di kiri multibyte | mb_ltrim() | + +## Serialization Method +| Method | Deskripsi | Fungsi PHP yang Sesuai | +|-------------------------|-----------------------------------------------|------------------------| +| jsonDecode() | Mendekode string JSON ke associative array | json_decode() | +| jsonDecodeToObject() | Mendekode string JSON ke object | json_decode() | +| unmarshal() | Alias dari unserialize | unserialize() | +| unserialize() | Mendeserialisasi variabel ke PHP variable | unserialize() | + + +## Perbedaan + +- `replace` dan `iReplace` menyesuaikan urutan parameter dibanding fungsi PHP, string yang dioperasikan dijadikan parameter pertama +- `jsonDecode` selalu mengembalikan associative array, bukan object. `jsonDecodeToObject()` selalu mengembalikan object `stdClass` +- Method `split` setara dengan fungsi `explode`, menyesuaikan urutan parameter dengan string yang dioperasikan sebagai parameter pertama +- Method `match()` dan `matchAll()` langsung mengembalikan array hasil pencocokan, bukan jumlah kecocokan. Gunakan `count($result[1])` untuk mendapatkan jumlah + +```php +$str = 'foobarbaz'; +$regex1 = '/(foo)(bar)(baz)/'; +$matches = $str->match($regex1, PREG_OFFSET_CAPTURE); + +preg_match($regex1, $str, $matches2, PREG_OFFSET_CAPTURE); +Assert::eq($matches, $matches2); +``` diff --git a/public/id/sw.js b/public/id/sw.js new file mode 100644 index 0000000..4a94652 --- /dev/null +++ b/public/id/sw.js @@ -0,0 +1,83 @@ +/* =========================================================== + * docsify sw.js + * =========================================================== + * Copyright 2016 @huxpro + * Licensed under Apache 2.0 + * Register service worker. + * ========================================================== */ + +const RUNTIME = 'docsify' +const HOSTNAME_WHITELIST = [ + self.location.hostname, + 'fonts.gstatic.com', + 'fonts.googleapis.com', + 'cdn.jsdelivr.net' +] + +// The Util Function to hack URLs of intercepted requests +const getFixedUrl = (req) => { + var now = Date.now() + var url = new URL(req.url) + + // 1. fixed http URL + // Just keep syncing with location.protocol + // fetch(httpURL) belongs to active mixed content. + // And fetch(httpRequest) is not supported yet. + url.protocol = self.location.protocol + + // 2. add query for caching-busting. + // Github Pages served with Cache-Control: max-age=600 + // max-age on mutable content is error-prone, with SW life of bugs can even extend. + // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. + // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 + if (url.hostname === self.location.hostname) { + url.search += (url.search ? '&' : '?') + 'cache-bust=' + now + } + return url.href +} + +/** + * @Lifecycle Activate + * New one activated when old isnt being used. + * + * waitUntil(): activating ====> activated + */ +self.addEventListener('activate', event => { + event.waitUntil(self.clients.claim()) +}) + +/** + * @Functional Fetch + * All network requests are being intercepted here. + * + * void respondWith(Promise r) + */ +self.addEventListener('fetch', event => { + // Skip some of cross-origin requests, like those for Google Analytics. + if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { + // Stale-while-revalidate + // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale + // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 + const cached = caches.match(event.request) + const fixedUrl = getFixedUrl(event.request) + const fetched = fetch(fixedUrl, { cache: 'no-store' }) + const fetchedCopy = fetched.then(resp => resp.clone()) + + // Call respondWith() with whatever we get first. + // If the fetch fails (e.g disconnected), wait for the cache. + // If there’s nothing in cache, wait for the fetch. + // If neither yields a response, return offline pages. + event.respondWith( + Promise.race([fetched.catch(_ => cached), cached]) + .then(resp => resp || fetched) + .catch(_ => { /* eat any errors */ }) + ) + + // Update the cache with the version we fetched (only for ok status) + event.waitUntil( + Promise.all([fetchedCopy, caches.open(RUNTIME)]) + .then(([response, cache]) => response.ok && cache.put(event.request, response)) + .catch(_ => { /* eat any errors */ }) + ) + } +}) \ No newline at end of file diff --git a/public/id/thread/arraylist.md b/public/id/thread/arraylist.md new file mode 100644 index 0000000..18572f3 --- /dev/null +++ b/public/id/thread/arraylist.md @@ -0,0 +1,136 @@ +# Container Konkuren Aman List + +Membuat struktur `List` konkuren, bisa dikirim sebagai parameter thread ke child thread. Operasi baca/tulis terlihat di thread lain. + +## Karakteristik +- `Map`, `ArrayList`, `Queue` akan mengalokasikan memori secara otomatis, tidak perlu alokasi tetap seperti `Table`. + +- Sistem akan mengunci secara otomatis, aman untuk thread. + +- Tipe variabel yang bisa dikirim lihat [Data Type](thread/transfer.md) + +- Tidak mendukung iterator, bisa gunakan `toArray()` sebagai gantinya + +- Object `Map`, `ArrayList`, `Queue` harus dikirim sebagai parameter thread ke child thread sebelum thread dibuat + +- `Thread\ArrayList` mengimplementasikan interface `ArrayAccess` dan `Countable`, bisa langsung dioperasikan sebagai array + +- `Thread\ArrayList` hanya mendukung operasi index numerik, non-numerik akan dipaksa dikonversi + +## Contoh +```php +use Swoole\Thread; +use Swoole\Thread\ArrayList; + +$args = Thread::getArguments(); +if (empty($args)) { + $list = new ArrayList; + $thread = new Thread(__FILE__, $i, $list); + sleep(1); + $list[] = uniqid(); + $thread->join(); +} else { + $list = $args[1]; + sleep(2); + var_dump($list[0]); +} +``` + +- Menambah atau mengubah: `$list[$index] = $value` +- Menghapus: `unset($list[$index])` +- Membaca: `$value = $list[$index]` +- Mendapatkan panjang: `count($list)` + +## Penghapusan +Perhatikan bahwa operasi hapus akan menyebabkan pergeseran massal `List`. Misalnya `List` memiliki `1000` elemen, saat `unset($list[4])`, +maka `$list[5:999]` akan digeser untuk mengisi kekosongan. Namun tidak akan menyalin elemen secara dalam, hanya memindahkan pointer-nya. + +> Jika `List` besar, menghapus elemen di awal bisa menghabiskan banyak `CPU` + +## Method + +### __construct() +Konstruktor container konkuren aman `ArrayList` + +```php +Swoole\Thread\ArrayList->__construct(?array $values = null) +``` + +- `$values` opsional, mengiterasi array dan menambahkan nilainya ke `ArrayList` +- Hanya menerima array tipe `list`, tidak menerima array asosiatif, jika tidak akan melempar exception +- Array asosiatif perlu dikonversi ke array tipe `list` menggunakan `array_values` + +### incr() +Menambah nilai di `ArrayList` secara aman, mendukung float atau integer. Jika tipe lain di-increment, akan otomatis dikonversi ke integer, diinisialisasi ke `0`, lalu di-increment. + +```php +Swoole\Thread\ArrayList->incr(int $index, mixed $value = 1) : int | float +``` + +* **Parameter** + * `int $index` + * Fungsi: Index numerik, harus alamat index yang valid, jika tidak akan melempar exception. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai increment. + * Nilai default: 1. + * Nilai lain: Tidak ada. + +* **Return value** + * Mengembalikan nilai setelah increment. + +### decr() +Mengurangi nilai di `ArrayList` secara aman, mendukung float atau integer. Jika tipe lain di-decrement, akan otomatis dikonversi ke integer, diinisialisasi ke `0`, lalu di-decrement. + +```php +Swoole\Thread\ArrayList->decr(int $index, mixed $value = 1) : int | float +``` + +* **Parameter** + * `int $index` + * Fungsi: Index numerik, harus alamat index yang valid, jika tidak akan melempar exception. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai decrement. + * Nilai default: 1. + * Nilai lain: Tidak ada. + +* **Return value** + * Mengembalikan nilai setelah decrement. + +### count() +Mendapatkan jumlah elemen `ArrayList` + +```php +Swoole\Thread\ArrayList()->count(): int +``` + +* **Return value** + * Mengembalikan jumlah elemen di List. + +### toArray() +Mengonversi `ArrayList` ke array + +```php +Swoole\Thread\ArrayList()->toArray(): array +``` + +* **Return value** Array, mengembalikan semua elemen di `ArrayList`. + +### clean() +Menghapus semua elemen + +```php +Swoole\Thread\ArrayList()->clean(): void +``` + +### sort() +Mengurutkan elemen di dalam container. Perilakunya sama dengan `sort()`. + +```php +Swoole\Thread\ArrayList()->sort(): void +``` diff --git a/public/id/thread/barrier.md b/public/id/thread/barrier.md new file mode 100644 index 0000000..6d48032 --- /dev/null +++ b/public/id/thread/barrier.md @@ -0,0 +1,55 @@ +# Sinkronisasi Eksekusi Thread Barrier + +`Thread\Barrier` adalah mekanisme sinkronisasi thread. Ini memungkinkan beberapa thread untuk sinkron di titik tertentu, memastikan semua thread menyelesaikan tugasnya sebelum mencapai titik kritis (barrier). Hanya ketika semua thread yang berpartisipasi mencapai barrier ini, mereka bisa melanjutkan eksekusi kode selanjutnya. + +Misalnya kita membuat `4` thread, kita ingin mereka semua siap lalu menjalankan tugas bersama-sama, seperti pistol start dalam lomba lari. Ini bisa diimplementasikan dengan `Thread\Barrier`. + + +## Contoh +```php +use Swoole\Thread; +use Swoole\Thread\Barrier; + +const N = 4; +$args = Thread::getArguments(); + +if (empty($args)) { + $barrier = new Barrier(N); + $n = N; + $threads = []; + while($n--) { + $threads[] = new Thread(__FILE__, $barrier, $n); + } +} else { + $barrier = $args[0]; + $n = $args[1]; + // Menunggu semua thread siap + $barrier->wait(); + echo "thread $n is running\n"; +} +``` + +## Method + +### __construct() +Konstruktor + +```php +Thread\Barrier()->__construct(int $count): void +``` + + * **Parameter** + * `int $count` + * Fungsi: Jumlah thread, harus lebih dari `1`. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + +Jumlah thread yang menjalankan operasi `wait` harus sesuai dengan nilai yang diatur, jika tidak semua thread akan terblokir. + +### wait() + +Memblokir dan menunggu thread lain. Saat semua thread berada dalam status `wait`, semua thread yang menunggu akan dibangunkan secara bersamaan untuk melanjutkan eksekusi. + +```php +Thread\Barrier()->wait(): void +``` diff --git a/public/id/thread/info.md b/public/id/thread/info.md new file mode 100644 index 0000000..db5fd9b --- /dev/null +++ b/public/id/thread/info.md @@ -0,0 +1,256 @@ +# Method dan Properti + +## Method + +### __construct() +Konstruktor multi-thread + +```php +Swoole\Thread->__construct(string $script_file, mixed ...$args) +``` +* **Parameter** + * `string $script_file` + * Fungsi: File yang akan dijalankan setelah thread mulai. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $args` + * Fungsi: Data shared yang dikirim dari main thread ke child thread, bisa didapatkan di child thread menggunakan `Swoole\Thread::getArguments()`. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + +!> Kegagalan pembuatan thread akan melempar `Swoole\Exception`, bisa ditangkap dengan `try catch`. + +### join() +Main thread menunggu child thread selesai. Jika child thread masih berjalan, `join()` akan memblokir sampai child thread selesai. + +```php +Swoole\Thread->join(): bool +``` +* **Return value** + * Mengembalikan `true` jika sukses, `false` jika gagal. + +### joinable() +Memeriksa apakah child thread bisa di-`join()`. Ada dua kondisi method ini mengembalikan `false`: +1. Thread telah menjalankan `detach()`, tidak bisa menggunakan `join()` untuk menunggu thread selesai +2. Thread sudah selesai dan sudah di-`join()` untuk direcycle + +```php +Swoole\Thread->joinable(): bool +``` + +### isAlive() +Memeriksa apakah thread masih hidup. Method ini bisa dipanggil kapan saja, terlepas dari apakah thread sudah menjalankan `detach()` atau `join()`. + +> Method ini tidak akan memblokir + +```php +Swoole\Thread->isAlive(): bool +``` + +* **Return value** + * Mengembalikan `true` jika thread masih hidup, `false` jika sudah selesai. + +### detach() +Melepaskan child thread dari kendali main thread, tidak perlu lagi `join()` untuk menunggu thread selesai. + +```php +Swoole\Thread->detach(): bool +``` +* **Return value** + * Mengembalikan `true` jika sukses, `false` jika gagal. + +### getId() +Static method, mendapatkan `ID` thread saat ini. + +```php +Swoole\Thread::getId(): int +``` +* **Return value** + * Mengembalikan integer yang merepresentasikan ID thread saat ini. + +### getArguments() +Static method, mendapatkan data shared yang dikirim dari main thread saat menggunakan `new Swoole\Thread()`, dipanggil di child thread. + +```php +Swoole\Thread::getArguments(): ?array +``` + +* **Return value** + * Di child thread mengembalikan data shared yang dikirim dari parent process. + +?> Main thread tidak akan memiliki thread arguments. Kamu bisa membedakan parent dan child thread dengan mengecek apakah thread arguments kosong, sehingga mereka bisa menjalankan logika yang berbeda. +```php +use Swoole\Thread; + +$args = Thread::getArguments(); // Jika main thread, $args kosong; jika child thread, $args tidak kosong +if (empty($args)) { + # Main thread + new Thread(__FILE__, 'child thread'); + echo "main thread\n"; +} else { + # Child thread + var_dump($args); // Output: ['child thread'] +} +``` + +### getInfo() +Static method, mendapatkan informasi tentang lingkungan multi-thread saat ini. + +```php +Swoole\Thread::getInfo(): array +``` +Array yang dikembalikan: + +- `is_main_thread`: Apakah thread saat ini adalah main thread +- `is_shutdown`: Apakah thread sudah dimatikan +- `thread_num`: Jumlah thread yang aktif + +### activeCount() +Static method, mendapatkan jumlah thread yang aktif saat ini + +```php +Swoole\Thread::activeCount(): int +``` +* **Return value** + * Mengembalikan integer yang merepresentasikan jumlah thread aktif. Nilai selalu lebih dari atau sama dengan `1` + +### yield() +Static method, melepaskan timeslice eksekusi thread saat ini. Setelah dipanggil, thread saat ini akan ditangguhkan, menunggu scheduler OS memprioritaskan thread lain, dan mengalokasikan timeslice kembali ke thread saat ini di siklus berikutnya. + +```php +Swoole\Thread::yield(): void +``` + + +### getPriority() +Static method, mendapatkan informasi scheduling thread saat ini + +```php +Swoole\Thread->getPriority(): array +``` +Array yang dikembalikan: + +- `policy`: Kebijakan scheduling thread +- `priority`: Prioritas scheduling thread + +### setPriority() +Static method, mengatur prioritas dan kebijakan scheduling thread saat ini + +?> Hanya pengguna `root` yang bisa menyesuaikan, pengguna non-`root` akan ditolak + +```php +Swoole\Thread->setPriority(int $priority, int $policy = -1): bool +``` + +* **Parameter** + * `int $priority` + * Fungsi: Mengatur prioritas scheduling thread + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $policy` + * Fungsi: Mengatur kebijakan prioritas scheduling thread + * Nilai default: `-1`, berarti tidak menyesuaikan kebijakan scheduling. + * Nilai lain: Konstanta terkait `Thread::SCHED_*`. + +* **Return value** + * Sukses mengembalikan `true` + * Mengembalikan `false`, gunakan `swoole_last_error()` untuk mendapatkan informasi error + +> `SCHED_BATCH/SCHED_ISO/SCHED_IDLE/SCHED_DEADLINE` hanya tersedia di sistem `Linux` + +> Thread dengan kebijakan `SCHED_FIFO/SCHED_RR` umumnya adalah real-time thread, prioritasnya lebih tinggi dari thread biasa, bisa mendapatkan lebih banyak timeslice `CPU` + +### getAffinity() +Static method, mendapatkan afinitas `CPU` thread saat ini + +```php +Swoole\Thread->getAffinity(): array +``` +Nilai yang dikembalikan berupa array, elemennya adalah nomor inti `CPU`, contoh: `[0, 1, 3, 4]` berarti thread ini akan dijadwalkan ke inti `CPU` `0/1/3/4` + +### setAffinity() +Static method, mengatur afinitas `CPU` thread saat ini + +```php +Swoole\Thread->setAffinity(array $cpu_set): bool +``` + +* **Parameter** + * `array $cpu_set` + * Fungsi: Daftar inti `CPU`, contoh `[0, 1, 3, 4]` + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + +* **Return value** + * Sukses mengembalikan `true` + * Mengembalikan `false`, gunakan `swoole_last_error()` untuk mendapatkan informasi error + +### setName() +Static method, mengatur nama thread saat ini. Memberikan tampilan yang lebih ramah saat menggunakan alat seperti `ps` dan `gdb`. + +```php +Swoole\Thread->setName(string $name): bool +``` + +* **Parameter** + * `string $name` + * Fungsi: Nama thread + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + +* **Return value** + * Sukses mengembalikan `true` + * Mengembalikan `false`, gunakan `swoole_last_error()` untuk mendapatkan informasi error + +```shell +$ ps aux|grep -v grep|grep pool.php +swoole 2226813 0.1 0.1 423860 49024 pts/6 Sl+ 17:38 0:00 php pool.php + +$ ps -T -p 2226813 + PID SPID TTY TIME CMD +2226813 2226813 pts/6 00:00:00 Master Thread +2226813 2226814 pts/6 00:00:00 Worker Thread 0 +2226813 2226815 pts/6 00:00:00 Worker Thread 1 +2226813 2226816 pts/6 00:00:00 Worker Thread 2 +2226813 2226817 pts/6 00:00:00 Worker Thread 3 +``` + +### getNativeId() +Mendapatkan `ID` sistem thread, mengembalikan integer, mirip dengan `PID` process. + +```php +Swoole\Thread->getNativeId(): int +``` + +Fungsi ini memanggil system call `gettid()` di sistem `Linux`, mendapatkan `ID` thread OS, berupa short integer. Saat thread process dimusnahkan, ID ini mungkin akan digunakan kembali oleh OS. + +`ID` ini bisa digunakan untuk debugging dengan `gdb`, `strace`, contoh `gdb -p $tid`. Juga bisa membaca `/proc/{PID}/task/{ThreadNativeId}` untuk mendapatkan informasi eksekusi thread. + +## Properti + +### id + +Mendapatkan `ID` child thread melalui properti object ini, bertipe `int`. + +> Properti ini hanya bisa digunakan di parent thread. Child thread tidak bisa mendapatkan object `$thread`, harus menggunakan static method `Thread::getId()` untuk mendapatkan `ID` thread + +```php +$thread = new Swoole\Thread(__FILE__, $i); +var_dump($thread->id); +``` + +## Konstanta + +Nama | Fungsi +---|--- +`Thread::HARDWARE_CONCURRENCY` | Jumlah thread konkurensi hardware, biasanya jumlah inti `CPU` +`Thread::API_NAME` | Nama `API` thread, misalnya `POSIX Threads` +`Thread::SCHED_OTHER` | Kebijakan scheduling thread `SCHED_OTHER` +`Thread::SCHED_FIFO` | Kebijakan scheduling thread `SCHED_FIFO` +`Thread::SCHED_RR` | Kebijakan scheduling thread `SCHED_RR` +`Thread::SCHED_BATCH` | Kebijakan scheduling thread `SCHED_BATCH` +`Thread::SCHED_ISO` | Kebijakan scheduling thread `SCHED_ISO` +`Thread::SCHED_IDLE` | Kebijakan scheduling thread `SCHED_IDLE` +`Thread::SCHED_DEADLINE` | Kebijakan scheduling thread `SCHED_DEADLINE` diff --git a/public/id/thread/join.md b/public/id/thread/join.md new file mode 100644 index 0000000..d2491e6 --- /dev/null +++ b/public/id/thread/join.md @@ -0,0 +1,58 @@ +# Manajemen Thread + +## Thread::join() + +Tunggu child thread sampe selesai. Kalo child thread masih jalan, `join()` bakal blocking. + +```php +$thread = Thread::exec(__FILE__, $i); +$thread->join(); +``` + +## Thread::joinable() + +Cek apakah child thread udah selesai. + +### Return Value +- `true` artinya child thread udah selesai, saat panggil `join()` nggak bakal blocking +- `false` artinya masih jalan + +```php +$thread = Thread::exec(__FILE__, $i); +var_dump($thread->joinable()); +``` + +## Thread::detach() + +Lepas child thread dari kontrol parent thread, biar bisa exit tanpa perlu panggil `join()` buunggu thread selesai dan bersihin resource. + +```php +$thread = Thread::exec(__FILE__, $i); +$thread->detach(); +unset($thread); +``` + +## Thread::getId() + +Static method buat dapetin `ID` thread saat ini. Dipanggil dari dalam child thread. + +```php +var_dump(Thread::getId()); +``` + +## Thread::getArguments() + +Static method buat dapetin argument dari thread saat ini. Dipanggil dari dalam child thread, argument dikirim oleh parent thread lewat `Thread::exec()`. + +```php +var_dump(Thread::getArguments()); +``` + +## Thread::$id + +Pake property object ini buat dapetin `ID` child thread. + +```php +$thread = Thread::exec(__FILE__, $i); +var_dump($thread->id); +``` diff --git a/public/id/thread/map.md b/public/id/thread/map.md new file mode 100644 index 0000000..56215d5 --- /dev/null +++ b/public/id/thread/map.md @@ -0,0 +1,183 @@ +# Container Konkuren Aman Map + +Membuat struktur `Map` konkuren, bisa dikirim sebagai parameter thread ke child thread. Operasi baca/tulis terlihat di thread lain. + +## Karakteristik +- `Map`, `ArrayList`, `Queue` akan mengalokasikan memori secara otomatis, tidak perlu alokasi tetap seperti `Table`. + +- Sistem akan mengunci secara otomatis, aman untuk thread. + +- Tipe variabel yang bisa dikirim lihat [Data Type](thread/transfer.md). + +- Tidak mendukung iterator, bisa gunakan `keys()`, `values()`, `toArray()` sebagai gantinya. + +- Object `Map`, `ArrayList`, `Queue` harus dikirim sebagai parameter thread ke child thread sebelum thread dibuat. + +- `Thread\Map` mengimplementasikan interface `ArrayAccess` dan `Countable`, bisa langsung dioperasikan sebagai array. + +## Contoh +```php +use Swoole\Thread; +use Swoole\Thread\Map; + +$args = Thread::getArguments(); +if (empty($args)) { + $map = new Map; + $thread = new Thread(__FILE__, $i, $map); + sleep(1); + $map['test'] = uniqid(); + $thread->join(); +} else { + $map = $args[1]; + sleep(2); + var_dump($map['test']); +} +``` + +- Menambah atau mengubah: `$map[$key] = $value` +- Menghapus: `unset($map[$key])` +- Membaca: `$value = $map[$key]` +- Mendapatkan panjang: `count($map)` + +## Method + +### __construct() +Konstruktor container konkuren aman `Map` + +```php +Swoole\Thread\Map->__construct(?array $values = null) +``` + +- `$values` opsional, mengiterasi array dan menambahkan nilainya ke `Map` + +### add() +Menulis data ke `Map` + +```php +Swoole\Thread\Map->add(mixed $key, mixed $value) : bool +``` + * **Parameter** + * `mixed $key` + * Fungsi: Key yang akan ditambahkan. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai yang akan ditambahkan. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * **Return value** + * Jika `$key` sudah ada, mengembalikan `false`, jika tidak mengembalikan `true` yang berarti berhasil. + +### update() +Memperbarui data di `Map` + +```php +Swoole\Thread\Map->update(mixed $key, mixed $value) : bool +``` + + * **Parameter** + * `mixed $key` + * Fungsi: Key yang akan diubah. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai yang akan diubah. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * **Return value** + * Jika `$key` tidak ada, mengembalikan `false`, jika tidak mengembalikan `true` yang berarti berhasil + +### incr() +Menambah nilai di `Map` secara aman, mendukung float atau integer. Jika tipe lain di-increment, akan otomatis dikonversi ke integer, diinisialisasi ke `0`, lalu di-increment + +```php +Swoole\Thread\Map->incr(mixed $key, mixed $value = 1) : int | float +``` +* **Parameter** + * `mixed $key` + * Fungsi: Key yang akan di-increment, jika tidak ada akan dibuat otomatis dengan nilai awal `0`. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai increment. + * Nilai default: 1. + * Nilai lain: Tidak ada. + +* **Return value** + * Mengembalikan nilai setelah increment. + +### decr() +Mengurangi nilai di `Map` secara aman, mendukung float atau integer. Jika tipe lain di-decrement, akan otomatis dikonversi ke integer, diinisialisasi ke `0`, lalu di-decrement + +```php +Swoole\Thread\Map->decr(mixed $key, mixed $value = 1) : int | float +``` +* **Parameter** + * `mixed $key` + * Fungsi: Key yang akan di-decrement, jika tidak ada akan dibuat otomatis dengan nilai awal `0`. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + * `mixed $value` + * Fungsi: Nilai decrement. + * Nilai default: 1. + * Nilai lain: Tidak ada. + +* **Return value** + * Mengembalikan nilai setelah decrement. + +### count() +Mendapatkan jumlah elemen + +```php +Swoole\Thread\Map()->count(): int +``` + + * **Return value** + * Mengembalikan jumlah elemen di Map. + +### keys() +Mengembalikan semua `key` + +```php +Swoole\Thread\Map()->keys(): array +``` + + * **Return value** + * Mengembalikan semua `key` dari `Map` + +### values() +Mengembalikan semua `value` + +```php +Swoole\Thread\Map()->values(): array +``` + +* **Return value** + * Mengembalikan semua `value` dari `Map` + +### toArray() +Mengonversi `Map` ke array + +```php +Swoole\Thread\Map()->toArray(): array +``` + +### clean() +Menghapus semua elemen + +```php +Swoole\Thread\Map()->clean(): void +``` + +### sort() +Mengurutkan elemen di dalam container. Sistem akan menyimpan mapping `key` dan `value`, hanya mengurutkan `value`. Perilakunya sama dengan `asort()`. + +```php +Swoole\Thread\Map()->sort(): void +``` diff --git a/public/id/thread/pool.md b/public/id/thread/pool.md new file mode 100644 index 0000000..200d765 --- /dev/null +++ b/public/id/thread/pool.md @@ -0,0 +1,118 @@ +# Thread Pool + +Thread pool bisa menjaga beberapa worker thread tetap berjalan, secara otomatis membuat, merestart, dan menutup child thread. + +## Method + +### __construct() + +Konstruktor. + +```php +Swoole\Thread\Pool::__construct(string $workerThreadClass, int $num); +``` + +* **Parameter** + * `string $workerThreadClass`: Class yang dijalankan oleh worker thread + * `int $worker_num`: Menentukan jumlah worker thread + + +### withArguments() + + Mengatur parameter worker thread, bisa didapatkan di method `run($args)`. + +```php +Swoole\Thread\Pool::withArguments(...$args): static; +``` + + +### withAutoloader() + +Memuat file `autoload` + +```php +Swoole\Thread\Pool::withAutoloader(string $autoloader): static; +``` +* **Parameter** + * `string $autoloader`: Path file `PHP` untuk `autoload` + +> Jika menggunakan `Composer`, sistem bisa mendeteksi dan memuat `vendor/autoload.php` secara otomatis di worker process, tidak perlu ditentukan manual + +### withClassDefinitionFile() + +Mengatur file definisi class worker thread. **File ini hanya boleh berisi kode `namespace`, `use`, `definisi class`, tidak boleh berisi kode yang bisa dieksekusi.** + +Class worker thread harus mewarisi class dasar `Swoole\Thread\Runnable` dan mengimplementasikan method `run(array $args)`. + +```php +Swoole\Thread\Pool::withClassDefinitionFile(string $classFile): static; +``` +* **Parameter** + * `string $classFile`: Path file `PHP` yang mendefinisikan class worker thread + +Jika class worker thread berada di path `autoload`, tidak perlu diatur + +### start() + +Menjalankan semua worker thread + +```php +Swoole\Thread\Pool::start(): void; +``` + + +### shutdown() +Menutup thread pool + +```php +Swoole\Thread\Pool::shutdown(): void; +``` + +## Contoh +```php +$map = new Swoole\Thread\Map(); + +(new Pool(TestThread::class, 4)) + ->withAutoloader(__DIR__ . '/vendor/autoload.php') + ->withClassDefinitionFile(__DIR__ . '/TestThread.php') + ->withArguments(uniqid(), $map) + ->start(); +``` + +## Thread\Runnable + +Class worker thread harus mewarisi class ini. + +### run(array $args) + +Harus meng-override method ini. `$args` adalah parameter yang diberikan ke object thread pool menggunakan method `withArguments()`. + +### shutdown() +Menutup thread pool + +### $id +Nomor thread saat ini, rentang `0~(total thread-1)`. Saat thread di-restart, thread pengganti baru memiliki nomor yang sama dengan thread lama. + + +### Contoh + +```php +use Swoole\Thread\Runnable; + +class TestThread extends Runnable +{ + public function run($uuid, $map): void + { + $map->incr('thread', 1); + + for ($i = 0; $i < 5; $i++) { + usleep(10000); + $map->incr('sleep'); + } + + if ($map['sleep'] > 50) { + $this->shutdown(); + } + } +} +``` diff --git a/public/id/thread/queue.md b/public/id/thread/queue.md new file mode 100644 index 0000000..ed9daf6 --- /dev/null +++ b/public/id/thread/queue.md @@ -0,0 +1,133 @@ +# Container Konkuren Aman Queue + +Membuat struktur `Queue` konkuren, bisa dikirim sebagai parameter thread ke child thread. Operasi baca/tulis terlihat di thread lain. + +## Karakteristik +- `Thread\Queue` adalah struktur data first-in-first-out. + +- `Map`, `ArrayList`, `Queue` akan mengalokasikan memori secara otomatis, tidak perlu alokasi tetap seperti `Table`. + +- Sistem akan mengunci secara otomatis, aman untuk thread + +- Tipe variabel yang bisa dikirim lihat [Thread Parameter Transfer](thread/transfer.md) + +- Tidak mendukung iterator, menggunakan `C++ std::queue`, hanya mendukung operasi first-in-first-out + +- Object `Map`, `ArrayList`, `Queue` harus dikirim sebagai parameter thread ke child thread sebelum thread dibuat + +- `Thread\Queue` hanya bisa push/pop elemen, tidak bisa mengakses elemen secara acak + +- `Thread\Queue` memiliki built-in thread condition variable, bisa membangunkan/menunggu thread lain di operasi `push/pop` + +## Contoh + +```php +use Swoole\Thread; +use Swoole\Thread\Queue; + +$args = Thread::getArguments(); +$c = 4; +$n = 128; + +if (empty($args)) { + $threads = []; + $queue = new Queue; + for ($i = 0; $i < $c; $i++) { + $threads[] = new Thread(__FILE__, $i, $queue); + } + while ($n--) { + $queue->push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} +``` + +## Konstanta + + +Nama | Fungsi +---|--- +`Queue::NOTIFY_ONE` | Membangunkan satu thread +`Queue::NOTIFY_ALL` | Membangunkan semua thread + +## Method + +### __construct() +Konstruktor container konkuren aman `Queue` + +```php +Swoole\Thread\Queue->__construct() +``` + +### push() +Menulis data ke bagian belakang antrian + +```php +Swoole\Thread\Queue()->push(mixed $value, int $notify_which = 0): void +``` + + * **Parameter** + * `mixed $value` + * Fungsi: Konten data yang akan ditulis. + * Nilai default: Tidak ada. + * Nilai lain: Tidak ada. + + !> Untuk menghindari ambiguitas, jangan menulis `null` dan `false` ke dalam channel + + * `int $notify` + * Fungsi: Apakah akan memberi tahu thread yang menunggu membaca data. + * Nilai default: `0`, tidak akan membangunkan thread mana pun + * Nilai lain: `Swoole\Thread\Queue::NOTIFY_ONE` membangunkan satu thread, `Swoole\Thread\Queue::NOTIFY_ALL` membangunkan semua thread. + + +### pop() +Mengambil data dari bagian depan antrian + +```php +Swoole\Thread\Queue()->pop(float $timeout = 0): mixed +``` + +* **Parameter** + * `float $wait` + * Fungsi: Waktu tunggu. + * Nilai default: `0`, berarti tidak menunggu. + * Nilai lain: Jika tidak `0`, berarti saat antrian kosong akan menunggu producer `push()` data dalam `$timeout` detik. Jika negatif, berarti tidak pernah timeout. + +* **Return value** + * Mengembalikan data dari depan antrian, saat antrian kosong langsung mengembalikan `NULL`. + +> Saat menggunakan `Queue::NOTIFY_ALL` untuk membangunkan semua thread, hanya satu thread yang bisa mendapatkan data yang ditulis oleh operasi `push()` + +### count() +Mendapatkan jumlah elemen antrian + +```php +Swoole\Thread\Queue()->count(): int +``` + +* **Return value** + * Mengembalikan jumlah antrian. + +### clean() +Menghapus semua elemen + +```php +Swoole\Thread\Queue()->clean(): void +``` diff --git a/public/id/thread/thread.md b/public/id/thread/thread.md new file mode 100644 index 0000000..01ed1a2 --- /dev/null +++ b/public/id/thread/thread.md @@ -0,0 +1,137 @@ +# Swoole\Thread + +Mulai dari versi `6.0` telah tersedia dukungan multi-thread, kamu bisa menggunakan `API` thread untuk menggantikan multi-proses. Dibanding multi-proses, `Thread` menyediakan container data konkuren yang lebih kaya, sehingga lebih nyaman saat mengembangkan game server dan communication server. + +- `PHP` harus dalam mode `ZTS`, saat kompilasi `PHP` perlu menambahkan `--enable-zts` +- Kompilasi `Swoole` perlu menambahkan opsi `--enable-swoole-thread` + +## Isolasi Resource + +`Swoole` Thread mirip dengan `Node.js Worker Thread`, di dalam child thread akan dibuat lingkungan `ZendVM` yang benar-benar baru. Child thread tidak mewarisi resource apa pun dari parent thread, oleh karena itu konten berikut telah dikosongkan di child thread dan perlu dibuat ulang atau diatur. + +- File `PHP` yang sudah dimuat, perlu di-load ulang dengan `include/require` +- Perlu mendaftarkan ulang fungsi `autoload` +- Class, function, constant akan dikosongkan, perlu me-load ulang file `PHP` +- Global variable, seperti `$GLOBALS`, `$_GET/$_POST`, dll, akan di-reset +- Static property class, static variable function, akan di-reset ke nilai awal +- Beberapa opsi `php.ini`, seperti `error_reporting()` perlu diatur ulang di child thread + +## Fitur yang Tidak Tersedia + +Dalam mode multi-thread, fitur berikut hanya bisa dioperasikan di main thread, tidak bisa dijalankan di child thread: + +- `swoole_async_set()` mengubah parameter thread +- `Swoole\Runtime::enableCoroutine()` dan `Swoole\Runtime::setHookFlags()` +- Hanya main thread yang bisa mengatur signal listener, termasuk `Process::signal()` dan `Coroutine\System::waitSignal()` tidak bisa digunakan di child thread +- Hanya main thread yang bisa membuat asynchronous server, termasuk `Server`, `Http\Server`, `WebSocket\Server` dll tidak bisa digunakan di child thread + +Selain itu, `Runtime Hook` setelah diaktifkan dalam mode multi-thread tidak bisa dimatikan. + +## Fatal Error +Saat main thread keluar, jika masih ada child thread yang aktif, akan muncul fatal error, kode keluar: `200`, pesan error: +``` +Fatal Error: 2 active threads are running, cannot exit safely. +``` + +## Mengecek Apakah Dukungan Thread Aktif + +```shell +php -v +PHP 8.1.23 (cli) (built: Mar 20 2024 19:48:19) (ZTS) +Copyright (c) The PHP Group +Zend Engine v4.1.23, Copyright (c) Zend Technologies +``` + +`(ZTS)` menandakan thread safety sudah diaktifkan + +```shell +php --ri swoole + +swoole +Swoole => enabled +thread => enabled +``` + +`thread => enabled` menandakan dukungan multi-thread sudah aktif + +### Membuat Multi-Thread +```php +use Swoole\Thread; + +$args = Thread::getArguments(); +$c = 4; + +// Main thread tidak punya thread arguments, $args bernilai null +if (empty($args)) { + # Main thread + for ($i = 0; $i < $c; $i++) { + $threads[] = new Thread(__FILE__, $i); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } +} else { + # Child thread + echo "Thread #" . $args[0] . "\n"; + while (1) { + sleep(1); + file_get_contents('https://www.baidu.com/'); + } +} +``` + +### Thread + Server (Async Style) +- Semua worker process akan menggunakan thread untuk berjalan, termasuk `Worker`, `Task Worker`, `User Process` +- Mode `SWOOLE_THREAD` baru, setelah diaktifkan akan menggunakan thread sebagai pengganti process +- Menambahkan konfigurasi [bootstrap](/server/setting?id=bootstrap) dan [init_arguments](/server/setting?id=init_arguments) untuk mengatur file script entry worker thread dan data shared thread +- `Server` harus dibuat di main thread, bisa membuat `Thread` baru di callback untuk menjalankan tugas lain +- Object `Server::addProcess()` Process tidak mendukung redirect std I/O + +```php +use Swoole\Process; +use Swoole\Thread; +use Swoole\Http\Server; + +$http = new Server("0.0.0.0", 9503, SWOOLE_THREAD); +$http->set([ + 'worker_num' => 2, + 'task_worker_num' => 3, + 'bootstrap' => __FILE__, + 'init_arguments' => function () use ($http) { + $map = new Swoole\Thread\Map; + return [$map]; + } +]); + +$http->on('Request', function ($req, $resp) use ($http) { + $resp->end('hello world'); +}); + +$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) { + echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; +}); + +$http->addProcess(new Process(function () { + echo "user process, id=" . Thread::getId(); + sleep(2000); +})); + +$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { + var_dump($taskId, $srcWorkerId, $data); + return ['result' => uniqid()]; +}); + +$http->on('Finish', function ($server, $taskId, $data) { + var_dump($taskId, $data); +}); + +$http->on('WorkerStart', function ($serv, $wid) { + var_dump(Thread::getArguments(), $wid); +}); + +$http->on('WorkerStop', function ($serv, $wid) { + var_dump('stop: T' . Thread::getId()); +}); + +$http->start(); +``` diff --git a/public/id/thread/transfer.md b/public/id/thread/transfer.md new file mode 100644 index 0000000..838b36e --- /dev/null +++ b/public/id/thread/transfer.md @@ -0,0 +1,103 @@ +# Data Type +Berikut adalah tipe data yang bisa dikirim dan dibagikan antar thread. + +## Basic Type +Variabel tipe `null/bool/int/float`, ukuran memori kurang dari `16 Bytes`, dikirim sebagai nilai. + +## String +String di-**copy memori**, disimpan ke `ArrayList`, `Queue`, `Map`. + +## Socket Resource + +### Daftar Tipe yang Didukung +- `Co\Socket` +- `PHP Stream` +- `PHP Socket(ext-sockets)`, perlu mengaktifkan parameter kompilasi `--enable-sockets` + +### Tipe yang Tidak Didukung +- `Swoole\Client` +- `Swoole\Server` +- `Swoole\Coroutine\Client` +- `Swoole\Coroutine\Http\Client` +- Koneksi `pdo` +- Koneksi `redis` +- Tipe resource `Socket` khusus lainnya + +### Duplikasi Resource + +- Saat menulis, akan melakukan `dup(fd)`, terpisah dari resource asli, tidak saling memengaruhi. Menutup resource asli tidak akan memengaruhi resource baru +- Saat membaca, akan melakukan `dup(fd)`, membangun resource `Socket` baru di `VM` child thread yang membaca +- Saat menghapus, akan melakukan `close(fd)`, membebaskan file handle + +Artinya, `Socket` resource akan memiliki `3` reference count: +- Thread tempat `Socket` resource pertama kali dibuat +- Container `ArrayList`, `Queue`, `Map` +- Child thread yang membaca container `ArrayList`, `Queue`, `Map` + +Saat tidak ada thread atau container yang memegang resource ini, dan reference count turun ke `0`, `Socket` resource baru benar-benar dibebaskan. Jika reference count tidak `0`, +meskipun `close` dijalankan, koneksi tidak akan ditutup dan tidak akan memengaruhi `Socket` resource yang dipegang thread atau container lain. + +Jika ingin mengabaikan reference count dan langsung menutup `Socket`, bisa menggunakan method `shutdown()`, contoh: +- `stream_socket_shutdown()` +- `Socket::shutdown()` +- `socket_shutdown()` + +> Operasi `shutdown` akan memengaruhi semua thread yang memegang `Socket` resource, setelah dijalankan tidak bisa digunakan lagi, tidak bisa melakukan operasi `read/write` + +## Array +Gunakan `array_is_list()` untuk menentukan tipe array. Jika array numeric index, akan diubah ke `ArrayList`; jika associative array, akan diubah ke `Map`. + +- Seluruh array akan diiterasi, elemen dimasukkan ke `ArrayList` atau `Map` +- Mendukung multidimensional array, rekursif mengubahnya menjadi `ArrayList` atau `Map` bersarang + +Contoh: +```php +$array = [ + 'a' => random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => uniqid(), + 'd' => time(), + 'e' => [ + 'key' => 'value', + 'hello' => 'world', + ]; +]; + +$map = new Map($array); + +// $map['e'] adalah object Map baru, berisi dua elemen, key dan hello, dengan nilai 'value' dan 'world' +var_dump($map['e']); +``` + +## Object +### Thread Resource Object + +Object resource thread seperti `Thread\Lock`, `Thread\Atomic`, `Thread\ArrayList`, `Thread\Map`, dll, bisa langsung disimpan ke `ArrayList`, `Queue`, `Map`. +Operasi ini hanya menyimpan referensi object ke container, tidak melakukan copy object. + +Saat menulis object ke `ArrayList` atau `Map`, hanya menambah reference count pada resource thread, tidak melakukan copy. Saat reference count object menjadi `0`, object akan dibebaskan. + +Contoh: + +```php +$map = new Thread\Map; +$lock = new Thread\Lock; // Reference count saat ini 1 +$map['lock'] = $lock; // Reference count saat ini 2 +unset($map['lock']); // Reference count saat ini 1 +unset($lock); // Reference count saat ini 0, Lock object dibebaskan +``` + +Daftar yang didukung: + +- `Thread\Lock` +- `Thread\Atomic` +- `Thread\Atomic\Long` +- `Thread\Barrier` +- `Thread\ArrayList` +- `Thread\Map` +- `Thread\Queue` + +Perhatikan bahwa object `Thread` tidak bisa diserialisasi dan tidak bisa dikirim, hanya tersedia di parent thread. + +### Object PHP Biasa +Akan otomatis diserialisasi saat ditulis, dan di-deserialisasi saat dibaca. Perhatikan bahwa jika object mengandung tipe yang tidak bisa diserialisasi, akan melempar exception. diff --git a/public/id/timer.md b/public/id/timer.md new file mode 100644 index 0000000..80524b3 --- /dev/null +++ b/public/id/timer.md @@ -0,0 +1,327 @@ +# Timer + +Timer dengan presisi milidetik. Level bawah diimplementasikan berdasarkan `epoll_wait` dan `setitimer`, struktur data menggunakan `heap minimum`, dapat mendukung penambahan banyak timer. + +* Di proses IO sinkron menggunakan `setitimer` dan sinyal, seperti proses `Manager` dan `TaskWorker` +* Di proses IO asinkron menggunakan timeout `epoll_wait`/`kevent`/`poll`/`select` + +## Performa + +Level bawah menggunakan struktur data heap minimum untuk mengimplementasikan timer, penambahan dan penghapusan timer semuanya operasi memori, sehingga performa sangat tinggi. + +> Di script benchmark resmi [timer.php](https://github.com/swoole/benchmark/blob/master/timer.php), menambah atau menghapus `10` ribu timer dengan waktu acak memakan waktu sekitar `0.08s`. + +```shell +~/workspace/swoole/benchmark$ php timer.php +add 100000 timer :0.091133117675781s +del 100000 timer :0.084658145904541s +``` + +!> Timer adalah operasi memori, tanpa konsumsi `IO` + +## Perbedaan + +`Timer` berbeda dengan `pcntl_alarm` di `PHP` sendiri. `pcntl_alarm` diimplementasikan berdasarkan `sinyal jam + fungsi tick`, memiliki beberapa kekurangan: + +* Maksimal hanya mendukung detik, sedangkan `Timer` bisa ke level milidetik +* Tidak mendukung pengaturan beberapa timer secara bersamaan +* `pcntl_alarm` bergantung pada `declare(ticks = 1)`, performanya sangat buruk + +## Timer Nol Milidetik + +Level bawah tidak mendukung timer dengan parameter waktu `0`. Ini berbeda dengan bahasa pemrograman seperti `Node.js`. Di `Swoole` dapat menggunakan [Swoole\Event::defer](/event?id=defer) untuk mencapai fungsi serupa. + +```php +Swoole\Event::defer(function () { + echo "hello\n"; +}); +``` + +!> Kode di atas memiliki efek yang sama persis dengan `setTimeout(func, 0)` di `JS`. + +## Alias + +`tick()`, `after()`, `clear()` semuanya memiliki alias gaya fungsi + +Method Statis Kelas | Alias Gaya Fungsi +---|--- +`Swoole\Timer::tick()` | `swoole_timer_tick()` +`Swoole\Timer::after()` | `swoole_timer_after()` +`Swoole\Timer::clear()` | `swoole_timer_clear()` + +## Method + +### tick() + +Mengatur timer interval clock. + +Berbeda dengan timer `after`, timer `tick` akan terus terpicu sampai dipanggil [Timer::clear](/timer?id=clear) untuk membersihkan. + +```php +Swoole\Timer::tick(int $msec, callable $callback_function, ...$params): int +``` + +!> 1. Timer hanya valid dalam ruang proses saat ini + 2. Timer diimplementasikan murni asinkron, tidak bisa digunakan bersama fungsi [IO sinkron](/learn?id=同步io异步io), jika tidak waktu eksekusi timer akan kacau + 3. Mungkin ada sedikit kesalahan selama eksekusi timer + +* **Parameter** + + * **`int $msec`** + * **Fungsi**: Menentukan waktu + * **Satuan**: Milidetik [misal `1000` berarti `1` detik, di versi `v4.2.10` ke bawah maksimal tidak boleh melebihi `86400000`] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $callback_function`** + * **Fungsi**: Fungsi yang akan dijalankan saat waktu habis, harus dapat dipanggil + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`...$params`** + * **Fungsi**: Memberikan data ke fungsi eksekusi [parameter ini juga opsional] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Dapat menggunakan sintaks `use` fungsi anonim untuk meneruskan parameter ke fungsi callback + +* **Fungsi Callback $callback_function** + + ```php + callbackFunction(int $timer_id, ...$params); + ``` + + * **`int $timer_id`** + * **Fungsi**: `ID` timer [dapat digunakan untuk [Timer::clear](/timer?id=clear) membersihkan timer ini] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`...$params`** + * **Fungsi**: Parameter ketiga `$param` yang dimasukkan oleh `Timer::tick` + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Ekstensi** + + * **Koreksi Timer** + + Waktu eksekusi fungsi callback timer tidak mempengaruhi waktu eksekusi timer berikutnya. Contoh: mengatur timer `tick` `10ms` pada `0.002s`, pertama kali akan menjalankan fungsi callback pada `0.012s`, jika fungsi callback membutuhkan `5ms`, timer berikutnya tetap akan terpicu pada `0.022s`, bukan `0.027s`. + + Tetapi jika waktu eksekusi fungsi callback timer terlalu lama, bahkan menutupi waktu eksekusi timer berikutnya. Level bawah akan melakukan koreksi waktu, membuang perilaku yang sudah kedaluwarsa, dan memicu callback di waktu berikutnya. Seperti contoh di atas, fungsi callback pada `0.012s` membutuhkan `15ms`, seharusnya pada `0.022s` ada satu callback timer. Sebenarnya timer ini baru kembali pada `0.027s`, sudah kedaluwarsa. Level bawah akan memicu callback timer lagi pada `0.032s`. + + * **Mode Korutin** + + Di lingkungan korutin, callback `Timer::tick` akan otomatis membuat korutin, dapat langsung menggunakan `API` terkait korutin, tanpa perlu memanggil `go` untuk membuat korutin. + + !> Dapat mengatur [enable_coroutine](/timer?id=close-timer-co) untuk menonaktifkan pembuatan korutin otomatis + +* **Contoh Penggunaan** + + ```php + Swoole\Timer::tick(1000, function(){ + echo "timeout\n"; + }); + ``` + + * **Contoh Benar** + + ```php + Swoole\Timer::tick(3000, function (int $timer_id, $param1, $param2) { + echo "timer_id #$timer_id, after 3000ms.\n"; + echo "param1 is $param1, param2 is $param2.\n"; + + Swoole\Timer::tick(14000, function ($timer_id) { + echo "timer_id #$timer_id, after 14000ms.\n"; + }); + }, "A", "B"); + ``` + + * **Contoh Salah** + + ```php + Swoole\Timer::tick(3000, function () { + echo "after 3000ms.\n"; + sleep(14); + echo "after 14000ms.\n"; + }); + ``` + +### after() + +Menjalankan fungsi setelah waktu yang ditentukan. Fungsi `Swoole\Timer::after` adalah timer satu kali, akan dihancurkan setelah selesai. + +Fungsi ini berbeda dengan fungsi `sleep` yang disediakan pustaka standar `PHP`, `after` bersifat non-blocking. Sedangkan `sleep` akan menyebabkan proses saat ini masuk ke blocking, tidak bisa menangani request baru. + +```php +Swoole\Timer::after(int $msec, callable $callback_function, ...$params): int +``` + +* **Parameter** + + * **`int $msec`** + * **Fungsi**: Menentukan waktu + * **Satuan**: Milidetik [misal `1000` berarti `1` detik, di versi `v4.2.10` ke bawah maksimal tidak boleh melebihi `86400000`] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`callable $callback_function`** + * **Fungsi**: Fungsi yang akan dijalankan saat waktu habis, harus dapat dipanggil. + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`...$params`** + * **Fungsi**: Memberikan data ke fungsi eksekusi [parameter ini juga opsional] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Dapat menggunakan sintaks `use` fungsi anonim untuk meneruskan parameter ke fungsi callback + +* **Return Value** + + * Mengembalikan `ID` timer, jika ingin membatalkan timer, panggil [Swoole\Timer::clear](/timer?id=clear) + +* **Ekstensi** + + * **Mode Korutin** + + Di lingkungan korutin, callback [Swoole\Timer::after](/timer?id=after) akan otomatis membuat korutin, dapat langsung menggunakan `API` terkait korutin, tanpa perlu memanggil `go` untuk membuat korutin. + + !> Dapat mengatur [enable_coroutine](/timer?id=close-timer-co) untuk menonaktifkan pembuatan korutin otomatis + +* **Contoh Penggunaan** + +```php +$str = "Swoole"; +Swoole\Timer::after(1000, function() use ($str) { + echo "Hello, $str\n"; +}); +``` + +### clear() + +Menggunakan `ID` timer untuk menghapus timer. + +```php +Swoole\Timer::clear(int $timer_id): bool +``` + +* **Parameter** + + * **`int $timer_id`** + * **Fungsi**: `ID` timer [setelah memanggil [Timer::tick](/timer?id=tick), [Timer::after](/timer?id=after) akan mengembalikan ID integer] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +!> `Swoole\Timer::clear` tidak bisa digunakan untuk menghapus timer di proses lain, hanya berlaku untuk proses saat ini + +* **Contoh Penggunaan** + +```php +$timer = Swoole\Timer::after(1000, function () { + echo "timeout\n"; +}); + +var_dump(Swoole\Timer::clear($timer)); +var_dump($timer); + +// Output: bool(true) int(1) +// Tidak output: timeout +``` + +### clearAll() + +Menghapus semua timer dalam proses Worker saat ini. + +!> Versi Swoole >= `v4.4.0` tersedia + +```php +Swoole\Timer::clearAll(): bool +``` + +### info() + +Mengembalikan informasi `timer`. + +!> Versi Swoole >= `v4.4.0` tersedia + +```php +Swoole\Timer::info(int $timer_id): array +``` + +* **Return Value** + +```php +array(5) { + ["exec_msec"]=> + int(6000) + ["exec_count"]=> // Ditambahkan di v4.8.0 + int(5) + ["interval"]=> + int(1000) + ["round"]=> + int(0) + ["removed"]=> + bool(false) +} +``` + +### list() + +Mengembalikan iterator timer, dapat menggunakan `foreach` untuk iterasi semua id `timer` dalam proses Worker saat ini. + +!> Versi Swoole >= `v4.4.0` tersedia + +```php +Swoole\Timer::list(): Swoole\Timer\Iterator +``` + +* **Contoh Penggunaan** + +```php +foreach (Swoole\Timer::list() as $timer_id) { + var_dump(Swoole\Timer::info($timer_id)); +} +``` + +### stats() + +Melihat status timer. + +!> Versi Swoole >= `v4.4.0` tersedia + +```php +Swoole\Timer::stats(): array +``` + +* **Return Value** + +```php +array(3) { + ["initialized"]=> + bool(true) + ["num"]=> + int(1000) + ["round"]=> + int(1) +} +``` + +### set() + +Mengatur parameter terkait timer. + +```php +Swoole\Timer::set(array $array): void +``` + +!> Method ini ditandai sebagai tidak digunakan lagi sejak versi `v4.6.0`. + +## Menonaktifkan Korutin :id=close-timer-co + +Secara default, timer akan otomatis membuat korutin saat menjalankan fungsi callback. Dapat mengatur timer secara terpisah untuk menonaktifkan korutin. + +```php +swoole_async_set([ + 'enable_coroutine' => false, +]); +``` diff --git a/public/id/version/bc.md b/public/id/version/bc.md new file mode 100644 index 0000000..9b16c70 --- /dev/null +++ b/public/id/version/bc.md @@ -0,0 +1,100 @@ +# Perubahan yang Tidak Kompatibel ke Bawah + +## v5.0.0 +* Mengubah mode berjalan default `Server` menjadi `SWOOLE_BASE` +* Menaikkan persyaratan versi minimum `PHP` ke `8.0` +* Semua metode kelas dan fungsi menambahkan pembatasan tipe, berubah ke mode tipe kuat +* Menghapus alias kelas `PSR-0` dengan garis bawah, hanya mempertahankan nama kelas bergaya namespace, misalnya `swoole_server` harus diubah menjadi `Swoole\Server` +* `Swoole\Coroutine\Redis` dan `Swoole\Coroutine\MySQL` ditandai tidak digunakan lagi, silakan gunakan `Runtime Hook` + klien `Redis`/`MySQL` asli + +## v4.8.0 + +- Dalam mode `BASE`, callback `onStart` akan selalu dipanggil saat proses pekerja pertama (`workerId` 0) dimulai, mendahului `onWorkerStart`. Dalam fungsi `onStart`, API coroutine selalu dapat digunakan. Jika Worker-0 mengalami error fatal dan restart, `onStart` akan dipanggil lagi. +Di versi sebelumnya, `onStart` hanya dipanggil di Worker-0 saat hanya ada satu proses pekerja. Jika ada beberapa proses pekerja, ia dijalankan di proses Manager. + +## v4.7.0 + +- Menghapus `Table\Row`, `Table` tidak lagi mendukung pembacaan/penulisan dengan gaya array + +## v4.6.0 + +- Menghapus batas maksimum `session id`, tidak lagi berulang +- Menonaktifkan fungsi tidak aman saat menggunakan coroutine, termasuk `pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` +- Mengaktifkan coroutine hook secara default +- Tidak lagi mendukung PHP7.1 +- `Event::rshutdown()` ditandai tidak digunakan lagi, silakan gunakan Coroutine\run + +## v4.5.4 + +- `SWOOLE_HOOK_ALL` termasuk `SWOOLE_HOOK_CURL` +- Menghapus `ssl_method`, mendukung `ssl_protocols` + +## v4.4.12 + +- Versi ini mendukung kompresi frame WebSocket, mengubah parameter ketiga metode push menjadi flags. Jika `strict_types` tidak diatur, kompatibilitas kode tidak terpengaruh, jika tidak akan terjadi error tipe bool tidak bisa dikonversi implisit ke int. Masalah ini akan diperbaiki di v4.4.13. + +## v4.4.1 + +- Sinyal yang terdaftar tidak lagi digunakan sebagai syarat untuk mempertahankan event loop. **Jika program hanya mendaftarkan sinyal tanpa melakukan pekerjaan lain, program akan dianggap idle dan segera keluar** (dalam hal ini, Anda dapat mencegah proses keluar dengan mendaftarkan timer). + +## v4.4.0 + +- Sejalan dengan resmi `PHP`, tidak lagi mendukung `PHP7.0` (@matyhtf) +- Menghapus modul `Serialize`, dipelihara di ekstensi terpisah [ext-serialize](https://github.com/swoole/ext-serialize) +- Menghapus modul `PostgreSQL`, dipelihara di ekstensi terpisah [ext-postgresql](https://github.com/swoole/ext-postgresql) +- `Runtime::enableCoroutine` tidak lagi secara otomatis kompatibel dengan lingkungan coroutine dan non-coroutine; setelah diaktifkan, semua operasi blocking harus dipanggil di dalam coroutine (@matyhtf) +- Karena diperkenalkannya driver klien coroutine `MySQL` yang baru, desain internal lebih terstandarisasi, tetapi ada beberapa perubahan kecil yang tidak kompatibel ke bawah (lihat [Log Pembaruan 4.4.0](https://wiki.swoole.com/wiki/page/p-4.4.0.html)) + +## v4.3.0 + +- Menghapus semua modul asinkron, lihat [Ekstensi Asinkron Mandiri](https://wiki.swoole.com/wiki/page/p-async_ext.html) atau [Log Pembaruan 4.3.0](https://wiki.swoole.com/wiki/page/p-4.3.0.html) + +## v4.2.13 + +> Perubahan tidak kompatibel yang tidak terhindarkan karena masalah desain API historis + +* Perubahan operasi mode berlangganan klien Redis coroutine, lihat [Mode Berlangganan](https://wiki.swoole.com/#/coroutine_client/redis?id=mode-berlangganan) + +## v4.2.12 + +> Fitur eksperimental + Perubahan tidak kompatibel yang tidak terhindarkan karena masalah desain API historis + +- Menghapus opsi konfigurasi `task_async`, digantikan oleh [task_enable_coroutine](https://wiki.swoole.com/#/server/setting?id=task_enable_coroutine) + +## v4.2.5 + +- Menghapus dukungan untuk klien UDP di `onReceive` dan `Server::getClientInfo` + +## v4.2.0 + +- Sepenuhnya menghapus `swoole_http2_client` asinkron, silakan gunakan klien HTTP2 coroutine + +## v4.0.4 + +Mulai versi ini, `Http2\Client` asinkron akan memicu peringatan `E_DEPRECATED` dan akan dihapus di versi berikutnya. Silakan gunakan `Coroutine\Http2\Client` sebagai gantinya. + +Properti `body` dari `Http2\Response` diubah namanya menjadi `data`. Perubahan ini untuk memastikan konsistensi antara `request` dan `response`, dan lebih sesuai dengan nama tipe frame protokol HTTP2. + +Sejak versi ini, `Coroutine\Http2\Client` memiliki dukungan protokol HTTP2 yang relatif lengkap, dapat memenuhi kebutuhan aplikasi produksi tingkat perusahaan seperti `grpc`, `etcd`, dll. Oleh karena itu, serangkaian perubahan terkait HTTP2 sangat diperlukan. + +## v4.0.3 + +Membuat `swoole_http2_response` dan `swoole_http2_request` konsisten, semua nama properti diubah ke bentuk jamak, melibatkan properti berikut: + +- `headers` +- `cookies` + +## v4.0.2 + +> Karena implementasi internal yang terlalu rumit, sulit dipelihara, dan pengguna sering salah menggunakannya, API berikut untuk sementara dihapus: + +- `Coroutine\Channel::select` + +Namun, parameter kedua `timeout` ditambahkan ke metode `Coroutine\Channel->pop` untuk memenuhi kebutuhan pengembangan. + +## v4.0 + +> Karena peningkatan kernel coroutine, coroutine dapat dipanggil di mana saja dalam fungsi apa pun tanpa perlakuan khusus, maka API berikut dihapus: + +- `Coroutine::call_user_func` +- `Coroutine::call_user_func_array` diff --git a/public/id/version/log.md b/public/id/version/log.md new file mode 100644 index 0000000..fb1ed91 --- /dev/null +++ b/public/id/version/log.md @@ -0,0 +1,1377 @@ +# Catatan Pembaruan Versi + +Mulai dari versi `v1.5`, catatan pembaruan versi yang ketat telah dibuat. Saat ini, rata-rata siklus iterasi adalah satu rilis besar setiap enam bulan, dan satu rilis kecil setiap `2-4` minggu. + +## Versi PHP yang Disarankan + +* 8.0 +* 8.1 +* 8.2 +* 8.3 +* 8.4 + +## Versi Swoole yang Disarankan +`Swoole6.x` dan `Swoole5.x` + +Perbedaan keduanya: `v6.x` adalah branch yang aktif dikembangkan, `v5.x` adalah branch yang **tidak** aktif dikembangkan, hanya memperbaiki `BUG`. + +!> Versi `v4.x` ke atas dapat menonaktifkan fitur coroutine dengan mengatur [enable_coroutine](/server/setting?id=enable_coroutine), menjadikannya versi non-coroutine. + +## Tipe Versi + +* `alpha` Versi pratinjau fitur, menandakan tugas dalam rencana pengembangan telah selesai, dibuka untuk pratinjau, mungkin mengandung banyak `BUG` +* `beta` Versi pengujian, menandakan sudah dapat digunakan untuk pengujian di lingkungan pengembangan, mungkin mengandung `BUG` +* `rc[1-n]` Versi kandidat rilis, menandakan sedang dalam siklus rilis, menjalani pengujian skala besar, selama periode ini `BUG` masih mungkin ditemukan +* Tanpa akhiran berarti versi stabil, menandakan versi ini telah selesai dikembangkan dan siap digunakan secara resmi + +## Melihat Informasi Versi Saat Ini + +```shell +php --ri swoole +``` + +## v6.0.2 + +### Fitur Baru: +- Menambahkan metode `Swoole\Thread::yield()`, `Swoole\Thread::activeCount()`, dan `Swoole\Thread::isAlive()`. + +### Perbaikan Bug: +- Memperbaiki masalah di mode `SWOOLE_THREAD` saat menggunakan mode single-thread dan pengaturan heartbeat menyebabkan error. +- Memperbaiki masalah segmentasi error setelah mengaktifkan `swoole.enable_fiber_mock`. +- Memperbaiki masalah overflow integer di server Redis. + +### Catatan: +- Server Redis saat ini hanya akan mendukung protokol `RESP2`, saat memformat string yang tidak sesuai dengan protokol tersebut, akan melempar exception, bukan mencatat log. + +## v6.0.1 + +### Perbaikan Bug: +- Memperbaiki masalah di mode `SWOOLE_THREAD` di mana proses tidak bisa keluar normal karena event listener tidak dihapus. +- Memperbaiki masalah upload file besar saat konfigurasi `single_thread` diaktifkan. +- Memperbaiki masalah jalur file tidak ditemukan saat kompilasi jika variabel yang sama sudah didefinisikan di `config.m4`. +- Memperbaiki masalah proses tidak bisa keluar normal setelah timeout di `Swoole\Process\Pool`. +- Memperbaiki masalah crash program saat memanggil `putenv` di mode `SWOOLE_THREAD`. +- Memperbaiki masalah tidak bisa mengatur callback event untuk port terpisah di mode `SWOOLE_THREAD`. +- Memperbaiki masalah tidak bisa mendapatkan berbagai parameter runtime di event `onWorkerStart` dll di mode `SWOOLE_THREAD`. +- Memperbaiki masalah PostgreSQL yang menurun ke mode sinkron saat menerima data besar setelah coroutine. +- Mengoptimalkan logika pengecekan parameter fungsi `swoole_substr_json_decode`/`swoole_substr_unserialize`. +- Memperbaiki masalah pengaturan CPU affinity di `config.m4`. +- Memperbaiki masalah deteksi heartbeat tidak berfungsi di mode `SWOOLE_THREAD`. + +### Catatan: +- Di layanan Http, jika proses restart,level bawah akan mengirim 500 Internal Server ke permintaan yang menunggu dalam antrian, setelah selesai mengirim, koneksi ditutup dan permintaan tersebut dibuang. +- Karena `stream factory` dan `stream ops` bawaan php bergantung pada konfigurasi runtime yang tidak thread-safe, dalam mode multi-thread hanya thread utama yang diizinkan mengubah konfigurasi runtime ini sebelum membuat thread anak. +- Meng-upgrade nghttp2 ke versi terbaru. + +## v5.1.7 + +### Perbaikan Bug: +- Memperbaiki masalah PostgreSQL yang menurun ke mode sinkron saat menerima data besar setelah coroutine. +- Memperbaiki masalah exception properti saat membuat kelas dinamis `Swoole\Http2\Request`. +- Memperbaiki masalah memori error akibat fatal error dalam satu-klik coroutine. + +## v6.0.0 + +### Fitur Baru: +- `Swoole` mendukung mode multi-thread, ketika `php` dalam mode `zts`, kompilasi `Swoole` dengan `--enable-swoole-thread`. +- Menambahkan kelas thread `Swoole\Thread`. @matyhtf +- Menambahkan thread lock `Swoole\Thread\Lock`. @matyhtf +- Menambahkan atomic count thread `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf +- Menambahkan container konkuren aman `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf +- Operasi file asinkron mendukung `iouring` sebagai mesinlevel bawah, setelah menginstal `liburing` dan mengompilasi `Swoole` dengan `--enable-iouring`, fungsi `file_get_contents`, `file_put_contents`, `fopen`, `fclose`, `fread`, `fwrite`, `mkdir`, `unlink`, `fsync`, `fdatasync`, `rename`, `fstat`, `lstat`, `filesize` akan diimplementasikan oleh `iouring`. @matyhtf @NathanFreeman +- Meng-upgrade `Boost Context` ke versi 1.84. Sekarang, CPU Loongson juga dapat mendukung coroutine. @NathanFreeman +- Menambahkan metode `Swoole\Thread\Map::find()`. @matyhtf +- Menambahkan metode `Swoole\Thread\ArrayList::find()`. @matyhtf +- Menambahkan metode `Swoole\Thread\ArrayList::offsetUnset()`. @matyhtf +- Menambahkan metode `Swoole\Process::getAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setName()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::getAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setPriority()`. @matyhtf +- Menambahkan metode `Swoole\Thread::getPriority()`. @matyhtf +- Menambahkan metode `Swoole\Thread::gettid()`. +- Mesin file asinkron `iouring` mendukung mode polling multi-thread `IORING_SETUP_SQPOLL`. @NathanFreeman +- Menambahkan `iouring_workers` untuk mengubah jumlah thread iouring. @NathanFreeman +- Menambahkan `iouring_flags` untuk mendukung perubahan mode kerja `iouring`. @NathanFreeman +- Menambahkan `Swoole\Thread\Barrier` untuk sinkronisasi multi-thread. @matyhtf +- Menambahkan fungsi pengaturan cookie baru. @matyhtf @NathanFreeman +- Menambahkan "coroutine lock non-blocking yang dapat reentrant", dapat digunakan antar proses/thread, dan tidak memblokir proses/thread. @NathanFreeman +- `Swoole\Coroutine\Socket::getOption()` mendukung opsi `TCP_INFO`. @matyhtf +- Klien sinkron blocking `Swoole\Client` mendukung proxy `http`. @matyhtf +- Menambahkan klien `TCP/UDP/Unixsocket` asinkron non-blocking `Swoole\Async\Client`. @matyhtf +- Mengoptimalkan metode `Swoole\Redis\Server::format()`, mendukung zero-copy memori dan struktur bertingkat redis. @matyhtf +- Mendukung alat kompresi berkinerja tinggi `Zstd`, cukup tambahkan `--enable-zstd` saat kompilasi `Swoole`, klien dan server http dapat menggunakan `zstd` untuk mengompresi atau mendekode respons. @NathanFreeman + +### Perbaikan Bug: +- Memperbaiki masalah tidak bisa diinstal melalui `pecl`. @remicollet +- Memperbaiki klien `Swoole\Coroutine\FastCGI\Client` tidak bisa mengatur keepalive. @NathanFreeman +- Memperbaiki masalah error saat parameter permintaan melebihi `max_input_vars` menyebabkan proses terus restart. @NathanFreeman +- Memperbaiki masalah tidak diketahui saat menggunakan `Swoole\Event::wait()` di coroutine. @matyhtf +- Memperbaiki masalah `proc_open` tidak mendukung pty saat coroutine. @matyhtf +- Memperbaiki masalah `pdo_sqlite` mengalami segmentasi error di PHP8.3. @NathanFreeman +- Memperbaiki peringatan tidak berguna saat kompilasi `Swoole`. @Appla @NathanFreeman +- Memperbaiki error saatlevel bawah memanggil zend_fetch_resource2_ex jika `STDOUT/STDERR` sudah ditutup. @Appla @matyhtf +- Memperbaiki konfigurasi `set_tcp_nodelay` yang tidak valid. @matyhtf +- Memperbaiki masalah kadang-kadang memicu cabang yang tidak terjangkau saat upload file. @NathanFreeman +- Memperbaiki masalah pengaturan `dispatch_func` menyebabkan error dilevel bawah php. @NathanFreeman +- Memperbaiki AC_PROG_CC_C99 yang sudah usang di autoconf >= 2.70. @petk +- Menangkap exception saat pembuatan thread gagal. @matyhtf +- Memperbaiki masalah `_tsrm_ls_cache` tidak terdefinisi. @jingjingxyk +- Memperbaiki masalah kompilasi fatal error di `GCC 14`. @remicollet +- Memperbaiki masalah properti dinamis `Swoole\Http2\Request`. @guandeng +- Memperbaiki masalah sumber daya `pgsql` coroutine client kadang tidak tersedia. @NathanFreeman +- Memperbaiki masalah 503 error karena parameter terkait tidak diatur ulang saat restart proses. @matyhtf +- Memperbaiki ketidaksesuaian hasil `$request->server['request_method']` dengan `$request->getMethod()` saat `HTTP2` diaktifkan. @matyhtf +- Memperbaiki `content-type` yang salah saat upload file. @matyhtf +- Memperbaiki kesalahan kode klien coroutine `http2`. @matyhtf +- Memperbaiki masalah `Swoole\Server` kekurangan properti `worker_id`. @cjavad +- Memperbaiki kesalahan `config.m4` terkait `brotli`. @fundawang +- Memperbaiki `Swoole\Http\Response::create` tidak berfungsi di multi-thread. @matyhtf +- Memperbaiki error kompilasi di lingkungan `macos`. @matyhtf +- Memperbaiki masalah thread tidak bisa keluar dengan aman. @matyhtf +- Memperbaiki masalah variabel statis waktu respons `Swoole\Http\Response` tidak dibuat per thread di mode multi-thread. @matyhtf @NathanFreeman +- Memperbaiki masalah `Fatal error` yang disebabkan oleh fitur `timeout` di `PHP-8.4` mode ZTS. @matyhtf +- Memperbaiki hook fungsi `exit()` di `PHP-8.4`. @remicollet +- Memperbaiki masalah `Swoole\Thread::getNativeId()` tidak berfungsi di `cygwin`. @matyhtf +- Memperbaiki masalah `Swoole\Coroutine::getaddrinfo()` menyebabkan `SIGSEGV`. @matyhtf +- Memperbaiki masalah modul `runtime tcp` tidak mendukung aktivasi enkripsi SSL dinamis. @matyhtf +- Memperbaiki masalah waktu timeout tidak tepat saat klien http berjalan lama. @matyhtf +- Memperbaiki masalah mutex `Swoole\Table` tidak bisa digunakan sebelum proses keluar. @matyhtf +- Memperbaiki masalah `Swoole\Server::stop()` gagal saat menggunakan parameter bernama. @matyhtf +- Memperbaiki masalah crash karena fungsi `Swoole\Thread\Map::toArray()` tidak menyalin `key`. @matyhtf +- Memperbaiki masalah `Swoole\Thread\Map` tidak bisa menghapus kunci numerik bertingkat. @matyhtf + +### Optimasi Kernel: +- Menghapus pengecekan tidak berguna pada `socket structs`. @petk +- Meng-upgrade Swoole Library. @deminy +- `Swoole\Http\Response` menambahkan dukungan untuk status code 451. @abnegate +- Menyinkronkan kode operasi `file` antar versi PHP yang berbeda. @NathanFreeman +- Menyinkronkan kode operasi `pdo` antar versi PHP yang berbeda. @NathanFreeman +- Mengoptimalkan kode `Socket::ssl_recv()`. @matyhtf +- Mengoptimalkan config.m4, beberapa konfigurasi dapat mengatur lokasi library dependensi melalui `pkg-config`. @NathanFreeman +- Mengoptimalkan masalah penggunaan array dinamis saat `mengurai header permintaan`. @NathanFreeman +- Mengoptimalkan masalah siklus hidup file descriptor `fd` di mode multi-thread. @matyhtf +- Mengoptimalkan beberapa logika dasar coroutine. @matyhtf +- Meng-upgrade versi database oracle untuk pengujian CI. @gvenzl +- Mengoptimalkan logika terkait `sendfile` dilevel bawah. @matyhtf +- Mengganti `PHP_DEF_HAVE` dengan `AC_DEFINE_UNQUOTED` di `config.m4`. @petk +- Mengoptimalkan logika terkait `heartbeat`, `shutdown`, dan `stop` server di mode multi-thread. @matyhtf +- Mengoptimalkan agar tidak perlu menautkan `librt` saat versi glibc di atas 2.17. @matyhtf +- Memperkuat klien `http` agar dapat menerima header permintaan duplikat. @matyhtf +- Mengoptimalkan `Swoole\Http\Response::write()`. @matyhtf +- `Swoole\Http\Response::write()` sekarang dapat mengirim protokol `http2`. @matyhtf +- Kompatibel dengan `PHP8.4`. @matyhtf @NathanFreeman +- Menambahkan kemampuan penulisan asinkron socket dilevel bawah. @matyhtf +- Mengoptimalkan `Swoole\Http\Response`. @NathanFreeman +- Mendukung berbagi socket native php di mode multi-thread. @matyhtf +- Mengoptimalkan layanan file statis, memperbaiki masalah jalur file statis yang salah. @matyhtf +- Server `Asinkron` mode multi-thread mendukung restart worker thread. @matyhtf +- Server `Asinkron` mode multi-thread mendukung pengaktifan timer di thread `Manager`. @matyhtf +- Kompatibel dengan ekstensi `curl` `PHP-8.4`. @matyhtf @NathanFreeman +- Menulis ulang kode penggunaan `iouring` dilevel bawah Swoole. @matyhtf @NathanFreeman +- Mengoptimalkan timer agar proses sinkron tidak bergantung pada sinyal. @matyhtf +- Mengoptimalkan metode `Swoole\Coroutine\System::waitSignal()`, memungkinkan mendengarkan beberapa sinyal sekaligus. @matyhtf + +### Tidak Lagi Didukung: +- Tidak lagi mendukung `PHP 8.0`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\MySQL`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\Redis`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\PostgreSQL`. +- Menghapus metode `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, dan `Swoole\Coroutine\System::fgets()`. + +## v5.1.5 + +### Perbaikan Bug: +- Memperbaiki kebutuhan menggunakan `zend_ini_parse_quantity` untuk mengurai string numerik saat versi php lebih besar dari 8.2. @matyhtf +- Memperbaiki masalah sumber daya `pdo_pgsql` kadang tidak tersedia saat coroutine. @NathanFreeman +- Memperbaiki masalah referensi file header `pdo_pgsql` saat coroutine. @NathanFreeman +- Memperbaiki pemeriksaan jalur relatif yang salah untuk menghindari bypass validasi jalur. @matyhtf +- Memperbaiki masalah jumlah konkurensi yang tidak akurat saat restart proses di lingkungan konkurensi tinggi. @matyhtf + +### Optimasi Kernel: +- Menyinkronkan beberapa kode terkait `php8.3 curl`. @NathanFreeman +- Memperbaiki error tes inti modul `process`. @NathanFreeman +- Semua koneksi harus ditutup pada tahap `PHP RSHUTDOWN` dalam mode `SWOOLE_BASE`. @matyhtf +- Mengoptimalkan kode kernel. @matyhtf + +## v6.0.0-beta + +### Fitur Baru: +- Menambahkan metode `Swoole\Thread\Map::find()`. @matyhtf +- Menambahkan metode `Swoole\Thread\ArrayList::find()`. @matyhtf +- Menambahkan metode `Swoole\Thread\ArrayList::offsetUnset()`. @matyhtf +- Menambahkan metode `Swoole\Process::getAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setName()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::getAffinity()`. @matyhtf +- Menambahkan metode `Swoole\Thread::setPriority()`. @matyhtf +- Menambahkan metode `Swoole\Thread::getPriority()`. @matyhtf +- Menambahkan metode `Swoole\Thread::gettid()`. +- Mesin file asinkron `iouring` mendukung mode polling multi-thread `IORING_SETUP_SQPOLL`. @NathanFreeman +- Menambahkan `iouring_workers` untuk mengubah jumlah thread iouring. @NathanFreeman +- Menambahkan `iouring_flags` untuk mendukung perubahan mode kerja `iouring`. @NathanFreeman +- Menambahkan `Swoole\Thread\Barrier` untuk sinkronisasi multi-thread. @matyhtf +- Menambahkan fungsi pengaturan cookie baru. @matyhtf @NathanFreeman + +### Perbaikan Bug: +- Memperbaiki masalah properti dinamis `Swoole\Http2\Request`. @guandeng +- Memperbaiki masalah sumber daya `pgsql` coroutine client kadang tidak tersedia. @NathanFreeman +- Memperbaiki masalah 503 error karena parameter terkait tidak diatur ulang saat restart proses. @matyhtf +- Memperbaiki ketidaksesuaian hasil `$request->server['request_method']` dengan `$request->getMethod()` saat `HTTP2` diaktifkan. @matyhtf +- Memperbaiki `content-type` yang salah saat upload file. @matyhtf +- Memperbaiki kesalahan kode klien coroutine `http2`. @matyhtf +- Memperbaiki masalah `Swoole\Server` kekurangan properti `worker_id`. @cjavad +- Memperbaiki kesalahan `config.m4` terkait `brotli`. @fundawang +- Memperbaiki `Swoole\Http\Response::create` tidak berfungsi di multi-thread. @matyhtf +- Memperbaiki error kompilasi di lingkungan `macos`. @matyhtf +- Memperbaiki masalah thread tidak bisa keluar dengan aman. @matyhtf +- Memperbaiki masalah variabel statis waktu respons `Swoole\Http\Response` tidak dibuat per thread di mode multi-thread. @matyhtf @NathanFreeman + +### Optimasi Kernel: +- Meng-upgrade versi database oracle untuk pengujian CI. @gvenzl +- Menulis ulang dan mengoptimalkan kode terkaitlevel bawah swoole. @matyhtf +- Mengoptimalkan logika terkait `sendfile` dilevel bawah. @matyhtf +- Mengoptimalkan penguraian parameter. @matyhtf +- Mengganti `PHP_DEF_HAVE` dengan `AC_DEFINE_UNQUOTED` di `config.m4`. @petk +- Mengoptimalkan logika terkait `heartbeat`, `shutdown`, dan `stop` server di mode multi-thread. @matyhtf +- Mengoptimalkan agar tidak perlu menautkan `librt` saat versi glibc di atas 2.17. @matyhtf +- Memperkuat klien `http` agar dapat menerima header permintaan duplikat. @matyhtf +- Mengoptimalkan `Swoole\Http\Response::write()`. @matyhtf +- `Swoole\Http\Response::write()` sekarang dapat mengirim protokol `http2`. @matyhtf +- Kompatibel dengan `PHP8.4`. @matyhtf @NathanFreeman +- Menambahkan kemampuan penulisan asinkron socket dilevel bawah. @matyhtf +- Mengoptimalkan `Swoole\Http\Response`. @NathanFreeman +- Mengoptimalkan informasi error dilevel bawah. @matyhtf +- Mendukung berbagi socket native php di mode multi-thread. @matyhtf +- Mengoptimalkan layanan file statis, memperbaiki masalah jalur file statis yang salah. @matyhtf + +### Tidak Lagi Didukung: +- Menghapus metode `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, dan `Swoole\Coroutine\System::fgets()`. + +## v6.0.0-alpha + +### Fitur Baru +- `Swoole` mendukung mode multi-thread, ketika `php` dalam mode `zts`, kompilasi `Swoole` dengan `--enable-swoole-thread`. +- Menambahkan kelas manajemen thread `Swoole\Thread`. @matyhtf +- Menambahkan thread lock `Swoole\Thread\Lock`. @matyhtf +- Menambahkan atomic count thread `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf +- Menambahkan container konkuren aman `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf +- Operasi file asinkron mendukung `iouring` sebagai mesinlevel bawah, setelah menginstal `liburing` dan mengompilasi `Swoole` dengan `--enable-iouring`, fungsi `file_get_contents`, `file_put_contents`, `fopen`, `fclose`, `fread`, `fwrite`, `mkdir`, `unlink`, `fsync`, `fdatasync`, `rename`, `fstat`, `lstat`, `filesize` akan diimplementasikan oleh `iouring`. @matyhtf @NathanFreeman +- Meng-upgrade `Boost Context` ke versi 1.84. Sekarang, CPU Loongson juga dapat menggunakan coroutine. @NathanFreeman + +### Perbaikan Bug +- Memperbaiki masalah tidak bisa diinstal melalui `pecl`. @remicollet +- Memperbaiki klien `Swoole\Coroutine\FastCGI\Client` tidak bisa mengatur keepalive. @NathanFreeman +- Memperbaiki masalah error saat parameter permintaan melebihi `max_input_vars` menyebabkan proses terus restart. @NathanFreeman +- Memperbaiki masalah tidak diketahui saat menggunakan `Swoole\Event::wait()` di coroutine. @matyhtf +- Memperbaiki masalah `proc_open` tidak mendukung pty saat coroutine. @matyhtf +- Memperbaiki masalah `pdo_sqlite` mengalami segmentasi error di PHP8.3. @NathanFreeman +- Memperbaiki peringatan tidak berguna saat kompilasi `Swoole`. @Appla @NathanFreeman +- Memperbaiki error saatlevel bawah memanggil zend_fetch_resource2_ex jika `STDOUT/STDERR` sudah ditutup. @Appla @matyhtf +- Memperbaiki konfigurasi `set_tcp_nodelay` yang tidak valid. @matyhtf +- Memperbaiki masalah kadang-kadang memicu cabang yang tidak terjangkau saat upload file. @NathanFreeman +- Memperbaiki masalah pengaturan `dispatch_func` menyebabkan error dilevel bawah php. @NathanFreeman +- Memperbaiki AC_PROG_CC_C99 yang sudah usang di autoconf >= 2.70. @petk +- Menangkap exception saat pembuatan thread gagal. @matyhtf +- Memperbaiki masalah `_tsrm_ls_cache` tidak terdefinisi. @jingjingxyk +- Memperbaiki masalah kompilasi fatal error di `GCC 14`. @remicollet + +### Optimasi Kernel +- Menghapus pengecekan tidak berguna pada `socket structs`. @petk +- Meng-upgrade Swoole Library. @deminy +- `Swoole\Http\Response` menambahkan dukungan untuk status code 451. @abnegate +- Menyinkronkan kode operasi `file` antar versi PHP yang berbeda. @NathanFreeman +- Menyinkronkan kode operasi `pdo` antar versi PHP yang berbeda. @NathanFreeman +- Mengoptimalkan kode `Socket::ssl_recv()`. @matyhtf +- Mengoptimalkan config.m4, beberapa konfigurasi dapat mengatur lokasi library dependensi melalui `pkg-config`. @NathanFreeman +- Mengoptimalkan masalah penggunaan array dinamis saat `mengurai header permintaan`. @NathanFreeman +- Mengoptimalkan masalah siklus hidup file descriptor `fd` di mode multi-thread. @matyhtf +- Mengoptimalkan beberapa logika dasar coroutine. @matyhtf + +### Tidak Lagi Didukung +- Tidak lagi mendukung `PHP 8.0`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\MySQL`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\Redis`. +- Tidak lagi mendukung klien coroutine `Swoole\Coroutine\PostgreSQL`. + +## v5.1.3 +### Perbaikan Bug: +- Memperbaiki masalah tidak bisa diinstal melalui `pecl`. +- Memperbaiki klien `Swoole\Coroutine\FastCGI\Client` tidak bisa mengatur keepalive. +- Memperbaiki masalah error saat parameter permintaan melebihi `max_input_vars` menyebabkan proses terus restart. +- Memperbaiki masalah tidak diketahui saat menggunakan `Swoole\Event::wait()` di coroutine. +- Memperbaiki masalah `proc_open` tidak mendukung pty saat coroutine. +- Memperbaiki masalah `pdo_sqlite` mengalami segmentasi error di PHP8.3. +- Memperbaiki peringatan tidak berguna saat kompilasi `Swoole`. +- Memperbaiki error saatlevel bawah memanggil zend_fetch_resource2_ex jika `STDOUT/STDERR` sudah ditutup. +- Memperbaiki konfigurasi `set_tcp_nodelay` yang tidak valid. +- Memperbaiki masalah kadang-kadang memicu cabang yang tidak terjangkau saat upload file. +- Memperbaiki masalah pengaturan `dispatch_func` menyebabkan error dilevel bawah php. +- Memperbaiki AC_PROG_CC_C99 yang sudah usang di autoconf >= 2.70. + +### Optimasi Kernel: +- Menghapus pengecekan tidak berguna pada `socket structs`. +- Meng-upgrade Swoole Library. +- `Swoole\Http\Response` menambahkan dukungan untuk status code 451. +- Menyinkronkan kode operasi `file` antar versi PHP yang berbeda. +- Menyinkronkan kode operasi `pdo` antar versi PHP yang berbeda. +- Mengoptimalkan kode `Socket::ssl_recv()`. +- Mengoptimalkan config.m4, beberapa konfigurasi dapat mengatur lokasi library dependensi melalui `pkg-config`. +- Mengoptimalkan masalah penggunaan array dinamis saat `mengurai header permintaan`. + +## v5.1.2 + +### Perbaikan Bug +- Mendukung embedded sapi. +- Memperbaiki masalah kompatibilitas ZEND_CHECK_STACK_LIMIT di PHP 8.3. +- Memperbaiki error tidak adanya header Content-Range saat range request mengembalikan seluruh konten file. +- Memperbaiki cookie yang terpotong. +- Memperbaiki crash native-curl di PHP 8.3. +- Memperbaiki error errno yang tidak valid setelah Server::Manager::wait(). +- Memperbaiki kesalahan ketik HTTP2. + +### Optimasi +- Mengoptimalkan kinerja HTTP server. +- Menambahkan CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY sebagai alasan penutupan websocket yang valid. + +## v5.1.1 +### Perbaikan Bug +- Memperbaiki masalah kebocoran memori `http coroutine client`. +- Memperbaiki masalah `pdo_odbc` tidak bisa di-coroutine-kan. +- Memperbaiki masalah eksekusi `socket_import_stream()` yang salah. +- Memperbaiki masalah `Context::parse_multipart_data()` tidak bisa menangani body permintaan kosong. +- Memperbaiki masalah parameter `PostgreSQL coroutine client` tidak berfungsi. +- Memperbaiki bug crash `curl` saat destruksi. +- Memperbaiki masalah ketidakcocokan `Swoole5.x` dengan `xdebug` versi baru. +- Memperbaiki masalah `kelas tidak ditemukan` akibat peralihan coroutine selama autoload. +- Memperbaiki masalah tidak bisa mengompilasi `swoole` di `OpenBSD`. + +## v5.1.0 + +### Fitur Baru +- Menambahkan dukungan coroutine untuk `pdo_pgsql` +- Menambahkan dukungan coroutine untuk `pdo_odbc` +- Menambahkan dukungan coroutine untuk `pdo_oci` +- Menambahkan dukungan coroutine untuk `pdo_sqlite` +- Menambahkan konfigurasi koneksi pool untuk `pdo_pgsql`, `pdo_odbc`, `pdo_oci`, `pdo_sqlite` + +### Peningkatan +- Meningkatkan kinerja `Http\Server`, dapat meningkat hingga `60%` dalam kasus ekstrem + +### Perbaikan +- Memperbaiki kebocoran memori setiap permintaan pada klien coroutine `WebSocket` +- Memperbaiki masalah `http coroutine server` keluar secara tidak graceful menyebabkan klien tidak keluar +- Memperbaiki masalah opsi `--enable-thread-context` saat kompilasi menyebabkan `Process::signal()` tidak berfungsi +- Memperbaiki masalah penghitungan koneksi yang salah saat proses keluar tidak normal dalam mode `SWOOLE_BASE` +- Memperbaiki kesalahan tanda tangan fungsi `stream_select()` +- Memperbaiki kesalahan sensitivitas huruf besar/kecil pada informasi MIME file +- Memperbaiki kesalahan ejaan `Http2\Request::$usePipelineRead` yang menyebabkan peringatan di lingkungan PHP8.2 +- Memperbaiki masalah kebocoran memori dalam mode `SWOOLE_BASE` +- Memperbaiki masalah kebocoran memori saat mengatur waktu kadaluarsa cookie dengan `Http\Response::cookie()` +- Memperbaiki masalah kebocoran koneksi dalam mode `SWOOLE_BASE` + +### Kernel +- Memperbaiki masalah tanda tangan fungsi `php_url_encode` di Swoole pada PHP 8.3 +- Memperbaiki masalah opsi unit test +- Mengoptimalkan dan menulis ulang kode +- Kompatibel dengan PHP8.3 +- Tidak mendukung kompilasi pada sistem operasi 32-bit + +## v5.0.3 + +### Peningkatan +- Menambahkan opsi `--with-nghttp2_dir` untuk menggunakan library `nghttp2` dalam sistem +- Mendukung opsi terkait panjang atau ukuran byte +- Menambahkan fungsi `Process\Pool::sendMessage()` +- `Http\Response:cookie()` mendukung `max-age` + +### Perbaikan +- Memperbaiki kebocoran memori yang disebabkan oleh event `Server task/pipemessage/finish` + +### Kernel +- Konflik header respons `http` tidak lagi memicu error +- Penutupan koneksi `Server` tidak lagi memicu error + +## v5.0.2 + +### Peningkatan +- Mendukung konfigurasi pengaturan default untuk `http2` +- Mendukung `xdebug` versi 8.1 atau lebih tinggi +- Menulis ulang curl native untuk mendukung handle curl dengan banyak socket, seperti protokol curl FTP +- Menambahkan parameter `who` di `Process::setPriority/getPriority` +- Menambahkan metode `Coroutine\Socket::getBoundCid()` +- Menyesuaikan nilai default parameter `length` dalam metode `Coroutine\Socket::recvLine/recvWithBuffer` menjadi `65536` +- Menulis ulang fitur keluar lintas-coroutine untuk pembebasan memori yang lebih aman dan memperbaiki masalah crash saat fatal error +- Menambahkan atribut `socket` untuk `Coroutine\Client`, `Coroutine\Http\Client`, `Coroutine\Http2\Client`, memungkinkan operasi langsung pada resource socket +- Mendukung pengiriman file kosong dari `Http\Server` ke klien `http2` +- Mendukung restart graceful untuk `Coroutine\Http\Server`. Saat server dimatikan, koneksi klien tidak lagi ditutup paksa, hanya berhenti mendengarkan permintaan baru +- Menambahkan `pcntl_rfork` dan `pcntl_sigwaitinfo` ke daftar fungsi tidak aman, akan dinonaktifkan saat container coroutine dimulai +- Menulis ulang manajer proses mode `SWOOLE_BASE`, perilaku shutdown dan reload akan konsisten dengan `SWOOLE_PROCESS` + +## v5.0.1 + +### Peningkatan +- Mendukung `PHP-8.2`, meningkatkan penanganan exception coroutine, kompatibilitas dengan `ext-soap` +- Menambahkan dukungan `LOB` untuk klien coroutine `pgsql` +- Meningkatkan klien `websocket`, header upgrade menyertakan `websocket` bukan menggunakan `=` +- Mengoptimalkan klien `http`, menonaktifkan `keep-alive` saat server mengirim `connection close` +- Mengoptimalkan klien `http`, menonaktifkan penambahan header `Accept-Encoding` tanpa library kompresi +- Meningkatkan informasi debug, mengatur kata sandi sebagai parameter sensitif di `PHP-8.2` +- Memperkuat `Server::taskWaitMulti()`, tidak memblokir di lingkungan coroutine +- Mengoptimalkan fungsi log, tidak lagi mencetak ke layar saat gagal menulis ke file log + +### Perbaikan +- Memperbaiki masalah kompatibilitas parameter untuk `Coroutine::printBackTrace()` dan `debug_print_backtrace()` +- Memperbaiki dukungan resource socket di `Event::add()` +- Memperbaiki error kompilasi saat `zlib` tidak tersedia +- Memperbaiki masalah crash saat membongkar task server ketika parsing string yang tidak terduga +- Memperbaiki masalah penambahan timer kurang dari `1ms` dipaksa menjadi `0` +- Memperbaiki masalah crash yang disebabkan oleh `Table::getMemorySize()` sebelum menambahkan kolom +- Mengubah nama parameter kadaluarsa dalam metode `Http\Response::setCookie()` menjadi `expires` + +## v5.0.0 + +### Fitur Baru +- Menambahkan opsi `max_concurrency` untuk `Server` +- Menambahkan opsi `max_retries` untuk `Coroutine\Http\Client` +- Menambahkan opsi global `name_resolver`. Menambahkan opsi `upload_max_filesize` untuk `Server` +- Menambahkan metode `Coroutine::getExecuteTime()` +- Menambahkan mode dispatch `SWOOLE_DISPATCH_CONCURRENT_LB` untuk `Server` +- Memperkuat sistem tipe, menambahkan tipe untuk semua parameter dan nilai kembali fungsi +- Mengoptimalkan penanganan error, semua konstruktor akan melempar exception saat gagal +- Menyesuaikan mode default `Server` menjadi `SWOOLE_BASE` +- Memindahkan klien coroutine `pgsql` ke library inti. Mencakup semua perbaikan bug dari branch `4.8.x` + +### Penghapusan +- Menghapus nama kelas bergaya `PSR-0` +- Menghapus fitur penambahan otomatis `Event::wait()` dalam fungsi shutdown +- Menghapus alias `Server::tick/after/clearTimer/defer` +- Menghapus `--enable-http2/--enable-swoole-json`, disesuaikan menjadi aktif default + +### Tidak Lagi Didukung +- Klien coroutine `Coroutine\Redis` dan `Coroutine\MySQL` tidak digunakan lagi secara default + +## v4.8.13 + +### Peningkatan +- Menulis ulang curl native untuk mendukung handle curl dengan banyak socket, seperti protokol curl FTP +- Mendukung pengaturan manual konfigurasi `http2` +- Meningkatkan `WebSocket client`, header upgrade menyertakan `websocket` bukan `equal` +- Mengoptimalkan klien HTTP, menonaktifkan `keep-alive` saat server mengirim penutupan koneksi +- Meningkatkan informasi debug, mengatur kata sandi sebagai parameter sensitif di PHP-8.2 +- Mendukung `HTTP Range Requests` + +### Perbaikan +- Memperbaiki masalah kompatibilitas parameter di `Coroutine::printBackTrace()` dan `debug_print_backtrace()` +- Memperbaiki masalah parsing panjang yang salah saat server `WebSocket` mengaktifkan protokol `HTTP2` dan `WebSocket` secara bersamaan +- Memperbaiki masalah kebocoran memori saat `send_yield` terjadi di `Server::send()`, `Http\Response::end()`, `Http\Response::write()`, dan `WebSocket/Server::push()` +- Memperbaiki masalah crash yang disebabkan oleh `Table::getMemorySize()` sebelum menambahkan kolom + +## v4.8.12 + +### Peningkatan +- Mendukung PHP8.2 +- Fungsi `Event::add()` mendukung resource socket +- `Http\Client::sendfile()` mendukung file lebih dari 4G +- `Server::taskWaitMulti()` mendukung lingkungan coroutine + +### Perbaikan +- Memperbaiki masalah penerimaan `multipart body` yang salah akan memicu pesan error +- Memperbaiki error yang disebabkan oleh waktu timeout timer kurang dari `1ms` +- Memperbaiki masalah deadlock yang disebabkan oleh disk penuh + +## v4.8.11 + +### Peningkatan +- Mendukung mekanisme pertahanan keamanan `Intel CET` +- Menambahkan properti `Server::$ssl` +- Saat mengompilasi `swoole` menggunakan `pecl`, menambahkan atribut `enable-cares` +- Menulis ulang interpreter `multipart_parser` + +### Perbaikan +- Memperbaiki segmentasi error akibat exception koneksi persisten `pdo` +- Memperbaiki segmentasi error akibat penggunaan coroutine di destruktor +- Memperbaiki pesan error yang salah di `Server::close()` + +## v4.8.10 + +### Perbaikan + +- Mengatur ulang parameter timeout `stream_select` menjadi `0` saat kurang dari `1ms` +- Memperbaiki kegagalan kompilasi akibat penambahan `-Werror=format-security` saat kompilasi +- Memperbaiki segmentasi error `Swoole\Coroutine\Http\Server` akibat penggunaan `curl` + +## v4.8.9 + +### Peningkatan + +- Mendukung opsi `http_auto_index` di server `Http2` + +### Perbaikan + +- Mengoptimalkan parser `Cookie`, mendukung opsi `HttpOnly` +- Memperbaiki #4657, masalah tipe kembali metode Hook `socket_create` +- Memperbaiki kebocoran memori `stream_select` + +### Pembaruan CLI + +- `CygWin` menyertakan rantai sertifikat SSL, memperbaiki error autentikasi SSL +- Diperbarui ke `PHP-8.1.5` + +## v4.8.8 + +### Optimasi + +- Mengurangi SW_IPC_BUFFER_MAX_SIZE menjadi 64k +- Mengoptimalkan pengaturan header_table_size http2 + +### Perbaikan + +- Memperbaiki banyak error socket saat menggunakan enable_static_handler untuk mengunduh file statis +- Memperbaiki error NPN server http2 + +## v4.8.7 + +### Peningkatan + +- Menambahkan dukungan curl_share + +### Perbaikan + +- Memperbaiki error simbol tidak terdefinisi pada arsitektur arm32 +- Memperbaiki kompatibilitas `clock_gettime()` +- Memperbaiki masalah server mode PROCESS gagal mengirim saat kernel kekurangan blok memori besar + +## v4.8.6 + +### Perbaikan + +- Menambahkan prefiks untuk nama API boost/context +- Mengoptimalkan opsi konfigurasi + +## v4.8.5 + +### Perbaikan + +- Mengembalikan tipe parameter Table +- Memperbaiki crash saat menerima data yang salah menggunakan protokol Websocket + +## v4.8.4 + +### Perbaikan + +- Memperbaiki kompatibilitas hook socket dengan PHP-8.1 +- Memperbaiki kompatibilitas Table dengan PHP-8.1 +- Memperbaiki masalah parsing parameter `POST` dengan `Content-Type` `application/x-www-form-urlencoded` di server HTTP gaya coroutine yang tidak sesuai harapan dalam beberapa kasus + +## v4.8.3 + +### API Baru + +- Menambahkan metode `Coroutine\Socket::isClosed()` + +### Perbaikan + +- Memperbaiki masalah kompatibilitas curl native hook di php8.1 +- Memperbaiki masalah kompatibilitas socket hook di php8 +- Memperbaiki nilai kembali fungsi socket hook yang salah +- Memperbaiki masalah Http2Server sendfile tidak bisa mengatur content-type +- Mengoptimalkan kinerja date header HttpServer, menambahkan cache + +## v4.8.2 + +### Perbaikan + +- Memperbaiki masalah kebocoran memori hook `proc_open` +- Memperbaiki masalah kompatibilitas curl native hook dengan PHP-8.0 dan PHP-8.1 +- Memperbaiki masalah tidak bisa menutup koneksi secara normal di proses Manager +- Memperbaiki masalah proses Manager tidak bisa menggunakan `sendMessage` +- Memperbaiki masalah parsing data POST sangat besar yang abnormal di `Coroutine\Http\Server` +- Memperbaiki masalah tidak bisa keluar langsung saat fatal error di lingkungan PHP 8 +- Menyesuaikan konfigurasi coroutine `max_concurrency`, hanya diizinkan digunakan di `Co::set()` +- Menyesuaikan `Coroutine::join()` untuk mengabaikan coroutine yang tidak ada + +## v4.8.1 + +### API Baru + +- Menambahkan fungsi `swoole_error_log_ex()` dan `swoole_ignore_error()` (#4440) (@matyhtf) + +### Peningkatan + +- Memigrasikan admin API di ext-swoole_plus ke ext-swoole (#4441) (@matyhtf) +- Admin server menambahkan perintah get_composer_packages (swoole/library@07763f46) (swoole/library@8805dc05) (swoole/library@175f1797) (@sy-records) (@yunbaoi) +- Menambahkan batasan permintaan metode POST untuk operasi tulis (swoole/library@ac16927c) (@yunbaoi) +- Admin server mendukung mendapatkan informasi metode kelas (swoole/library@690a1952) (@djw1028769140) (@sy-records) +- Mengoptimalkan kode admin server (swoole/library#128) (swoole/library#131) (@sy-records) +- Admin server mendukung permintaan konkuren ke banyak target dan API (swoole/library#124) (@sy-records) +- Admin server mendukung mendapatkan informasi antarmuka (swoole/library#130) (@sy-records) +- SWOOLE_HOOK_CURL mendukung CURLOPT_HTTPPROXYTUNNEL (swoole/library#126) (@sy-records) + +### Perbaikan + +- Metode `join` melarang pemanggilan konkuren pada coroutine yang sama (#4442) (@matyhtf) +- Memperbaiki masalah pelepasan kunci atomik Table yang tidak terduga (#4446) (@Txhua) (@matyhtf) +- Memperbaiki opsi helper yang hilang (swoole/library#123) (@sy-records) +- Memperbaiki parameter perintah `get_static_property_value` yang salah (swoole/library#129) (@sy-records) + +## v4.8.0 + +### Perubahan Tidak Kompatibel + +- Dalam mode base, callback `onStart` akan selalu dipicu saat proses pekerja pertama (worker id 0) dimulai, mendahului eksekusi `onWorkerStart` (#4389) (@matyhtf) + +### API Baru + +- Menambahkan metode `Co::getStackUsage()` (#4398) (@matyhtf) (@twose) +- Menambahkan beberapa API `Coroutine\Redis` (#4390) (@chrysanthemum) +- Menambahkan metode `Table::stats()` (#4405) (@matyhtf) +- Menambahkan metode `Coroutine::join()` (#4406) (@matyhtf) + +### Fitur Baru + +- Mendukung perintah server (#4389) (@matyhtf) +- Mendukung callback event `Server::onBeforeShutdown` (#4415) (@matyhtf) + +### Peningkatan + +- Mengatur kode error saat Websocket pack gagal (swoole/swoole-src@d27c5a5) (@matyhtf) +- Menambahkan field `Timer::exec_count` (#4402) (@matyhtf) +- Hook mkdir mendukung penggunaan konfigurasi ini open_basedir (#4407) (@NathanFreeman) +- Menambahkan skrip vendor_init.php ke library (swoole/library@6c40b02) (@matyhtf) +- SWOOLE_HOOK_CURL mendukung CURLOPT_UNIX_SOCKET_PATH (swoole/library#121) (@sy-records) +- Client mendukung pengaturan item konfigurasi ssl_ciphers (#4432) (@amuluowin) +- Menambahkan informasi baru ke `Server::stats()` (#4410) (#4412) (@matyhtf) + +### Perbaikan + +- Memperbaiki URL decode yang tidak perlu pada nama file saat upload file (swoole/swoole-src@a73780e) (@matyhtf) +- Memperbaiki masalah HTTP2 max_frame_size (#4394) (@twose) +- Memperbaiki bug curl_multi_select #4393 (#4418) (@matyhtf) +- Memperbaiki opsi coroutine yang hilang (#4425) (@sy-records) +- Memperbaiki masalah koneksi tidak bisa ditutup saat buffer pengiriman penuh (swoole/swoole-src@2198378) (@matyhtf) + +## v4.7.1 + +### Peningkatan + +- `System::dnsLookup` mendukung query `/etc/hosts` (#4341) (#4349) (@zmyWL) (@NathanFreeman) +- Menambahkan dukungan boost context untuk mips64 (#4358) (@dixyes) +- `SWOOLE_HOOK_CURL` mendukung opsi `CURLOPT_RESOLVE` (swoole/library#107) (@sy-records) +- `SWOOLE_HOOK_CURL` mendukung opsi `CURLOPT_NOPROGRESS` (swoole/library#117) (@sy-records) +- Menambahkan dukungan boost context untuk riscv64 (#4375) (@dixyes) + +### Perbaikan + +- Memperbaiki error memori yang terjadi di PHP-8.1 pada shutdown (#4325) (@twose) +- Memperbaiki kelas yang tidak dapat diserialisasi di 8.1.0beta1 (#4335) (@remicollet) +- Memperbaiki masalah pembuatan direktori rekursif di banyak coroutine gagal (#4337) (@NathanFreeman) +- Memperbaiki masalah timeout sesekali dengan native curl saat mengirim file besar, dan masalah crash saat menggunakan API file coroutine di CURL WRITEFUNCTION (#4360) (@matyhtf) +- Memperbaiki masalah `PDOStatement::bindParam()` mengharapkan parameter 1 berupa string (swoole/library#116) (@sy-records) + +## v4.7.0 + +### API Baru + +- Menambahkan metode `Process\Pool::detach()` (#4221) (@matyhtf) +- `Server` mendukung fungsi callback `onDisconnect` (#4230) (@matyhtf) +- Menambahkan metode `Coroutine::cancel()` dan `Coroutine::isCanceled()` (#4247) (#4249) (@matyhtf) +- `Http\Client` mendukung opsi `http_compression` dan `body_decompression` (#4299) (@matyhtf) + +### Peningkatan + +- Mendukung klien MySQL coroutine untuk mengetik field secara ketat saat `prepare` (#4238) (@Yurunsoft) +- DNS mendukung library `c-ares` (#4275) (@matyhtf) +- `Server` mendukung konfigurasi deteksi heartbeat untuk port yang berbeda saat mendengarkan multi-port (#4290) (@matyhtf) +- `dispatch_mode` `Server` mendukung mode `SWOOLE_DISPATCH_CO_CONN_LB` dan `SWOOLE_DISPATCH_CO_REQ_LB` (#4318) (@matyhtf) +- `ConnectionPool::get()` mendukung parameter `timeout` (swoole/library#108) (@leocavalcante) +- Hook Curl mendukung opsi `CURLOPT_PRIVATE` (swoole/library#112) (@sy-records) +- Mengoptimalkan deklarasi fungsi metode `PDOStatementProxy::setFetchMode()` (swoole/library#109) (@yespire) + +### Perbaikan + +- Memperbaiki masalah exception saat membuat banyak coroutine menggunakan thread context (8ce5041) (@matyhtf) +- Memperbaiki masalah kehilangan file header php_swoole.h saat menginstal Swoole (#4239) (@sy-records) +- Memperbaiki masalah kompatibilitas mundur EVENT_HANDSHAKE (#4248) (@sy-records) +- Memperbaiki masalah makro SW_LOCK_CHECK_RETURN yang mungkin memanggil fungsi dua kali (#4302) (@zmyWL) +- Memperbaiki masalah `Atomic\Long` pada chip M1 (e6fae2e) (@matyhtf) +- Memperbaiki masalah kehilangan nilai kembali di `Coroutine\go()` (swoole/library@1ed49db) (@matyhtf) +- Memperbaiki masalah tipe nilai kembali `StringObject` (swoole/library#111) (swoole/library#113) (@leocavalcante) (@sy-records) + +### Kernel + +- Melarang hook fungsi yang sudah dinonaktifkan oleh PHP (#4283) (@twose) + +### Pengujian + +- Menambahkan build di lingkungan `Cygwin` (#4222) (@sy-records) +- Menambahkan tes kompilasi untuk `alpine 3.13` dan `3.14` (#4309) (@limingxinleo) + +## v4.6.7 + +### Peningkatan + +- Proses Manager dan proses Task sinkron mendukung pemanggilan fungsi `Process::signal()` (#4190) (@matyhtf) + +### Perbaikan + +- Memperbaiki masalah sinyal tidak bisa didaftarkan berulang kali (#4170) (@matyhtf) +- Memperbaiki kegagalan kompilasi di OpenBSD/NetBSD (#4188) (#4194) (@devnexen) +- Memperbaiki masalah kehilangan event `onClose` dalam kasus khusus saat mendengarkan event tulis (#4204) (@matyhtf) +- Memperbaiki masalah Symfony HttpClient menggunakan native curl (#4204) (@matyhtf) +- Memperbaiki masalah metode `Http\Response::end()` selalu mengembalikan true (swoole/swoole-src@66fcc35) (@matyhtf) +- Memperbaiki PDOStatementProxy menghasilkan PDOException (swoole/library#104) (@twose) + +### Kernel + +- Menulis ulang worker buffer, menambahkan flag msg id ke event data (#4163) (@matyhtf) +- Mengubah level log Request Entity Too Large menjadi warning (#4175) (@sy-records) +- Mengganti fungsi inet_ntoa dan inet_aton (#4199) (@remicollet) +- Mengubah nilai default output_buffer_size menjadi UINT_MAX (swoole/swoole-src@46ab345) (@matyhtf) + +## v4.6.6 + +### Peningkatan + +- Mendukung pengiriman sinyal SIGTERM ke proses Manager setelah proses Master keluar di FreeBSD (#4150) (@devnexen) +- Mendukung kompilasi statis Swoole ke dalam PHP (#4153) (@matyhtf) +- Mendukung SNI menggunakan proxy HTTP (#4158) (@matyhtf) + +### Perbaikan + +- Memperbaiki error koneksi asinkron klien sinkron (#4152) (@matyhtf) +- Memperbaiki kebocoran memori yang disebabkan oleh Hook native curl multi (swoole/swoole-src@91bf243) (@matyhtf) + +## v4.6.5 + +### API Baru + +- Menambahkan metode `count` di WaitGroup (swoole/library#100) (@sy-records) (@deminy) + +### Peningkatan + +- Mendukung native curl multi (#4093) (#4099) (#4101) (#4105) (#4113) (#4121) (#4147) (swoole/swoole-src@cd7f51c) (@matyhtf) (@sy-records) (@huanghantao) +- Mengizinkan pengaturan header menggunakan array di Response dengan HTTP/2 + +### Perbaikan + +- Memperbaiki build NetBSD (#4080) (@devnexen) +- Memperbaiki build OpenBSD (#4108) (@devnexen) +- Memperbaiki build illumos/solaris, hanya alias anggota (#4109) (@devnexen) +- Memperbaiki kasus di mana deteksi heartbeat koneksi SSL tidak efektif saat handshake belum selesai (#4114) (@matyhtf) +- Memperbaiki error di Http\Client saat menggunakan proxy dengan `host:port` di `host` (#4124) (@Yurunsoft) +- Memperbaiki pengaturan header dan cookie di Swoole\Coroutine\Http::request (swoole/library#103) (@leocavalcante) (@deminy) + +### Kernel + +- Mendukung asm context di BSD (#4082) (@devnexen) +- Menggunakan arc4random_buf untuk mengimplementasikan getrandom di FreeBSD (#4096) (@devnexen) +- Mengoptimalkan konteks darwin arm64: menghapus workaround menggunakan label (#4127) (@devnexen) + +### Pengujian + +- Menambahkan skrip build untuk alpine (#4104) (@limingxinleo) + +## v4.6.4 + +### API Baru + +- Menambahkan fungsi Coroutine\Http::request, Coroutine\Http::post, Coroutine\Http::get (swoole/library#97) (@matyhtf) + +### Peningkatan + +- Menambahkan dukungan build ARM 64 (#4057) (@devnexen) +- Menambahkan dukungan pengaturan open_http_protocol di server TCP Swoole (#4063) (@matyhtf) +- Menambahkan dukungan pengaturan hanya sertifikat untuk klien SSL (91704ac) (@matyhtf) +- Menambahkan dukungan untuk opsi tcp_defer_accept di FreeBSD (#4049) (@devnexen) + +### Perbaikan + +- Memperbaiki masalah kurangnya otorisasi proxy saat menggunakan Coroutine\Http\Client (edc0552) (@matyhtf) +- Memperbaiki masalah alokasi memori di Swoole\Table (3e7770f) (@matyhtf) +- Memperbaiki masalah crash saat menggunakan Coroutine\Http2\Client untuk koneksi konkuren (630536d) (@matyhtf) +- Memperbaiki masalah enable_ssl_encrypt di DTLS (842733b) (@matyhtf) +- Memperbaiki kebocoran memori di Coroutine\Barrier (swoole/library#94) (@Appla) (@FMiS) +- Memperbaiki error offset yang disebabkan oleh urutan CURLOPT_PORT dan CURLOPT_URL (swoole/library#96) (@sy-records) +- Memperbaiki error di `Table::get($key, $field)` saat tipe field adalah float (08ea20c) (@matyhtf) +- Memperbaiki kebocoran memori di Swoole\Table (d78ca8c) (@matyhtf) + +## v4.4.24 + +### Perbaikan + +- Memperbaiki crash saat koneksi konkuren di klien http2 (#4079) + +## v4.6.3 + +### API Baru + +- Menambahkan fungsi Swoole\Coroutine\go (swoole/library@82f63be) (@matyhtf) +- Menambahkan fungsi Swoole\Coroutine\defer (swoole/library@92fd0de) (@matyhtf) + +### Peningkatan + +- Menambahkan opsi compression_min_length untuk server HTTP (#4033) (@matyhtf) +- Mengizinkan pengaturan header HTTP Content-Length di lapisan aplikasi (#4041) (@doubaokun) + +### Perbaikan + +- Memperbaiki coredump saat program mencapai batas buka file (swoole/swoole-src@709813f) (@matyhtf) +- Memperbaiki masalah JIT dinonaktifkan (#4029) (@twose) +- Memperbaiki error parameter di `Response::create()` (swoole/swoole-src@a630b5b) (@matyhtf) +- Memperbaiki pelaporan yang salah `task_worker_id` saat mengirim task di platform ARM (#4040) (@doubaokun) +- Memperbaiki masalah coredump saat mengaktifkan native curl hook di PHP8 (#4042)(#4045) (@Yurunsoft) (@matyhtf) +- Memperbaiki error memori di luar batas pada fase shutdown saat fatal error (#4050) (@matyhtf) + +### Kernel + +- Mengoptimalkan ssl_connect/ssl_shutdown (#4030) (@matyhtf) +- Keluar dari proses langsung saat fatal error (#4053) (@matyhtf) + +## v4.6.2 + +### API Baru + +- Menambahkan metode `Http\Request\getMethod()` (#3987) (@luolaifa000) +- Menambahkan metode `Coroutine\Socket->recvLine()` (#4014) (@matyhtf) +- Menambahkan metode `Coroutine\Socket->readWithBuffer()` (#4017) (@matyhtf) + +### Peningkatan + +- Meningkatkan metode `Response\create()` agar dapat digunakan independen dari Server (#3998) (@matyhtf) +- Mendukung `Coroutine\Redis->hExists` mengembalikan tipe bool setelah mengatur compatibility_mode (swoole/swoole-src@b8cce7c) (@matyhtf) +- Mendukung pengaturan opsi PHP_NORMAL_READ untuk `socket_read` (swoole/swoole-src@b1a0dcc) (@matyhtf) + +### Perbaikan + +- Memperbaiki masalah coredump `Coroutine::defer` di PHP8 (#3997) (@huanghantao) +- Memperbaiki pengaturan `Coroutine\Socket::errCode` yang salah saat menggunakan thread context (swoole/swoole-src@004d08a) (@matyhtf) +- Memperbaiki kegagalan kompilasi Swoole di macOS terbaru (#4007) (@matyhtf) +- Memperbaiki masalah passing URL sebagai parameter ke `md5_file` menyebabkan null pointer di PHP stream context (#4016) (@ZhiyangLeeCN) + +### Kernel + +- Menggunakan AIO thread pool untuk hook stdio (menyelesaikan masalah perlakuan stdio sebagai socket yang menyebabkan masalah multi-coroutine) (#4002) (@matyhtf) +- Menulis ulang HttpContext (#3998) (@matyhtf) +- Menulis ulang `Process::wait()` (#4019) (@matyhtf) + +## v4.6.1 + +### Peningkatan + +- Menambahkan opsi kompilasi `--enable-thread-context` (#3970) (@matyhtf) +- Memeriksa keberadaan koneksi saat beroperasi pada session_id (#3993) (@matyhtf) +- Meningkatkan CURLOPT_PROXY (swoole/library#87) (@sy-records) + +### Perbaikan + +- Memperbaiki versi PHP minimum dalam instalasi pecl (#3979) (@remicollet) +- Memperbaiki opsi `--enable-swoole-json` dan `--enable-swoole-curl` yang hilang dalam instalasi pecl (#3980) (@sy-records) +- Memperbaiki masalah keamanan thread openssl (b516d69f) (@matyhtf) +- Memperbaiki enableSSL coredump (#3990) (@huanghantao) + +### Kernel + +- Mengoptimalkan ipc writev untuk menghindari coredump saat data event kosong (9647678) (@matyhtf) + +## v4.5.11 + +### Peningkatan + +- Mengoptimalkan Swoole\Table (#3959) (@matyhtf) +- Meningkatkan CURLOPT_PROXY (swoole/library#87) (@sy-records) + +### Perbaikan + +- Memperbaiki masalah semua kolom tidak bisa dibersihkan saat Table ditambah dan dikurangi (#3956) (@matyhtf) (@sy-records) +- Memperbaiki error `clock_id_t` selama kompilasi (49fea171) (@matyhtf) +- Memperbaiki bug fread (#3972) (@matyhtf) +- Memperbaiki crash multi-thread ssl (7ee2c1a0) (@matyhtf) +- Kompatibel dengan format uri yang salah menyebabkan error (swoole/library#80) (@sy-records) +- Memperbaiki error parameter trigger_error (swoole/library#86) (@sy-records) + +## v4.6.0 + +### Perubahan Tidak Kompatibel + +- Menghapus batas maksimum `session id` untuk menghindari duplikasi (#3879) (@matyhtf) +- Menonaktifkan fitur tidak aman saat menggunakan coroutine, termasuk `pcntl_fork`/`pcntl_wait`/`pcntl_waitpid`/`pcntl_sigtimedwait` (#3880) (@matyhtf) +- Mengaktifkan coroutine hook secara default (#3903) (@matyhtf) + +### Penghapusan + +- Tidak lagi mendukung PHP7.1 (4a963df) (9de8d9e) (@matyhtf) + +### Tidak Lagi Didukung + +- Menandai `Event::rshutdown()` sebagai tidak digunakan lagi, gunakan `Coroutine\run` sebagai gantinya (#3881) (@matyhtf) + +### API Baru + +- Mendukung setPriority/getPriority (#3876) (@matyhtf) +- Mendukung native-curl hook (#3863) (@matyhtf) (@huanghantao) +- Mendukung passing parameter gaya objek dalam fungsi callback event Server secara default (#3888) (@matyhtf) +- Mendukung hook ekstensi sockets (#3898) (@matyhtf) +- Mendukung header duplikat (#3905) (@matyhtf) +- Mendukung SSL sni (#3908) (@matyhtf) +- Mendukung hook stdio (#3924) (@matyhtf) +- Mendukung opsi capture_peer_cert dari stream_socket (#3930) (@matyhtf) +- Menambahkan Http\Request::create/parse/isCompleted (#3938) (@matyhtf) +- Menambahkan Http\Response::isWritable (db56827) (@matyhtf) + +### Peningkatan + +- Semua akurasi waktu Server diubah dari int menjadi double (#3882) (@matyhtf) +- Memeriksa situasi EINTR fungsi poll di swoole_client_select (#3909) (@shiguangqi) +- Menambahkan deteksi deadlock coroutine (#3911) (@matyhtf) +- Mendukung penutupan koneksi di proses lain menggunakan mode SWOOLE_BASE (#3916) (@matyhtf) +- Mengoptimalkan kinerja komunikasi antara proses master Server dan worker, mengurangi penyalinan memori (#3910) (@huanghantao) (@matyhtf) + +### Perbaikan + +- Saat Coroutine\Channel ditutup, pop semua data di dalamnya (960431d) (@matyhtf) +- Memperbaiki error memori saat menggunakan JIT (#3907) (@twose) +- Memperbaiki error kompilasi `port->set()` dtls (#3947) (@Yurunsoft) +- Memperbaiki error connection_list (#3948) (@sy-records) +- Memperbaiki ssl verify (#3954) (@matyhtf) +- Memperbaiki masalah Table tidak dapat membersihkan semua kolom saat increment dan decrement (#3956) (@matyhtf) (@sy-records) +- Memperbaiki kegagalan kompilasi dengan LibreSSL 2.7.5 (#3962) (@matyhtf) +- Memperbaiki konstanta tidak terdefinisi CURLOPT_HEADEROPT dan CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) + +### Kernel + +- Mengabaikan sinyal SIGPIPE secara default (9647678) (@matyhtf) +- Mendukung menjalankan coroutine PHP dan coroutine C secara bersamaan (c94bfd8) (@matyhtf) +- Menambahkan tes get_elapsed (#3961) (@luolaifa000) +- Menambahkan tes get_init_msec (#3964) (@luffluo) + +## v4.5.10 + +### Perbaikan + +- Memperbaiki coredump yang disebabkan oleh Event::cycle (93901dc) (@matyhtf) +- Kompatibel dengan PHP8 (f0dc6d3) (@matyhtf) +- Memperbaiki error connection_list (#3948) (@sy-records) + +## v4.4.23 + +### Perbaikan + +- Memperbaiki error data saat Swoole\Table decrement (bcd4f60d)(0d5e72e7) (@matyhtf) +- Memperbaiki pesan error klien sinkron (#3784) +- Memperbaiki masalah overflow memori saat parsing batas data form (#3858) +- Memperbaiki bug channel di mana data yang ada tidak bisa di-pop setelah ditutup + +## v4.5.9 + +### Peningkatan + +- Menambahkan konstanta SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED ke Coroutine\Http\Client (#3873) (@sy-records) + +### Perbaikan + +- Kompatibel dengan PHP8 (#3868) (#3869) (#3872) (@twose) (@huanghantao) (@doubaokun) +- Memperbaiki konstanta tidak terdefinisi CURLOPT_HEADEROPT dan CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records) +- Memperbaiki CURLOPT_USERPWD (swoole/library@7952a7b) (@twose) + +## v4.5.8 + +### API Baru + +- Menambahkan fungsi swoole_error_log, mengoptimalkan log_rotation (swoole/swoole-src@67d2bff) (@matyhtf) +- readVector dan writeVector mendukung SSL (#3857) (@huanghantao) + +### Peningkatan + +- Saat proses anak keluar, biarkan System::wait keluar dari blocking (#3832) (@matyhtf) +- DTLS mendukung paket 16K (#3849) (@matyhtf) +- Metode Response::cookie mendukung parameter priority (#3854) (@matyhtf) +- Mendukung lebih banyak opsi CURL (swoole/library#71) (@sy-records) +- Menangani masalah header HTTP CURL yang tidak membedakan huruf besar/kecil menyebabkan overwrite (swoole/library#76) (@filakhtov) (@twose) (@sy-records) + +### Perbaikan + +- Memperbaiki masalah penanganan error EAGAIN di readv_all dan writev_all (#3830) (@huanghantao) +- Memperbaiki peringatan kompilasi PHP8 (swoole/swoole-src@03f3fb0) (@matyhtf) +- Memperbaiki masalah keamanan biner di Swoole\Table (#3842) (@twose) +- Memperbaiki masalah overwrite file dalam mode append untuk System::writeFile di MacOS (swoole/swoole-src@a71956d) (@matyhtf) +- Memperbaiki masalah dengan CURLOPT_WRITEFUNCTION di CURL (swoole/library#74) (swoole/library#75) (@sy-records) +- Memperbaiki masalah overflow memori saat parsing HTTP form-data (#3858) (@twose) +- Memperbaiki masalah di mana `is_callable()` tidak dapat mengakses metode privat kelas di PHP8 (#3859) (@twose) + +### Kernel + +- Menulis ulang fungsi alokasi memori, menggunakan SwooleG.std_allocator (#3853) (@matyhtf) +- Menulis ulang pipeline (#3841) (@matyhtf) + +## v4.5.7 + +### API Baru + +- Menambahkan metode writeVector, writeVectorAll, readVector, readVectorAll ke klien Coroutine\Socket (#3764) (@huanghantao) + +### Peningkatan + +- Menambahkan task_worker_num dan dispatch_count ke server->stats (#3771) (#3806) (@sy-records) (@matyhtf) +- Menambahkan dependensi ekstensi termasuk json, mysqlnd, sockets (#3789) (@remicollet) +- Membatasi nilai minimum uid untuk server->bind ke INT32_MIN (#3785) (@sy-records) +- Menambahkan opsi kompilasi untuk swoole_substr_json_decode untuk mendukung offset negatif (#3809) (@matyhtf) +- Mendukung opsi CURLOPT_TCP_NODELAY untuk CURL (swoole/library#65) (@sy-records) (@deminy) + +### Perbaikan + +- Memperbaiki informasi koneksi klien sinkron yang salah (#3784) (@twose) +- Memperbaiki masalah hook fungsi scandir (#3793) (@twose) +- Memperbaiki error di coroutine barrier (swoole/library#68) (@sy-records) + +### Kernel + +- Menggunakan boost.stacktrace untuk mengoptimalkan print-backtrace (#3788) (@matyhtf) + +## v4.5.6 + +### API Baru + +- Menambahkan [swoole_substr_unserialize](/functions?id=swoole_substr_unserialize) dan [swoole_substr_json_decode](/functions?id=swoole_substr_json_decode) (#3762) (@matyhtf) + +### Peningkatan + +- Mengubah metode `onAccept` dari `Coroutine\Http\Server` menjadi private (dfcc83b) (@matyhtf) + +### Perbaikan + +- Memperbaiki masalah coverity (#3737) (#3740) (@matyhtf) +- Memperbaiki beberapa masalah di lingkungan Alpine (#3738) (@matyhtf) +- Memperbaiki swMutex_lockwait (0fc5665) (@matyhtf) +- Memperbaiki kegagalan instalasi PHP 8.1 (#3757) (@twose) + +### Kernel + +- Menambahkan deteksi liveness untuk `Socket::read/write/shutdown` (#3735) (@matyhtf) +- Mengubah tipe session_id dan task_id menjadi int64 (#3756) (@matyhtf) + +## v4.5.5 + +!> Versi ini menambahkan fitur deteksi [opsi konfigurasi](/server/setting). Jika opsi yang tidak disediakan oleh Swoole diatur, Warning akan dihasilkan. + +```shell +PHP Warning: unsupported option [foo] in @swoole-src/library/core/Server/Helper.php +``` + +```php +$http = new Swoole\Http\Server('0.0.0.0', 9501); + +$http->set(['foo' => 'bar']); + +$http->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); +}); + +$http->start(); +``` + +### API Baru + +- Menambahkan Process\Manager, mengubah Process\ProcessManager menjadi alias (swoole/library#eac1ac5) (@matyhtf) +- Mendukung server HTTP2 GOAWAY (#3710) (@doubaokun) +- Menambahkan fungsi `Co\map()` (swoole/library#57) (@leocavalcante) + +### Peningkatan + +- Mendukung klien http2 unix socket (#3668) (@sy-records) +- Mengatur status proses pekerja menjadi SW_WORKER_EXIT setelah proses pekerja keluar (#3724) (@matyhtf) +- Menambahkan send_queued_bytes dan recv_queued_bytes ke nilai kembali `Server::getClientInfo()` (#3721) (#3731) (@matyhtf) (@Yurunsoft) +- Server mendukung opsi konfigurasi stats_file (#3725) (@matyhtf) (@Yurunsoft) + +### Perbaikan + +- Memperbaiki masalah kompilasi di PHP8 (zend_compile_string change) (#3670) (@twose) +- Memperbaiki masalah kompilasi di PHP8 (ext/sockets compatibility) (#3684) (@twose) +- Memperbaiki masalah kompilasi di PHP8 (php_url_encode_hash_ex change) (#3713) (@remicollet) +- Memperbaiki konversi tipe error dari 'const char*' ke 'char*' (#3686) (@remicollet) +- Memperbaiki masalah klien HTTP2 tidak berfungsi di bawah proxy HTTP (#3677) (@matyhtf) (@twose) +- Memperbaiki masalah korupsi data saat PDO reconnect (swoole/library#54) (@sy-records) +- Memperbaiki error parsing port untuk server UDP menggunakan ipv6 +- Memperbaiki masalah timeout tidak valid untuk Lock::lockwait + +## v4.5.4 + +### Perubahan Tidak Kompatibel + +- SWOOLE_HOOK_ALL termasuk SWOOLE_HOOK_CURL (#3606) (@matyhtf) +- Menghapus ssl_method, menambahkan ssl_protocols (#3639) (@Yurunsoft) + +### API Baru + +- Menambahkan metode firstKey dan lastKey untuk array (swoole/library#51) (@sy-records) + +### Peningkatan + +- Menambahkan item konfigurasi open_websocket_ping_frame, open_websocket_pong_frame untuk server Websocket (#3600) (@Yurunsoft) + +### Perbaikan + +- Memperbaiki masalah fseek ftell yang salah saat ukuran file lebih besar dari 2G (#3619) (@Yurunsoft) +- Memperbaiki masalah Socket barrier (#3627) (@matyhtf) +- Memperbaiki masalah http proxy handshake (#3630) (@matyhtf) +- Memperbaiki masalah parsing HTTP Header error saat menerima data chunk dari peer (#3633) (@matyhtf) +- Memperbaiki masalah kegagalan asersi zend_hash_clean (#3634) (@twose) +- Memperbaiki masalah tidak bisa menghapus broken fd dari event loop (#3650) (@matyhtf) +- Memperbaiki masalah coredump akibat menerima paket tidak valid (#3653) (@matyhtf) +- Memperbaiki bug array_key_last (swoole/library#46) (@sy-records) + +### Kernel + +- Optimasi kode (#3615) (#3617) (#3622) (#3635) (#3640) (#3641) (#3642) (#3645) (#3658) (@matyhtf) +- Mengurangi operasi memori yang tidak perlu saat menulis data ke Swoole Table (#3620) (@matyhtf) +- Menulis ulang AIO (#3624) (@Yurunsoft) +- Mendukung readlink/opendir/readdir/closedir hook (#3628) (@matyhtf) +- Mengoptimalkan swMutex_create, mendukung SW_MUTEX_ROBUST (#3646) (@matyhtf) + +## v4.5.3 + +### API Baru + +- Menambahkan `Swoole\Process\ProcessManager` (swoole/library#88f147b) (@huanghantao) +- Menambahkan ArrayObject::append, StringObject::equals (swoole/library#f28556f) (@matyhtf) +- Menambahkan [Coroutine::parallel](/coroutine/coroutine?id=parallel) (swoole/library#6aa89a9) (@matyhtf) +- Menambahkan [Coroutine\Barrier](/coroutine/barrier) (swoole/library#2988b2a) (@matyhtf) + +### Peningkatan + +- Menambahkan `usePipelineRead` untuk mendukung streaming klien http2 (#3354) (@twose) +- Saat mengunduh file dengan klien http, jangan buat file sebelum menerima data (#3381) (@twose) +- Klien http mendukung konfigurasi `bind_address` dan `bind_port` (#3390) (@huanghantao) +- Klien http mendukung konfigurasi `lowercase_header` (#3399) (@matyhtf) +- `Swoole\Server` mendukung konfigurasi `tcp_user_timeout` (#3404) (@huanghantao) +- `Coroutine\Socket` menambahkan event barrier untuk mengurangi peralihan coroutine (#3409) (@matyhtf) +- Menambahkan `memory allocator` untuk swString tertentu (#3418) (@matyhtf) +- cURL mendukung `__toString` (swoole/library#38) (@twose) +- Mendukung pengaturan `wait count` langsung di konstruktor WaitGroup (swoole/library#2fb228b8) (@matyhtf) +- Menambahkan `CURLOPT_REDIR_PROTOCOLS` (swoole/library#46) (@sy-records) +- Server Http1.1 mendukung trailer (#3485) (@huanghantao) +- Coroutine dengan waktu tidur kurang dari 1ms akan menyerahkan coroutine saat ini (#3487) (@Yurunsoft) +- Http static handler mendukung file symbolic linked (#3569) (@LeiZhang-Hunter) +- Segera tutup koneksi WebSocket setelah memanggil metode close di Server (#3570) (@matyhtf) +- Mendukung hook stream_set_blocking (#3585) (@Yurunsoft) +- Server HTTP2 asinkron mendukung flow control (#3486) (@huanghantao) (@matyhtf) +- Melepaskan buffer socket setelah menjalankan fungsi callback onPackage (#3551) (@huanghantao) (@matyhtf) + +### Perbaikan + +- Memperbaiki WebSocket coredump, menangani status error protokol (#3359) (@twose) +- Memperbaiki error null pointer di fungsi swSignalfd_setup dan wait_signal (#3360) (@twose) +- Memperbaiki masalah pemanggilan `Swoole\Server::close` dengan dispatch_func menyebabkan error (#3365) (@twose) +- Memperbaiki masalah inisialisasi di fungsi format `Swoole\Redis\Server::format` di format_buffer (#3369) (@matyhtf) (@twose) +- Memperbaiki masalah mendapatkan alamat MAC di MacOS (#3372) (@twose) +- Memperbaiki kasus uji MySQL (#3374) (@qiqizjl) +- Memperbaiki beberapa masalah kompatibilitas PHP8 (#3384) (#3458) (#3578) (#3598) (@twose) +- Memperbaiki php_error_docref yang hilang, timeout_event, dan masalah nilai kembali di socket write hook (#3383) (@twose) +- Memperbaiki masalah server asinkron tidak bisa menutup Server di fungsi callback `WorkerStart` (#3382) (@huanghantao) +- Memperbaiki masalah coredump potensial di thread heartbeat saat memanipulasi conn->socket (#3396) (@huanghantao) +- Memperbaiki masalah logika di send_yield (#3397) (@twose) (@matyhtf) +- Memperbaiki masalah kompilasi di Cygwin64 (#3400) (@twose) +- Memperbaiki atribut finish yang tidak valid di WebSocket (#3410) (@matyhtf) +- Memperbaiki status error transaksi MySQL yang hilang (#3429) (@twose) +- Memperbaiki perilaku `stream_select` yang tidak konsisten setelah hook (#3440) (@Yurunsoft) +- Memperbaiki masalah sinyal `SIGCHLD` hilang saat membuat proses anak dengan `Coroutine\System` (#3446) (@huanghantao) +- Memperbaiki masalah dukungan SSL di `sendwait` (#3459) (@huanghantao) +- Memperbaiki beberapa masalah di `ArrayObject` dan `StringObject` (swoole/library#44) (@matyhtf) +- Memperbaiki informasi exception mysqli yang salah (swoole/library#45) (@sy-records) +- Memperbaiki masalah `Swoole\Client` tidak bisa mendapatkan `errCode` yang benar setelah mengatur `open_eof_check` (#3478) (@huanghantao) +- Memperbaiki berbagai masalah di MacOS dengan `atomic->wait()`/`wakeup()` (#3476) (@Yurunsoft) +- Memperbaiki masalah status berhasil dikembalikan saat `Client::connect` ditolak (#3484) (@matyhtf) +- Memperbaiki masalah nullptr_t tidak dideklarasikan di lingkungan alpine (#3488) (@limingxinleo) +- Memperbaiki masalah double-free saat mengunduh file di HTTP Client (#3489) (@Yurunsoft) +- Memperbaiki masalah kebocoran memori yang disebabkan oleh tidak dirilisnya `Server\Port` saat `Server` dihancurkan (#3507) (@twose) +- Memperbaiki masalah parsing protokol MQTT (318e33a) (84d8214) (80327b3) (efe6c63) (@GXhua) (@sy-records) +- Memperbaiki masalah coredump yang disebabkan oleh metode `Coroutine\Http\Client->getHeaderOut` (#3534) (@matyhtf) +- Memperbaiki kehilangan pesan error setelah kegagalan verifikasi SSL (#3535) (@twose) +- Memperbaiki tautan yang salah di README untuk `Swoole benchmark` (#3536) (@sy-records) (@santalex) +- Memperbaiki masalah injeksi header menggunakan `CRLF` di `HTTP header/cookie` (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) +- Memperbaiki error variabel yang disebutkan di issue #3463 (#3547) (chromium1337) (@huanghantao) +- Memperbaiki typo yang disebutkan di pr #3463 (#3547) (@deminy) +- Memperbaiki masalah frame->fd kosong di server WebSocket coroutine (#3549) (@huanghantao) +- Memperbaiki masalah kebocoran koneksi yang disebabkan oleh penilaian salah di thread heartbeat mengenai status koneksi (#3534) (@matyhtf) +- Memperbaiki masalah blocking sinyal di `Process\Pool` (#3582) (@huanghantao) (@matyhtf) +- Memperbaiki masalah di `SAPI` saat menggunakan send headers (#3571) (@twose) (@sshymko) +- Memperbaiki unset `errCode` dan `errMsg` saat eksekusi `CURL` gagal (swoole/library#1b6c65e) (@sy-records) +- Memperbaiki masalah coredump di `swoole_socket_coro` accept setelah memanggil metode `setProtocol` (#3591) (@matyhtf) + +### Kernel + +- Menggunakan gaya C++ (#3349) (#3351) (#3454) (#3479) (#3490) (@huanghantao) (@matyhtf) +- Menambahkan `Swoole known strings` untuk meningkatkan kinerja pembacaan properti objek `PHP` (#3363) (@huanghantao) +- Beberapa optimasi kode (#3350) (#3356) (#3357) (#3423) (#3426) (#3461) (#3463) (#3472) (#3557) (#3583) (@huanghantao) (@twose) (@matyhtf) +- Optimasi kode tes di beberapa tempat (#3416) (#3481) (#3558) (@matyhtf) +- Menyederhanakan tipe `int` dari `Swoole\Table` (#3407) (@matyhtf) +- Menambahkan `sw_memset_zero` dan mengganti fungsi `bzero` (#3419) (@CismonX) +- Mengoptimalkan modul log (#3432) (@matyhtf) +- Menulis ulang beberapa bagian libswoole (#3448) (#3473) (#3475) (#3492) (#3494) (#3497) (#3498) (#3526) (@matyhtf) +- Menulis ulang beberapa penyertaan file header (#3457) (@matyhtf) (@huanghantao) +- Menambahkan `Channel::count()` dan `Channel::get_bytes()` (f001581) (@matyhtf) +- Menambahkan `scope guard` (#3504) (@huanghantao) +- Menambahkan tes cakupan libswoole (#3431) (@huanghantao) +- Menambahkan tes untuk lib-swoole/ext-swoole di lingkungan MacOS (#3521) (@huanghantao) +- Menambahkan tes untuk lib-swoole/ext-swoole di lingkungan Alpine (#3537) (@limingxinleo) + +## v4.5.2 + +[v4.5.2](https://github.com/swoole/swoole-src/releases/tag/v4.5.2), ini adalah versi perbaikan bug, tanpa perubahan yang tidak kompatibel. + +### Peningkatan + +- Mendukung `Server->set(['log_rotation' => SWOOLE_LOG_ROTATION_DAILY])` untuk menghasilkan log harian (#3311) (@matyhtf) +- Mendukung `swoole_async_set(['wait_signal' => true])`, reaktor tidak akan keluar saat ada pendengar sinyal (#3314) (@matyhtf) +- Mendukung `Server->sendfile` untuk mengirim file kosong (#3318) (@twose) +- Mengoptimalkan pesan peringatan worker idle/busy (#3328) (@huanghantao) +- Mengoptimalkan konfigurasi header Host di bawah proxy HTTPS (gunakan ssl_host_name untuk konfigurasi) (#3343) (@twose) +- SSL secara default menggunakan mode ecdh auto (#3316) (@matyhtf) +- Klien SSL menggunakan silent exit saat koneksi terputus (#3342) (@huanghantao) + +### Perbaikan + +- Memperbaiki masalah `Server->taskWait` di platform OSX (#3330) (@matyhtf) +- Memperbaiki bug dalam parsing protokol MQTT (8dbf506b) (@guoxinhua) (2ae8eb32) (@twose) +- Memperbaiki masalah overflow untuk tipe integer Content-Length (#3346) (@twose) +- Memperbaiki masalah pemeriksaan panjang paket PRI yang hilang (#3348) (@twose) +- Memperbaiki masalah CURLOPT_POSTFIELDS tidak bisa diatur ke kosong (swoole/library@ed192f64) (@twose) +- Memperbaiki masalah objek koneksi terbaru tidak bisa dirilis hingga koneksi berikutnya diterima (swoole/library@1ef79339) (@twose) + +### Kernel + +- Fitur penulisan zero-copy Socket (#3327) (@twose) +- Menggunakan swoole_get_last_error/swoole_set_last_error sebagai pengganti pembacaan/penulisan variabel global (e25f262a) (@matyhtf) (#3315) (@huanghantao) + +## v4.5.1 + +[v4.5.1](https://github.com/swoole/swoole-src/releases/tag/v4.5.1) adalah versi perbaikan bug yang melengkapi tag tidak digunakan lagi yang seharusnya diperkenalkan di `v4.5.0`. + +### Peningkatan + +- Mendukung konfigurasi bindto di socket_context di bawah hook (#3275) (#3278) (@codinghuang) +- Mendukung resolusi DNS otomatis untuk alamat client::sendto (#3292) (@codinghuang) +- Process->exit(0) akan langsung menyebabkan proses keluar. Untuk menjalankan shutdown_functions sebelum keluar, gunakan exit yang disediakan oleh PHP (a732fe56) (@matyhtf) +- Mendukung konfigurasi `log_date_format` untuk mengubah format tanggal log, `log_date_with_microseconds` menampilkan mikrodetik di timestamp log (baf895bc) (@matyhtf) +- Mendukung CURLOPT_CAINFO dan CURLOPT_CAPATH (swoole/library#32) (@sy-records) +- Mendukung CURLOPT_FORBID_REUSE (swoole/library#33) (@sy-records) + +### Perbaikan + +- Memperbaiki kegagalan build di 32-bit (#3276) (#3277) (@remicollet) (@twose) +- Memperbaiki masalah kehilangan pesan error EISCONN saat Coroutine Client reconnect (#3280) (@codinghuang) +- Memperbaiki bug potensial di modul Table (d7b87b65) (@matyhtf) +- Memperbaiki masalah null pointer di Server karena perilaku tidak terdefinisi (defensive programming) (#3304) (#3305) (@twose) +- Memperbaiki masalah error null pointer yang dihasilkan setelah mengaktifkan konfigurasi heartbeat (#3307) (@twose) +- Memperbaiki konfigurasi mysqli tidak berfungsi (swoole/library#35) +- Memperbaiki masalah parsing saat respons mengandung header non-standar (spasi hilang) (swoole/library#27) (@Yurunsoft) + +### Tidak Lagi Didukung + +- Menandai metode seperti Coroutine\System::(fread/fgets/fwrite) sebagai tidak digunakan lagi (gunakan fitur hook sebagai gantinya, langsung menggunakan fungsi file yang disediakan oleh PHP) (c7c9bb40) (@twose) + +### Kernel + +- Menggunakan zend_object_alloc untuk mengalokasikan memori untuk objek kustom (cf1afb25) (@twose) +- Beberapa optimasi, menambahkan lebih banyak opsi konfigurasi untuk modul log (#3296) (@matyhtf) +- Banyak pekerjaan optimasi kode dan penambahan unit test (swoole/library) (@deminy) + +## v4.5.0 + +[v4.5.0](https://github.com/swoole/swoole-src/releases/tag/v4.5.0), ini adalah pembaruan versi besar yang hanya menghapus beberapa modul yang sudah tidak digunakan lagi di v4.4.x. + +### API Baru + +- Menambahkan dukungan DTLS, sekarang fitur ini dapat digunakan untuk membangun aplikasi WebRTC (#3188) (@matyhtf) +- Klien `FastCGI` bawaan, dapat memproksi permintaan ke FPM atau memanggil aplikasi FPM menggunakan satu baris kode (swoole/library#17) (@twose) +- `Co::wait`, `Co::waitPid` (untuk memulihkan proses anak), `Co::waitSignal` (untuk menunggu sinyal) (#3158) (@twose) +- `Co::waitEvent` (untuk menunggu event tertentu pada socket) (#3197) (@twose) +- `Co::set(['exit_condition' => $callable])` (untuk menyesuaikan kondisi keluar program) (#2918) (#3012) (@twose) +- `Co::getElapsed` (mendapatkan waktu coroutine telah berjalan untuk analisis, statistik, atau menemukan zombie coroutine) (#3162) (@doubaokun) +- `Socket::checkLiveness` (memeriksa liveness koneksi menggunakan panggilan sistem), `Socket::peek` (mengintip buffer baca) (#3057) (@twose) +- `Socket->setProtocol(['open_fastcgi_protocol' => $bool])` (dukungan unpacking FastCGI bawaan) (#3103) (@twose) +- `Server::get(Master|Manager|Worker)Pid`, `Server::getWorkerId` (mendapatkan informasi tentang singleton server asinkron dan worker-nya) (#2793) (#3019) (@matyhtf) +- `Server::getWorkerStatus` (mendapatkan status proses worker, mengembalikan konstanta SWOOLE_WORKER_BUSY, SWOOLE_WORKER_IDLE) (#3225) (@matyhtf) +- `Server->on('beforeReload', $callable)` dan `Server->on('afterReload', $callable)` (event reload server, terjadi di proses manager) (#3130) (@hantaohuang) +- Handler file statis `Http\Server` sekarang mendukung konfigurasi `http_index_files` dan `http_autoindex` (#3171) (@hantaohuang) +- Metode `Http2\Client->read(float $timeout = -1)` mendukung pembacaan respons streaming (#3011) (#3117) (@twose) +- `Http\Request->getContent` (alias untuk metode rawContent) (#3128) (@hantaohuang) +- `swoole_mime_type_(add|set|delete|get|exists)()` (API terkait mime, dapat menambah, menghapus, mengambil, dan memeriksa tipe mime bawaan) (#3134) (@twose) + +### Peningkatan + +- Mengoptimalkan penyalinan memori antara proses master dan worker (hingga peningkatan kinerja empat kali lipat dalam kasus ekstrem) (#3075) (#3087) (@hantaohuang) +- Mengoptimalkan logika dispatch WebSocket (#3076) (@matyhtf) +- Mengoptimalkan penyalinan memori satu kali saat membangun frame WebSocket (#3097) (@matyhtf) +- Mengoptimalkan modul verifikasi SSL (#3226) (@matyhtf) +- Memisahkan proses SSL accept dan SSL handshake untuk menyelesaikan masalah klien SSL lambat yang dapat menyebabkan server coroutine macet (#3214) (@twose) +- Menambahkan dukungan untuk arsitektur MIPS (#3196) (@ekongyun) +- Klien UDP sekarang dapat secara otomatis menyelesaikan nama domain yang masuk (#3236) (#3239) (@huanghantao) +- Menambahkan dukungan untuk beberapa opsi yang umum digunakan di Coroutine\Http\Server (#3257) (@twose) +- Menambahkan dukungan pengaturan cookie selama handshake WebSocket (#3270) (#3272) (@twose) +- Mendukung CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) +- Mendukung CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) +- Mendukung CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) + +### Penghapusan + +- Menghapus metode `Runtime::enableStrictMode` (b45838e3) (@twose) +- Menghapus kelas `Buffer` (559a49a8) (@twose) + +### Terkait Kernel + +- API C++ baru: fungsi coroutine::async menerima lambda untuk memulai tugas thread asinkron (#3127) (@matyhtf) +- Menulis ulang fd tipe integer di event-APIlevel bawah menjadi objek swSocket (#3030) (@matyhtf) +- Semua file C inti telah dikonversi ke file C++ (#3030) (71f987f3) (@matyhtf) +- Serangkaian optimasi kode (#3063) (#3067) (#3115) (#3135) (#3138) (#3139) (#3151) (#3168) (@hantaohuang) +- Optimasi standarisasi file header (#3051) (@matyhtf) +- Menulis ulang item konfigurasi `enable_reuse_port` agar lebih terstandarisasi (#3192) (@matyhtf) +- Menulis ulang API terkait Socket agar lebih terstandarisasi (#3193) (@matyhtf) +- Mengurangi panggilan sistem yang tidak perlu melalui prediksi buffer (3b5aa85d) (@matyhtf) +- Menghapus penyegaran timerlevel bawah swServerGS::now, langsung menggunakan fungsi time untuk mendapatkan waktu (#3152) (@hantaohuang) +- Mengoptimalkan konfigurator protokol (#3108) (@twose) +- Gaya inisialisasi struktur C yang lebih kompatibel (#3069) (@twose) +- Menyatukan bit fields sebagai tipe uchar (#3071) (@twose) +- Mendukung pengujian paralel untuk kecepatan yang lebih baik (#3215) (@twose) + +### Perbaikan + +- Memperbaiki crash WebSocket, menangani status error protokol (#3359) (@twose) +- Memperbaiki error null pointer di fungsi swSignalfd_setup dan wait_signal (#3360) (@twose) +- Memperbaiki masalah pemanggilan `Swoole\Server::close` dengan dispatch_func menyebabkan error (#3365) (@twose) +- Memperbaiki masalah inisialisasi di fungsi format_format `Swoole\Redis\Server::format` di format_buffer (#3369) (@matyhtf) (@twose) +- Memperbaiki masalah mendapatkan alamat MAC di MacOS (#3372) (@twose) +- Memperbaiki kasus uji MySQL (#3374) (@qiqizjl) +- Memperbaiki beberapa masalah kompatibilitas PHP8 (#3384) (#3458) (#3578) (#3598) (@twose) +- Memperbaiki php_error_docref yang hilang, timeout_event, dan masalah nilai kembali di socket write hook (#3383) (@twose) +- Memperbaiki masalah server asinkron tidak bisa menutup Server di fungsi callback `WorkerStart` (#3382) (@huanghantao) +- Memperbaiki masalah coredump potensial di thread heartbeat saat memanipulasi conn->socket (#3396) (@huanghantao) +- Memperbaiki masalah logika di send_yield (#3397) (@twose) (@matyhtf) +- Memperbaiki masalah kompilasi di Cygwin64 (#3400) (@twose) +- Memperbaiki atribut finish yang tidak valid di WebSocket (#3410) (@matyhtf) +- Memperbaiki status error transaksi MySQL yang hilang (#3429) (@twose) +- Memperbaiki perilaku `stream_select` yang tidak konsisten setelah hook (#3440) (@Yurunsoft) +- Memperbaiki masalah sinyal `SIGCHLD` hilang saat membuat proses anak dengan `Coroutine\System` (#3446) (@huanghantao) +- Memperbaiki masalah dukungan SSL di `sendwait` (#3459) (@huanghantao) +- Memperbaiki beberapa masalah di `ArrayObject` dan `StringObject` (swoole/library#44) (@matyhtf) +- Memperbaiki informasi exception mysqli yang salah (swoole/library#45) (@sy-records) +- Memperbaiki masalah `Swoole\Client` tidak bisa mendapatkan `errCode` yang benar setelah mengatur `open_eof_check` (#3478) (@huanghantao) +- Memperbaiki berbagai masalah di MacOS dengan `atomic->wait()`/`wakeup()` (#3476) (@Yurunsoft) +- Memperbaiki masalah status berhasil dikembalikan saat `Client::connect` ditolak (#3484) (@matyhtf) +- Memperbaiki masalah nullptr_t tidak dideklarasikan di lingkungan alpine (#3488) (@limingxinleo) +- Memperbaiki masalah double-free saat mengunduh file di HTTP Client (#3489) (@Yurunsoft) +- Memperbaiki masalah kebocoran memori yang disebabkan oleh tidak dirilisnya `Server\Port` saat `Server` dihancurkan (#3507) (@twose) +- Memperbaiki masalah parsing protokol MQTT (318e33a) (84d8214) (80327b3) (efe6c63) (@GXhua) (@sy-records) +- Memperbaiki masalah coredump yang disebabkan oleh metode `Coroutine\Http\Client->getHeaderOut` (#3534) (@matyhtf) +- Memperbaiki kehilangan pesan error setelah kegagalan verifikasi SSL (#3535) (@twose) +- Memperbaiki tautan yang salah di README untuk `Swoole benchmark` (#3536) (@sy-records) (@santalex) +- Memperbaiki masalah injeksi header menggunakan `CRLF` di `HTTP header/cookie` (#3539) (#3541) (#3545) (@chromium1337) (@huanghantao) +- Memperbaiki error variabel yang disebutkan di issue #3463 (#3547) (chromium1337) (@huanghantao) +- Memperbaiki typo yang disebutkan di pr #3463 (#3547) (@deminy) +- Memperbaiki masalah frame->fd kosong di server WebSocket coroutine (#3549) (@huanghantao) +- Memperbaiki masalah kebocoran koneksi yang disebabkan oleh penilaian salah di thread heartbeat mengenai status koneksi (#3534) (@matyhtf) +- Memperbaiki masalah blocking sinyal di `Process\Pool` (#3582) (@huanghantao) (@matyhtf) +- Memperbaiki masalah di `SAPI` saat menggunakan send headers (#3571) (@twose) (@sshymko) +- Memperbaiki unset `errCode` dan `errMsg` saat eksekusi `CURL` gagal (swoole/library#1b6c65e) (@sy-records) +- Memperbaiki masalah coredump di `swoole_socket_coro` accept setelah memanggil metode `setProtocol` (#3591) (@matyhtf) diff --git a/public/id/version/supported.md b/public/id/version/supported.md new file mode 100644 index 0000000..a4b6670 --- /dev/null +++ b/public/id/version/supported.md @@ -0,0 +1,34 @@ +# Rencana Dukungan + +| Branch | Versi PHP | Tanggal Mulai | Dukungan Aktif Berakhir | Pemeliharaan Keamanan Berakhir | +|---------------------------------------------------------------|-------------|---------------|-------------------------|-------------------------------| +| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.3 | 2023-11-29 | 2024-11-29 | 2025-04-29 | +| [v6.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4 | 2024-12-31 | 2025-12-31 | 2026-06-31 | + +- **Dukungan Aktif**: Mendapat dukungan aktif dari tim pengembang resmi, bug yang dilaporkan dan masalah keamanan akan segera diperbaiki, dan rilis resmi akan dikeluarkan sesuai proses reguler. +- **Pemeliharaan Keamanan**: Hanya perbaikan masalah keamanan kritis yang didukung, dan rilis resmi hanya dilakukan jika diperlukan. + +Referensi: [https://github.com/swoole/swoole-src/blob/master/docs/SUPPORTED.md](https://github.com/swoole/swoole-src/blob/master/docs/SUPPORTED.md) + +## Branch yang Tidak Lagi Didukung + +!> Versi-versi ini tidak lagi didukung oleh tim resmi. Pengguna yang masih menggunakan versi berikut harus segera meningkatkan versi karena mereka mungkin menghadapi kerentanan keamanan yang belum diperbaiki. + +- `v1.x` (2012-7-1 ~ 2018-05-14) +- `v2.x` (2016-12-30 ~ 2018-05-23) +- `v3.x` (tidak digunakan) +- `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` (2018-06-14 ~ 2019-12-31) +- `v4.4.x` (2019-04-15 ~ 2020-04-30) +- `v4.5.x` (2019-12-20 ~ 2021-01-06) +- `v4.6.x`, `v4.7.x` (2021-01-06 ~ 2021-12-31) +- `v4.8.x` (2021-10-14 ~ 2024-06-30) +- `v5.0.x` (2022-01-20 ~ 2023-07-20) + +## Karakteristik Versi +- `v1.x`: Mode callback asinkron. +- `v2.x`: Coroutine single-stack berbasis `setjmp/longjmp`, implementasi masih berbasis callback asinkron, beralih tumpukan panggilan `PHP` setelah event callback terpicu. +- `v4.0-v4.3`: Coroutine dual-stack berbasis `boost context asm`, kernel mengimplementasikan coroutine secara komprehensif, dengan penjadwal coroutine berbasis `EventLoop`. +- `v4.4-v4.8`: Mengimplementasikan `runtime coroutine hook`, secara otomatis mengganti fungsi sinkron blokir bawaan PHP dengan mode asinkron non-blocking untuk coroutine, membuat Coroutine Swoole kompatibel dengan sebagian besar library PHP. +- `v5.0`: Coroutine penuh, menghapus modul non-coroutine; tipe kuat, menghapus banyak beban historis; menyediakan mode runtime `swoole-cli` yang baru. +- `v5.1`: Mendukung coroutine `pdo_pgsql`, `pdo_oci`, `pdo_odbc`, `pdo_sqlite`, meningkatkan kinerja `Http\Server`. +- `v6.0`: Mendukung mode multi-thread. diff --git a/public/id/version/v5.md b/public/id/version/v5.md new file mode 100644 index 0000000..27d6d78 --- /dev/null +++ b/public/id/version/v5.md @@ -0,0 +1,108 @@ +# v5.0 + +Pembaruan Utama +---- +1. Menambahkan mode runtime mandiri `swoole-cli` +2. Persyaratan versi minimum `PHP` disesuaikan menjadi `8.0` +3. Tipe kuat, semua parameter dan nilai kembali fungsi serta metode kelas menjadi tipe kuat +4. Memperkuat kemampuan tata kelola layanan internal +5. Menghapus beberapa fitur peninggalan historis + +Mode Runtime Baru (`swoole-cli`) +---- +`5.0` akan menambahkan dukungan `swoole-cli`, `swoole` akan disediakan sebagai program mandiri seperti `node.js`, bukan sebagai ekstensi `PHP`. +Selain itu `swoole-cli` akan memangkas `php-src` sebanyak mungkin, menghapus mekanisme, modul, ekstensi, fungsi, tipe, konstanta, kode yang tidak berguna, sehingga seluruh program dapat dikompilasi dalam hitungan menit. + +```shell +htf@htf-ThinkPad-T470p:~/workspace$ ./swoole-cli -v +Swoole 5.0.0 (cli) (built: Nov 26 2021 07:22:46) +``` + +### Kompilasi Statis Tanpa Dependensi +`swoole-cli` akan menggunakan kompilasi statis sepenuhnya, `libc` dan `libstdc++` menggunakan `musl`, dan akan menyertakan library dan ekstensi `openssl` dan `curl`. Pengguna dapat memodifikasi skrip build untuk menambahkan lebih banyak ekstensi pihak ketiga. +Sehingga program `swoole` tidak lagi bergantung pada `.so` sistem apa pun, dapat disalin dan didistribusikan di sistem `linux` mana pun. + +```shell +htf@htf-ThinkPad-T470p:~/workspace$ ldd swoole-cli +Bukan file eksekusi dinamis +htf@htf-ThinkPad-T470p:~/workspace$ +``` + +### Menghapus Mekanisme Pemuatan Ekstensi Dinamis +Menghapus fungsi `dl()` dan mekanisme pemuatan ekstensi dinamis konfigurasi `extension={name}.so` di `php.ini`, semua ekstensi harus dikompilasi statis. +`swoole-cli` mengizinkan sistem build kustom untuk menambahkan ekstensi ke daftar modul. + +### Menghapus Server Bawaan `php -S` +Tidak berarti di lingkungan `swoole` + +### Menghapus Ekstensi Bawaan +Menghapus ekstensi bawaan dengan penggunaan rendah, peninggalan historis, dan tidak mendukung coroutine, termasuk namun tidak terbatas pada: +``` +com_dotnet +dba +ftp +ffi +imap +ldap +oci8 +odbc +pdo_dblib +pdo_firebird +pdo_oci +pdo_odbc +pdo_pgsql +pgsql +pspell +shmop +snmp +sysvmsg +sysvsem +tidy +``` + +### Menghapus Beberapa Fungsi +Menghapus fungsi bawaan non-cli, penggunaan rendah, peninggalan historis, dan tidak mendukung coroutine, termasuk namun tidak terbatas pada: +``` +checkdnsrr +dns_check_record +dns_get_mx +header +header_register_callback +header_remove +session_start +mail +``` + +### Menghapus ZTS +Menghapus mode `ZTS`, dalam mode `cli` `ZTS` tidak berarti. + +Upload File Besar +---- +Mulai `5.0` server `HTTP` menambahkan dukungan untuk upload file besar, dengan mengatur parameter `upload_max_filesize` untuk mengontrol ukuran maksimum file yang diizinkan untuk diupload. +Berbeda dengan `package_max_length`, fitur upload file besar menulis konten file dari `form-data` langsung ke file sementara, keuntungannya adalah tidak memakan terlalu banyak memori, sehingga dapat menyelesaikan upload file besar. +`package_max_length` dapat diatur ke `2M`, `upload_max_filesize` diatur ke `2G`, setiap koneksi hanya membutuhkan memori `2M` untuk menyelesaikan upload file `2G`. + +```php +$server->set([ + 'upload_max_filesize' => 2 * 1024 * 1024 * 1024, // 2G + 'package_max_length' => 2 * 1024 * 1024, // 2M +]); +``` + +Daftar Detail +---- +### Penambahan +- Menambahkan item konfigurasi `Server`: `max_concurrency`, dapat membatasi jumlah maksimum permintaan konkuren untuk layanan `HTTP1/2`, mengembalikan error `503` jika terlampaui +- Menambahkan item konfigurasi `Coroutine\Http\Client`: `max_retries`, saat terjadi kegagalan koneksi atau `HTTP 502/503`, secara otomatis mengganti node untuk percobaan ulang +- Menambahkan item konfigurasi global `name_resolver`, memperkuat kemampuan resolusi nama domain, sebelum `5.0` nama domain hanya bisa menggunakan resolusi `DNS`, setelah `5.0` dapat dikonfigurasi `name_resolver` untuk mengubah resolusi nama menggunakan plugin penemuan layanan seperti `Consul` atau `Nacos` +- Menambahkan `Coroutine::getExecuteTime()`, dapat memperoleh waktu eksekusi aktual coroutine (tidak termasuk waktu dalam status `yield`) +- Menambahkan konfigurasi `upload_max_filesize`, mengizinkan upload file besar + +### Penguatan +- Tipe kuat, semua parameter dan nilai kembali fungsi serta metode kelas menambahkan batasan tipe +- Semua metode konstruktor yang gagal diubah menjadi melempar exception + +### Penghapusan +- Menghapus nama kelas bergaya `PSR-0`, seperti `swoole_http_server` harus diubah menjadi `Swoole\Http\Server` +- Menghapus penambahan otomatis `Event::wait()` di `shutdown function`, harus memanggil `Event::wait()` secara eksplisit untuk memasuki event wait, atau menggunakan container seperti `Co\run()`, `Server::start()`, `Process::start()`, `Process\Pool::start()`, dll. +- Menghapus alias `Server::tick/after/clearTimer/defer`, gunakan langsung `Timer::tick()/Timer::after()/Timer::clear()/Event::defer()` diff --git a/public/id/version/v6.md b/public/id/version/v6.md new file mode 100644 index 0000000..404867a --- /dev/null +++ b/public/id/version/v6.md @@ -0,0 +1,142 @@ +# v6.0 +- Menambahkan dukungan mode multi-thread +- Menghapus klien `Coroutine\Redis`, `Coroutine\MySQL`, `Coroutine\PostgreSQL`, telah digantikan oleh `ext-redis`, `mysqli`, `pdo_mysql`, `pdo_pgsql` + +Mulai versi `6.0`, thread secara bertahap akan menggantikan proses sebagai container runtime default, membawa perubahan baru: + +## Struktur Data Konkuren yang Lebih Baik + +Di versi sebelumnya, diperlukan `Redis` atau `Table` atau komunikasi pipa `IPC` untuk berbagi data antar proses, +di versi baru internal menyediakan tiga struktur data konkuren yang aman untuk thread: `Map`, `ArrayList`, `Queue`. Selain itu `Atomic` dan `Lock` juga telah diadaptasi untuk mode multi-thread. + +### Concurrent Map +Container asosiatif `Key-Value`, kompleksitas waktu membaca data berdasarkan `Key` adalah `O(1)` + +```php +$args = Thread::getArguments(); + +if (empty($args)) { + $map = new Swoole\Thread\Map; + $thread = new Thread(__FILE__, $map); + // Tulis di thread induk + $map['uuid'] = uniqid(); + $thread->join(); +} else { + $map = $args[0]; + sleep(1); + // Baca di thread anak + var_dump($map['uuid']); +} +``` + +### Concurrent ArrayList +Container berurutan, dapat menambahkan elemen, kompleksitas waktu membaca data berdasarkan indeks adalah `O(1)` + +```php +$args = Thread::getArguments(); + +if (empty($args)) { + $list = new Swoole\Thread\ArrayList; + $thread = new Thread(__FILE__, $list); + // Tulis di thread induk + $list[] = uniqid(); + $list[] = uniqid(); + $thread->join(); +} else { + $list = $args[0]; + sleep(1); + // Baca di thread anak + var_dump($list[0]); + var_dump($list[1]); +} +``` + +### Concurrent Queue +Struktur data first-in-first-out, dapat push dan pop data. +- `push($data)` memasukkan ke antrian, parameter kedua dapat mengontrol `push()` untuk membangunkan thread lain yang sedang menunggu `pop()` +- `pop()` mengeluarkan elemen, parameter kedua dapat menentukan waktu tunggu, jika antrian kosong dapat tertidur, menunggu operasi `push()` untuk membangunkan + +```php +use Swoole\Thread; +use Swoole\Thread\Queue; + +$args = Thread::getArguments(); +$c = 4; +$n = 128; + +if (empty($args)) { + $threads = []; + $queue = new Queue; + for ($i = 0; $i < $c; $i++) { + $threads[] = new Thread(__FILE__, $i, $queue); + } + while ($n--) { + $queue->push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} +``` + +### Lock +```php +$args = Thread::getArguments(); + +if (empty($args)) { + $lock = new Swoole\Lock; + $lock->lock(); + $thread = new Thread(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + $lock = $args[0]; + sleep(1); + $lock->unlock(); +} +``` + +### Atomic Counter +```php +$args = Thread::getArguments(); +$c = 4; +$n = 128; + +if (empty($args)) { + $threads = []; + $a1 = new Swoole\Atomic; + $a2 = new Swoole\Atomic\Long; + for ($i = 0; $i < $c; $i++) { + $threads[] = new Thread(__FILE__, $i, $a1, $a2); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($a1->get(), $a2->get()); +} else { + $a1 = $args[1]; + $a2 = $args[2]; + + $a1->add(3); + $a2->add(7); +} +``` + +## Alat Debug yang Lebih Nyaman +Semua worker thread berada dalam satu proses, alat seperti `gdb`, `strace` dapat langsung melacak semua thread. diff --git a/public/id/websocket_server.md b/public/id/websocket_server.md new file mode 100644 index 0000000..a96dad8 --- /dev/null +++ b/public/id/websocket_server.md @@ -0,0 +1,747 @@ +# Swoole\WebSocket\Server + +?> Dengan dukungan server `WebSocket` bawaan, hanya dengan beberapa baris kode `PHP` dapat menulis server `WebSocket` multi-proses [IO asinkron](/learn?id=同步io异步io). + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); +}); + +$server->on('close', function ($server, $fd) { + echo "client {$fd} closed\n"; +}); + +$server->start(); +``` + +* **Klien** + + * Browser `Chrome/Firefox/IE/Safari` versi tinggi memiliki klien `WebSocket` bahasa `JS` bawaan + * Klien `WebSocket` bawaan framework pengembangan mini program WeChat + * Di program `PHP` [IO asinkron](/learn?id=同步io异步io) dapat menggunakan [Swoole\Coroutine\Http](/coroutine_client/http_client) sebagai klien `WebSocket` + * Di program `PHP` sinkron blocking seperti `Apache/PHP-FPM` dapat menggunakan [klien WebSocket sinkron](https://github.com/matyhtf/framework/blob/master/libs/Swoole/Client/WebSocket.php) yang disediakan `swoole/framework` + * Klien non-`WebSocket` tidak bisa berkomunikasi dengan server `WebSocket` + +* **Cara menentukan apakah koneksi adalah klien WebSocket** + +?> Dengan menggunakan [contoh berikut](/server/methods?id=getclientinfo) untuk mendapatkan informasi koneksi, array yang dikembalikan memiliki item [websocket_status](/websocket_server?id=连接状态), berdasarkan status ini dapat menentukan apakah itu klien `WebSocket`. +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + $client = $server->getClientInfo($frame->fd); + // Atau $client = $server->connection_info($frame->fd); + if (isset($client['websocket_status'])) { + echo "Ini koneksi websocket"; + } else { + echo "Bukan koneksi websocket"; + } +}); +``` + +## Event + +?> Selain menerima fungsi callback dari kelas dasar [Swoole\Server](/server/methods) dan [Swoole\Http\Server](/http_server), server `WebSocket` menambahkan `4` pengaturan fungsi callback. Di antaranya: + +* Fungsi callback `onMessage` wajib +* Fungsi callback `onOpen`, `onHandShake` dan `onBeforeHandShakeResponse` (disediakan Swoole5) opsional + +### onBeforeHandshakeResponse + +!> Swoole versi >= `v5.0.0` tersedia + +?> **Terjadi sebelum koneksi `WebSocket` dibuat. Jika Anda tidak perlu menangani proses jabat tangan secara kustom, tetapi ingin mengatur beberapa informasi `http header` ke header respons, maka bisa memanggil event ini.** + +```php +onBeforeHandshakeResponse(Swoole\Http\Request $request, Swoole\Http\Response $response); +``` + +### onHandShake + +?> **Melakukan jabat tangan setelah koneksi `WebSocket` dibuat. Server `WebSocket` akan otomatis melakukan proses jabat tangan. Jika pengguna ingin menangani sendiri proses jabat tangan, dapat mengatur fungsi callback event `onHandShake`.** + +```php +onHandShake(Swoole\Http\Request $request, Swoole\Http\Response $response); +``` + +* **Catatan** + + * Callback event `onHandShake` opsional + * Setelah mengatur fungsi callback `onHandShake`, event `onOpen` tidak akan terpicu lagi, kode aplikasi harus menangani sendiri, dapat menggunakan `$server->defer` untuk memanggil logika `onOpen` + * Di `onHandShake` harus memanggil [response->status()](/http_server?id=status) untuk mengatur kode status menjadi `101` dan memanggil [response->end()](/http_server?id=end) sebagai respons, jika tidak jabat tangan akan gagal + * Protokol jabat tangan bawaan adalah `Sec-WebSocket-Version: 13`, browser versi rendah perlu mengimplementasikan jabat tangan sendiri + +* **Perhatian** + +!> Jika perlu menangani `handshake` sendiri, baru atur fungsi callback ini. Jika tidak perlu proses jabat tangan "kustom", jangan atur callback ini, gunakan jabat tangan default `Swoole`. Berikut adalah yang harus ada dalam fungsi callback event `handshake` "kustom": + +```php +$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + // print_r( $request->header ); + // if (Jika tidak memenuhi kondisi kustom saya, maka return end output, return false, jabat tangan gagal) { + // $response->end(); + // return false; + // } + + // Verifikasi algoritma koneksi jabat tangan websocket + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + echo $request->header['sec-websocket-key']; + $key = base64_encode( + sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + ) + ); + + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + + // WebSocket connection to 'ws://127.0.0.1:9502/' + // failed: Error during WebSocket handshake: + // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + + $response->status(101); + $response->end(); +}); +``` + +!> Setelah mengatur fungsi callback `onHandShake`, event `onOpen` tidak akan terpicu lagi, kode aplikasi harus menangani sendiri, dapat menggunakan `$server->defer` untuk memanggil logika `onOpen` + +```php +$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { + // Melewatkan konten jabat tangan + $response->status(101); + $response->end(); + + global $server; + $fd = $request->fd; + $server->defer(function () use ($fd, $server) + { + echo "Client connected\n"; + $server->push($fd, "hello, welcome\n"); + }); +}); +``` + +### onOpen + +?> **Fungsi ini akan dipanggil saat klien `WebSocket` berhasil terhubung dengan server dan menyelesaikan jabat tangan.** + +```php +onOpen(Swoole\WebSocket\Server $server, Swoole\Http\Request $request); +``` + +* **Catatan** + + * `$request` adalah objek request [HTTP](/http_server?id=httprequest), berisi informasi request jabat tangan yang dikirim klien + * Di fungsi event `onOpen` dapat memanggil [push](/websocket_server?id=push) untuk mengirim data ke klien atau [close](/server/methods?id=close) untuk menutup koneksi + * Callback event `onOpen` opsional + +### onMessage + +?> **Fungsi ini akan dipanggil saat server menerima frame data dari klien.** + +```php +onMessage(Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) +``` + +* **Catatan** + + * `$frame` adalah objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe), berisi informasi frame data yang dikirim klien + * Callback `onMessage` harus diatur, jika tidak server tidak dapat dijalankan + * Frame `ping` yang dikirim klien tidak akan memicu `onMessage`, level bawah akan otomatis membalas paket `pong`, dapat juga mengatur parameter [open_websocket_ping_frame](/websocket_server?id=open_websocket_ping_frame) untuk penanganan manual + +!> Jika `$frame->data` bertipe teks, format encoding pasti `UTF-8`, ini ditentukan oleh protokol `WebSocket` + +### onRequest + +?> `Swoole\WebSocket\Server` mewarisi dari [Swoole\Http\Server](/http_server), jadi semua `API` dan opsi konfigurasi yang disediakan `Http\Server` dapat digunakan. Silakan lihat bab [Swoole\Http\Server](/http_server). + +* Jika mengatur callback [onRequest](/http_server?id=on), `WebSocket\Server` juga dapat berfungsi sebagai server `HTTP` +* Jika tidak mengatur callback [onRequest](/http_server?id=on), `WebSocket\Server` akan mengembalikan halaman error `HTTP 400` saat menerima request `HTTP` +* Jika ingin memicu semua push `WebSocket` melalui penerimaan `HTTP`, perhatikan masalah lingkup. Untuk gaya prosedural gunakan `global` untuk mereferensi `Swoole\WebSocket\Server`, untuk gaya berorientasi objek dapat mengatur `Swoole\WebSocket\Server` sebagai properti anggota + +#### Gaya Kode Prosedural + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; +}); +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); +}); +$server->on('close', function ($server, $fd) { + echo "client {$fd} closed\n"; +}); +$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + global $server;//memanggil server eksternal + // $server->connections iterasi semua fd pengguna koneksi websocket, push ke semua pengguna + foreach ($server->connections as $fd) { + // Perlu memeriksa dulu apakah koneksi websocket yang benar, jika tidak bisa gagal push + if ($server->isEstablished($fd)) { + $server->push($fd, $request->get['message']); + } + } +}); +$server->start(); +``` + +#### Gaya Kode Berorientasi Objek + +```php +class WebSocketServer +{ + public $server; + + public function __construct() + { + $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501); + $this->server->on('open', function (Swoole\WebSocket\Server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }); + $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }); + $this->server->on('close', function ($ser, $fd) { + echo "client {$fd} closed\n"; + }); + $this->server->on('request', function ($request, $response) { + // Menerima http request, ambil nilai parameter message dari get, push ke pengguna + // $this->server->connections iterasi semua fd koneksi websocket, push ke semua pengguna + foreach ($this->server->connections as $fd) { + // Perlu memeriksa dulu apakah koneksi websocket yang benar, jika tidak bisa gagal push + if ($this->server->isEstablished($fd)) { + $this->server->push($fd, $request->get['message']); + } + } + }); + $this->server->start(); + } +} + +new WebSocketServer(); +``` + +### onDisconnect + +?> **Hanya terpicu saat koneksi non-WebSocket ditutup.** + +!> Swoole versi >= `v4.7.0` tersedia + +```php +onDisconnect(Swoole\WebSocket\Server $server, int $fd) +``` + +!> Jika mengatur callback event `onDisconnect`, request non-WebSocket atau saat memanggil method `$response->close()` di [onRequest](/websocket_server?id=onrequest), akan memicu callback `onDisconnect`. Sedangkan di event [onRequest](/websocket_server?id=onrequest) yang berakhir normal tidak akan memanggil event `onClose` atau `onDisconnect`. + +## Method + +`Swoole\WebSocket\Server` adalah subclass dari [Swoole\Server](/server/methods), jadi dapat memanggil semua method `Server`. + +Perlu diperhatikan server `WebSocket` mengirim data ke klien harus menggunakan method `Swoole\WebSocket\Server::push`, method ini akan melakukan pengemasan protokol `WebSocket`. Sedangkan method [Swoole\Server->send()](/server/methods?id=send) adalah antarmuka pengiriman `TCP` mentah. + +Method [Swoole\WebSocket\Server->disconnect()](/websocket_server?id=disconnect) dapat menutup koneksi `WebSocket` dari sisi server secara aktif, dapat menentukan [kode status penutupan](/websocket_server?id=websocket关闭帧状态码) (menurut protokol `WebSocket`, kode status yang dapat digunakan adalah integer desimal, nilai dapat `1000` atau `4000-4999`) dan alasan penutupan (string encoding `utf-8`, panjang byte tidak melebihi `125`). Jika tidak ditentukan, kode status `1000`, alasan penutupan kosong. + +### push + +?> **Mengirim data ke koneksi klien `WebSocket`, panjang maksimum tidak boleh melebihi `2M`.** + +```php +Swoole\WebSocket\Server->push(int $fd, \Swoole\WebSocket\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true): bool + +// v4.4.12 diubah menjadi parameter flags +Swoole\WebSocket\Server->push(int $fd, \Swoole\WebSocket\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool +``` + +* **Parameter** + + * **`int $fd`** + * **Fungsi**: `ID` koneksi klien [jika `$fd` yang ditentukan bukan klien `WebSocket`, pengiriman akan gagal] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`Swoole\WebSocket\Frame|string $data`** + * **Fungsi**: Data yang akan dikirim + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Swoole versi >= v4.2.0, jika `$data` yang dimasukkan adalah objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe), parameter selanjutnya akan diabaikan + + * **`int $opcode`** + * **Fungsi**: Menentukan format data yang dikirim [default teks. Untuk mengirim konten biner, parameter `$opcode` perlu diatur ke `WEBSOCKET_OPCODE_BINARY`] + * **Default**: `WEBSOCKET_OPCODE_TEXT` + * **Nilai Lain**: `WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + * **Fungsi**: Apakah pengiriman selesai + * **Default**: `true` + * **Nilai Lain**: `false` + +* **Return Value** + + * Operasi berhasil mengembalikan `true`, gagal mengembalikan `false` + +!> Sejak versi `v4.4.12`, parameter `finish` (tipe `bool`) diubah menjadi parameter `flags` (tipe `int`) untuk mendukung kompresi `WebSocket`, `finish` sesuai dengan `SWOOLE_WEBSOCKET_FLAG_FIN` nilai `1`, nilai `bool` asli akan dikonversi implisit ke `int`, perubahan ini kompatibel ke bawah tanpa efek. Selain itu, `flag` kompresi adalah `SWOOLE_WEBSOCKET_FLAG_COMPRESS`. + +!> [Mode BASE](/learn?id=base模式的限制:) tidak mendukung `push` data lintas proses. + +### exist + +?> **Menentukan apakah klien `WebSocket` ada dan statusnya `Active`.** + +!> Sejak `v4.3.0`, `API` ini hanya digunakan untuk menentukan apakah koneksi ada, gunakan `isEstablished` untuk menentukan apakah itu koneksi `WebSocket` + +```php +Swoole\WebSocket\Server->exist(int $fd): bool +``` + +* **Return Value** + + * Koneksi ada dan sudah menyelesaikan jabat tangan `WebSocket`, mengembalikan `true` + * Koneksi tidak ada atau belum menyelesaikan jabat tangan, mengembalikan `false` + +### pack + +?> **Mengemas pesan WebSocket.** + +```php +Swoole\WebSocket\Server::pack(\Swoole\WebSocket\Frame|string $data $data, int $opcode = WEBSOCKET_OPCODE_TEXT, bool $finish = true, bool $mask = false): string + +// v4.4.12 diubah menjadi parameter flags +Swoole\WebSocket\Server::pack(\Swoole\WebSocket\Frame|string $data $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string + +Swoole\WebSocket\Frame::pack(\Swoole\WebSocket\Frame|string $data $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string +``` + +* **Parameter** + + * **`Swoole\WebSocket\Frame|string $data $data`** + * **Fungsi**: Konten pesan + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $opcode`** + * **Fungsi**: Menentukan format data yang akan dikirim [default teks. Untuk mengirim konten biner, parameter `$opcode` perlu diatur ke `WEBSOCKET_OPCODE_BINARY`] + * **Default**: `WEBSOCKET_OPCODE_TEXT` + * **Nilai Lain**: `WEBSOCKET_OPCODE_BINARY` + + * **`bool $finish`** + * **Fungsi**: Apakah frame selesai + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + !> Sejak versi `v4.4.12`, parameter `finish` (tipe `bool`) diubah menjadi parameter `flags` (tipe `int`) untuk mendukung kompresi `WebSocket`, `finish` sesuai dengan `SWOOLE_WEBSOCKET_FLAG_FIN` nilai `1`, nilai `bool` asli akan dikonversi implisit ke `int`, perubahan ini kompatibel ke bawah tanpa efek. + + * **`bool $mask`** + * **Fungsi**: Apakah mengatur mask [`v4.4.12` telah menghapus parameter ini] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Mengembalikan paket `WebSocket` yang sudah dikemas, dapat dikirim ke lawan melalui [send()](/server/methods?id=send) dari kelas dasar `Swoole\Server` + +* **Contoh** + +```php +$ws = new Swoole\Server('127.0.0.1', 9501 , SWOOLE_BASE); + +$ws->set(array( + 'log_file' => '/dev/null' +)); + +$ws->on('WorkerStart', function (\Swoole\Server $serv) { +}); + +$ws->on('receive', function ($serv, $fd, $threadId, $data) { + $sendData = "HTTP/1.1 101 Switching Protocols\r\n"; + $sendData .= "Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\r\n"; + $sendData .= "Sec-WebSocket-Version: 13\r\nServer: swoole-http-server\r\n\r\n"; + $sendData .= Swoole\WebSocket\Server::pack("hello world\n"); + $serv->send($fd, $sendData); +}); + +$ws->start(); +``` + +### unpack + +?> **Mengurai frame data `WebSocket`.** + +```php +Swoole\WebSocket\Server::unpack(string $data): Swoole\WebSocket\Frame|false; +``` + +* **Parameter** + + * **`string $data`** + * **Fungsi**: Konten pesan + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Gagal mengurai mengembalikan `false`, berhasil mengurai mengembalikan objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) + +### disconnect + +?> **Mengirim frame penutupan ke klien `WebSocket` secara aktif dan menutup koneksi tersebut.** + +!> Swoole versi >= `v4.0.3` tersedia + +```php +Swoole\WebSocket\Server->disconnect(int $fd, int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = ''): bool +``` + +* **Parameter** + + * **`int $fd`** + * **Fungsi**: `ID` koneksi klien [jika `$fd` yang ditentukan bukan klien `WebSocket`, pengiriman akan gagal] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + + * **`int $code`** + * **Fungsi**: Kode status penutupan koneksi [menurut `RFC6455`, untuk kode status penutupan koneksi aplikasi, rentang nilai `1000` atau antara `4000-4999`] + * **Default**: `SWOOLE_WEBSOCKET_CLOSE_NORMAL` + * **Nilai Lain**: Tidak ada + + * **`string $reason`** + * **Fungsi**: Alasan penutupan koneksi [string format `utf-8`, panjang byte tidak melebihi `125`] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Berhasil dikirim mengembalikan `true`, gagal dikirim atau kode status ilegal mengembalikan `false` + +### isEstablished + +?> **Memeriksa apakah koneksi adalah koneksi klien `WebSocket` yang valid.** + +?> Fungsi ini berbeda dengan method `exist`, method `exist` hanya menentukan apakah itu koneksi `TCP`, tidak bisa menentukan apakah itu klien `WebSocket` yang sudah menyelesaikan jabat tangan. + +```php +Swoole\WebSocket\Server->isEstablished(int $fd): bool +``` + +* **Parameter** + + * **`int $fd`** + * **Fungsi**: `ID` koneksi klien [jika `$fd` yang ditentukan bukan klien `WebSocket`, pengiriman akan gagal] + * **Default**: Tidak ada + * **Nilai Lain**: Tidak ada + +* **Return Value** + + * Jika koneksi valid mengembalikan `true`, jika tidak mengembalikan `false` + +## Kelas Frame Data Websocket + +### Swoole\WebSocket\Frame + +?> Di versi `v4.2.0`, ditambahkan dukungan pengiriman objek [Swoole\WebSocket\Frame](/websocket_server?id=swoolewebsocketframe) dari server dan klien +Di versi `v4.4.12`, ditambahkan properti `flags` untuk mendukung frame kompresi `WebSocket`, dan menambahkan subclass baru [Swoole\WebSocket\CloseFrame](/websocket_server?id=swoolewebsocketcloseframe) + +Objek `frame` biasa memiliki properti berikut: + +Konstanta | Penjelasan +---|--- +fd | `Socket id` klien, digunakan saat mengirim data dengan `$server->push` +data | Konten data, bisa teks atau data biner, dapat ditentukan melalui nilai `opcode` +opcode | [Jenis frame data](/websocket_server?id=数据帧类型) `WebSocket`, dapat merujuk ke dokumen standar protokol `WebSocket` +finish | Menunjukkan apakah frame data lengkap, request `WebSocket` dapat dibagi menjadi beberapa frame data untuk dikirim (level bawah sudah mengimplementasikan penggabungan frame data otomatis, jadi tidak perlu khawatir frame data yang diterima tidak lengkap) + +Kelas ini memiliki [Swoole\WebSocket\Frame::pack()](/websocket_server?id=pack) dan [Swoole\WebSocket\Frame::unpack()](/websocket_server?id=unpack), untuk mengemas dan membongkar pesan `websocket`, penjelasan parameter sama dengan `Swoole\WebSocket\Server::pack()` dan `Swoole\WebSocket\Server::unpack()` + +### Swoole\WebSocket\CloseFrame + +Objek `close frame` biasa memiliki properti berikut: + +Konstanta | Penjelasan +---|--- +opcode | [Jenis frame data](/websocket_server?id=数据帧类型) `WebSocket`, dapat merujuk ke dokumen standar protokol `WebSocket` +code | [Kode status frame penutupan](/websocket_server?id=WebSocket断开状态码) `WebSocket`, dapat merujuk ke kode error yang ditentukan dalam [protokol websocket](https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent) +reason | Alasan penutupan, jika tidak diberikan secara jelas, kosong + +Jika server perlu menerima `close frame`, perlu mengaktifkan parameter [open_websocket_close_frame](/websocket_server?id=open_websocket_close_frame) melalui `$server->set` + +## Konstanta + +### Jenis Frame Data + +Konstanta | Nilai | Penjelasan +---|---|--- +WEBSOCKET_OPCODE_TEXT | 0x1 | Data karakter teks UTF-8 +WEBSOCKET_OPCODE_BINARY | 0x2 | Data biner +WEBSOCKET_OPCODE_CLOSE | 0x8 | Data tipe frame penutupan +WEBSOCKET_OPCODE_PING | 0x9 | Data tipe ping +WEBSOCKET_OPCODE_PONG | 0xa | Data tipe pong + +### Status Koneksi + +Konstanta | Nilai | Penjelasan +---|---|--- +WEBSOCKET_STATUS_CONNECTION | 1 | Koneksi masuk menunggu jabat tangan +WEBSOCKET_STATUS_HANDSHAKE | 2 | Sedang menjabat tangan +WEBSOCKET_STATUS_ACTIVE | 3 | Jabat tangan berhasil, menunggu browser mengirim frame data +WEBSOCKET_STATUS_CLOSING | 4 | Koneksi sedang melakukan jabat tangan penutupan, akan segera ditutup + +### Kode Status Frame Penutupan WebSocket + +Konstanta | Nilai | Penjelasan +---|---|--- +WEBSOCKET_CLOSE_NORMAL | 1000 | Penutupan normal, koneksi sudah menyelesaikan tugas +WEBSOCKET_CLOSE_GOING_AWAY | 1001 | Server memutus koneksi +WEBSOCKET_CLOSE_PROTOCOL_ERROR | 1002 | Error protokol, koneksi terputus +WEBSOCKET_CLOSE_DATA_ERROR | 1003 | Error data, misalnya membutuhkan data teks, tapi menerima data biner +WEBSOCKET_CLOSE_STATUS_ERROR | 1005 | Menunjukkan tidak menerima kode status yang diharapkan +WEBSOCKET_CLOSE_ABNORMAL | 1006 | Tidak mengirim frame penutupan +WEBSOCKET_CLOSE_MESSAGE_ERROR | 1007 | Koneksi terputus karena menerima data format tidak sesuai (seperti data non UTF-8 dalam pesan teks) +WEBSOCKET_CLOSE_POLICY_ERROR | 1008 | Koneksi terputus karena menerima data yang tidak sesuai kesepakatan. Ini adalah kode status umum, untuk skenario yang tidak cocok dengan kode status 1003 dan 1009 +WEBSOCKET_CLOSE_MESSAGE_TOO_BIG | 1009 | Koneksi terputus karena menerima frame data terlalu besar +WEBSOCKET_CLOSE_EXTENSION_MISSING | 1010 | Klien mengharapkan server menyetujui satu atau lebih ekstensi, tetapi server tidak memprosesnya, sehingga klien memutus koneksi +WEBSOCKET_CLOSE_SERVER_ERROR | 1011 | Klien karena menghadapi situasi tak terduga yang mencegahnya menyelesaikan request, sehingga server memutus koneksi +WEBSOCKET_CLOSE_TLS | 1015 | Dicadangkan. Menunjukkan koneksi ditutup karena tidak dapat menyelesaikan jabat tangan TLS (misalnya tidak dapat memverifikasi sertifikat server) + +## Opsi + +?> `Swoole\WebSocket\Server` adalah subclass dari `Server`, dapat menggunakan method [Swoole\WebSocket\Server::set()](/server/methods?id=set) untuk memasukkan opsi konfigurasi, mengatur parameter tertentu. + +### websocket_subprotocol + +?> **Mengatur sub-protokol `WebSocket`.** + +?> Setelah diatur, header `HTTP` respons jabat tangan akan menambahkan `Sec-WebSocket-Protocol: {$websocket_subprotocol}`. Cara penggunaan spesifik silakan merujuk ke dokumen `RFC` terkait protokol `WebSocket`. + +```php +$server->set([ + 'websocket_subprotocol' => 'chat', +]); +``` + +### open_websocket_close_frame + +?> **Mengaktifkan penerimaan frame penutupan (`opcode` `0x08`) di callback `onMessage` dalam protokol `WebSocket`, default `false`.** + +?> Setelah diaktifkan, dapat menerima frame penutupan yang dikirim klien atau server di callback `onMessage` di `Swoole\WebSocket\Server`, pengembang dapat menanganinya sendiri. + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_close_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x08) { + echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### open_websocket_ping_frame + +?> **Mengaktifkan penerimaan frame `Ping` (`opcode` `0x09`) di callback `onMessage` dalam protokol `WebSocket`, default `false`.** + +?> Setelah diaktifkan, dapat menerima frame `Ping` yang dikirim klien atau server di callback `onMessage` di `Swoole\WebSocket\Server`, pengembang dapat menanganinya sendiri. + +!> Swoole versi >= `v4.5.4` tersedia + +```php +$server->set([ + 'open_websocket_ping_frame' => true, +]); +``` + +!> Saat nilai `false`, level bawah akan otomatis membalas frame `Pong`, tetapi jika diatur `true` maka pengembang harus membalas frame `Pong` sendiri. + +* **Contoh** + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_ping_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0x09) { + echo "Ping frame received: Code {$frame->opcode}\n"; + // Balas frame Pong + $pongFrame = new Swoole\WebSocket\Frame; + $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; + $server->push($frame->fd, $pongFrame); + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### open_websocket_pong_frame + +?> **Mengaktifkan penerimaan frame `Pong` (`opcode` `0x0A`) di callback `onMessage` dalam protokol `WebSocket`, default `false`.** + +?> Setelah diaktifkan, dapat menerima frame `Pong` yang dikirim klien atau server di callback `onMessage` di `Swoole\WebSocket\Server`, pengembang dapat menanganinya sendiri. + +!> Swoole versi >= `v4.5.4` tersedia + +```php +$server->set([ + 'open_websocket_pong_frame' => true, +]); +``` + +* **Contoh** + +```php +$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); +$server->set(array("open_websocket_pong_frame" => true)); +$server->on('open', function (Swoole\WebSocket\Server $server, $request) { +}); + +$server->on('message', function (Swoole\WebSocket\Server $server, $frame) { + if ($frame->opcode == 0xa) { + echo "Pong frame received: Code {$frame->opcode}\n"; + } else { + echo "Message received: {$frame->data}\n"; + } +}); + +$server->on('close', function ($server, $fd) { +}); + +$server->start(); +``` + +### websocket_compression + +?> **Mengaktifkan kompresi data** + +?> Jika `true`, frame dapat dikompresi menggunakan `zlib`. Apakah kompresi dapat dilakukan tergantung pada apakah klien dapat menangani kompresi (ditentukan berdasarkan informasi jabat tangan, lihat `RFC-7692`). Perlu digunakan bersama parameter `flags` `SWOOLE_WEBSOCKET_FLAG_COMPRESS` untuk benar-benar mengompresi frame tertentu. Cara penggunaan spesifik [lihat bagian ini](/websocket_server?id=websocket帧压缩-(rfc-7692)) + +!> Swoole versi >= `v4.4.12` tersedia + +## Lain-lain + +!> Kode contoh terkait dapat ditemukan di [WebSocket unit test](https://github.com/swoole/swoole-src/tree/master/tests/swoole_websocket_server) + +### Kompresi Frame WebSocket (RFC-7692) + +?> Pertama, Anda perlu mengonfigurasi `'websocket_compression' => true` untuk mengaktifkan kompresi (saat jabat tangan `WebSocket` akan bertukar informasi dukungan kompresi dengan lawan), lalu Anda dapat menggunakan `flag SWOOLE_WEBSOCKET_FLAG_COMPRESS` untuk mengompresi frame tertentu. + +#### Contoh + +* **Server** + +```php +use Swoole\WebSocket\Frame; +use Swoole\WebSocket\Server; + +$server = new Server('127.0.0.1', 9501); +$server->set(['websocket_compression' => true]); +$server->on('message', function (Server $server, Frame $frame) { + $server->push( + $frame->fd, + 'Hello Swoole', + SWOOLE_WEBSOCKET_OPCODE_TEXT, + SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS + ); + // $server->push($frame->fd, $frame); // Atau server bisa langsung meneruskan objek frame klien apa adanya +}); +$server->start(); +``` + +* **Klien** + +```php +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9501); + $cli->set(['websocket_compression' => true]); + $cli->upgrade('/'); + $cli->push( + 'Hello Swoole', + SWOOLE_WEBSOCKET_OPCODE_TEXT, + SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS + ); +}); +``` + +### Mengirim Frame Ping + +?> Karena WebSocket adalah koneksi panjang, jika tidak ada komunikasi dalam waktu tertentu, koneksi mungkin terputus. Diperlukan mekanisme heartbeat. Protokol WebSocket mencakup dua frame, Ping dan Pong, dapat mengirim frame Ping secara periodik untuk menjaga koneksi tetap aktif. + +#### Contoh + +* **Server** + +```php +use Swoole\WebSocket\Frame; +use Swoole\WebSocket\Server; + +$server = new Server('127.0.0.1', 9501); +$server->on('message', function (Server $server, Frame $frame) { + $pingFrame = new Frame; + $pingFrame->opcode = WEBSOCKET_OPCODE_PING; + $server->push($frame->fd, $pingFrame); +}); +$server->start(); +``` + +* **Klien** + +```php +use Swoole\WebSocket\Frame; +use Swoole\Coroutine\Http\Client; +use function Swoole\Coroutine\run; + +run(function () { + $cli = new Client('127.0.0.1', 9501); + $cli->upgrade('/'); + $pingFrame = new Frame; + $pingFrame->opcode = WEBSOCKET_OPCODE_PING; + // Kirim PING + $cli->push($pingFrame); + + // Terima PONG + $pongFrame = $cli->recv(); + var_dump($pongFrame->opcode === WEBSOCKET_OPCODE_PONG); +}); +``` diff --git a/public/ja/_navbar.md b/public/ja/_navbar.md index e930b97..7da409b 100644 --- a/public/ja/_navbar.md +++ b/public/ja/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/ko/_navbar.md b/public/ko/_navbar.md index e930b97..7da409b 100644 --- a/public/ko/_navbar.md +++ b/public/ko/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/ru/_navbar.md b/public/ru/_navbar.md index e930b97..7da409b 100644 --- a/public/ru/_navbar.md +++ b/public/ru/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/zh-Hant/_navbar.md b/public/zh-Hant/_navbar.md index e930b97..7da409b 100644 --- a/public/zh-Hant/_navbar.md +++ b/public/zh-Hant/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/) diff --git a/public/zh-cn/_navbar.md b/public/zh-cn/_navbar.md index e930b97..7da409b 100644 --- a/public/zh-cn/_navbar.md +++ b/public/zh-cn/_navbar.md @@ -7,4 +7,5 @@ * [French](https://wiki.swoole.com/fr/) * [German](https://wiki.swoole.com/de/) * [Russian](https://wiki.swoole.com/ru/) + * [Indonesia](https://wiki.swoole.com/id/)