Bagikan melalui


Praktik terbaik performa dengan gRPC

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Oleh James Newton-King

gRPC dirancang untuk layanan berkinerja tinggi. Dokumen ini menjelaskan cara mendapatkan performa terbaik dari gRPC.

Menggunakan kembali saluran gRPC

Saluran gRPC harus digunakan kembali ketika melakukan panggilan gRPC. Menggunakan kembali saluran memungkinkan panggilan untuk di-multipleks melalui koneksi HTTP/2 yang ada.

Jika saluran baru dibuat untuk setiap panggilan gRPC, jumlah waktu yang diperlukan untuk menyelesaikannya dapat meningkat secara signifikan. Setiap panggilan akan memerlukan beberapa perjalanan pulang-pergi jaringan antara klien dan server untuk membuat koneksi HTTP/2 baru:

  1. Membuka soket
  2. Membuat koneksi TCP
  3. Menegosiasikan TLS
  4. Memulai koneksi HTTP/2
  5. Melakukan panggilan gRPC

Saluran aman untuk dibagikan dan digunakan kembali antara panggilan gRPC:

  • Klien-klien gRPC dibuat dengan saluran itu. Klien gRPC adalah objek ringan dan tidak perlu di-cache atau digunakan kembali.
  • Beberapa klien gRPC dapat dibuat dari saluran, termasuk jenis klien yang berbeda.
  • Saluran dan klien yang dibuat dari saluran dapat digunakan dengan aman oleh beberapa utas.
  • Klien yang dibuat dari saluran dapat melakukan beberapa panggilan simultan.

Pabrik klien gRPC menawarkan cara terpusat untuk mengonfigurasi saluran. Ini secara otomatis menggunakan kembali saluran dasar. Untuk informasi selengkapnya, lihat integrasi pabrik klien gRPC di .NET.

Keserentakan koneksi

Koneksi HTTP/2 biasanya memiliki batasan jumlah aliran bersamaan maksimum (permintaan HTTP aktif) pada koneksi pada satu waktu. Secara default, sebagian besar server menetapkan batas ini ke 100 aliran bersamaan.

Saluran gRPC menggunakan satu koneksi HTTP/2, dan panggilan bersamaan dikalikan pada koneksi tersebut. Ketika jumlah panggilan aktif mencapai batas aliran koneksi, panggilan tambahan diantrekan di klien. Panggilan yang antre menunggu panggilan aktif selesai baru kemudian dikirim. Aplikasi dengan beban tinggi, atau panggilan gRPC streaming yang berjalan lama, dapat mengalami masalah performa yang disebabkan oleh antrean panggilan karena batas keterbatasan ini.

.NET 5 memperkenalkan properti SocketsHttpHandler.EnableMultipleHttp2Connections. Ketika diatur ke true, koneksi HTTP/2 tambahan dibuat oleh saluran ketika batas aliran bersamaan tercapai. Ketika sebuah GrpcChannel dibuat, komponen internal SocketsHttpHandler dikonfigurasi secara otomatis untuk membuat koneksi HTTP/2 tambahan. Jika aplikasi mengonfigurasi handler-nya sendiri, pertimbangkan untuk mengatur EnableMultipleHttp2Connections ke true:

var channel = GrpcChannel.ForAddress("https://localhost", new GrpcChannelOptions
{
    HttpHandler = new SocketsHttpHandler
    {
        EnableMultipleHttp2Connections = true,

        // ...configure other handler settings
    }
});

Aplikasi .NET Framework yang melakukan panggilan gRPC harus dikonfigurasi untuk menggunakan WinHttpHandler. Aplikasi .NET Framework dapat mengatur WinHttpHandler.EnableMultipleHttp2Connections properti ke true untuk membuat koneksi tambahan.

Ada beberapa solusi untuk aplikasi .NET Core 3.1:

  • Buat saluran gRPC terpisah untuk area aplikasi dengan beban tinggi. Misalnya, Logger layanan gRPC mungkin memiliki beban tinggi. Gunakan saluran terpisah untuk membuat LoggerClient di aplikasi.
  • Gunakan kumpulan saluran gRPC, misalnya, membuat daftar saluran gRPC. Random digunakan untuk memilih saluran dari daftar setiap kali saluran gRPC diperlukan. Menggunakan Random mendistribusikan panggilan secara acak melalui beberapa koneksi.

Penting

Meningkatkan batas aliran bersamaan maksimum pada server adalah cara lain untuk menyelesaikan masalah ini. Hal ini dikonfigurasi dengan KestrelMaxStreamsPerConnection.

Meningkatkan batas aliran bersamaan maksimum tidak disarankan. Terlalu banyak aliran pada satu koneksi HTTP/2 memperkenalkan masalah performa baru:

  • Pertikaian utas antara aliran yang mencoba menulis ke koneksi.
  • Kehilangan paket koneksi menyebabkan semua panggilan diblokir di lapisan TCP.

ServerGarbageCollection di aplikasi klien

Pengumpul sampah .NET memiliki dua mode: pengumpulan sampah stasiun kerja (GC) dan pengumpulan sampah server. Masing-masing disetel untuk beban kerja yang berbeda. aplikasi ASP.NET Core menggunakan server GC secara default.

Aplikasi dengan tingkat konkruensi tinggi umumnya berkinerja lebih baik dengan GC server. Jika aplikasi klien gRPC mengirim dan menerima sejumlah besar panggilan gRPC secara bersamaan, maka mungkin ada manfaat performa dalam memperbarui aplikasi untuk menggunakan server GC.

Untuk mengaktifkan GC server, atur <ServerGarbageCollection> dalam file proyek aplikasi:

<PropertyGroup>
  <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>

Untuk informasi selengkapnya tentang pengumpulan sampah, lihat pengumpulan sampah workstation dan server.

Catatan

aplikasi ASP.NET Core menggunakan server GC secara default. Mengaktifkan <ServerGarbageCollection> hanya berguna di aplikasi klien gRPC non-server, misalnya di aplikasi konsol klien gRPC.

Panggilan asinkron di aplikasi klien

Lebih suka menggunakan pemrograman asinkron dengan asinkron dan menunggu saat memanggil metode gRPC. Melakukan panggilan gRPC yang bersifat memblokir, seperti menggunakan Task.Result atau Task.Wait(), mencegah penggunaan utas oleh tugas lain. Hal ini dapat menyebabkan kekurangan sumber daya pada kumpulan utas, performa buruk, dan aplikasi mengalami kebuntuan yang menyebabkan hang.

Semua jenis metode gRPC menghasilkan API asinkron pada klien gRPC. Pengecualian adalah metode unary, yang menghasilkan metode asinkron dan metode pemblokiran.

Pertimbangkan layanan gRPC berikut yang ditentukan dalam file .proto:

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

Jenis GreeterClient yang dihasilkan memiliki dua metode .NET untuk memanggil SayHello:

  • GreeterClient.SayHelloAsync - memanggil layanan Greeter.SayHello secara asinkron. Bisa ditunggu.
  • GreeterClient.SayHello - memanggil layanan Greeter.SayHello dan memblokir sampai selesai.

Metode pemblokiran GreeterClient.SayHello tidak boleh digunakan dalam kode asinkron. Ini dapat menyebabkan masalah performa dan keandalan.

Penyeimbangan Beban

Beberapa penyeimbang beban tidak berfungsi secara efektif dengan gRPC. Penyeimbang beban L4 (transportasi) beroperasi pada tingkat koneksi, dengan mendistribusikan koneksi TCP di seluruh titik akhir. Pendekatan ini berfungsi dengan baik untuk memuat panggilan API penyeimbangan yang dilakukan dengan HTTP/1.1. Panggilan bersamaan yang dilakukan dengan HTTP/1.1 dikirim pada koneksi yang berbeda, memungkinkan panggilan untuk diseimbangkan beban di seluruh titik akhir.

Karena load balancer L4 beroperasi pada tingkat koneksi, penyeimbang beban L4 tidak berfungsi dengan baik dengan gRPC. gRPC menggunakan HTTP/2, yang melakukan beberapa panggilan pada satu koneksi TCP. Semua panggilan gRPC melalui koneksi tersebut masuk ke satu titik akhir.

Ada dua opsi untuk mengatur keseimbangan muatan gRPC secara efektif.

  • Penyeimbangan beban sisi klien
  • Penyeimbangan beban proksi Layer 7 (aplikasi)

Catatan

Hanya panggilan gRPC yang dapat diseimbangkan beban di antara titik akhir. Setelah panggilan gRPC streaming dibuat, semua pesan yang dikirim melalui streaming masuk ke satu titik akhir.

Penyeimbangan beban sisi klien

Dengan penyeimbangan beban sisi klien, klien tahu tentang titik akhir. Untuk setiap panggilan gRPC, ia memilih titik akhir yang berbeda untuk mengirim panggilan. Penyeimbangan beban sisi klien adalah pilihan yang baik ketika latensi penting. Tidak ada proksi antara klien dan layanan, sehingga panggilan dikirim ke layanan secara langsung. Kelemahan dari penyeimbangan beban sisi klien adalah setiap klien harus melacak titik akhir yang tersedia yang harus digunakan.

Penyeimbangan beban klien Lookaside adalah teknik di mana status penyeimbangan beban disimpan di lokasi pusat. Klien secara berkala meminta lokasi pusat untuk informasi yang akan digunakan saat membuat keputusan penyeimbangan beban.

Untuk informasi selengkapnya, lihat penyeimbangan beban sisi klien gRPC.

Penyeimbangan beban proksi

Proksi L7 (aplikasi) bekerja pada tingkat yang lebih tinggi daripada proksi L4 (transportasi). Proksi L7 memahami HTTP/2. Proksi menerima panggilan gRPC yang digabungkan melalui satu koneksi HTTP/2 dan mendistribusikannya ke berbagai titik akhir di backend. Menggunakan proksi lebih sederhana daripada penyeimbangan beban sisi klien, tetapi menambahkan latensi ekstra ke panggilan gRPC.

Ada banyak proksi L7 yang tersedia. Beberapa opsinya adalah:

Komunikasi antar-proses

Panggilan gRPC antara klien dan layanan biasanya dikirim melalui soket TCP. TCP sangat bagus untuk berkomunikasi di seluruh jaringan, tetapi komunikasi antarproses (IPC) lebih efisien ketika klien dan layanan berada di komputer yang sama.

Pertimbangkan untuk menggunakan transportasi seperti soket domain Unix atau pipa bernama untuk panggilan gRPC antar proses pada komputer yang sama. Untuk informasi selengkapnya, lihat Komunikasi antarproses dengan gRPC.

Pertahankan ping tetap aktif

Ping keep-alive dapat digunakan untuk menjaga koneksi HTTP/2 tetap aktif selama periode tidak aktif. Memiliki koneksi HTTP/2 yang sudah ada dan siap ketika aplikasi melanjutkan aktivitas memungkinkan panggilan gRPC awal dilakukan dengan cepat, tanpa penundaan yang disebabkan oleh koneksi yang didirikan kembali.

Ping tetap hidup dikonfigurasi pada SocketsHttpHandler:

var handler = new SocketsHttpHandler
{
    PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
    KeepAlivePingDelay = TimeSpan.FromSeconds(60),
    KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
    EnableMultipleHttp2Connections = true
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    HttpHandler = handler
});

Kode sebelumnya mengonfigurasi saluran yang mengirim ping tetap hidup ke server setiap 60 detik selama periode tidak aktif. Ping memastikan server dan proksi apa pun yang digunakan tidak akan menutup koneksi karena tidak aktif.

Catatan

Ping keep-alive hanya berfungsi untuk menjaga koneksi tetap aktif. Panggilan gRPC yang berlangsung lama pada koneksi bisa saja masih dihentikan oleh server atau proksi perantara karena tidak aktif.

Pengendalian aliran

Kontrol alur HTTP/2 adalah fitur yang mencegah aplikasi kewalahan dengan data. Saat menggunakan kontrol alur:

  • Setiap koneksi HTTP/2 dan permintaan memiliki jendela buffer yang tersedia. Jendela buffer adalah berapa banyak data yang dapat diterima aplikasi sekaligus.
  • Kontrol alur diaktifkan jika jendela buffer terisi. Saat diaktifkan, aplikasi pengirim menjeda pengiriman lebih banyak data.
  • Setelah aplikasi penerima memproses data, maka ruang di jendela buffer tersedia. Aplikasi pengirim melanjutkan pengiriman data.

Kontrol alur dapat berdampak negatif pada performa saat menerima pesan besar. Jika jendela buffer lebih kecil dari payload pesan masuk atau ada latensi antara klien dan server, maka data dapat dikirim dalam ledakan mulai/berhenti.

Masalah performa kontrol aliran dapat diperbaiki dengan meningkatkan ukuran jendela buffer. Di Kestrel, ini dikonfigurasi dengan InitialConnectionWindowSize dan InitialStreamWindowSize saat pengaktifan aplikasi:

builder.WebHost.ConfigureKestrel(options =>
{
    var http2 = options.Limits.Http2;
    http2.InitialConnectionWindowSize = 1024 * 1024 * 2; // 2 MB
    http2.InitialStreamWindowSize = 1024 * 1024; // 1 MB
});

Rekomendasi:

  • Jika layanan gRPC sering menerima pesan yang lebih besar dari 768 KB, Kestrelukuran jendela streaming default, pertimbangkan untuk meningkatkan ukuran jendela koneksi dan aliran.
  • Ukuran jendela koneksi harus selalu sama dengan atau lebih besar dari ukuran jendela aliran. Aliran adalah bagian dari koneksi, dan pengirim dibatasi oleh keduanya.

Untuk informasi selengkapnya tentang cara kerja kontrol alur, lihat Kontrol Alur HTTP/2 (posting blog).

Penting

Meningkatkan ukuran jendela Kestrel memungkinkan Kestrel untuk menyimpan lebih banyak data untuk aplikasi, sehingga mungkin meningkatkan penggunaan memori. Hindari mengonfigurasi ukuran jendela yang tidak perlu besar.

Menyelesaikan panggilan streaming dengan anggun

Cobalah untuk menyelesaikan panggilan streaming dengan anggun. Menyelesaikan panggilan dengan baik menghindari kesalahan yang tidak perlu dan memungkinkan server untuk menggunakan kembali struktur data internal antar permintaan.

Panggilan selesai dengan lancar ketika klien dan server telah menyelesaikan pengiriman pesan dan rekan telah membaca semua pesan.

Aliran permintaan klien:

  1. Klien telah selesai menulis pesan ke aliran permintaan dan menyelesaikan aliran dengan call.RequestStream.CompleteAsync().
  2. Server telah membaca semua pesan dari aliran permintaan. Bergantung pada cara Anda membaca pesan, entah requestStream.MoveNext() mengembalikan false atau requestStream.ReadAllAsync() telah selesai.

Aliran respons server:

  1. Server telah selesai menulis pesan ke aliran respons dan metode di server telah selesai dijalankan.
  2. Klien telah membaca semua pesan dari aliran respons. Bergantung pada cara Anda membaca pesan, entah call.ResponseStream.MoveNext() mengembalikan false atau call.ResponseStream.ReadAllAsync() telah selesai.

Untuk contoh menyelesaikan panggilan streaming dua arah, lihat melakukan panggilan streaming dua arah.

Panggilan streaming server tidak memiliki aliran permintaan. Ini berarti bahwa satu-satunya cara bagi klien untuk memberitahu server bahwa aliran harus dihentikan adalah dengan membatalkannya. Jika overhead dari panggilan yang dibatalkan memengaruhi aplikasi, pertimbangkan untuk mengubah panggilan streaming server ke panggilan streaming dua arah. Dalam panggilan streaming dua arah, klien yang menyelesaikan aliran permintaan dapat menjadi sinyal ke server untuk mengakhiri panggilan.

Membuang panggilan streaming

Selalu buang panggilan streaming setelah tidak lagi diperlukan. Jenis yang dikembalikan saat memulai panggilan streaming mengimplementasikan IDisposable. Membuang panggilan setelah tidak lagi diperlukan memastikan panggilan dihentikan dan semua sumber daya dibersihkan.

Dalam contoh berikut, deklarasi penggunaan untuk AccumulateCount() panggilan memastikan bahwa selalu ada pembuangan jika terjadi kesalahan yang tidak terduga.

var client = new Counter.CounterClient(channel);
using var call = client.AccumulateCount();

for (var i = 0; i < 3; i++)
{
    await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
}
await call.RequestStream.CompleteAsync();

var response = await call;
Console.WriteLine($"Count: {response.Count}");
// Count: 3

Idealnya panggilan streaming harus diselesaikan dengan anggun. Membuang panggilan memastikan permintaan HTTP antara klien dan server dibatalkan jika terjadi kesalahan tak terduga. Panggilan streaming yang tidak sengaja dibiarkan berjalan tidak hanya membocorkan memori dan sumber daya pada klien, tetapi juga dibiarkan berjalan di server. Banyak panggilan streaming yang bocor dapat memengaruhi stabilitas aplikasi.

Mengakhiri panggilan streaming yang telah selesai dengan lancar tidak berdampak negatif.

Ganti panggilan unari dengan streaming

Streaming dua arah gRPC dapat digunakan untuk menggantikan panggilan gRPC satu arah dalam skenario performa tinggi. Setelah aliran dua arah dimulai, mengalirkan pesan secara bolak-balik lebih cepat daripada mengirim pesan melalui beberapa panggilan gRPC unary. Pesan yang dialirkan dikirim sebagai data pada permintaan HTTP/2 yang ada dan menghilangkan overhead pembuatan permintaan HTTP/2 baru untuk setiap panggilan unary.

Contoh layanan:

public override async Task SayHello(IAsyncStreamReader<HelloRequest> requestStream,
    IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
    await foreach (var request in requestStream.ReadAllAsync())
    {
        var helloReply = new HelloReply { Message = "Hello " + request.Name };

        await responseStream.WriteAsync(helloReply);
    }
}

Contoh klien:

var client = new Greet.GreeterClient(channel);
using var call = client.SayHello();

Console.WriteLine("Type a name then press enter.");
while (true)
{
    var text = Console.ReadLine();

    // Send and receive messages over the stream
    await call.RequestStream.WriteAsync(new HelloRequest { Name = text });
    await call.ResponseStream.MoveNext();

    Console.WriteLine($"Greeting: {call.ResponseStream.Current.Message}");
}

Mengganti panggilan unary dengan streaming dua arah untuk alasan kinerja adalah teknik tingkat lanjut dan tidak cocok dalam banyak situasi.

Menggunakan panggilan streaming adalah pilihan yang baik saat:

  1. Diperlukan throughput yang tinggi atau latensi yang rendah.
  2. gRPC dan HTTP/2 diidentifikasi sebagai penyempitan performa.
  3. Pekerja di klien mengirim atau menerima pesan reguler dengan layanan gRPC.

Ketahui kompleksitas dan batasan tambahan penggunaan panggilan streaming alih-alih unary:

  1. Aliran dapat terganggu oleh kesalahan layanan atau koneksi. Logika diperlukan untuk memulai ulang aliran jika ada kesalahan.
  2. RequestStream.WriteAsync tidak aman untuk multi-utas. Hanya satu pesan yang dapat ditulis ke aliran pada satu waktu. Mengirim pesan dari beberapa utas melalui satu aliran memerlukan antrean produsen/konsumen seperti Channel<T> untuk memproses pesan.
  3. Metode streaming gRPC terbatas untuk menerima satu jenis pesan dan mengirim satu jenis pesan. Misalnya, rpc StreamingCall(stream RequestMessage) returns (stream ResponseMessage) menerima RequestMessage dan mengirim ResponseMessage. Dukungan Protobuf untuk pesan yang tidak diketahui atau bersyarat menggunakan Any dan oneof dapat mengatasi batasan ini.

Payload biner

Payload biner didukung di Protobuf dengan bytes jenis nilai skalar. Properti yang dihasilkan di C# menggunakan ByteString sebagai jenis properti.

syntax = "proto3";

message PayloadResponse {
    bytes data = 1;
}  

Protobuf adalah format biner yang secara efisien menserialisasikan payload biner besar dengan overhead minimal. Format berbasis teks seperti JSON memerlukan byte pengodean ke base64 dan menambahkan 33% ke ukuran pesan.

Saat bekerja dengan payload besar ByteString , ada beberapa praktik terbaik untuk menghindari salinan dan alokasi yang tidak perlu yang dibahas di bawah ini.

Mengirim payload biner

ByteString instans biasanya dibuat menggunakan ByteString.CopyFrom(byte[] data). Metode ini mengalokasikan ByteString dan byte[] yang baru. Data disalin ke dalam array byte baru.

Alokasi dan salinan tambahan dapat dihindari dengan menggunakan UnsafeByteOperations.UnsafeWrap(ReadOnlyMemory<byte> bytes) untuk membuat ByteString instance.

var data = await File.ReadAllBytesAsync(path);

var payload = new PayloadResponse();
payload.Data = UnsafeByteOperations.UnsafeWrap(data);

Byte tidak disalin dengan UnsafeByteOperations.UnsafeWrap sehingga tidak boleh dimodifikasi saat ByteString sedang digunakan.

UnsafeByteOperations.UnsafeWrap memerlukan Google.Protobuf versi 3.15.0 atau yang lebih baru.

Membaca payload biner

Data dapat dibaca secara efisien dari ByteString instance dengan menggunakan properti ByteString.Memory dan properti ByteString.Span.

var byteString = UnsafeByteOperations.UnsafeWrap(new byte[] { 0, 1, 2 });
var data = byteString.Span;

for (var i = 0; i < data.Length; i++)
{
    Console.WriteLine(data[i]);
}

Properti ini memungkinkan kode untuk membaca data langsung dari ByteString tanpa alokasi atau salinan.

Sebagian besar API .NET memiliki ReadOnlyMemory<byte> dan byte[] kelebihan beban, jadi ByteString.Memory adalah cara yang disarankan untuk menggunakan data yang mendasar. Namun, ada keadaan di mana aplikasi mungkin perlu mendapatkan data dalam bentuk array byte. Jika array byte diperlukan, maka metode MemoryMarshal.TryGetArray dapat digunakan untuk mendapatkan array dari ByteString tanpa menyalin data baru.

var byteString = GetByteString();

ByteArrayContent content;
if (MemoryMarshal.TryGetArray(byteString.Memory, out var segment))
{
    // Success. Use the ByteString's underlying array.
    content = new ByteArrayContent(segment.Array, segment.Offset, segment.Count);
}
else
{
    // TryGetArray didn't succeed. Fall back to creating a copy of the data with ToByteArray.
    content = new ByteArrayContent(byteString.ToByteArray());
}

var httpRequest = new HttpRequestMessage();
httpRequest.Content = content;

Kode sebelumnya:

  • Mencoba untuk mendapatkan array dari ByteString.Memory dengan MemoryMarshal.TryGetArray.
  • Menggunakan ArraySegment<byte> jika berhasil diambil. Segmen memiliki referensi ke array, offset, dan jumlah.
  • Jika tidak, kembali mengalokasikan array baru dengan ByteString.ToByteArray().

Layanan gRPC dan muatan biner besar

gRPC dan Protobuf dapat mengirim dan menerima payload biner besar. Meskipun Biner Protobuf lebih efisien daripada JSON berbasis teks pada serialisasi payload biner, masih ada karakteristik performa penting yang perlu diingat saat bekerja dengan payload biner besar.

gRPC adalah kerangka kerja RPC berbasis pesan, yang berarti:

  • Seluruh pesan dimuat ke dalam memori sebelum gRPC dapat mengirimkannya.
  • Saat pesan diterima, seluruh pesan tersebut dideserialisasi ke memori.

Payload biner dialokasikan dalam bentuk array byte. Misalnya, payload biner 10 MB mengalokasikan sebuah array byte 10 MB. Pesan dengan payload biner besar dapat mengalokasikan array byte pada timbunan objek besar. Alokasi besar memengaruhi performa dan skalabilitas server.

Saran untuk membuat aplikasi berkinerja tinggi dengan payload biner besar: