المقدمة
يسعدنا الإعلان عن إصدار مراجعة رئيسية لـ Go API للمخازن المؤقتة للبروتوكول ، وهو تنسيق تبادل البيانات المستقل عن اللغة من Google.
المتطلبات الأساسية لتحديث API
تم تقديم أول ارتباطات بروتوكول عازلة لـ Go بواسطة Rob Pike في مارس 2010. لن يتم إصدار Go 1 لمدة عامين آخرين.
في السنوات العشر منذ الإصدار الأول ، نمت الحزمة وتطورت جنبًا إلى جنب مع Go. كما نمت طلبات مستخدميها.
يرغب العديد من الأشخاص في كتابة برامج باستخدام الانعكاس للعمل مع رسائل المخزن المؤقت للبروتوكول.
reflectتتيح لك الحزمة عرض أنواع وقيم Go ، ولكنها تحذف معلومات نظام نوع المخزن المؤقت للبروتوكول. على سبيل المثال ، قد نحتاج إلى كتابة دالة تبحث في السجل بالكامل وتمسح أي حقل تم التعليق عليه على أنه يحتوي على بيانات حساسة. التعليقات التوضيحية ليست جزءًا من نظام نوع Go.
هناك حاجة شائعة أخرى وهي استخدام هياكل البيانات غير تلك التي تم إنشاؤها بواسطة برنامج التحويل البرمجي المؤقت للبروتوكول ، مثل ، على سبيل المثال ، نوع رسالة ديناميكي قادر على تمثيل رسائل من نوع غير معروف في وقت الترجمة.
لاحظنا أيضًا أن أحد المصادر الشائعة للمشاكل هو الواجهة
proto.Message، الذي يحدد قيم أنواع الرسائل التي تم إنشاؤها ، يبخل في وصف سلوك هذه الأنواع. عندما ينشئ المستخدمون أنواعًا تنفذ هذه الواجهة (غالبًا عن غير قصد عن طريق تضمين رسالة في بنية أخرى) ويمررون قيم هذه الأنواع إلى الوظائف التي تتوقع قيم الرسائل المُنشأة ، فإن البرامج تتعطل أو تتصرف بشكل غير متوقع.
جميع المشكلات الثلاثة لها نفس الجذر وحل واحد:
Messageيجب أن تحدد الواجهة تمامًا سلوك الرسالة ، Messageويجب أن تقبل الوظائف التي تعمل على القيم بحرية أي نوع ينفذ الواجهة بشكل صحيح.
نظرًا لأنه لا يمكن تغيير التعريف الحالي لنوع الرسالة مع الحفاظ على توافق واجهة برمجة التطبيقات للحزمة ، فقد قررنا أن الوقت قد حان لبدء العمل على مراجعة رئيسية جديدة غير متوافقة لوحدة protobuf.
اليوم يسعدنا إطلاق هذه الوحدة الجديدة. نأمل أن تستمتع به.
انعكاس
الانعكاس هو السمة الرئيسية للتطبيق الجديد. تمامًا مثل الحزمة التي
reflectتوفر عرضًا لأنواع وقيم Go ، توفر الحزمة google.golang.org/protobuf/reflect/protoreflect عرضًا للقيم وفقًا لنظام نوع المخزن المؤقت للبروتوكول. قد يستغرق
وصف الحزمة الكامل
protoreflectوقتًا طويلاً لهذا المنشور ، ولكن مع ذلك ، دعنا نرى كيف يمكننا كتابة وظيفة تنظيف السجل التي ذكرناها سابقًا.
أولاً ، يتعين علينا كتابة ملف
.protoيحدد امتدادًا مثل google.protobuf.FieldOptions حتى نتمكن من إضافة تعليق توضيحي للحقول على أنها تحتوي على معلومات حساسة أم لا.
syntax = "proto3";
import "google/protobuf/descriptor.proto";
package golang.example.policy;
extend google.protobuf.FieldOptions {
bool non_sensitive = 50000;
}
يمكننا استخدام هذا الخيار لتمييز بعض الحقول على أنها غير حساسة.
message MyMessage {
string public_name = 1 [(golang.example.policy.non_sensitive) = true];
}
بعد ذلك ، نحتاج إلى كتابة دالة Go تأخذ قيمة رسالة عشوائية وتزيل جميع الحقول الحساسة.
// Redact pb.
func Redact(pb proto.Message) {
// ...
}
تقبل هذه الوظيفة
proto.Message - واجهة يتم تنفيذها بواسطة جميع أنواع الرسائل التي تم إنشاؤها. هذا النوع هو اسم مستعار للنوع المحدد في الحزمة protoreflect:
type ProtoMessage interface{
ProtoReflect() Message
}
لتجنب ملء مساحة اسم الرسالة التي تم إنشاؤها ، تحتوي الواجهة على طريقة إرجاع واحدة فقط
protoreflect.Messageتوفر الوصول إلى محتوى الرسالة.
(لماذا الاسم المستعار؟ لأنه
protoreflect.Messageيحتوي على طريقة مقابلة تُرجع الأصل proto.Message، ونحن بحاجة إلى تجنب دورة الاستيراد بين الحزمتين.) تستدعي
الطريقة
protoreflect.Message.Rangeوظيفة لكل حقل مملوء في الرسالة.
m := pb.ProtoReflect()
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
// ...
return true
})
يتم استدعاء دالة النطاق مع
protoreflect.FieldDescriptorوصف نوع المخزن المؤقت لبروتوكول المجال والانعكاس الوقائي . القيمة التي تحتوي على قيمة الحقل.
تقوم الطريقة
protoreflect.FieldDescriptor.Optionsبإرجاع خيارات الحقول كرسالة google.protobuf.FieldOptions.
opts := fd.Options().(*descriptorpb.FieldOptions)
(لماذا نوع التأكيد؟ نظرًا لأن الحزمة التي تم إنشاؤها
descriptorpbتعتمد على protoreflect، لا يمكن لحزمة protoreflect إرجاع نوع معين من الخيارات دون استدعاء دورة الاستيراد.)
ثم يمكننا التحقق من الخيارات لمعرفة قيمة المتغير المنطقي الخاص بالامتداد:
if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
return true // non-sensitive
}
لاحظ أننا هنا نبحث في واصف الحقل ، وليس قيمة الحقل. المعلومات التي نهتم بها مأخوذة من نظام نوع المخزن المؤقت للبروتوكول ، وليس من Go.
هذا أيضًا مثال على منطقة قمنا فيها بتبسيط
protoحزمة API . proto.GetExtensionأرجع الأصل قيمة وخطأ معًا. يُرجع الجديد proto.GetExtensionالقيمة فقط ، ويعيد القيمة الافتراضية للحقل إذا كان مفقودًا. تم الإبلاغ عن أخطاء فك تشفير الامتداد إلى Unmarshal.
بمجرد تحديد الحقل الذي يحتاج إلى تعديل ، يكون من السهل مسحه:
m.Clear(fd)
بتجميع كل ما سبق معًا ، تبدو وظيفة التحرير لدينا كما يلي:
// 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
})
}
يمكن أن تنخفض النسخة الأفضل بشكل متكرر إلى حقول قيمة الرسالة. نأمل أن يوفر هذا المثال البسيط مقدمة للتفكير في المخزن المؤقت للبروتوكول واستخدامه.
إصدارات
نحن نطلق على النسخة الأصلية من البروتوكول المؤقت Go APIv1 والإصدار الأحدث APIv2. نظرًا لأن APIv2 لا يتوافق مع الإصدارات السابقة مع APIv1 ، فنحن بحاجة إلى استخدام مسارات وحدة مختلفة لكل منها.
(هذا الإصدار من API ليست هي نفس النسخة من بروتوكول عازلة اللغة:
proto1، proto2و proto3. APIv1 وAPIv2 - هذا التطبيق معين في العودة، والذي دعم كل من إصدار لغة proto2و proto3)
في وحدة github.com/golang/protobuf - APIv1.
في google.golang.org/protobuf module - APIv2. لقد استفدنا من الحاجة إلى تغيير مسار الاستيراد للتبديل إلى مسار غير مرتبط بموفر استضافة معين. (اعتبرنا
google.golang.org/protobuf/v2لتوضيح أن هذا هو الإصدار الرئيسي الثاني من واجهة برمجة التطبيقات ، ولكنه استقر على مسار أقصر باعتباره الخيار الأفضل على المدى الطويل.)
نحن نتفهم أنه لن ينتقل جميع المستخدمين إلى الإصدار الرئيسي الجديد من الحزمة بنفس السرعة. سوف يتحول البعض بسرعة ؛ قد يظل الآخرون في الإصدار القديم إلى أجل غير مسمى. حتى داخل نفس البرنامج ، قد تستخدم بعض الأجزاء واجهة برمجة تطبيقات واحدة وقد يستخدم البعض الآخر. لذلك ، من المهم أن نستمر في دعم البرامج التي تستخدم APIv1.
github.com/golang/protobuf@v1.3.4هو أحدث إصدار من APIv1 قبل APIv2.github.com/golang/protobuf@v1.4.0هي نسخة من APIv1 مطبقة على أساس APIv2. واجهة برمجة التطبيقات هي نفسها ، ولكن يتم دعم التنفيذ الأساسي بواسطة واجهة برمجة التطبيقات الجديدة. يحتوي هذا الإصدار على وظائف للتحويل بينproto.MessageAPIv1 و APIv2 لتسهيل الانتقال بينهما.google.golang.org/protobuf@v1.20.0— APIv2.github.com/golang/protobuf@v1.4.0, , APIv2, APIv1, .
(لماذا بدأنا بإصدار
v1.20.0؟ للتوضيح. لا نتوقع أن يصل APIv1 أبدًا v1.20.0، لذلك يجب أن يكون رقم إصدار واحد كافيًا للتمييز بشكل فريد بين APIv1 و APIv2.)
نعتزم الاستمرار في دعم APIv1 دون تحديد أي مواعيد نهائية.
يضمن هذا الترتيب أن أي برنامج سيستخدم فقط تنفيذ بروتوكول واحد مؤقتًا ، بغض النظر عن إصدار API الذي يستخدمه. يتيح ذلك للبرامج تنفيذ واجهة برمجة التطبيقات الجديدة تدريجيًا أو عدم تنفيذها على الإطلاق ، مع الحفاظ على فوائد التطبيق الجديد. مبدأ اختيار الإصدار الأدنى يعني أن البرامج يمكن أن تظل في التطبيق القديم حتى يقرر القائمون على الصيانة تحديثه إلى الإصدار الجديد (مباشرة أو عن طريق تحديث التبعيات).
ميزات إضافية للبحث عنها
تقوم الحزمة
google.golang.org/protobuf/encoding/protojsonبتحويل رسائل المخزن المؤقت للبروتوكول من وإلى JSON باستخدام تعيين JSON الأساسي ، كما تعمل أيضًا على إصلاح عدد من المشكلات مع الحزمة القديمة jsonpbالتي كان من الصعب تغييرها دون التسبب في مشاكل جديدة للمستخدمين الحاليين. توفر
الحزمة
google.golang.org/protobuf/types/dynamicpbتطبيقًا proto.Messageللرسائل التي يتم تحديد نوع المخزن المؤقت للبروتوكول الخاص بها في وقت التشغيل. توفر
الحزمة
google.golang.org/protobuf/testing/protocmpوظائف لمقارنة المخزن المؤقت للبروتوكول للرسالة بالحزمة github.com/google/cmp. توفر
هذه الحزمة
google.golang.org/protobuf/compiler/protogenدعمًا لكتابة ملحقات برنامج التحويل البرمجي المؤقت للبروتوكول.
خاتمة
الوحدة
google.golang.org/protobufعبارة عن إصلاح شامل لدعم Go للمخزن المؤقت للبروتوكول ، مما يوفر دعمًا من الدرجة الأولى للانعكاس ، والرسائل المخصصة ، وواجهة برمجة التطبيقات (API) التي تم تنظيفها. نعتزم الاستمرار في دعم واجهة برمجة التطبيقات السابقة باعتبارها غلافًا جديدًا ، مما يسمح للمستخدمين بتنفيذ واجهة برمجة التطبيقات الجديدة تدريجيًا وفقًا لسرعتهم الخاصة.
هدفنا من هذا التحديث هو تعزيز مزايا واجهة برمجة التطبيقات القديمة ومعالجة نقاط ضعفها. عندما أكملنا كل مكون من مكونات التطبيق الجديد ، بدأنا استخدامه في قاعدة بيانات Google. أعطانا هذا النشر التدريجي الثقة في كل من قابلية استخدام واجهة برمجة التطبيقات الجديدة والأداء الأفضل وصحة التنفيذ الجديد. نحن واثقون من أنها جاهزة للإنتاج.
نحن متحمسون جدًا لهذا الإصدار ونأمل أن يخدم نظام Go الإيكولوجي جيدًا خلال العقد القادم أو أكثر!
تعلم المزيد عن الدورة.