הבלוג של ארתיום :: CppCMS http://artyom.cppcms.com/ בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא! לחבר שני תחביבים http://artyom.cppcms.com/post/338 http://artyom.cppcms.com/post/338 <div style="direction:rtl"> <p>אני חובב אסטרונומיה. בתור חובב אסטרונומיה אני סובל ואחת הבעיות הגדולות של העולם המודרני - זיהום אור. יש המון גרמי שמיים חיוורים כמו גלסקסיות וערפיליות שפשוט לא ניתן לראות מהעיר. בשביל זה צריך לנסוע לנגב או לרמת הגולן ולבלות לילה בתצפית. זה מאוד כיף כמובן. אבל זה לא תמיד נגיש.</p> <p>אחד המעקפים לבעיה זה שימוש בצילום. מצלמה שיכולה לאגור פוטונים מגלקסיות מרוחקות ומאפשרת לחדור דרך שכבת זיהום אור כבדה ולהראות לנו גרמי שמיים עמומים. זה כמובן לא תחליף לצפייה ישירה אבל גם נותן יתרונות רבים אחרים. במובן, צילום אסטרונומי הוא נושא מורכב שדורש שימוש בתוכנות ייעודיות: איסוף תמונות רבות ככל הניתן, ועיבוד שלהם (הערמה) כדי לקבל תמונה יפה של איזו ערפיליות או גלקסיה.</p> <h2>אמרנו לינוקס?</h2> <p>אז מה המצב התוכנה בתחום זה מבחינת תוכנה חופשית ולינוקס? יש ויש. חלק הארי של הכלים הם חופשיים/קוד־פתוח. יש לא מעט תוכנה ללינוקס, אם כי הדברים הטובים ביותר רצים על חלונות. רוב הדרייברים של המצלמות דווקא סגורים. אבל לרוב יש גרסאות לינוקס, Raspberry PI ועוד.</p> <p>עכשיו, בצילום אסטרונומי יש נדבך חשוב שמעניין אותו במיוחד: Electronically Assisted Astronomy או EAA בקיצור. פירושו ביצוע כל הפעולות הנדרשות לצילום (כולל עיבוד, איסוף תמונות והערמה) בזמן אמת, כאשר עם כל תמונה חדשה של האובייקט, אתה מקבל את התמונה הסופית המשופרת יותר ויותר. המטרה של EAA בניגוד לצילום, לא להגיע לתמונה הטובה ביותר אפשרית, אלא להגיע לתמונה שמספיק טובה כדי לראות את האובייקט ולהנות ממנו.</p> <p>למעשה, במקום לצפות באובייקט דרך עינית, צופים בו דרך המסך. ובניגוד לצילום אסטרונומי שיכול להמשך שעות ארוכות, מסתפקים בזמן איסוף כולל קצת יחסית - מעשרות שניות עד דקות בודדות - כי המטרה לראות ולעבור לאובייקט מעניין הבא. מה מצב התוכנה פה? אם בצילום היה מאתגר בלינוקס, פה המצב קשה. יש מעט מאוד פתרונות ולא כולם עובדים ונוחים.</p> <p>הבעיה השניה, מבחינתי, זה ש־EAA דורשת לרוב להביא מחשב נייד לשטח כדי להפעיל את כל התוכנה המסובכת הזו. למעשה, אם תצפיתן שצופה ויזואלית יכול להביא איתו תיק אחד ובו טלסקופ, חצובה וכמה עיניות, צלם צריך להביא איתו לשטח: חצובה ממונעת, עשרות כבלים, מחשב, ספק כוח שיספיק למספר רב של שעות ועוד. הקמה וקיפול של הציוד לצילום יכולים לקחת בקלות בין חצי־שעה ולשעה בניגוד לצופים בעין - העושים הכל במספר דקות בודדות.</p> <p>אבל לרובינו יש כבר מחשב די חזק ונייד: טלפון או טאבלט! לו רק יכולתי לחבר את המצלמה ישירות לאליהם...</p> <h2>אז הרמתי את דגל</h2> <p>בניתי פתרון ל־EAA עבור לינוקס ואנדרואיד ושמו <a href="https://github.com/artyom-beilis/OpenLiveStacker">OpenLiveStacker</a>. והוא בנוי בצורה הבאה:</p> <ul> <li>הקוד כתוב ב־C++‎ עם שימוש ב־OpenCV לצורך עיבוד תמונה</li> <li>הממשק בנוי כ־web interface שמדבר ב־REST עם השרת - מה שמאפשר בניית ממשק בלינוקס ואנדרואיד באותה צורה וגם מקל על גישה מרחוק במקרה והתוכנה רצה על pi. כמובן שהשרת מבוסס CppCMS. מה שמאפשר חיבור קל ונוח בין הקוד שדורש ביצועים הגובהים לממשק משתמש.</li> <li>הדרייברים נטענים דינאמית: <ul> <li>אחד עבור מצלמה גנרית עם פרוטוקול UVC על בסיס libusb/libuvc- שתומך במצלמות רשת או במצלמות כמו SVBony sv105 - אבל הוא מוגבל לצורכים אסטרונומיים</li> <li>דרייבר של ASI ZWO - החברה המובילה בתחום, שעובד מול SDK שלהם. לצערי הדרייבר עצמו הוא קוד סגור, אבל יש להם גרסה לאנדרואיד.</li> <li>דרייבר גנרי שיודע לקרוא קבצים מהספרייה איך שהם מגיעים - מה שמאפשר חיבור לכל מצלמה אחרת דרך כלים קיימים כמו indi/ekos.</li> </ul> </li> <li>לצורך תמיכה באנדרואיד יש אפליקציה קטנה שעוטפת את השרת ומנהלת גישה ל־USB (כי באנדרואיד הכל צריך להיות מסובך)</li> <li>לצורך הקלה על התמצאות יש חיבור לתוכנה פופולרית מאוד בתחום אסטרונומיה: ASTAP שיש לה גם גרסה (קובץ ריצה) לאנדרואיד. הדבר המעניין בתוכנה הזו שהיא כתובה בפסקל! לא חשבתי שאתקל בדבר כזה בימינו.</li> </ul> <h2>מה למדתי?</h2> <ul> <li>בניית אפליקציות אנדרואיד זה די סיוט וזה לא בגלל השפה אלא בגלל שצריך ללמוד פחות או יותר הכל מ־0. מזל שרוב הקוד ניתן לכתוב ב־C++‎.</li> <li>כמעט כל דבר באנדרואיד עובד "קצת שונה". למשל: אין לך ‎/tmp, להריץ exe חיצוני זה סיפור שעלה לי בלילה לבן, להביא קבצים עם אפליקציה זה גם לא משהו טריוויאלי. בקיצור. זה לינוקס, אבל לא בדיוק.</li> <li>אני שונא לעבוד עם קוד סגור. אומנם ASI ZWO משחררים דרייברים לאנדרואיד, אבל הם גם הכניסו <a href="https://bbs.astronomy-imaging-camera.com/d/16038-asi-zwo-android-sdk-critical-bugs">באג מעצבן</a> שגורם ל־RTTI לא לעבוד! למעשה כל תכנת החיבור ל־SDK שלהם כתבתי ב־C+-‎ בגלל אי זמינות של RTTI. וזה לא היה משהו מסובך אם הייתי יכול לקמפל את הדרייבר מחדש הבעיה הייתה פשוט נעלמת.</li> </ul> <h2>שורה תחתונה</h2> <p>אבל מה שחשוב, שבשורה תחתונה, יש לי פתרון פשוט - לעבוד עם טאבלט שבקושי צורך חשמל, קל ונוח.</p> <p><img src="https://user-images.githubusercontent.com/14816918/229337011-72031279-8de8-4be0-b1a1-61d592525230.jpeg" width="450" height="400"></p> </div> שוחררה גרסת בטא ראשונה של CppCMS 2.0.0 http://artyom.cppcms.com/post/323 http://artyom.cppcms.com/post/323 <div style="direction:rtl"> <p>שוחררה גרסת בטא הראשונה. השינויי העיקרי - לפי דרישת הקהילה זה מעבר ל־C++11 כברירת מחדל - מה שאפשר לנקות חלקים נכבדים מספריית booster הממשים פונקציונליות שהייתה חסרה ב־C++2003:</p> <p>ביניהם:</p> <ul> <li>מצביעים חכמים</li> <li>תמיכה ב־threads</li> <li>שימוש ב־<code>std::error_code</code> ונגזרותיו</li> <li>החלפת <code>auto_ptr</code> ז"ל ל־<code>unique_ptr</code> ועוד.</li> </ul> <p>ראוי לציין שהגרסה הקודמת עבדה עם C++11 אבל לא ניצלה את היכולות שלה כמו למשל move-constructor וכד'.</p> <p>כיוון שהשינויים לא ב־100% תואמים לאחור זוהי גרסת משמעותית מבחינת משתמשים.</p> <p>חייב לציין שעל אף שינויים בחלק מה־APIים - כל הדוגמאות ואפליקציות שונות כגון הבלוג הזה עברו בנייה ללא שינווים בכלל.</p> <p>בנוסף כיוון ש־python2.7 הגיע ל־End-Of-Life הסבתי את הקוד של unit-tests ושל ה־template compiler לתמיכה גם python2.7 וגם ב־python >= 3.5. חייב לציין שאני עדיין מתפלא מהשטות הזו של הסבת מחרוזות ל"unicode" במקום שימוש ב־utf-8 פשוט. למה להרוס לאנשים שאת הקוד הקיים?</p> </div> CppCMS עובר מ-LGPLv3 ל-MIT http://artyom.cppcms.com/post/320 http://artyom.cppcms.com/post/320 <div style="direction:rtl"> <p>היום עדכנתי רישיון של CppCMS ל-MIT. הגרסה הקרובה 1.2 תשוחרר עם רישיון מעודכן</p> <p>ההחלטה נובעת ממספר סיבות:</p> <ol> <li>רצון להגדיל נתח השוק של CppCMS ולהקל על כניסה של משתמשים חדשים</li> <li>להביא יותר מפתחים לפרויקט</li> </ol> </div> אחרי תקופה ארוכה שוחררה בטא של CppCMS 1.1.0 http://artyom.cppcms.com/post/319 http://artyom.cppcms.com/post/319 <div style="direction:rtl"> <p>אחרי תקופה ארוכה של המתנה שוחררה גרסת בטא 1.1.0 של CppCMS. גרסה זו <a href="http://cppcms.com/wikipp/en/page/cppcms_1_2_whats_new">מכילה המון שיפורים ושיוניים</a>. ביניהם:</p> <ul> <li>תכנון מחדש של application pool לתיקון מספר בעיות מובנות שהיו קיימות בו עד כה</li> <li>API חדש של פעולות non-blocking לשיפור משמעותי בביצועי אפליקציות אסינכרוניות</li> <li>סיכון ועיבוד בזמן העלאה של התוכן (למשל עיבוד או בדיקה של קבצים תוך כדי העלאה)</li> <li>תמיכה משופרת ב-RESTful API</li> <li>מערכת לניהול plugin ושיפורים מערכת תבניות (templates) בהם.</li> <li>אפשרות ניהול session ללא עוגיות (למקרה והמשתמש ממש צריך)</li> <li>שילוב ניהול ה-session מול טכנולוגיות אחרות כולל מימוש עבור PHP, Java Servlet, ASP.Net ו-Python Django</li> <li>שיפורי ביצועים רבים</li> <li>ועוד...</li> </ul> <p>התכנון הוא לבצע בדיקת בטא מהירה, לשחרר 1.2 כגרסה יציבה (גם ככה חלק ניכר מהמשתמשים כבר עובד בענף הפיתוח ב-git).</p> <p>לאחר מכן אני רוצה לעבור לפיתוח בחלקים יותר קטנים ללא השהיות גדולות בין הגרסאות. בהמשך <a href="http://cppcms.com/wikipp/en/page/cppcms_1x_tasks">מתכנון</a>:</p> <ul> <li>ל-1.4 תמיכה מלאה ב-HTTP/1.1, ב-HTTPS ומימוש של websockets</li> <li>ל-2.0 להעביר CppCMS ל-C++11 תוך ניקוי חלק מה-API שילקח מספריה סטנדרטית כמו <code>std::shared_ptr</code></li> </ul> <p>אתם מוזמנים להתנסות בגרסת בטא האחרונה!</p> </div> שילוב בין טכנולוגיות Web שונות ו-CppCMS http://artyom.cppcms.com/post/317 http://artyom.cppcms.com/post/317 <div style="direction:rtl"> <p>כשיש לך מערכת ווב גדולה ומפותחת ואתה צריך להעביר חלקים קריטיים ממנה לטכנולוגיה מהירה יותר (קרי CppCMS) אתה נתקל קודם כל בבעיה הבסיסית - איך לשתף מידע.</p> <p>נתונים גולמיים זה קל - יש מסדי נתונים, יש קבצים - אין פה משהו מסובך, אבל אתה צריך גם לשתף מידע על המשתמש ועל ה-session שלו בין הטכנולוגיות ופה כל אחד ממש את מה שהוא צריך בצורה אחרת. מצד שני זה מאוד חיוני לשתף מידע קריטי כמו זיהוי משתמש בצורה מאובטחת.</p> <p>לכן, יצרתי שכבת תיאמות שמאפשרת לגשת ל-Session של CppCMS מכל שפת תכנות אחרת.</p> <p>בשלב ראשון עדכנתי את המחלקות הקשירות לניהול ה-Session של CppCMS שיוכלו לפעול בצורה בלתי תלויה מהמערכת. <a href="http://sourceforge.net/p/cppcms/code/HEAD/tree/framework/trunk/cppcms/capi/session.h">יצרתי API נקי</a> מבוסס C בלבד כדי שלא יהיו בעיות כמו חריגות (exceptions) האופייניות ל-++C וגם כדי שאפשר היה לטעון את הפונקיות ישירות בעזרת dlopen.</p> <p>אחר כך יצרתי מספר מודולים עבור שפות שונות:</p> <ul> <li>PHP עם שימוש ב-Swig</li> <li>Java/Servlet עם שימוש ב-JNA</li> <li>Python עם שימוש ב-ctypes והתממשקות עם Django (אבל לא מוגבל ל-Django)</li> <li>Asp.Net עם שימוש ב-PInvoke</li> </ul> <p>כמובן זה לא מוגבל אליהם בלבד. אבל לכל שפה חדשה צריך לבנות מעטפת. שקלתי לעשות גם ל-Ruby on Rails אבל ויתרתי לבינתיים כי אין לי ניסיון עם Ruby בכלל, אז שמישהו יתרום בעת הצורך.</p> <p>חייב להגיד לכל טכנולויה היו בעיות משלה... למשל ב-Python היו התנהגויות מוזרות כשניסיתי לבנות מתודות באופן דינאמי, ב-Java/JNA הכל עבר חלק להבפליא. PInvoke שיגע אותי עם חוסר רצון שלו להמיר UTF-8 ל-string ובחזרה (אגב Mono כן עושה זאת בצורה שקופה אבל לא Net. של Windows מתעקש להשתמש בקידוד ANSI). יצירת מודולים עם Swig עבדה לא רע בכלל, אבל נדרשת עוד מעטפת כדי להתאים את הכל בסופו של דבר לשפה עצמה והתנהגותה.</p> <p>ואיך זה נראה</p> <p>PHP:</p> <pre><code>// pool initialization $pool=CppCMS_SessionPool::from_config('cppcms-config.js'); // per request session access $session=$pool-&gt;session(); $session-&gt;load(); $x=0; if($session-&gt;is_set('x')) { $x=$session['x']; } $x=intval($x)+1; $session['x']=$x; $session-&gt;save(); ... </code></pre> <p>Java/Servlet:</p> <pre><code>static SessionPool pool; public void init() throws ServletException { pool = SessionPool.openFromConfig("/path/to/cppcms-config.js"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Session session = pool.getSession(); session.load(request); String x="0"; if(session.isSet("x")) x=session.get("x"); x=Integer.toString(Integer.parseInt(x)+1); session.set("x",x); session.save(response); session.close(); ... } </code></pre> <p>Python/Django:</p> <pre><code># Create global pool pool=cppcms.SessionPool('/path/to/cppcms-config.js') # Actual view def home(request): s=pool.session() s.load(django_request=request) v='0' if 'x' in s: v= s['x'] s['x']=str(int(v)+1) response = HttpResponse() s.save(django_response=response) ... </code></pre> <p>C#/ASP.Net:</p> <pre><code>static SessionPool pool; static Example() { pool = SessionPool.FromConfig("cppcms-config.js"); } protected void Page_Load(object sender,EventArgs e) { using(Session s = pool.Session()) { s.Load(Request); string v="0"; if(s.IsSet("x")) v=s["x"]; v = (int.Parse(v) + 1).ToString(); s["x"]=v; s.Save(Response); } ... } </code></pre> <p>אפשר לראות את הקוד והדוגמאות המלאות <a href="http://sourceforge.net/p/cppcms/code/HEAD/tree/framework/trunk/contrib/integration/session/">כאן</a></p> <p>אני עדיין תוהה עם איזו עוד פלטפורמה כדי לעשות אינטגרציה. בסופו של דבר חשובה לי הפשוטות והנוחות של העבודה.</p> </div> לשרת את כל אתרי החדשות בארץ בעזרת... CppCMS http://artyom.cppcms.com/post/315 http://artyom.cppcms.com/post/315 <div style="direction:rtl"> <p>אני לא יודע אם שמתם לב, אבל בשנה האחרונה קצב הפיתוח של תשתית CppCMS ירד בצורה ניכרת. ריכזתי את מרבית המאמצים במערכת פרסום מיוחדת הבנויה על תשתית ה־CppCMS שפותח עבור לקוח.</p> <p>הפרויקט נקרא <a href="http://linicom.co.il">ליניקום</a>.</p> <p>היום, כשליניקום כבר פעיל זמן רב ומתוחזק ע"י צוות מורחב, אני אוכל להקדיש יותר זמן לתשתית CppCMS עצמה.</p> <p>מספר מילים על "ליניקום":</p> <p>"ליניקום" הוא מנוע שמביא פרסומות תלויות תוכן ומותאמות למשתמש לאתרים שונים בקלות רבה. מרבית אתרי החדשות הגדולים בארץ משתמשים בשירותי ליניקום, ביניהם: ynet, הארץ, מאקו, Jerusalem Post, ואללה ועוד רבים אחרים כולל מספר אתרים גדולים בחו"ל.</p> <p>להלן כמה עובדות מעניינות:</p> <ul> <li>ליניקום מבוססת על טכנולוגית CppCMS</li> <li>המערכת משרתת כ־10,000,000 פניות המותאמות למשתמש ביום - קרי כ־115 פניות בשניה.</li> <li>בשעות העמוסות הקצב מגיע לכ־160 פניות בשניה.</li> <li>השרת מייצר תעבורה יוצאת ממוצעת של כ־11 מגאביט בשניה.</li> <li>צריכת זיכרון הכוללת של המערכת (שרת וואב, בסיס נתונים, יישום, מערכת ההפעלה) הוא בסביבות 360MB</li> <li>העומס הממוצע על המעבדים הוא כ-5%</li> <li>השרת רץ על c1.medium instance בודד ב־Amazon EC2</li> </ul> <p>המערכת רצה מאחורי lighttpd ומשתמשת ב־PosgreSQL לשמירה וניהול הנתונים בצורה אינטנסיבית, עם זאת, מרבית הנתונים הנדרשים בזמן אמת שמורים ומנוהלים בזיכרון.</p> <p>כמעט כל פניה לשרת דורשת עיבוד נתונים על מנת לספק פרסומות מותאמות אישית, מבחינה טכנית, זה אומר שלא ניתן לעשות "מיקור חוץ" של הפניות האלה לקבצים הסטטיים וכל פניה של כל לקוח צריכה להיות מטופלת בנפרד.</p> <p>מערכת הפרסום הזו, היא הדוגמה הקלאסית לשימוש בטכנולוגיית CppCMS - מערכת שצריכה להיות מהירה ואפקטיבית. מערכת שמסוגל להתמודד עם עומסים גבוהים ולעתים חריגים ללא בעיות ולספק איכות השירות גבוהה ביותר.</p> <p>שימוש בנתונים השמורים בזיכרון, ניהול נתונים שלא יכולים להיות שמורים בזיכרון מטמון - זה המקום בו יכולות CppCMS באות לידי ביטוי במלואן. יכולת גדילה גבוהה עם דרישות תחזוקה מינימליות, אמינות גבוהה - האם אלה שמאפשרים לדאוג לצד העסקי בלי לחשוב על בעיות ביצועים אפשריות.</p> </div> שוחררה גרסה 1.0.3 של CppCMS הכוללת תיקון של פריצת אבטחה http://artyom.cppcms.com/post/313 http://artyom.cppcms.com/post/313 <div style="direction:rtl"> <p>היום שוחררה גרסה 1.0.3 של CppCMS שמתקנת פריצת אבטחה במנגנון בדיקת תקינות קלט UTF-8, שאפשר למערכת לקלוט מחרוזות לא תקינות. באג כזה יכול בתיאוריה להביא לפריצת XSS.</p> <p>מומלץ לשדרג CppCMS לגרסה האחרונה.</p> <p>המקור של הבאג נמצא ב־Boost.Locale. הבאג תוקן בגרסה 1.53 שעומדת להשתחרר בקרוב. מי שמשתמש בפונקציות של Boost.Locale לבדיקת תקינות הקלט UTF-8 צריך לקחת את אפשרות פריצת האבטחה הזו בחשבון.</p> <p>מי שלא יכול לשדרג את גרסת ה־Boost יכול להשתמש בטלאי הבא:</p> <p><a href="http://cppcms.com/files/locale/boost_locale_utf.patch">http://cppcms.com/files/locale/boost_locale_utf.patch</a></p> </div> Comet בצורה שפויה http://artyom.cppcms.com/post/311 http://artyom.cppcms.com/post/311 <div style="direction:rtl"> <h3>‏ HTML5 ו־Comet</h3> <p>היום HTML5 מציע שני כלים עיקריים ליישומי <a href="http://en.wikipedia.org/wiki/Comet_%28programming%29">‏Comet‏</a>:</p> <ul> <li>טכנולוגיית <a href="http://en.wikipedia.org/wiki/WebSocket">WebSockets‏</a></li> <li>טכנולוגיית <a href="http://dev.w3.org/html5/eventsource/">Server-Sent Events‏</a></li> </ul> <p>מבחינה טכנית, WebSockets ‏(WS) מייצרים קשר דו־כיווני מלא ומאפשרים הן לשרת והן לקוח לשלוח הודעות בזמן אמת - בלי לפתוח קשרים נוספים. לעומת זאת Server-Sent Events‏ (SSE) זהו קשר חד־כיווני, בו השרת הוא זה ששולח אירועים ללקוח ואם הלקוח צריך לשלוח משהו לשרת, הוא משתשמש בכלי Ajax נוכחיים כמו XMLHttpRequest‏ (XHR).</p> <p>למען האמת, עד עכשיו יצא לי לעקוב אחרי התפתחות של WS. הפרוטוקול החליף מספר גרסאות, הדפדפנים שינו את המימושים בהתאם עד שהפרוטוקול התייצב. כיום התמיכה ב־WS קיימת ברוב הדפדפנים עדכניים ואמורה להיכנס גם ל־IE10. לעומת זאת, כל הנושא של SSE חלף על פניי ולא שמתי עליו לב.</p> <p>כיוון שאני מעסק בצד השרת בעיקר, הכנסתי את נושא ה־WS <a href="http://cppcms.com/wikipp/en/page/cppcms_1x_tasks#Provide.WebSockets.support">לתכנית העבודה שלי</a>. אבל עדיין לא הצלחתי להגיע אליו; ולא במקרה.</p> <p>למרות העתיד ה"זוהר" של WS, המימוש הוא לא פשוט. לא מדובר כאן בבעיה טכנית, הרי תמיכה ב־Comet כבר <a href="http://cppcms.com/wikipp/en/page/cppcms_1x#Comet.Programming">קיימת ועובדת יפה</a>, הבעיה היא בעיה מהותית: <strong>כדי לממש WS צריך לשנות פרוטוקול HTTP.</strong> ברגע ש"לחיצה היד" של WS נגמרת, לא מדובר עוד ב־HTTP, אלא בפרוטוקול שונה לחלוטין.</p> <p>לכן, הכלים ופרוטוקולים שבעזרתם יישומים מתקשרים עם שרתי Web, לא מתאימים. למשל, אי אפשר להעביר תקשורת של WS מעל FastCGI, SCGI או CGI. גם אם היישום שלך עובד ב־HTTP, לא כל שרת web ידע להתמודד עם הבעיה: החלפת ה־HTTP בפרוטוקול חלופי.</p> <p>לכן, אפילו ש־WS, זאת טכנולוגיה מבטיחה, היא עדיין רחוקה מהבשלות האמתית.</p> <p>לעומת זאת, SSE, שנדחקו הצדה, לא סובלים מהבעיות האלה!</p> <ul> <li>SSE לא משנים את פרוטוקול ה־HTTP, לכן, אין בעיות שימוש בתשתיות הקיימות.</li> <li>הביצועים של SSE לא נופלים על אלה של WS.</li> <li><p>SSE הרבה יותר קלים לתפעול:</p> <ul> <li>מכילים מנגנון סנכרון אוטומטי, במקרה של התנתקות</li> <li>מאפשרים לשלוח אירועים שונים ברמת JavaScript בלי שכבות נוספות</li> <li>ניתן לסגת <strong>בקלות</strong> ל־Long Polling עם XHR פשוט במקרה שהדפדפן לא תומך ב־SSE.</li> </ul> </li> </ul> <p>החיסרון היחידי של SSE לעומת WS: לא ניתן לשלוח הודעות באותו הקשר לשרת. אלא לכל הודעה לשרת צריך לשלוח XHR משלו. אבל? SSE מכסה את הרוב המקרים של שימוש ב־Comet!</p> <p>המימוש? קיים ב־Firefox,‏ Opera‏, Chrome‏, Safari ואמור להיכנס ל־IE10</p> <h3>אז איך זה עובד</h3> <p>בואו נדגים אפליקציה שמציגה את מחיר המניה העדכני ביותר בדפדפן:</p> <p>בצד הלקוח, אנחנו נכתוב את הקוד הפשוט הבא:</p> <pre><code>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(); </code></pre> <p>אנחנו פותחים EventSource ועל כל אירוע שמכיל מידע אנחנו מעדכנים שדה html עם המחיר העדכני ביותר.</p> <p>בצד השרת הנושא שקצת יותר מורכב כי אנחנו צריכים לנהל מספר קשרים:</p> <p>כשנכנס קשר חדש, אנחנו מכינים אותו - מגדירים את ה-Content-Type כ־<code>text/event-stream</code>, מבטלים caching בדרך.</p> <p>לוקחים את ה־id של הערך הידוע האחרון, זה יאפשר לנו לדעת אם הלקוח התנתק אז אם הוא יודע את המחיר העדכני ביותר או לא. אם הערך שונה מהערך הנוכחי, אנחנו שולחים עדכון, אחרת מכניסית אותו ל"רשימת המתנה" - כל הלקוחות הממתינים לעדכון.</p> <pre><code>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); } </code></pre> <p>כיצד אנחנו שולחים עדכונים:</p> <p>התוכן מורכב מה־id - של המחיר - מנגנון הסנכרון שלנו והתוכן - המחיר עצמו.</p> <pre><code>void async_send(booster::shared_ptr&lt;cppcms::http::context&gt; waiter) { waiter-&gt;response().out() &lt;&lt; "id:" &lt;&lt; counter_ &lt;&lt;"\n" "data:" &lt;&lt; price_ &lt;&lt; "\n" "\n"; </code></pre> <p>אחר כך אנחנו מגדירים completion-handler שיבדוק אם פעולה הצליח (אם לא אז קשר נסגר אין מה לעשות) ואם כן, ואין עדכונים נוספים ומכניסים אותו לרשימת ההמתנה.</p> <pre><code> waiter-&gt;async_flush_output([=,counter_](cppcms::http::context::completion_type status){ if(status!=0) return; if(counter_ != this-&gt;counter_) { this-&gt;async_send(waiter); } else { this-&gt;waiters_.insert(waiter); } }); } </code></pre> <p>עכשיו נכתוב פונקציה קטנה שמאפשרת לנו לעדכן את אלו שממתינים למחיר חדש:</p> <pre><code>void update_price(double new_one) { counter_++; price_ = new_one; for(auto waiter : waiters_) { async_send(waiter); } waiters_.clear(); } </code></pre> <p>נעדכן מונה כדי לסמן מחיר חדש, נעבור על הרשימה ונשלח מחיר מעודכן לכולם. מנגנון פשוט שניתן לממש עם כלים פשוטים (במידה והתשתית שלך תומכת ב־Comet).</p> <p>ניתן למצוא <a href="http://cppcms.com/files/ticker/">כאן</a> את הקוד המלא.</p> <h3>חיבור לשרת</h3> <p>כדי שהמנגנון הזה יעבוד צריך לוודא ששרת ה־Web ישלח את התוכן באופן מידי ולא ישמור אותו בזיכרון (בציפייה שעוד מידע יגיע).</p> <ul> <li>Lighttpd עושה את זה בלי בעיה ב־FastCGI,‏ SCGI ו־HTTP כברירת מחדל.</li> <li>Apache עושה את זה כברירת מחדל ב־SCGI ו־HTTP אבל ב־FastCGI דרושה אופציה ‎-flush ואז הכל תקין.</li> </ul> <p>לעומת זאת, "Nginx המהולל" עושה בעיות. בפרוטוקולים SCGI ו־HTTP ניתן לבטל buffering עם אופציה <code>http_buffering off</code> או <code>scgi_buffering off</code> אבל ב־FastCGI, שהוא הפרוטוקול הכי נפוץ בעבודה עם שרת Web <a href="http://trac.nginx.org/nginx/ticket/159">אופציה כזו לא קיימת!</a> כרגיל, Nginx מפתיע... (או שלא).</p> <p>אז כרגיל, עוד סיבה טובה <strong>לא להשתמש ב־Nginx</strong>. אז אני אחזור את הטענה שלי: <a href="http://artyom.cppcms.com/post/303">Nginx, תודה לא, Lighttpd‏</a>.</p> </div> מצגת מאוגוסט פינגווין 2012 http://artyom.cppcms.com/post/310 http://artyom.cppcms.com/post/310 <div style="direction:rtl"> <p>המצגת של ההרצאה שלי באוגוסט פינגווין 2012 על CppCMS נמצאת <a href="http://cppcms.com/files/ap2012/">כאן</a></p> <p>גרסת ‏<a href="http://cppcms.com/files/ap2012/ap-cppcms-for-dw.odp">ODF‏</a>, גרסת <a href="http://cppcms.com/files/ap2012/ap-cppcms-for-dw.pdf">‏PDF‏</a></p> <p>רישיון: ייחוס-שיתוף זהה 1.0 ישראל של Creative Commons‏</p> </div> לבנות RPM להרבה הפצות http://artyom.cppcms.com/post/306 http://artyom.cppcms.com/post/306 <div style="direction:rtl"> <p>כשאתה מפתח הפרויקט קוד־פתוח, קל מאוד להפיץ קוד מקור, אבל כשזה מגיע לקבצים בינאריים, זה הופך לבעיה הרבה יותר משמעותית. יש עשרות הפצות, כל אחת מגיע במספר גרסאות שונות ומביא אתה חבילות טיפה שונות. לכן, לאדם אחד זה כמעט ובלתי אפשרי לבנות חבילות לכל הפצה אפשרית.</p> <p>בזכות debootstrap היה לי יחסית קל לבנות חבילות deb ל־Debian ו־Ubuntu, אבל המצב הרבה יותר מורכב כשמדובר ב־rpm כי אין דרך קלה לשים את הפצת rpm בספריה ולעשות לתוכה chroot.</p> <p>בהמלצת שגיא בן־עקיבא התחלתי להשתמש ב־<a href="https://build.opensuse.org/">Open Build Service</a> של OpenSuse.</p> <p>האמת, אני מאוד מרוצה! כל מה שצריך זה להעלות Source RPM או קובץ spec, כל השאר ייעשה בצורה אוטומטית: בניה למספר הפצות ופלטפורמות, הכנת מקורות מסודרים ואפילו אתה מקבל repository מסודר.</p> <p>בצורה כזו הכנתי rpmים ל־3 הפצות (Fedora, Suse, CentOS) כולל מספר גרסאות וגם הכל עבור שתי ארכיטקטורות: x86 ו־x86_64.</p> <p><a href="http://download.opensuse.org/repositories/home:/artyom-beilis/">http://download.opensuse.org/repositories/home:/artyom-beilis/</a></p> <p>מה שנותר... להבין כיצד משתמשים בשירות עבור debים</p> </div>