7 Eylül 2021 Salı

gRPC Protobuf Dosyası

Giriş
Protokolü anlamak için Protobuf yazısına bakabilirsiniz.

- Dosya uzantısı *.proto şeklindedir.
- İlk satır syntax'i belirtir
- İkinci satır package ile paket ismini belirtir
- Daha sonra option satırları ile seçenekler belirtilir
- Daha sonra message yapıları gelir. Mesaj yapıları iç içe (nested) olabilir veya birbirlerine kullanabilirler. Field olarak string, int64, int32, double, repeated kelimeleri kullanılabilir.
- Daha sonra service metodları gelir. gRPC Protobuf Dosyası - Service Tanımlama yazısına bakabilirsiniz.

1. Option Seçenekleri
Çok kullanılan option değerleri şöyle. Açıklaması şöyle
java_multiple_files — option allows the compiler to create different classes for all it’s components i.e. HelloRequest.java and HelloReply.java
java_package — option allows the compiler to create a new package and keep the gRPC classes in it
Örnek
Şöyle yaparız
syntax = "proto3"; import "google/protobuf/timestamp.proto"; package mobilepackage; option java_package = "com.ardab.gprc.mobilepackage"; option java_multiple_files = true;
2. Scalar Value Types
Boolean için şu kullanılır 
bool
String için şu kullanılır 
string
byte[] için şu kullanılır 
bytes
Floating point için şunlar kullanılır 
double, float 
Variable length encoding için şunlar kullanılır . sint32 negatif sayılarda int32'den daha iyidir.
int32, int64,
uint32, uint64,
sint32, sint64
Fixed length encoding için şunlar kullanılır. fixed32 C++'ta uint32sfixed32 C++'ta int32 anlamına gelir. Yani signed/unsigned farkı yaratır. Java'da her ikisi de int anlamına gelir
fixed32, fixed64
sfixed32, sfixed64

Örnek - Tek Mesaj ve Sadece Scalar Tipler (string vs)
Şöyle yaparız
syntax = "proto3";
package com.codingharbour.protobuf;

message SimpleMessage {
  string content = 1;
  string date_time = 2;
}
Açıklaması şöyle
In the first line, we define that we're using Protobuf version 3. Our message type called SimpleMessage defines two string fields: content and date_time. Each field is assigned a so-called field number, which has to be unique in a message type. These numbers identify the fields when the message is serialized to the Protobuf binary format. Google suggests using numbers 1 through 15 for most frequently used fields because it takes one byte to encode them.

Protobuf supports common scalar types like string, int32, int64 (long), double, bool etc. For the full list of all scalar types in Protobuf check the Protobuf documentation.
Örnek - Farklı Bir Mesaj İçeren Complex Type
Şöyle yaparız
message Order {
  int64 order_id = 1;
  int64 date_time = 2;
  repeated Product product = 3;
}

message Product {
  int32 product_id = 1;
  string name = 2;
  string description = 3;
}
3. Google Tipleri
google.protobuf.Timestamp
Örnek
Şöyle yaparız
syntax = "proto3"; import "google/protobuf/timestamp.proto"; ... message SubscriptionExceptionResponse { google.protobuf.Timestamp timestamp = 1; SubscriptionErrorCode error_code = 2; } ...
4. Kullanım Örnekleri

Örnek - java_multiple_files option
Şöyle yaparız
syntax = "proto3";
option java_multiple_files = true;
package com.milind.grpc;

message Order {
  string currency = 1;
  double totalAmount = 2;
  string orderId = 3;
  string emailAddress = 4;
  string cartURL =5;
  repeated LineItems lineItems = 6;
}

message LineItems {
  string sku = 1;
  string name = 2;
  string description = 3;
  string category = 4;
  string other = 5;
  double unitPrice = 6;
  double salePrice = 7;
  double quantity = 8;
  double totalPrice = 9;
}

message OrderConfirmation {
  string orderId = 1;
  repeated ConfirmedLineItems confirmedLineItems = 2;
}

message ConfirmedLineItems {
  string sku = 1;
  double confirmQuantity = 2;
}
Örnek - nested message
Şöyle yaparız
syntax = "proto3";
package demo.camel;
option java_package = "demo.camel";
option java_outer_classname = "TransactionProtos";

message Transaction {
  string transactionid = 1;
  string transactiontype = 2;
  User sender = 3;
  string currency = 4;
  double amt = 5;
  string receiverid = 6;
 
  message User {
    string username = 1;
    string userid = 2;
  }
}
5. FieldMask Tipi
Açıklaması şöyle
FieldMask is a protobuf message. There are a number of utilities and conventions on how to use this message when it is present in an RPC request. A FieldMask message contains a single field named paths, which is used to specify fields that should be returned by a read operation or modified by an update operation.
Mesaj şöyle
message FieldMask {
  // The set of field mask paths.
  repeated string paths = 1;
}
FieldMask mesajı alan ismini string olarak taşıyor. Dolayısıyla sunucu tarafında alan isimlerinde değişiklik yapılırsa sorun çıkabilir. Şeklen şöyle


Örnek
Elimizde şöyle bir servis olsun
// Contains Production-related information  
message Production {
  string id = 1;
  string title = 2;
  ProductionFormat format = 3;
  repeated ProductionScript scripts = 4;
  ProductionSchedule schedule = 5;
  // ... more fields
}

service ProductionService {
  // returns Production by ID
  rpc GetProduction (GetProductionRequest) returns (GetProductionResponse);
}

import "google/protobuf/field_mask.proto"; message GetProductionRequest { string production_id = 1; google.protobuf.FieldMask field_mask = 2; }

message GetProductionResponse {
  Production production = 1;
}
İstemci çağırmak için şöyle yapar
FieldMask fieldMask = FieldMask.newBuilder()
    .addPaths("title")
    .addPaths("format")
    .build();

GetProductionRequest request = GetProductionRequest.newBuilder()
    .setProductionId(LA_CASA_DE_PAPEL_PRODUCTION_ID)
    .setFieldMask(fieldMask)
    .build();
Eğer alan isimlerini string olarak kullanmak istemiyorsak istemci çağırmak için şöyle yapar
FieldMask fieldMask = FieldMaskUtil.fromFieldNumbers(Production.class,
Production.TITLE_FIELD_NUMBER, Production.FORMAT_FIELD_NUMBER); GetProductionRequest request = GetProductionRequest.newBuilder() .setProductionId(LA_CASA_DE_PAPEL_PRODUCTION_ID) .setFieldMask(fieldMask) .build();
Sunucu tarafında sadece istenilen alanları dönmek için şöyle yaparız
@Override
public void getProduction(GetProductionRequest request, 
                          StreamObserver<GetProductionResponse> response) {
   
    Production production = fetchProduction(request.getProductionId());
    FieldMask fieldMask = request.getFieldMask();

    Production.Builder productionWithMaskedFields = Production.newBuilder();
    FieldMaskUtil.merge(fieldMask, production, productionWithMaskedFields);
   
    GetProductionResponse response = GetProductionResponse.newBuilder()
        .setProduction(productionWithMaskedFields).build();
    responseObserver.onNext(response);
    responseObserver.onCompleted();
}
Eğer çağrıyı da belli boolean logic ile yapmak istersek şöyle yaparız
private static final String FIELD_SEPARATOR_REGEX = "\\.";
private static final String MAX_FIELD_NESTING = 2; private static final String SCHEDULE_FIELD_NAME = // (1) Production.getDescriptor() .findFieldByNumber(Production.SCHEDULE_FIELD_NUMBER).getName(); @Override public void getProduction(GetProductionRequest request, StreamObserver<GetProductionResponse> response) { FieldMask canonicalFieldMask = FieldMaskUtil.normalize(request.getFieldMask()); // (2) boolean scheduleFieldRequested = // (3) canonicalFieldMask.getPathsList().stream() .map(path -> path.split(FIELD_SEPARATOR_REGEX, MAX_FIELD_NESTING)[0]) .anyMatch(SCHEDULE_FIELD_NAME::equals); if (scheduleFieldRequested) { ProductionSchedule schedule = makeExpensiveCallToScheduleService(request.getProductionId()); // (4) ... } ... }
Açıklaması şöyle
(1) The SCHEDULE_FIELD_NAME constant contains the name of the field. This code sample uses message type Descriptor and FieldDescriptor to lookup field name by field number. The difference between protobuf field names and field numbers is described in the Protobuf Field Names vs Field Numbers section above.
(2) FieldMaskUtil.normalize() returns FieldMask with alphabetically sorted and deduplicated field paths (aka canonical form).
(3) Expression (lines ##14 - 17) that yields the scheduleFieldRequestedvalue takes a stream of FieldMask paths, maps it to a stream of top-level fields, and returns true if top-level fields contain the value of the SCHEDULE_FIELD_NAME constant.
(4) ProductionSchedule is retrieved only if scheduleFieldRequested is true.
6. Modifiers
deprecated
Örnek
Şöyle yaparız
message Production {
  string id = 1;
  string title = 2 [deprecated = true];  // use "title_name" field instead
  ProductionFormat format = 3;
  repeated ProductionScript scripts = 4;
  ProductionSchedule schedule = 5;
  string title_name = 6;
}

Hiç yorum yok:

Yorum Gönder