آشنایی با Protobuf
یکی از مباحث همیشگی پیرامون پروژه ها، بحث شیوه جابجایی اطلاعات میان برنامه ها است. Protobuf یکی از پاسخ های مناسب برای این بحث است. بنابراین قصد داریم تا در این مقاله به این مبحث بپردازیم.
یکی از مباحثی که میتوان گفت در تمام پروژه ها وجود دارد، مبحث جابجایی اطلاعات است. این جابجایی اطلاعات میتواند به صورت client-server و Server-Server باشد. بنابراین باید Protocol که میخواهیم استفاده کنیم و همچنین فرمت داده ها را تعیین کنیم. حجم داده ها و میزان بهینگی روش ها برای جابجایی آنها، مواردی هستند که در این انتخاب به ما کمک میکنند.
فرمت های متدوال برای جابجایی اطلاعات
دو فرمت برای جابجایی اطلاعات بسیار متداول است:
- JavaScript Object Natation : JSON
- Extensible Markup Language : XML
یکی از ویژگی های بسیار مفید این فرمت ها، آسان بودن فهم آنها است. برای مثال کد زیر یک نمونه کد XML است:
<simple-xml> <author>Vahid rajabi</author> <article>protobuf</article> <site>https://zerotohero.ir</site> </simple-xml>
و کد زیر یک نمونه JSON است:
{ "simple-json":{ "author":"vahid rajabi", "article":"protobuf", "site":"https://zerotohero.ir" } }
هماطور که میدانیم جابجایی این اطلاعات به serialization نیاز دارد. یکی از سوالاتی که برای serialization مطرح میشود این است که چگونه مطمئن شویم بعد از desrerialize به همان type اصلی برمیگردیم. همچنین کارایی و compatibale بودن آن را نیز باید حائز اهمیت است. Compatibale بودن به این معناست که اگر تغییری در فرمت داده ها ایجاد شد٬ برنامه با مشکل مواجه نشود. برای این منظور سعی میکنیم protobuf را در کنار JSON مورد بررسی قرار دهیم تا بتوانیم بهتر آن را درک کنیم. زیرا استفاده از JSON برای جابجایی اطلاعات میان Client-Server و سرویس ها بسیار متداول است .بنابراین با قیاس این دو protocol میتوانیم به کارایی و موارد استفاده آن بیشتر اشراف پیدا کنیم.
یک مثال
برای مثال قصد داریم کلاس زیر را به صورت JSON ارسال کنیم:
class Person { private String username // vhdrjb; private String company // zerotohero; private long code //234453242; }
اکنون قصد داریم این کلاس را به صورت JSON ارسال کنیم:
{ "username":"vhdrjb", "company":"zerotohero", "code":"234453242" }
بنابراین اطلاعات ارسالی را میتوانیم در یک سرویس دیگر با استفاده از Jackson بخوانیم:
new ObjectMapper().readValue(json, Person.class);
از ویژگی هایی که JSON دارد این است که خوانایی آن بالا است. یعنی اطلاعات در قالب یک فایل ارسال میشود و برای کاربر فرستنده قابل فهم است. همچنین اطلاعات ارسالی به صورت self-contained است. به این معنا که تمام اطلاعاتی که در قالب JSON میخواهیم ارسال کنیم، در قالب یک فایل است. همچنین این فایل قابل توسعه بوده و میتوانیم اطلاعات را به آن اضافه یا کم کنیم.
اما فایل های JSON معایبی نیز دارند. برای مثال فایل بالا ۷۰ بایت است. اما اطلاعاتی که شامل میشود به این صورت است:
بنابراین همانطور که مشاهده میکنید ۲۴ بایت Overhead در آن وجود دارد. این حجم میتواند در فایل هایی با ابعاد بزرگتر، بیشتر باشد.
مشکل بعدی که در فایل های json وجود دارد این است که نوع داده ها در آن مشخص نیستند. این امر باعث میشود تا حجمی که به آن اختصاص مییابد به صورت دلخواه باشد.
پیاده سازی
اکنون با توجه به این محدودیت ها٬ protobuf را بررسی میکنیم. ساختار پیام های protobuf به این صورت است:
syntax="proto3"; package proto_entity; option java_package = "ir.zerotohero.entity"; option java_outer_classname = "ZthPerson"; message Person{ string username = 1; string company =2; int64 code =3 ; }
در قطعه کد بالا٬ ابتدا Syntax را مشخص کردهایم که مشخص میکند از کدام نسخه protobuf استفاده کنیم. در اینجا از proto3 استفاده کردهایم. سپس با کلمه کلیدی package نام package را مشخص میکنیم. این کلمه کلیدی مانند package جاوا است و برای جلوگیری از conflict است. سپس با java_package مسیری که در پروژه ذخیره میشود را مشخص میکنیم.
پیش از آن کلمه کلیدی optional است. optional مشخص میکند که این مقدار به صورت اختیاری است. با استفاده از java_outer_classname نام کلاسی که توسط protobuf compiler ایجاد میشود را مشخص میکنیم. بدنه کلاس که ساختار داده ها را مشخص میکند با استفاده از message ایجاد میشود و بعد از آن نام کلاس را مشخص میکنیم.
در مرحله بعد نوع فیلد ها را مشخص میکنیم و پس از آن نام فیلد تعیین میگردد. عددی که در مقابل آن قرار میگیرد، اولویت قرارگرفتن و برداشته شدن اطلاعات در حافظه است. به این ترتیب میتوانیم بر ذخیره و بازیابی اطلاعات نیز اشراف داشته باشیم. البته ایجاد کلاس های protobuf شامل موارد بیشتری است که در مقالات بعد به آن میپردازیم.
اکنون باید این کلاس را به java file تبدیل کنیم. این عمل را با استفاده از protobuf compiler انجام میدهیم. برای نصب این compiler میتوانید به این لینک مراجعه کنید.
سپس با استفاده از دستور:
protoc -I=. --java_out=. model.proto
کلاس مورد نظر را ایجاد میکنیم. protoc همان compiler است که نصب شده است. I- مسیر فایل ورودی proto را مشخص میکند. java_out– مسیر فایل خروبی java را مشخص میکند. و در پایان فایل proto را مشخص میکنیم. در نهایت یک کلاس درون package های تعیین شده ایجاد میشود. فایل ایجاد شده حاوی 809 خط کد است که شامل Builder و باقی کلاس های مورد نیاز است. سپس میتوانیم به این صورت از آن استفاده کنیم:
ZthPerson.Person zthPerson = ZthPerson.Person.newBuilder() .setCode(234453242) .setCompany("zerotohero") .setUsername("vhdrjb") .build(); System.out.println(zthPerson);
و خروجی آن به این صورت است:
username: "vhdrjb" company: "zerotohero" code: 234453242
Serialization و Deserialization
اکنون فرض کنید یک Service داریم با نام A که میخواهد این اطلاعات را ارسال کند و Service دیگری با نام B میخواهد آن را دریافت کند. کد در A به این صورت است:
ZthPerson.Person zthPerson = ZthPerson.Person.newBuilder() .setCode(234453242) .setCompany("zerotohero") .setUsername("vhdrjb") .build(); try( FileOutputStream fileOutputStream = new FileOutputStream(new File("out.zth"))){ zthPerson.writeTo(fileOutputStream); } catch (IOException e) { e.printStackTrace(); }
و به این صورت میتوانیم در B دریافت کنیم:
try(FileInputStream fileInputStream=new FileInputStream(new File("out.zth"))){ ZthPerson.Person person = ZthPerson.Person.newBuilder().mergeFrom(fileInputStream).build(); System.out.println(person); } catch (IOException e) { e.printStackTrace(); }
و خروجی آن به همان صورتی است که ارسال شده است. بنابراین ارسال و دریافت آن بسیار آسان تر است زیرا این کد ها به کد های باینری تبدیل میشوند و ارسال و دریافت آنها آسان تر و بهینه تر از JSON است. زیرا JSON به صورت string ارسال و دریافت میشود.
موارد استفاده از JSON
- برای اطلاعات با حجم کم
- برای اطلاعاتی که نیاز است تا باز شود و اطلاعاتی که ارسال میشود بررسی گردد
- محتویات پیام های ارسالی با یکدیگر تفاوت دارند
موارد استفاده از Protobuf
- وقتی حجم اطلاعات ارسالی زیاد است
- پیام ها ساختاری مشابه دارند اما مقادیر آنها متفاوت است
- کارایی بسیار حائز اهمیت است
بنابراین میتوان نتیجه گرفت که برای ارسال اطلاعات به مرورگر ها و بعضی client ها میتوانیم از JSON استفاده کنیم و در مقابل برای ارتباط میان Service ها میتوانیم از protobuf استفاده کنیم. استفاده درست از این ابزار میتواند کارایی سیستم های ما را به صورت چشم گیری افزایش دهد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.