<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[aqbtech]]></title><description><![CDATA[Technical blog of aqbtech]]></description><link>https://blog.aqbtech.app</link><image><url>https://cdn.hashnode.com/uploads/logos/69cd48d83085402b9c345f3d/cf9a87f5-000a-4f81-9a3e-193bf20e5a95.jpg</url><title>aqbtech</title><link>https://blog.aqbtech.app</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 04 Jun 2026 13:09:47 GMT</lastBuildDate><atom:link href="https://blog.aqbtech.app/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Google remote procedure call - Giao thức giao tiếp client-server]]></title><description><![CDATA[I. Code generation từ .proto file (Java gRPC plugin)
Trong gRPC, file .proto đóng vai trò như hợp đồng (contract) giữa client và server.
1. proto là gì?
Đây là nơi định nghĩa:

Message (cấu trúc dữ li]]></description><link>https://blog.aqbtech.app/google-remote-procedure-call-giao-th-c-giao-ti-p-client-server</link><guid isPermaLink="true">https://blog.aqbtech.app/google-remote-procedure-call-giao-th-c-giao-ti-p-client-server</guid><dc:creator><![CDATA[Anh Quân]]></dc:creator><pubDate>Sat, 04 Apr 2026 16:23:44 GMT</pubDate><content:encoded><![CDATA[<h2>I. Code generation từ .proto file (Java gRPC plugin)</h2>
<p>Trong gRPC, file <code>.proto</code> đóng vai trò như <strong>hợp đồng (contract)</strong> giữa client và server.</p>
<h3>1. <code>proto</code> là gì?</h3>
<p>Đây là nơi định nghĩa:</p>
<ul>
<li><strong>Message</strong> (cấu trúc dữ liệu request/response)</li>
<li><strong>Service</strong> (các API)</li>
</ul>
<p>Ví dụ:</p>
<pre><code class="language-protobuf">service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}
</code></pre>
<h3>2. Code generation hoạt động thế nào?</h3>
<p>Thay vì viết code thủ công, gRPC sử dụng <code>protoc</code> để generate:</p>
<ul>
<li><strong>Model class</strong> (POJO từ message)</li>
<li><strong>Stub</strong> (client gọi server)</li>
<li><strong>Base server class</strong> (để implement logic)</li>
</ul>
<p>Trong java ta dùng các thư viện để gen code từ proto</p>
<ul>
<li><code>protobuf-maven-plugin</code>  – để chạy <code>protoc</code></li>
<li><code>protoc-gen-grpc-java</code> – để generate gRPC stub</li>
</ul>
<p>Còn trong spring thì ta dùng thêm <code>net.devh:grpc-spring-boot-starter</code> để tích hợp gRPC với Spring Boot, giúp:</p>
<ul>
<li>Auto config server/client</li>
<li>Inject stub như Spring Bean</li>
<li>Dễ dàng dùng interceptor, security</li>
</ul>
<p>Cụ thể ta có thể cấu hình plugin maven như sau:</p>
<pre><code class="language-xml">&lt;properties&gt;
    &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;grpc.version&gt;1.61.0&lt;/grpc.version&gt;
    &lt;protobuf.version&gt;3.25.3&lt;/protobuf.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;!-- gRPC Spring Boot Starter --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;net.devh&lt;/groupId&gt;
        &lt;artifactId&gt;grpc-spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;2.15.0.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;!-- Protobuf --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.google.protobuf&lt;/groupId&gt;
        &lt;artifactId&gt;protobuf-java&lt;/artifactId&gt;
        &lt;version&gt;${protobuf.version}&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;!-- gRPC --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.grpc&lt;/groupId&gt;
        &lt;artifactId&gt;grpc-stub&lt;/artifactId&gt;
        &lt;version&gt;${grpc.version}&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;io.grpc&lt;/groupId&gt;
        &lt;artifactId&gt;grpc-protobuf&lt;/artifactId&gt;
        &lt;version&gt;${grpc.version}&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;build&gt;
    &lt;extensions&gt;
        &lt;!-- Detect OS để tải đúng binary protoc --&gt;
        &lt;extension&gt;
            &lt;groupId&gt;kr.motd.maven&lt;/groupId&gt;
            &lt;artifactId&gt;os-maven-plugin&lt;/artifactId&gt;
            &lt;version&gt;1.7.1&lt;/version&gt;
        &lt;/extension&gt;
    &lt;/extensions&gt;

    &lt;plugins&gt;
        &lt;!-- Plugin generate code từ proto --&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.xolstice.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;protobuf-maven-plugin&lt;/artifactId&gt;
            &lt;version&gt;0.6.1&lt;/version&gt;

            &lt;configuration&gt;
                &lt;protocArtifact&gt;
                    com.google.protobuf:protoc:\({protobuf.version}:exe:\){os.detected.classifier}
                &lt;/protocArtifact&gt;

                &lt;pluginId&gt;grpc-java&lt;/pluginId&gt;

                &lt;pluginArtifact&gt;
                    io.grpc:protoc-gen-grpc-java:\({grpc.version}:exe:\){os.detected.classifier}
                &lt;/pluginArtifact&gt;
            &lt;/configuration&gt;

            &lt;executions&gt;
                &lt;execution&gt;
                    &lt;goals&gt;
                        &lt;goal&gt;compile&lt;/goal&gt;
                        &lt;goal&gt;compile-custom&lt;/goal&gt;
                    &lt;/goals&gt;
                &lt;/execution&gt;
            &lt;/executions&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;
</code></pre>
<p>Ví dụ: với proto sau:</p>
<pre><code class="language-protobuf">syntax = "proto3";

package auth;

option java_multiple_files = true;
option java_package = "com.aqbtech.common.proto.auth";

service AuthService {
  rpc Login (LoginRequest) returns (LoginResponse);
}

message LoginRequest {
  string username = 1;
  string password = 2;
}

message LoginResponse {
  string access_token = 1;
  string refresh_token = 2;
}
</code></pre>
<p>Sau khi chạy <code>protoc</code>, ta sẽ có:</p>
<ul>
<li><code>LoginRequest</code>, <code>LoginResponse</code> class đại diện cho dữ liệu (immutable, builder pattern)</li>
<li><code>AuthServiceGrpc</code>chứa:<ul>
<li><code>AuthServiceImplBase</code> (server implement)</li>
<li><code>AuthServiceBlockingStub</code></li>
<li><code>AuthServiceStub</code></li>
<li><code>AuthServiceFutureStub</code></li>
</ul>
</li>
</ul>
<p>Ta có 1 service implement như sau:</p>
<pre><code class="language-java">public class AuthServiceImpl extends AuthServiceGrpc.AuthServiceImplBase {

    @Override
    public void login(LoginRequest request,
                      StreamObserver&lt;LoginResponse&gt; responseObserver) {

        LoginResponse response = LoginResponse.newBuilder()
                .setAccessToken("token")
                .setRefreshToken("refresh")
                .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}
</code></pre>
<p>Trong đó <code>StreamObserver</code> là một interface trong gRPC Java, dùng để <strong>nhận hoặc gửi dữ liệu theo dạng stream</strong> giữa client và server.</p>
<p>Interface này có 3 phương thức chính:</p>
<pre><code class="language-java">publicinterfaceStreamObserver&lt;V&gt; {
void onNext(V value);
void onError(Throwable t);
void onCompleted();
}
</code></pre>
<ul>
<li><code>onNext(V value)</code>Gửi hoặc nhận <strong>một message</strong></li>
<li><code>onError(Throwable t)</code>Báo lỗi và kết thúc stream</li>
<li><code>onCompleted()</code>Kết thúc stream thành công</li>
</ul>
<h3>3. Các loại stub trong Java</h3>
<ul>
<li><strong>Blocking stub</strong>: gọi đồng bộ (dễ dùng, nhưng block thread)</li>
<li><strong>Async stub</strong>: bất đồng bộ (non-blocking, dùng callback)</li>
<li><strong>Future stub</strong>: trả về <code>ListenableFuture</code></li>
</ul>
<h4>3.1. Blocking sub</h4>
<p>Blocking stub là stub gọi <strong>đồng bộ (synchronous)</strong>, nghĩa là:</p>
<ul>
<li>Thread sẽ bị block cho đến khi nhận được response</li>
<li>Code viết đơn giản, giống gọi function bình thường</li>
</ul>
<p>Ví dụ:</p>
<pre><code class="language-java">LoginResponse response = blockingStub.login(request);
</code></pre>
<p>Đặc điểm: Không phù hợp với: High throughput system, UI thread</p>
<h4>3.2. Async stub</h4>
<p>Async stub là stub gọi <strong>bất đồng bộ (non-blocking)</strong>:</p>
<ul>
<li>Không block thread</li>
<li>Nhận kết quả thông qua callback (<code>StreamObserver</code>)</li>
</ul>
<p>Callback là một cơ chế trong lập trình bất đồng bộ, trong đó:</p>
<ul>
<li>Một <strong>hàm (function/method)</strong> được truyền vào như một tham số</li>
<li>Hàm này sẽ được <strong>gọi lại (call back)</strong> khi một sự kiện xảy ra (ví dụ: nhận được response, xảy ra lỗi, hoàn tất xử lý)</li>
</ul>
<p>Trong ngữ cảnh gRPC Async stub, khi gọi async:</p>
<pre><code class="language-java">asyncStub.login(request, new StreamObserver&lt;LoginResponse&gt;() {
    @Override
    public void onNext(LoginResponse value) {
        // xử lý khi nhận được response
    }

    @Override
    public void onError(Throwable t) {
        // xử lý khi có lỗi
    }

    @Override
    public void onCompleted() {
        // xử lý khi kết thúc
    }
});
</code></pre>
<p>Thì <code>StreamObserver</code> chính là <strong>callback object</strong>.</p>
<p>Cách hoạt động</p>
<ol>
<li>Client gửi request</li>
<li>Không chờ response (non-blocking)</li>
<li>Khi server trả dữ liệu gRPC runtime sẽ gọi <code>onNext()</code></li>
<li>Nếu có lỗi thì gọi <code>onError()</code></li>
<li>Khi hoàn tất thì gọi <code>onCompleted()</code></li>
</ol>
<p>Đặc điểm của callback</p>
<ul>
<li>Không chạy ngay lập tức</li>
<li>Được thực thi khi có kết quả trả về</li>
<li>Thường được dùng trong:<ul>
<li>I/O (network, file)</li>
<li>API bất đồng bộ</li>
<li>Event-driven system</li>
</ul>
</li>
</ul>
<h4>3.3. Future stub</h4>
<p>Future stub là dạng trung gian giữa blocking và async:</p>
<ul>
<li>Trả về <code>ListenableFuture</code></li>
<li>Có thể:<ul>
<li>block (<code>get()</code>)</li>
<li>hoặc xử lý async</li>
</ul>
</li>
</ul>
<p>Ví dụ:</p>
<pre><code class="language-java">ListenableFuture&lt;LoginResponse&gt; future = futureStub.login(request);

LoginResponse response = future.get();
</code></pre>
<p>Đặc điểm:</p>
<ul>
<li>Linh hoạt hơn blocking</li>
<li>Dễ integrate với framework async (CompletableFuture, Guava)</li>
<li>Phù hợp khi cần control flow rõ ràng hơn async callback</li>
</ul>
<h3>4. Lợi ích của gRPC</h3>
<ul>
<li>Giảm lỗi do không đồng bộ contract</li>
<li>Tăng tốc độ phát triển</li>
<li>Dễ maintain khi hệ thống lớn</li>
</ul>
<h2>II. Cách cách gửi dữ liệu trong gRPC</h2>
<p>Trong gRPC, cách client gửi dữ liệu phụ thuộc vào <strong>kiểu RPC được định nghĩa trong <code>.proto</code></strong>. Có các kiểu gửi (RPC types) trong gRPC, định nghĩa <strong>4 kiểu giao tiếp chính</strong> giữa client và server:</p>
<h3>2.1. Unary RPC</h3>
<ul>
<li><strong>Đặc điểm</strong>: 1 request → 1 response</li>
<li><strong>Luồng</strong>: Client gửi 1 message, server xử lý và trả về 1 message</li>
</ul>
<p><strong>Proto syntax:</strong></p>
<pre><code class="language-protobuf">rpc GetUser (UserRequest) returns (UserResponse);
</code></pre>
<h3>2.2. Streaming RPC</h3>
<h4>2.2.1 Server Streaming RPC</h4>
<ul>
<li><strong>Đặc điểm</strong>: 1 request → nhiều response (stream từ server)</li>
<li><strong>Luồng</strong>: Client gửi 1 message, server trả về nhiều message theo stream</li>
</ul>
<p><strong>Proto syntax:</strong></p>
<pre><code class="language-protobuf">rpc GetOrders (OrderRequest) returns (stream OrderResponse);
</code></pre>
<h4>2.2.2. Client Streaming RPC</h4>
<ul>
<li><strong>Đặc điểm</strong>: nhiều request → 1 response</li>
<li><strong>Luồng</strong>: Client gửi nhiều message (stream), server trả về 1 message</li>
</ul>
<p><strong>Proto syntax:</strong></p>
<pre><code class="language-protobuf">rpc UploadLogs (stream LogRequest) returns (LogResponse);
</code></pre>
<h4>2.2.3. Bidirectional Streaming RPC</h4>
<ul>
<li><strong>Đặc điểm</strong>: nhiều request ↔ nhiều response (2 chiều, độc lập)</li>
<li><strong>Luồng</strong>: Client và server đều gửi stream song song</li>
</ul>
<p><strong>Proto syntax:</strong></p>
<pre><code class="language-protobuf">rpc Chat (stream ChatMessage) returns (stream ChatMessage);
</code></pre>
<h2>III. gRPC Error Handling (Status codes)</h2>
<p>Trong gRPC, việc xử lý lỗi không sử dụng HTTP status như REST, mà dựa trên một hệ thống <strong>status code chuẩn hoá</strong> được định nghĩa sẵn. Điều này giúp đảm bảo tính nhất quán giữa các service, bất kể ngôn ngữ hay nền tảng.</p>
<h3>3.1. Status Code là gì?</h3>
<p><strong>Status code</strong> trong gRPC là mã trạng thái được server trả về để mô tả kết quả của một RPC call.</p>
<p>Mỗi response trong gRPC đều đi kèm:</p>
<ul>
<li><strong>Status code</strong> (bắt buộc)</li>
<li><strong>Message mô tả lỗi</strong> (tuỳ chọn)</li>
<li><strong>Metadata bổ sung</strong> (tuỳ chọn)</li>
</ul>
<p>Cấu trúc logic:</p>
<pre><code class="language-protobuf">status = {
  code: StatusCode,
  description: String,
  metadata: Metadata (optional)
}
</code></pre>
<h3>3.2. Các nhóm Status Code phổ biến</h3>
<p>gRPC định nghĩa một tập hợp status code chuẩn (theo <code>io.grpc.Status</code> trong Java). Các code quan trọng:</p>
<h4>Nhóm thành công</h4>
<ul>
<li><p><strong>OK (0)</strong></p>
<p>  Request xử lý thành công.</p>
</li>
</ul>
<h4>Nhóm lỗi phía client (4xx tương đương)</h4>
<ul>
<li><p><strong>INVALID_ARGUMENT</strong></p>
<p>  Dữ liệu đầu vào không hợp lệ (validation fail)</p>
</li>
<li><p><strong>NOT_FOUND</strong></p>
<p>  Không tìm thấy resource</p>
</li>
<li><p><strong>ALREADY_EXISTS</strong></p>
<p>  Resource đã tồn tại</p>
</li>
<li><p><strong>FAILED_PRECONDITION</strong></p>
<p>  Trạng thái hệ thống không phù hợp để thực hiện request</p>
</li>
<li><p><strong>OUT_OF_RANGE</strong></p>
<p>  Giá trị nằm ngoài phạm vi cho phép</p>
</li>
<li><p><strong>UNAUTHENTICATED</strong></p>
<p>  Chưa xác thực (thiếu/invalid token)</p>
</li>
<li><p><strong>PERMISSION_DENIED</strong></p>
<p>  Không có quyền truy cập</p>
</li>
</ul>
<h4>Nhóm lỗi phía server (5xx tương đương)</h4>
<ul>
<li><p><strong>INTERNAL</strong></p>
<p>  Lỗi nội bộ server</p>
</li>
<li><p><strong>UNAVAILABLE</strong></p>
<p>  Service không sẵn sàng (down, timeout, network)</p>
</li>
<li><p><strong>DEADLINE_EXCEEDED</strong></p>
<p>  Request bị timeout</p>
</li>
<li><p><strong>RESOURCE_EXHAUSTED</strong></p>
<p>  Hết tài nguyên (rate limit, memory, quota)</p>
</li>
<li><p><strong>ABORTED</strong></p>
<p>  Operation bị huỷ do conflict</p>
</li>
</ul>
<h4>Nhóm đặc biệt</h4>
<ul>
<li><p><strong>CANCELLED</strong></p>
<p>  Client chủ động huỷ request</p>
</li>
<li><p><strong>UNKNOWN</strong></p>
<p>  Lỗi không xác định</p>
</li>
<li><p><strong>UNIMPLEMENTED</strong></p>
<p>  Method chưa được implement</p>
</li>
</ul>
<h3>3.3. Cách gRPC trả lỗi</h3>
<p>Trong gRPC, lỗi <strong>không trả qua response message</strong>, mà được đẩy qua <strong>channel error</strong>.</p>
<p>Ví dụ (Server - Java)</p>
<pre><code class="language-java">public void getUser(UserRequest request, StreamObserver&lt;UserResponse&gt; responseObserver) {
    if (request.getUserId().isEmpty()) {
        responseObserver.onError(
            Status.INVALID_ARGUMENT
                .withDescription("userId must not be empty")
                .asRuntimeException()
        );
        return;
    }

    User user = findUser(request.getUserId());

    if (user == null) {
        responseObserver.onError(
            Status.NOT_FOUND
                .withDescription("User not found")
                .asRuntimeException()
        );
        return;
    }

    responseObserver.onNext(toResponse(user));
    responseObserver.onCompleted();
}
</code></pre>
<h3>3.4. Phía Client nhận lỗi như thế nào?</h3>
<p>Client sẽ nhận lỗi thông qua exception:</p>
<pre><code class="language-java">try {
    UserResponse res = blockingStub.getUser(request);
} catch (StatusRuntimeException e) {
    Status.Code code = e.getStatus().getCode();

    if (code == Status.Code.NOT_FOUND) {
        // handle not found
    } else if (code == Status.Code.INVALID_ARGUMENT) {
        // handle validation error
    }
}
</code></pre>
<h2>IV. gRPC Interceptors (Client &amp; Server side)</h2>
<h3>4.1. gRPC Interceptor là gì</h3>
<p><strong>gRPC Interceptor</strong> là cơ chế cho phép chèn logic vào trước hoặc sau khi một RPC được thực thi ở cả phía client và server.</p>
<p>Đặc điểm:</p>
<ul>
<li>Hoạt động như một lớp trung gian trong pipeline xử lý request/response</li>
<li>Không thay đổi business logic chính</li>
<li>Dùng để xử lý các cross-cutting concerns:<ul>
<li>Authentication / Authorization</li>
<li>Logging</li>
<li>Tracing</li>
<li>Metrics</li>
<li>Retry / Timeout</li>
</ul>
</li>
</ul>
<h3>4.2. Client-side Interceptor</h3>
<p>Chạy ở phía client trước khi request được gửi đi.</p>
<h4>Mục đích</h4>
<ul>
<li>Thêm metadata (authorization, user-id)</li>
<li>Logging request</li>
<li>Retry / cấu hình timeout</li>
</ul>
<h4>Luồng xử lý</h4>
<pre><code>Client → ClientInterceptor → gRPC call → Server
</code></pre>
<h4>Ví dụ (Java)</h4>
<pre><code class="language-java">public class AuthClientInterceptor implements ClientInterceptor {

    @Override
    public &lt;ReqT, RespT&gt; ClientCall&lt;ReqT, RespT&gt; interceptCall(
            MethodDescriptor&lt;ReqT, RespT&gt; method,
            CallOptions callOptions,
            Channel next) {

        return new ForwardingClientCall.SimpleForwardingClientCall&lt;&gt;(
                next.newCall(method, callOptions)) {

            @Override
            public void start(Listener&lt;RespT&gt; responseListener, Metadata headers) {
                headers.put(
                    Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER),
                    "Bearer token-value"
                );
                super.start(responseListener, headers);
            }
        };
    }
}
</code></pre>
<h3>4.3. Server-side Interceptor</h3>
<p>Chạy ở phía server trước khi request được xử lý bởi service.</p>
<h4>Mục đích</h4>
<ul>
<li>Xác thực (authentication)</li>
<li>Kiểm tra quyền (authorization)</li>
<li>Logging</li>
<li>Extract metadata</li>
</ul>
<h4>Luồng xử lý</h4>
<pre><code>Client → ServerInterceptor → Service Handler
</code></pre>
<h4>Ví dụ (Java)</h4>
<pre><code class="language-java">public class AuthServerInterceptor implements ServerInterceptor {

    @Override
    public &lt;ReqT, RespT&gt; ServerCall.Listener&lt;ReqT&gt; interceptCall(
            ServerCall&lt;ReqT, RespT&gt; call,
            Metadata headers,
            ServerCallHandler&lt;ReqT, RespT&gt; next) {

        String token = headers.get(
            Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)
        );

        if (token == null) {
            call.close(Status.UNAUTHENTICATED, headers);
            return new ServerCall.Listener&lt;ReqT&gt;() {};
        }

        return next.startCall(call, headers);
    }
}
</code></pre>
<h2>V. gRPC Metadata (Header handling)</h2>
<h3>5.1. gRPC Metadata là gì</h3>
<p><strong>gRPC Metadata</strong> là cơ chế truyền <strong>dữ liệu dạng key–value</strong> kèm theo request/response, tương tự như <strong>HTTP headers</strong>.</p>
<p>Đặc điểm:</p>
<ul>
<li>Không nằm trong message protobuf (không thuộc payload chính)</li>
<li>Được gửi kèm trong quá trình gọi RPC</li>
<li>Dùng để truyền thông tin bổ sung</li>
</ul>
<p>Các loại dữ liệu thường dùng:</p>
<ul>
<li><code>authorization</code> (JWT, token)</li>
<li><code>x-user-id</code></li>
<li><code>trace-id</code></li>
<li><code>request-id</code></li>
</ul>
<h3>5.2. Vai trò của Metadata</h3>
<p>Metadata dùng để xử lý các thông tin ngoài business data:</p>
<ul>
<li><strong>Authentication / Authorization</strong><ul>
<li>Truyền token từ client → server</li>
</ul>
</li>
<li><strong>Tracing / Logging</strong><ul>
<li>Truyền <code>trace-id</code> xuyên suốt hệ thống</li>
</ul>
</li>
<li><strong>Context propagation</strong><ul>
<li>Truyền thông tin user, locale, timezone</li>
</ul>
</li>
<li><strong>Custom headers</strong><ul>
<li>Các thông tin nội bộ giữa các service</li>
</ul>
</li>
</ul>
<h3>5.3. Phân loại Metadata</h3>
<p>Trong gRPC, metadata được chia thành:</p>
<h4>(1) Request Metadata (Headers)</h4>
<ul>
<li>Gửi từ client → server</li>
<li>Được xử lý trước khi service chạy</li>
</ul>
<h4>(2) Response Metadata (Headers &amp; Trailers)</h4>
<ul>
<li>Server → client</li>
<li>Bao gồm:<ul>
<li>Headers (gửi sớm)</li>
<li>Trailers (gửi khi kết thúc response)</li>
</ul>
</li>
</ul>
<h3>5.4. Cách hoạt động trong luồng gRPC</h3>
<pre><code>Client → Metadata (headers) → Server
Server → Metadata (headers/trailers) → Client
</code></pre>
<ul>
<li>Client attach metadata vào request</li>
<li>Server đọc metadata trong interceptor hoặc handler</li>
<li>Server có thể trả lại metadata cho client</li>
</ul>
<h3>5.5. Cách sử dụng Metadata (Java)</h3>
<h4>5.5.1. Tạo Metadata Key</h4>
<pre><code class="language-java">Metadata.Key&lt;String&gt; AUTHORIZATION =
    Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
</code></pre>
<ul>
<li>Key phải khai báo với kiểu dữ liệu</li>
<li>Thường dùng:<ul>
<li><code>ASCII_STRING_MARSHALLER</code></li>
<li><code>BINARY_BYTE_MARSHALLER</code> (cho dữ liệu nhị phân)</li>
</ul>
</li>
</ul>
<h4>5.5.2. Gửi Metadata từ Client</h4>
<pre><code class="language-java">Metadata metadata = new Metadata();
metadata.put(AUTHORIZATION, "Bearer token-value");

stub = MetadataUtils.attachHeaders(stub, metadata);
</code></pre>
<h4>5.5.3. Nhận Metadata ở Server (Interceptor)</h4>
<pre><code class="language-java">String token = headers.get(AUTHORIZATION);
</code></pre>
<ul>
<li>Thường đọc trong <strong>ServerInterceptor</strong></li>
<li>Sau đó:<ul>
<li>Validate token</li>
<li>Gắn vào context</li>
</ul>
</li>
</ul>
<h4>5.5.4. Gửi Metadata từ Server về Client</h4>
<pre><code class="language-java">Metadata responseHeaders = new Metadata();
responseHeaders.put(
    Metadata.Key.of("custom-header", Metadata.ASCII_STRING_MARSHALLER),
    "value"
);

call.sendHeaders(responseHeaders);
</code></pre>
<h3>5.6. Quy tắc và lưu ý</h3>
<ul>
<li>Key phải viết thường (lowercase)</li>
<li>Key kết thúc bằng <code>bin</code> nếu là binary</li>
<li>Metadata không dùng để thay thế payload chính, vì</li>
<li>Kích thước metadata có giới hạn (tùy implementation, thường vài KB)</li>
</ul>
<h3>5.7. Kết hợp với Interceptor</h3>
<p>Metadata thường được xử lý thông qua interceptor:</p>
<h4>Client Interceptor có nhiệm vụ gắn metadata vào request</h4>
<pre><code>Client → Interceptor → attach metadata → gửi request
</code></pre>
<h4>Server Interceptor được dùng để đọc và xử lý metadata</h4>
<pre><code>Nhận request → Interceptor → đọc metadata → validate → handler
</code></pre>
<h2>VI. Syntax cơ bản của gRPC (Protobuf IDL)</h2>
<p>Một file <code>.proto</code> thường gồm các thành phần chính:</p>
<ul>
<li>Syntax version</li>
</ul>
<pre><code class="language-protobuf">syntax = "proto3";
</code></pre>
<ul>
<li>Package (namespace)</li>
</ul>
<pre><code class="language-protobuf">package user;
</code></pre>
<ul>
<li>Option (tuỳ chọn cho code generation)</li>
</ul>
<p>Ví dụ Java:</p>
<pre><code class="language-protobuf">option java_package = "com.example.user";
option java_multiple_files = true;
</code></pre>
<ul>
<li>Message (định nghĩa data structure)</li>
</ul>
<pre><code class="language-protobuf">message UserRequest {
  string user_id = 1;
}

message UserResponse {
  string name = 1;
  int32 age = 2;
}
</code></pre>
<ul>
<li>Mỗi field có:<ul>
<li>kiểu dữ liệu (<code>string</code>, <code>int32</code>, …)</li>
<li><strong>field number</strong> (1, 2, 3, …) → dùng cho serialization</li>
</ul>
</li>
<li>Service (định nghĩa API)</li>
</ul>
<pre><code class="language-protobuf">service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}
</code></pre>
<ul>
<li><p>Các kiểu dữ liệu thường dùng</p>
<ul>
<li><p>Scalar types:</p>
<ul>
<li><code>string</code>, <code>int32</code>, <code>int64</code>, <code>bool</code>, <code>bytes</code>, …</li>
</ul>
</li>
<li><p>Repeated (array):</p>
<pre><code class="language-protobuf">repeated string tags = 3;
</code></pre>
</li>
<li><p>Map:</p>
<pre><code class="language-protobuf">map&lt;string, string&gt; metadata = 4;
</code></pre>
</li>
</ul>
</li>
<li><p>Enum</p>
</li>
</ul>
<pre><code class="language-protobuf">enum Status {
  UNKNOWN = 0;
  ACTIVE = 1;
  INACTIVE = 2;
}
</code></pre>
<ul>
<li>Nested message (lồng nhau)</li>
</ul>
<pre><code class="language-protobuf">message Order {
  string id = 1;

  message Item {
    string name = 1;
    int32 quantity = 2;
  }

  repeated Item items = 2;
}
</code></pre>
<ul>
<li>Tổng kết cấu trúc một file <code>.proto</code></li>
</ul>
<pre><code class="language-protobuf">syntax = "proto3";

package example;

option java_package = "com.example";

message Request {
  string id = 1;
}

message Response {
  string result = 1;
}

service ExampleService {
  rpc ExampleMethod (Request) returns (Response);
}
</code></pre>
]]></content:encoded></item></channel></rss>