הבלוג של ארתיום
בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא!
מאמרים בנושא פיתוח.
הרהורים על Java... או זה לא מגניב, אבל זה עובד
לאחרונה יצא לכתוב מעט ב־Java, אחד מהם היה תוסף ל־ImageJ כחלק מהעבדת התזה שלי, במקרה השני כתבתי יישום GUI קטן שהייתי צריך אפשרות להריץ אותו במספר פלטפורמות בצורה פשוטה.
התחושה העיקרית שקיבלתי מהשפה:
בעוד 50 שנה ימשיכו לתחזק ולכתוב קוד שכתוב בה
אולי למשפט הזה יש קונוטציה שלילית, אבל למעשה אני מתכוון לזה במובן הטוב ביותר, זאת השפה, סליחה אני אתקן, זאת פלטפורמה, שבנויה לשרוד שנים. זאת לא טכנולוגיה מגניבה שקמה שלשום, התפתחה לכיוונים שונים ומשונים, אלא סביבה מסודרת, מתוכננת היטב וגם היא מכילה "אי־נוחויות" הן לא מספיק קריטיות. עכשיו אני אפרט.
הסדר פשוט מובנה בשפה בצורה שלפעמים "מציקה" אבל מצד שני מאוד מקלה עליך. החל משמות הקבצים ומחלקות, צורה של הגדרת מתודות, התיעוד (JavaDoc) הסטנדרטי, הספציפיקציות המדויקות (ללא Undefined Behaviour). בקוד Java קל התמצא, קל לקרוא, קל לכתוב.
ניהול החריגות שחלק מהאנשים לא אוהבים ב־Java פשוט עושה סדר. אם צריך לטפל במשהו אתה לא תשכח. הן חלק מהחוזה שלך עם המתודה. לפעמים זה מציק (מה לעזאזל אתה צריך להתייחס ל־IOException כשאני יודע שיש קובץ וזה לא מעניין אותי כרגע), אבל בפועל זה גורם לך לכתוב בצורה הרבה יותר מסודרת.
פשוטות: אין בה תכונות מגניבות, אתה לא יכול לכתוב בשורה אחת חישוב מסובך כמו ב־Matlab, אתה לא תוכל לכתוב בשלוש שורות פונקציה מגניבה שתעשה משהו יפה, מצד שני, אתה יודע בדיוק מה קורה, ההתנהגות היא ברורה וקל להבין מה קורה.
עושר ה־API: אני חשוב שזה אחד הצדים חזקים ביותר של השפה: GUI, Unicode, Web, מתמטיקה, רשת, I/O ועוד אלף ואחד דברים וכל זה מובנה, עובד, ותואם לאחור. אתה יכול כתוב יישומים ענקיים בלי להזדקק לספריית צד ג'. אתה רוצה לשלוח חללית למאדים: תייבא org.marse ו־org.nasa.spaceship ותקרא למתודה "lunch".
הפצת תכנה: הקלות בה יכולתי לכתוב תוסף ל־ImageJ ולחבר אותו הייתה פשוט מעולה - אתה מוריד Class וזה "פשוט עובד". נכון, אם לוקחים תכנות כמו GIMP גם להם קל לכתוב תוסף ב־Python, אבל אתה לא תוכל תכתוב שום דבר רציני שדורש חישוב אמתי בו, לעומת זאת ב־Java המהירות קרובה הרבה יותר למהירות קוד ה־C או C++.
בדיוק בעזרת שפה (סליחה פלטפורמה) עם התכונות האלה אתה בונה מערכות שתשרודנה שנים על גבי שנים ותוכל תחזק אותם בקלות לאורך הזמן. בקיצור, זאת פלטפורמה מאוד לא מגניבה, אבל, אולי דווקא בגלל זה, היא תשרוד עוד שנים רבות.
שורה תחתונה, אני מאוד נהניתי לעבוד ב־Java.
Comet בצורה שפויה
HTML5 ו־Comet
היום HTML5 מציע שני כלים עיקריים ליישומי Comet:
- טכנולוגיית WebSockets
- טכנולוגיית Server-Sent Events
מבחינה טכנית, WebSockets (WS) מייצרים קשר דו־כיווני מלא ומאפשרים הן לשרת והן לקוח לשלוח הודעות בזמן אמת - בלי לפתוח קשרים נוספים. לעומת זאת Server-Sent Events (SSE) זהו קשר חד־כיווני, בו השרת הוא זה ששולח אירועים ללקוח ואם הלקוח צריך לשלוח משהו לשרת, הוא משתשמש בכלי Ajax נוכחיים כמו XMLHttpRequest (XHR).
למען האמת, עד עכשיו יצא לי לעקוב אחרי התפתחות של WS. הפרוטוקול החליף מספר גרסאות, הדפדפנים שינו את המימושים בהתאם עד שהפרוטוקול התייצב. כיום התמיכה ב־WS קיימת ברוב הדפדפנים עדכניים ואמורה להיכנס גם ל־IE10. לעומת זאת, כל הנושא של SSE חלף על פניי ולא שמתי עליו לב.
כיוון שאני מעסק בצד השרת בעיקר, הכנסתי את נושא ה־WS לתכנית העבודה שלי. אבל עדיין לא הצלחתי להגיע אליו; ולא במקרה.
למרות העתיד ה"זוהר" של WS, המימוש הוא לא פשוט. לא מדובר כאן בבעיה טכנית, הרי תמיכה ב־Comet כבר קיימת ועובדת יפה, הבעיה היא בעיה מהותית: כדי לממש WS צריך לשנות פרוטוקול HTTP. ברגע ש"לחיצה היד" של WS נגמרת, לא מדובר עוד ב־HTTP, אלא בפרוטוקול שונה לחלוטין.
לכן, הכלים ופרוטוקולים שבעזרתם יישומים מתקשרים עם שרתי Web, לא מתאימים. למשל, אי אפשר להעביר תקשורת של WS מעל FastCGI, SCGI או CGI. גם אם היישום שלך עובד ב־HTTP, לא כל שרת web ידע להתמודד עם הבעיה: החלפת ה־HTTP בפרוטוקול חלופי.
לכן, אפילו ש־WS, זאת טכנולוגיה מבטיחה, היא עדיין רחוקה מהבשלות האמתית.
לעומת זאת, SSE, שנדחקו הצדה, לא סובלים מהבעיות האלה!
- SSE לא משנים את פרוטוקול ה־HTTP, לכן, אין בעיות שימוש בתשתיות הקיימות.
- הביצועים של SSE לא נופלים על אלה של WS.
SSE הרבה יותר קלים לתפעול:
- מכילים מנגנון סנכרון אוטומטי, במקרה של התנתקות
- מאפשרים לשלוח אירועים שונים ברמת JavaScript בלי שכבות נוספות
- ניתן לסגת בקלות ל־Long Polling עם XHR פשוט במקרה שהדפדפן לא תומך ב־SSE.
החיסרון היחידי של SSE לעומת WS: לא ניתן לשלוח הודעות באותו הקשר לשרת. אלא לכל הודעה לשרת צריך לשלוח XHR משלו. אבל? SSE מכסה את הרוב המקרים של שימוש ב־Comet!
המימוש? קיים ב־Firefox, Opera, Chrome, Safari ואמור להיכנס ל־IE10
אז איך זה עובד
בואו נדגים אפליקציה שמציגה את מחיר המניה העדכני ביותר בדפדפן:
בצד הלקוח, אנחנו נכתוב את הקוד הפשוט הבא:
function read_data() {
var stream = new EventSource('/ticker/get');
stream.onmessage = function(e){
document.getElementById('price').innerHTML=e.data;
};
stream.onerror = function(e){
console.log(e);
};
}
read_data();
אנחנו פותחים EventSource ועל כל אירוע שמכיל מידע אנחנו מעדכנים שדה html עם המחיר העדכני ביותר.
בצד השרת הנושא שקצת יותר מורכב כי אנחנו צריכים לנהל מספר קשרים:
כשנכנס קשר חדש, אנחנו מכינים אותו - מגדירים את ה-Content-Type כ־text/event-stream
, מבטלים caching בדרך.
לוקחים את ה־id של הערך הידוע האחרון, זה יאפשר לנו לדעת אם הלקוח התנתק אז אם הוא יודע את המחיר העדכני ביותר או לא. אם הערך שונה מהערך הנוכחי, אנחנו שולחים עדכון, אחרת מכניסית אותו ל"רשימת המתנה" - כל הלקוחות הממתינים לעדכון.
void main(std::string /*url*/)
{
response().set_content_header("text/event-stream");
response().set_header("Cache-Control", "no-cache");
auto last_id = atoi(request().cgetenv("HTTP_LAST_EVENT_ID"));
auto context=release_context();
if(last_id != counter_) {
async_send(context);
}
else
waiters_.insert(context);
}
כיצד אנחנו שולחים עדכונים:
התוכן מורכב מה־id - של המחיר - מנגנון הסנכרון שלנו והתוכן - המחיר עצמו.
void async_send(booster::shared_ptr<cppcms::http::context> waiter)
{
waiter->response().out() <<
"id:" << counter_ <<"\n"
"data:" << price_ << "\n"
"\n";
אחר כך אנחנו מגדירים completion-handler שיבדוק אם פעולה הצליח (אם לא אז קשר נסגר אין מה לעשות) ואם כן, ואין עדכונים נוספים ומכניסים אותו לרשימת ההמתנה.
waiter->async_flush_output([=,counter_](cppcms::http::context::completion_type status){
if(status!=0)
return;
if(counter_ != this->counter_) {
this->async_send(waiter);
}
else {
this->waiters_.insert(waiter);
}
});
}
עכשיו נכתוב פונקציה קטנה שמאפשרת לנו לעדכן את אלו שממתינים למחיר חדש:
void update_price(double new_one)
{
counter_++;
price_ = new_one;
for(auto waiter : waiters_) {
async_send(waiter);
}
waiters_.clear();
}
נעדכן מונה כדי לסמן מחיר חדש, נעבור על הרשימה ונשלח מחיר מעודכן לכולם. מנגנון פשוט שניתן לממש עם כלים פשוטים (במידה והתשתית שלך תומכת ב־Comet).
ניתן למצוא כאן את הקוד המלא.
חיבור לשרת
כדי שהמנגנון הזה יעבוד צריך לוודא ששרת ה־Web ישלח את התוכן באופן מידי ולא ישמור אותו בזיכרון (בציפייה שעוד מידע יגיע).
- Lighttpd עושה את זה בלי בעיה ב־FastCGI, SCGI ו־HTTP כברירת מחדל.
- Apache עושה את זה כברירת מחדל ב־SCGI ו־HTTP אבל ב־FastCGI דרושה אופציה -flush ואז הכל תקין.
לעומת זאת, "Nginx המהולל" עושה בעיות. בפרוטוקולים SCGI ו־HTTP ניתן לבטל buffering עם אופציה http_buffering off
או scgi_buffering off
אבל ב־FastCGI, שהוא הפרוטוקול הכי נפוץ בעבודה עם שרת Web אופציה כזו לא קיימת! כרגיל, Nginx מפתיע... (או שלא).
אז כרגיל, עוד סיבה טובה לא להשתמש ב־Nginx. אז אני אחזור את הטענה שלי: Nginx, תודה לא, Lighttpd.
כיצד לא לעשות יוניקוד
הסיפורו של הניסיון להדפיס "שלום" במספר שפות במסוף של חלונות.
http://blog.cppcms.com/post/105
שוחררה גרסה יציבה 1.0.0 של CppCMS המופצת תחת רישיון כפול
היום, אחרי מספר שנות פיתוח, שוחררה גרסה יציבה של CppCMS 1.0.0, אחרי תקופה ארוכה של גרסאות בטא. הגרסה הזו מהווה אבן דרך משמעותית בהתפתחות של CppCMS, הן מבחינה טכנולוגיות והן מבחינה עסקית.
החל מגרסה 1.0.0, ספריית CppCMS תהיה זמינה תחת רישיון חופשי LGPLv3 ותחת רישיון מסחרי שיאפשר פיתוח יישומים קנייניים תוך שמירה על שלמות המוצר והגנה על הסודות המסחריים שלו.
למידע נוסף על הרישוי החלופי והמחירים אנא כנסו לאתר שלנו:
http://commercial.cppcms.com
גרסה זו, עברה שכתוב יסודי ושינויים ארכיטקטוניים עמוקים שהזניקו את הפופולריות שלה מיד עם שחרור גרסאות בטא ראשונות. מבין החידושים הטכנולוגיים:
- הכנסה של API ו־ABI יציבים
- תמיכה מובנית ב־Ajax ו־Comet
- שיפורים מהותיים בלוקליזציה
- תמיכה מובנית ב־Windows
- שיפורים משמעותיים באבטחה: מסנני XSS וכלים למניעת CSRF ועוד.
- שרת HTTP מובנה לפיתוח ומערכות משובצות
הענף הישן יציב CppCMS 0.0.x לא נתמך יותר. כל משתמשיי הענף הישן מתבקשים לעבור לגרסה החדשה. יש לציין, לא ידוע לי על משתמש אחד שנשאר עם הגרסה הישנה.
שפת תכנות לא משנה...
מאז שהמציאו שפת תכנות ראשונה ממשיכים למצוא את השפה האולטימטיבית שתפתור לנו את כל הבעיות:
- תהיה מהירה
- תהיה פשוטה
- תהיה חזקה
- תהיה ברורה
- תהיה נוחה
- תכין לנו קפה לארוחת הבוקר...
אז נגיד מחר אני אצליח ליצור שפה חדשה:
- מהירה כמו C או C++
- קלה להבנה כמו Java או Pascal
- גמישה כמו Python או JavaScript
- ועוד כמה תכונות נוספות שתרצו לפרט
ואז כשיהיה לי מהדר (או משערך) אולטימטיבי שיעבוד על כל מערכת הפעלה ובכל סביבה החל ממערכות משובצות עד מחשבי על, החל מ־ARM עד ל־Itanium ואפתח IDE אולטימטיבי יהפוך אותי לפרודוקטיבי־על, אני אתחיל לעבוד...
מה יקרה?
רוב המפתחים היום לא כותבים אלגוריתמים הם כותב אפליקציות. רוב המפתחים היום צריכים לדעת את הספריות שלהם יותר טוב מהשפה, למתכנת GUI ממוצא יותר חשוב להבין אין עובד Qt מאשר לדעת כיצד לעשות partial template specialization. למתכנת יישומי עיבוד תמונה ב־Matlab יותר חשוב לזכור את הפונקציות שמבצעות convolution על מטריצה, מאפשר לזכור כיצד להגדיר מחלקות. למפתח Web יהיה יותר חשוב לדעת כיצד לייצר טופס, מאשר כיצד להשתמש בפונקציות למבדה.
בסופו של דבר גם אם פיתחת שפת־על אתה צריך כלים. ואם אין לך כלים השפה תמות.
זאת הסיבה רוב השפות שנולדות לאחרונה הן רצות על JVM או על .Net או כמו Vala משתמשות באוסף הספריות העשיר של Gnome כדי להיות פרודוקטיבי.
ומה עם "שפות־העל" שפותחו והמון מחשבה הושקעה בהן כמו, למשל, D? הן לא התרוממו.
למעומת זאת, שפות "עתיקות" כמו C ממשיכות להתקיים והמונים כותבים בהן. Vala בזכות האימוץ המלבב של GTK ו־GObject תופסת תאוצה. Go? כנראה לא תתרומם בקרוב בלי השקעה אדירה.
במילים אחרות.
זה נחמד אם תהיה שפה חדשה שתפתור לנו את כל הבעיות האפשריות, אבל אם היא לא "תירש" בצורה אוטומטית או חצי אוטומטית SDK עשיר, היא לא תחיה הרבה.