Go API baru untuk Protocol Buffer

Menjelang dimulainya kursus "Pengembang Golang", kami telah menyiapkan terjemahan artikel dari blog resmi Golang untuk Anda.








pengantar



Kami sangat senang mengumumkan rilis revisi utama Go API untuk buffer protokol , format pertukaran data Google yang tidak bergantung pada bahasa.



Prasyarat untuk memperbarui API



Binding penyangga protokol pertama untuk Go diperkenalkan oleh Rob Pike pada Maret 2010. Go 1 tidak akan dirilis selama dua tahun lagi.



Dalam sepuluh tahun sejak rilis pertama, paket tersebut telah tumbuh dan berkembang bersama Go. Permintaan penggunanya juga meningkat.



Banyak orang ingin menulis program menggunakan refleksi untuk bekerja dengan pesan penyangga protokol. Paket ini reflectmemungkinkan Anda untuk melihat tipe dan nilai Go, tetapi mengabaikan informasi sistem tipe buffer protokol. Misalnya, kita mungkin perlu menulis fungsi yang memindai seluruh log dan membersihkan bidang apa pun yang dianotasi sebagai berisi data sensitif. Anotasi bukan bagian dari sistem tipe Go.



Kebutuhan umum lainnya adalah menggunakan struktur data selain yang dihasilkan oleh penyusun buffer protokol, seperti, misalnya, jenis pesan dinamis yang mampu merepresentasikan jenis pesan yang tidak diketahui pada waktu kompilasi.



Kami juga memperhatikan bahwa sumber masalah yang umum adalah antarmukaproto.Message, yang mengidentifikasi nilai-nilai jenis pesan yang dihasilkan, berhemat dalam mendeskripsikan perilaku jenis-jenis ini. Ketika pengguna membuat tipe yang mengimplementasikan antarmuka ini (seringkali secara tidak sengaja dengan menyematkan pesan di struktur lain) dan meneruskan nilai tipe tersebut ke fungsi yang mengharapkan nilai pesan yang dihasilkan, program crash atau berperilaku tidak terduga.



Ketiga masalah memiliki akar yang sama dan satu solusi: antarmuka Messageharus sepenuhnya menentukan perilaku pesan, dan fungsi yang beroperasi pada nilai Messageharus dengan bebas menerima jenis apa pun yang mengimplementasikan antarmuka dengan benar.



Karena tidak mungkin mengubah definisi yang sudah ada dari jenis Pesan sambil mempertahankan kompatibilitas API paket, kami memutuskan bahwa inilah saatnya untuk mulai mengerjakan revisi besar baru yang tidak kompatibel dari modul protobuf.



Hari ini kami dengan senang hati merilis modul baru ini. Kami harap Anda menikmatinya.



Refleksi



Refleksi adalah fitur unggulan dari implementasi baru. Sama seperti paket yang reflectmenyediakan tampilan jenis dan nilai Go, paket google.golang.org/protobuf/reflect/protoreflect menyediakan tampilan nilai sesuai dengan sistem jenis buffer protokol.



Penjelasan lengkap paket protoreflectakan memakan waktu terlalu lama untuk posting ini, namun demikian, mari kita lihat bagaimana kita dapat menulis fungsi pembersihan log yang kita sebutkan sebelumnya.



Pertama kita harus menulis file yang .protomendefinisikan ekstensi seperti google.protobuf.FieldOptions sehingga kita dapat menganotasi kolom sebagai berisi informasi sensitif atau tidak.



syntax = "proto3";
import "google/protobuf/descriptor.proto";
package golang.example.policy;
extend google.protobuf.FieldOptions {
    bool non_sensitive = 50000;
}


Kita dapat menggunakan opsi ini untuk menandai bidang tertentu sebagai tidak sensitif.



message MyMessage {
    string public_name = 1 [(golang.example.policy.non_sensitive) = true];
}


Kemudian kita perlu menulis fungsi Go yang mengambil nilai pesan arbitrer dan menghapus semua bidang sensitif.



// Redact      pb.
func Redact(pb proto.Message) {
   // ...
}


Fungsi ini menerima proto.Message - antarmuka yang diimplementasikan oleh semua jenis pesan yang dihasilkan. Tipe ini adalah alias untuk tipe yang ditentukan dalam paket protoreflect:



type ProtoMessage interface{
    ProtoReflect() Message
}


Untuk menghindari pengisian ruang nama pesan yang dihasilkan, antarmuka hanya berisi satu metode pengembalian protoreflect.Messageyang menyediakan akses ke konten pesan.



(Mengapa alias? Karena ia protoreflect.Messagememiliki metode terkait yang mengembalikan aslinya proto.Message, dan kita perlu menghindari siklus impor antara dua paket.)



Metode tersebut protoreflect.Message.Rangememanggil fungsi untuk setiap bidang yang terisi dalam pesan.



m := pb.ProtoReflect()
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
    // ...
    return true
})


Fungsi range dipanggil dengan protoreflect.FieldDescriptormendeskripsikan tipe dari field protokol buffer dan protoreflect. Nilai yang mengandung nilai field.



Metode protoreflect.FieldDescriptor.Optionsmengembalikan opsi bidang sebagai pesan google.protobuf.FieldOptions.



opts := fd.Options().(*descriptorpb.FieldOptions)


(Why type assertion? Karena paket yang dihasilkan descriptorpbbergantung pada protoreflect, paket protoreflect tidak dapat mengembalikan jenis opsi tertentu tanpa memanggil siklus impor.)



Kemudian kita dapat memeriksa opsi untuk melihat nilai variabel boolean ekstensi kita:



if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
    return true //   non-sensitive 
}


Perhatikan bahwa di sini kita melihat deskriptor bidang, bukan nilai bidang. Informasi yang kami minati adalah tentang sistem tipe buffer protokol, bukan Go.



Ini juga merupakan contoh area tempat kami telah menyederhanakan API protopaket. Dokumen asli proto.GetExtensionmengembalikan nilai dan kesalahan. Yang baru proto.GetExtensionhanya mengembalikan nilai, mengembalikan nilai default untuk bidang jika tidak ada. Kesalahan pendekodean ekstensi dilaporkan ke Unmarshal.



Setelah kami mengidentifikasi bidang yang perlu diedit, cukup mudah untuk menghapusnya:



m.Clear(fd)


Menggabungkan semua hal di atas, fungsi pengeditan kami terlihat seperti ini:



// Redact      pb.
func Redact(pb proto.Message) {
    m := pb.ProtoReflect()
    m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        opts := fd.Options().(*descriptorpb.FieldOptions)
        if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
            return true
        }
        m.Clear(fd)
        return true
    })
}


Versi yang lebih baik dapat secara rekursif turun ke bidang nilai pesan. Kami berharap contoh sederhana ini memberikan pengenalan tentang refleksi dalam buffer protokol dan penggunaannya.



Versi



Kami menyebut protokol versi asli buffer Go APIv1, dan versi APIv2 yang lebih baru. Karena APIv2 tidak kompatibel dengan APIv1, kita perlu menggunakan jalur modul yang berbeda untuk masing-masing.



(Versi API ini tidak sama dengan versi protokol buffer Bahasa: proto1,, proto2dan proto3. APIv1 dan APIv2 - implementasi khusus ini di Go, yang mendukung versi bahasa proto2dan proto3)



dalam modul github.com/golang/protobuf - APIv1.



Dalam modul google.golang.org/protobuf - APIv2. Kami memanfaatkan kebutuhan untuk mengubah jalur impor untuk beralih ke jalur yang tidak terkait dengan penyedia hosting tertentu. (Kami mempertimbangkangoogle.golang.org/protobuf/v2untuk memperjelas bahwa ini adalah versi utama kedua dari API, tetapi memilih jalur yang lebih pendek sebagai pilihan terbaik dalam jangka panjang.)



Kami memahami bahwa tidak semua pengguna akan bermigrasi ke versi utama baru dari paket dengan kecepatan yang sama. Beberapa akan beralih dengan cepat; yang lain mungkin tetap menggunakan versi lama tanpa batas waktu. Bahkan dalam program yang sama, beberapa bagian mungkin menggunakan satu API dan yang lainnya mungkin menggunakan yang lain. Oleh karena itu, penting bagi kami untuk terus mendukung program yang menggunakan APIv1.



  • github.com/golang/protobuf@v1.3.4 Apakah versi APIv1 terbaru sebelum APIv2.
  • github.com/golang/protobuf@v1.4.0Adalah versi APIv1 yang diimplementasikan berdasarkan APIv2. API-nya sama, tetapi implementasi dasarnya didukung oleh API baru. Versi ini berisi fungsi untuk mengubah antara proto.MessageAPIv1 dan APIv2 untuk memfasilitasi transisi di antara keduanya.
  • google.golang.org/protobuf@v1.20.0 — APIv2. github.com/golang/protobuf@v1.4.0, , APIv2, APIv1, .


(Mengapa kami memulai dengan sebuah versi v1.20.0? Untuk kejelasan. Kami tidak berharap APIv1 akan pernah tercapai v1.20.0, jadi satu nomor versi seharusnya cukup untuk membedakan secara unik antara APIv1 dan APIv2.)



Kami bermaksud untuk terus mendukung APIv1 tanpa menyetel tenggat waktu apa pun.



Pengaturan ini memastikan bahwa program apa pun hanya akan menggunakan satu implementasi buffer protokol, apa pun versi API yang digunakannya. Hal ini memungkinkan program untuk mengimplementasikan API baru secara bertahap atau tidak sama sekali, sambil mempertahankan manfaat dari implementasi baru. Prinsip memilih versi minimum berarti bahwa program dapat tetap dalam implementasi lama sampai pengelola memutuskan untuk memperbaruinya ke yang baru (secara langsung atau dengan memperbarui dependensi).



Fitur tambahan yang harus diperhatikan



Paket google.golang.org/protobuf/encoding/protojsonmengonversi pesan buffering protokol ke dan dari JSON menggunakan pemetaan JSON kanonik , dan juga memperbaiki sejumlah masalah dengan paket lama jsonpbyang sulit diubah tanpa menyebabkan masalah baru bagi pengguna yang sudah ada.



Paket ini google.golang.org/protobuf/types/dynamicpbmenyediakan implementasi proto.Messageuntuk pesan yang jenis buffer protokolnya ditentukan saat runtime.



Paket google.golang.org/protobuf/testing/protocmpmenyediakan fungsi untuk membandingkan buffer protokol dari sebuah pesan ke sebuah paket github.com/google/cmp.



Paket ini google.golang.org/protobuf/compiler/protogenmenyediakan dukungan untuk menulis plugin compiler buffer protokol.



Kesimpulan



Modul google.golang.org/protobufini merupakan perombakan besar-besaran dari dukungan Go untuk buffer protokol, yang menyediakan dukungan kelas satu untuk refleksi, pesan khusus, dan API yang dibersihkan. Kami bermaksud untuk terus mendukung API sebelumnya sebagai pembungkus untuk yang baru, memungkinkan pengguna untuk menerapkan API baru secara bertahap dengan kecepatan mereka sendiri.



Tujuan kami dengan pembaruan ini adalah untuk memperkuat manfaat dari API lama dan mengatasi kelemahannya. Saat kami menyelesaikan setiap komponen implementasi baru, kami mulai menggunakannya di basis kode Google. Penerapan bertahap ini memberi kami keyakinan pada kegunaan API baru dan kinerja yang lebih baik serta ketepatan penerapan baru. Kami yakin sudah siap untuk produksi.



Kami sangat senang dengan rilis ini dan berharap ini akan melayani ekosistem Go dengan baik untuk dekade berikutnya atau lebih!






Pelajari lebih lanjut tentang kursus tersebut.







All Articles