وبلاگ

پروتکل بافرها

پروتکل بافرها

3 دی , 1396

این مقاله درباره ی پروتکل بافرها- به عنوان زبانی خنثی و همچنین یک بستر نرم افزاری خنثی روشی قابل تعمیم جهت سریال سازی داده های ساخت یافته و مورد استفاده در پروتکل های ارتباطی، ذخیره ی داده ها و غیره می باشند.

هدف مقاله ی حاضر توسعه دهندگان برنامه ی C++ ، Java و Python می باشد که قصد دارند از پروتکل بافرها در اپلیکیشن های خود استفاده کنند. در این بررسی اجمالی پروتکل بافرها معرفی می شوند و آنچه برای شروع به کار نیاز است، مطرح می گردد.

پروتکل بافر چیست؟

پروتکل بافر، مکانیزمی انعطاف پذیر، کارآمد و خودکار برای سریال سازی داده های ساخت یافته، مانند XML اما کوچک تر، سریع تر و ساده تر است. زمانی که چگونگی ایجاد ساخت برای داده های خود را تعریف می کنید، می توانید از رمز منبع ویژه جهت سهولت در نوشتن و خواندن داده های ساخت یافته ی خود استفاده کنید و انواع مختلفی از داده ها را داشته باشید و از زبان های متفاوتی بهره ببرید. حتی می توانید ساختار داده های خود را بدون تفکیک برنامه هایی که در برابر فرمت های قدیمی ایجاد شده اند، به روز رسانی کنید.

نحوه ی عملکرد پروتکل بافرها:

شما چگونگی ساختار بندی اطلاعاتی که قصد سریال سازی آنها را دارید، با تعریف انواع پیام های پروتکل بافر در فایل های proto، مشخص می کنید. هر پیام پروتکل بافر، اطلاعاتی جزئی و منطقی است که شامل مجموعه ای از جفت های دارای عنوان اسمی می باشد. در اینجا مثال بسیار ساده ای از یک فایل proto می آوریم که پیامی حاوی اطلاعات مربوط به یک شخص را ارائه می کند:

[message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4]

همان طور که مشاهده می کنید، فرمت این پیام بسیار ساده است؛ هر پیام دارای یک یا چند فیلد مخصوص بوده و هر فیلد شامل یک نام و یا نوعی مقدار است که می تواند به صورت عدد (اعداد صحیح یا ممیزی)، بولین، رشته، بایت خام، یا حتی (مانند مثال بالا) دارای سایر انواع پیام های پروتکل بافر باشد که به شما این امکان را می دهد تا داده های خود را به صورت سلسله مراتبی بسازید. شما می توانید فیلدهای اختیاری، ضروری و تکراری را مشخص کنید.

زمانی که پیام های خود را تعریف می کنید، کامپایلر پروتکل بافر را برای زبان اپلیکیشن خود بر روی فایل proto، و به منظور تولید کلاس های دسترسی به داده ها اجرا می کنید. این ها دسترسی های ساده برای هر فیلد (مانند () name و () set_name) و همچنین روش هایی را جهت سریال سازی یا تجزیه ی کل ساختار آن به صورت بایت های خام فراهم می کنند. بنابراین، برای نمونه، اگر زبان انتخابی شما ++C باشد، اجرای کامپایلر بر روی نمونه ی بالا، کلاسی به نام Person را ایجاد می کند. پس از این شما می توانید از این کلاس به منظور همگانی کردن، سریال سازی کردن و بازیابی پیام های پروتکل بافر Person بر روی اپلیکیشن خود استفاده کنید و رمزی مانند نمونه ی زیر بنویسید:

[Person person;
person.set_name(“John Doe”);
person.set_id(1234);
person.set_email(“jdoe@example.com”);
fstream output(“myfile”, ios::out | ios::binary);
person.SerializeToOstream(&output)];

سپس، می توانید پیام خود را به صورت زیر بخوانید:

fstream input(“myfile”, ios::in | ios::binary);[
Person person;
person.ParseFromIstream(&input);
cout << “Name: ” << person.name() << endl;
cout << “E-mail: ” << person.email() << endl

شما می توانید بدون حذف همسان سازی های قبلی، فیلدهای جدیدی به فرمت های پیام خود بیفزایید. فایل های باینری قدیمی، به آسانی از فیلدهای جدید به هنگام تجزیه سازی چشم پوشی می کنند. بنابراین، اگر یک پروتکل ارتباطی دارید که از پروتکل بافرها به عنوان فرمت داده های خود استفاده می کند، می توانید پروتکل خود را بدون هیچ گونه نگرانی در خصوص حذف رمز کنونی، توسعه دهید.

چرا نباید فقط از XML استفاده کرد؟

پروتکل بافرها مزایای بیشتری نسبت به XML در سریال سازی داده های دارای ساخت دارند. پروتکل بافرها در مقایسه با XML:

ساده تر هستند

سه تا ده مرتبه کوچک تر هستند

بیست تا یک صد مرتبه سریع تر هستند

ابهام کم تری دارند

و کلاس های دسترسی به داده ها را  ایجاد می کنند که برای استفاده در برنامه نویسی آسان تر هستند.

برای نمونه، اجازه دهید بگوییم شما قصد طراحی یک شخص با یک نام و یک ایمیل دارید. در XML بایستی به صورت زیر عمل کنید:

<person>[    <name>John Doe</name>

    <email>jdoe@example.com</email> ]</person

در حالی که پیام پروتکل بافر آن (در فرمت نوشتاری پروتکل بافر) به صورت زیر است:

# [Textual representation of a protocol buffer.# This is *not* the binary format used on the wire.
person {
name: “John Doe”
email: jdoe@example.com]

زمانی که این پیام به صورت فرمت باینری پروتکل بافر رمزگذاری می شود (فرمت نوشتاری بالا نمونه ی قابل خواندن برای انسان جهت اشکال زدایی و ویرایش است)، احتمالاً 28 بایت طولانی تر است و تجزیه ی آن حدود 100 تا 200 نانوثانیه طول می کشد. اگر فضاهای خالی را حذف کنید، XML حداقل 69 بایت است و تجزیه ی آن حدوداً 5000 تا 10000 نانوثانیه زمان می بَرد. همچنین اجرای یک پروتکل بافر بسیار ساده تر است:

[cout << “Name: ” << person.name() << endl;
cout << “E-mail: ” << person.email() << end]l;

در حالی که با استفاده از XML باید مانند زیر عمل کرد:

cout << “Name:
<< person.getElementsByTagName(“name”)->item(0)->innerText()
<< end
cout << “E-mail: “
<< person.getElementsByTagName(“email”)->item(0)->innerText()
<< endl]];

با این وجود، پروتکل بافرها نسبت به XML ها همیشه بهترین راه حل  نیستند؛ برای نمونه، پروتکل بافرها، روش مناسبی برای طراحی یک سند متن- محور و دارای نشانه گذاری (مانند HTML) نیستند، زیرا نمی توانید به راحتی ساختاری را در متن قرار دهید. به علاوه، XML برنامه ای قابل خواندن و قابل ویرایش برای انسان است، در صورتی که پروتکل بافرها حداقل در فرمت اصلی خود این چنین نیستند. همچنین XML تا حدودی خود- توصیف است. یک پروتکل بافر تنها زمانی معنی دار است که شما تعریف پیام  را داشته باشید.

به نظر می رسد این راه حلی برای من است! از کجا شروع کنم؟

این بسته بندی را دانلود کنید. این بسته شامل رمز کامل منبع برای کامپایلرهای C++ ،Java و Python، و همچنین کلاس های مورد نیاز شما برای I/O و آزمون سازی می باشد. برای نصب و راه اندازی کامپایلر خود، مطابق دستورالعمل های آمده در README عمل کنید.

زمانی که همه ی موارد را تنظیم کردید، از بخش آموزش برای زبان انتخابی خود استفاده کنید؛ به این ترتیب شما در مسیر ایجاد اپلیکیشنی ساده قدم خواهید گذاشت که از پروتکل بافرها استفاده می کند.

معرفی Proto3

نسخه ی 3 جدیدترین نسخه ی منتشر شده ی ما می باشد که نسخه ی یک زبان جدید را معرفی می کند؛ و نام آن Protocol Buffers language version 3 (aka proto3) است. این نسخه شامل ویژگی های جدیدی در مقایسه با نسخه ی دوم (یعنی aka proto2) می باشد. Proto3، زبان پروتکل بافر را ساده تر می کند، تا هم استفاده ی آسان تری داشته باشد و هم در دسترس طیف گسترده ای از زبان های برنامه نویسی باشد. نسخه ی کنونی، این امکان را به شما می دهد که رمز پروتکل بافر را در برنامه های Java ،C++،Python ،C# ،Objective-C ،JavaScript ،Ruby و Java Lite ایجاد کنید. به علاوه، می توانید رمز proto3 را برای Go ایجاد کنید که از آخرین Go protoc plugin استفاده می کند و از بخش gollang/protobuf Github در دسترس قرار می گیرد. زبان های بیشتر در کانال اطلاعات وجود دارند.

اکنون، توصیه می شود تنها در موارد زیر از نسخه ی proto3 استفاده کنید:

– چنانچه قصد دارید از پروتکل بافرها در یکی از زبان های جدید برنامه نویسی مورد تأیید ما استفاده کنید.

– اگر مایل هستید برنامه ی RPC ما را اجرا کنید، توصیه می کنیم از نسخه ی proto3 برای تمام سرورها و کلاینت های جدید gRPC استفاده نمایید، زیرا این نسخه مانع ایجاد اختلال های همسان سازی می شود.

– توجه داشته باشید که دو نسخه ی زبانی API به طور کامل همسان نیستند. به منظور جلوگیری از بروز هر گونه اشکال برای کاربران، همچنان نسخه ی قبلی زبان را در پروتکل بافرهای تازه منتشر شده تأیید می کنیم.

 

مستندات کامل proto3 به زودی منتشر خواهد شد.

(چنانچه نام دو نسخه ی proto2 و proto3 کمی گمراه کننده است، به این دلیل است که وقتی ابتدا از نسخه ی proto2 رونمایی کردیم، این نسخه در گوگل با عنوان proto2 موجود بود. به همین دلیل است که نسخه ی منبع نیز به صورت v2.0.0 است.)

تاریخچه ی مختصری از پروتکل بافرها:

پروتکل بافرها ابتدا در گوگل برای پرداختن به پروتکل های درخواست یا پاسخ کاربران به وجود آمد. پیش از پروتکل بافرها، از یک فرمت برای در خواست ها و پاسخ ها استفاده می شد که به صورت دستی مرتب می شدند و تعدادی از نسخه های پروتکل را تأیید می کرد. این امر منجر به پیدایش برخی از رمزهای نادرست مانند زیر می شد:

[if (version == 3) {

else if (version > 4) {
if (version == 5) {

}

در واقع، پروتکل های فرمت بندی شده، گسترش نسخه های جدید را پیچیده کردند، زیرا توسعه دهندگان می بایست مطمئن می شدند که همه ی سرورها، بین پدیدآورنده ی درخواست و سرور حقیقی که مسؤل رسیدگی به درخواست است، متوجه پروتکل جدید شوند، پیش از آن که با استفاده از پروتکل جدید تغییری ایجاد کنند.

پروتکل بافرها برای حل بسیاری از مسائل طراحی شده بودند، از جمله:

– فیلدهای جدید به آسانی معرفی شوند، و کاربرانی که نیاز به بررسی داده نداشتند، به آسانی آنها را تجزیه نموده و بدون نیاز به دانشی در خصوص فیلدها، از بررسی داده ها صرفنظر کنند.

– فرمت ها بیشتر به صورت خود- توصیف بودند و با بسیاری از زبان های برنامه نویسی (همچون C++ ،Java و غیره) سر و کار داشتند.

با این وجود، کاربران همچنان مجبور به یادداشت رمز تجزیه ی خود به صورت دستی بودند.

به محض ارتقای سیستم، نیاز به برخی از ویژگی ها و کاربردهای دیگر نیز نمایان شد که عبارتند از:

– ایجاد سریال سازی و غیر سریال سازی خودکار رمز مانع تجزیه با دست شد.

– علاوه بر کاربرد آن در اجرای درخواست های کوتاه مدت RPC، افراد از پروتکل بافرها به عنوان یک فرمت خود- توصیف جهت ذخیره ی دائمی داده ها استفاده کردند (برای نمونه در Bigtable).

– اتصالات سرور RPC به عنوان بخشی از فایل های پروتکل در نظر گرفته شد. کامپایلرهای پروتکل، کلاس های کوچکی را تولید می کردند که کاربران با اجرای دقیق اتصال به سرور می توانستند آنها را از میان بردارند.

پروتکل بافرها، هم اکنون، به عنوان زبان میانجی گوگل برای ایجاد داده ها در نظر گرفته می شوند؛ هنگام نوشتن، 48,162 پیام مختلف وجود دارد که رمز آنها در گوگل به صورت 12,183 فایل proto تعریف می شوند. این ها هم در سیستم های RPC و هم برای ذخیره ی دائمی داده ها در سیستم های  مختلف ذخیره سازی به کار می روند.