Membuat layanan dan metode gRPC
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 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 8 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 8 dari artikel ini.
Oleh James Newton-King
Dokumen ini menjelaskan cara membuat layanan dan metode gRPC di C#. Topik meliputi:
- Cara menentukan layanan dan metode dalam
.proto
file. - Kode yang dihasilkan menggunakan alat gRPC C#.
- Menerapkan layanan dan metode gRPC.
Membuat layanan gRPC baru
Layanan gRPC dengan C# memperkenalkan pendekatan pertama kontrak gRPC untuk pengembangan API. Layanan dan pesan ditentukan dalam .proto
file. Alat C# kemudian menghasilkan kode dari .proto
file. Untuk aset sisi server, jenis dasar abstrak dihasilkan untuk setiap layanan, bersama dengan kelas untuk pesan apa pun.
File berikut .proto
:
Greeter
Menentukan layanan.- Layanan
Greeter
mendefinisikanSayHello
panggilan. SayHello
HelloRequest
mengirim pesan dan menerimaHelloReply
pesan
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Alat C# menghasilkan jenis dasar C# GreeterBase
:
public abstract partial class GreeterBase
{
public virtual Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
}
}
public class HelloRequest
{
public string Name { get; set; }
}
public class HelloReply
{
public string Message { get; set; }
}
Secara default yang dihasilkan GreeterBase
tidak melakukan apa pun. Metode virtualnya SayHello
akan mengembalikan kesalahan UNIMPLEMENTED
ke klien mana pun yang menyebutnya. Agar layanan dapat berguna, aplikasi harus membuat implementasi konkret dari GreeterBase
:
public class GreeterService : GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
}
}
ServerCallContext
memberikan konteks untuk panggilan sisi server.
Implementasi layanan terdaftar di aplikasi. Jika layanan dihosting oleh ASP.NET Core gRPC, layanan harus ditambahkan ke alur perutean dengan MapGrpcService
metode .
app.MapGrpcService<GreeterService>();
Lihat layanan gRPC dengan ASP.NET Core untuk informasi selengkapnya.
Menerapkan metode gRPC
Layanan gRPC dapat memiliki berbagai jenis metode. Bagaimana pesan dikirim dan diterima oleh layanan tergantung pada jenis metode yang ditentukan. Jenis metode gRPC adalah:
- Unary
- Streaming server
- Streaming klien
- Streaming dua arah
Panggilan streaming ditentukan dengan stream
kata kunci dalam .proto
file. stream
dapat ditempatkan pada pesan permintaan panggilan, pesan respons, atau keduanya.
syntax = "proto3";
service ExampleService {
// Unary
rpc UnaryCall (ExampleRequest) returns (ExampleResponse);
// Server streaming
rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);
// Client streaming
rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);
// Bi-directional streaming
rpc StreamingBothWays (stream ExampleRequest) returns (stream ExampleResponse);
}
Setiap jenis panggilan memiliki tanda tangan metode yang berbeda. Mengambil alih metode yang dihasilkan dari jenis layanan dasar abstrak dalam implementasi konkret memastikan argumen yang benar dan jenis pengembalian digunakan.
Metode unary
Metode unary memiliki pesan permintaan sebagai parameter, dan mengembalikan respons. Panggilan tidak sah selesai saat respons dikembalikan.
public override Task<ExampleResponse> UnaryCall(ExampleRequest request,
ServerCallContext context)
{
var response = new ExampleResponse();
return Task.FromResult(response);
}
Panggilan unary adalah yang paling mirip dengan tindakan pada pengontrol API web. Salah satu perbedaan penting yang dimiliki metode gRPC dari tindakan adalah metode gRPC tidak dapat mengikat bagian permintaan ke argumen metode yang berbeda. Metode gRPC selalu memiliki satu argumen pesan untuk data permintaan masuk. Beberapa nilai masih dapat dikirim ke layanan gRPC dengan menambahkan bidang ke pesan permintaan:
message ExampleRequest {
int32 pageIndex = 1;
int32 pageSize = 2;
bool isDescending = 3;
}
Metode streaming server
Metode streaming server memiliki pesan permintaan sebagai parameter. Karena beberapa pesan dapat dialirkan kembali ke pemanggil, responseStream.WriteAsync
digunakan untuk mengirim pesan respons. Panggilan streaming server selesai ketika metode kembali.
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
for (var i = 0; i < 5; i++)
{
await responseStream.WriteAsync(new ExampleResponse());
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
Klien tidak memiliki cara untuk mengirim pesan atau data tambahan setelah metode streaming server dimulai. Beberapa metode streaming dirancang untuk berjalan selamanya. Untuk metode streaming berkelanjutan, klien dapat membatalkan panggilan saat tidak lagi diperlukan. Ketika pembatalan terjadi, klien mengirim sinyal ke server dan ServerCallContext.CancellationToken dinaikkan. Token CancellationToken
harus digunakan pada server dengan metode asinkron sehingga:
- Setiap pekerjaan asinkron dibatalkan bersama dengan panggilan streaming.
- Metode keluar dengan cepat.
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
while (!context.CancellationToken.IsCancellationRequested)
{
await responseStream.WriteAsync(new ExampleResponse());
await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken);
}
}
Metode streaming klien
Metode streaming klien dimulai tanpa metode menerima pesan. Parameter requestStream
digunakan untuk membaca pesan dari klien. Panggilan streaming klien selesai saat pesan respons dikembalikan:
public override async Task<ExampleResponse> StreamingFromClient(
IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
{
await foreach (var message in requestStream.ReadAllAsync())
{
// ...
}
return new ExampleResponse();
}
Metode streaming dua arah
Metode streaming dua arah dimulai tanpa metode menerima pesan. Parameter requestStream
digunakan untuk membaca pesan dari klien. Metode ini dapat memilih untuk mengirim pesan dengan responseStream.WriteAsync
. Panggilan streaming dua arah selesai saat metode mengembalikan:
public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
await foreach (var message in requestStream.ReadAllAsync())
{
await responseStream.WriteAsync(new ExampleResponse());
}
}
Kode sebelumnya:
- Mengirim respons untuk setiap permintaan.
- Adalah penggunaan dasar streaming dua arah.
Dimungkinkan untuk mendukung skenario yang lebih kompleks, seperti membaca permintaan dan mengirim respons secara bersamaan:
public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
// Read requests in a background task.
var readTask = Task.Run(async () =>
{
await foreach (var message in requestStream.ReadAllAsync())
{
// Process request.
}
});
// Send responses until the client signals that it is complete.
while (!readTask.IsCompleted)
{
await responseStream.WriteAsync(new ExampleResponse());
await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken);
}
}
Dalam metode streaming dua arah, klien dan layanan dapat mengirim pesan satu sama lain kapan saja. Implementasi terbaik dari metode dua arah bervariasi tergantung pada persyaratan.
Mengakses header permintaan gRPC
Pesan permintaan bukan satu-satunya cara bagi klien untuk mengirim data ke layanan gRPC. Nilai header tersedia dalam layanan menggunakan ServerCallContext.RequestHeaders
.
public override Task<ExampleResponse> UnaryCall(ExampleRequest request,
ServerCallContext context)
{
var userAgent = context.RequestHeaders.GetValue("user-agent");
// ...
return Task.FromResult(new ExampleResponse());
}
Multi-utas dengan metode streaming gRPC
Ada pertimbangan penting untuk menerapkan metode streaming gRPC yang menggunakan beberapa utas.
Keamanan utas pembaca dan penulis
IAsyncStreamReader<TMessage>
dan IServerStreamWriter<TMessage>
masing-masing hanya dapat digunakan oleh satu utas pada satu waktu. Untuk metode streaming gRPC, beberapa utas tidak dapat membaca pesan baru secara requestStream.MoveNext()
bersamaan. Dan beberapa utas tidak dapat menulis pesan baru secara responseStream.WriteAsync(message)
bersamaan.
Cara aman untuk mengaktifkan beberapa utas untuk berinteraksi dengan metode gRPC adalah dengan menggunakan pola produsen-konsumen dengan System.Threading.Channels.
public override async Task DownloadResults(DataRequest request,
IServerStreamWriter<DataResult> responseStream, ServerCallContext context)
{
var channel = Channel.CreateBounded<DataResult>(new BoundedChannelOptions(capacity: 5));
var consumerTask = Task.Run(async () =>
{
// Consume messages from channel and write to response stream.
await foreach (var message in channel.Reader.ReadAllAsync())
{
await responseStream.WriteAsync(message);
}
});
var dataChunks = request.Value.Chunk(size: 10);
// Write messages to channel from multiple threads.
await Task.WhenAll(dataChunks.Select(
async c =>
{
var message = new DataResult { BytesProcessed = c.Length };
await channel.Writer.WriteAsync(message);
}));
// Complete writing and wait for consumer to complete.
channel.Writer.Complete();
await consumerTask;
}
Metode streaming server gRPC sebelumnya:
- Membuat saluran terikat untuk memproduksi dan mengonsumsi
DataResult
pesan. - Memulai tugas untuk membaca pesan dari saluran dan menulisnya ke aliran respons.
- Menulis pesan ke saluran dari beberapa utas.
Catatan
Metode streaming dua arah mengambil IAsyncStreamReader<TMessage>
dan IServerStreamWriter<TMessage>
sebagai argumen. Aman untuk menggunakan jenis ini pada utas terpisah satu sama lain.
Berinteraksi dengan metode gRPC setelah panggilan berakhir
Panggilan gRPC berakhir di server setelah metode gRPC keluar. Argumen berikut yang diteruskan ke metode gRPC tidak aman digunakan setelah panggilan berakhir:
ServerCallContext
IAsyncStreamReader<TMessage>
IServerStreamWriter<TMessage>
Jika metode gRPC memulai tugas latar belakang yang menggunakan jenis ini, metode tersebut harus menyelesaikan tugas sebelum metode gRPC keluar. Terus menggunakan konteks, pembaca streaming, atau penulis streaming setelah metode gRPC ada menyebabkan kesalahan dan perilaku yang tidak dapat diprediksi.
Dalam contoh berikut, metode streaming server dapat menulis ke aliran respons setelah panggilan selesai:
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
_ = Task.Run(async () =>
{
for (var i = 0; i < 5; i++)
{
await responseStream.WriteAsync(new ExampleResponse());
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
await PerformLongRunningWorkAsync();
}
Untuk contoh sebelumnya, solusinya adalah menunggu tugas tulis sebelum keluar dari metode:
public override async Task StreamingFromServer(ExampleRequest request,
IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
var writeTask = Task.Run(async () =>
{
for (var i = 0; i < 5; i++)
{
await responseStream.WriteAsync(new ExampleResponse());
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
await PerformLongRunningWorkAsync();
await writeTask;
}
Sumber Daya Tambahan:
ASP.NET Core