הבלוג של ארתיום
בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא!
שילוב בין טכנולוגיות Web שונות ו-CppCMS
כשיש לך מערכת ווב גדולה ומפותחת ואתה צריך להעביר חלקים קריטיים ממנה לטכנולוגיה מהירה יותר (קרי CppCMS) אתה נתקל קודם כל בבעיה הבסיסית - איך לשתף מידע.
נתונים גולמיים זה קל - יש מסדי נתונים, יש קבצים - אין פה משהו מסובך, אבל אתה צריך גם לשתף מידע על המשתמש ועל ה-session שלו בין הטכנולוגיות ופה כל אחד ממש את מה שהוא צריך בצורה אחרת. מצד שני זה מאוד חיוני לשתף מידע קריטי כמו זיהוי משתמש בצורה מאובטחת.
לכן, יצרתי שכבת תיאמות שמאפשרת לגשת ל-Session של CppCMS מכל שפת תכנות אחרת.
בשלב ראשון עדכנתי את המחלקות הקשירות לניהול ה-Session של CppCMS שיוכלו לפעול בצורה בלתי תלויה מהמערכת. יצרתי API נקי מבוסס C בלבד כדי שלא יהיו בעיות כמו חריגות (exceptions) האופייניות ל-++C וגם כדי שאפשר היה לטעון את הפונקיות ישירות בעזרת dlopen.
אחר כך יצרתי מספר מודולים עבור שפות שונות:
- PHP עם שימוש ב-Swig
- Java/Servlet עם שימוש ב-JNA
- Python עם שימוש ב-ctypes והתממשקות עם Django (אבל לא מוגבל ל-Django)
- Asp.Net עם שימוש ב-PInvoke
כמובן זה לא מוגבל אליהם בלבד. אבל לכל שפה חדשה צריך לבנות מעטפת. שקלתי לעשות גם ל-Ruby on Rails אבל ויתרתי לבינתיים כי אין לי ניסיון עם Ruby בכלל, אז שמישהו יתרום בעת הצורך.
חייב להגיד לכל טכנולויה היו בעיות משלה... למשל ב-Python היו התנהגויות מוזרות כשניסיתי לבנות מתודות באופן דינאמי, ב-Java/JNA הכל עבר חלק להבפליא. PInvoke שיגע אותי עם חוסר רצון שלו להמיר UTF-8 ל-string ובחזרה (אגב Mono כן עושה זאת בצורה שקופה אבל לא Net. של Windows מתעקש להשתמש בקידוד ANSI). יצירת מודולים עם Swig עבדה לא רע בכלל, אבל נדרשת עוד מעטפת כדי להתאים את הכל בסופו של דבר לשפה עצמה והתנהגותה.
ואיך זה נראה
PHP:
// pool initialization
$pool=CppCMS_SessionPool::from_config('cppcms-config.js');
// per request session access
$session=$pool->session();
$session->load();
$x=0;
if($session->is_set('x')) {
$x=$session['x'];
}
$x=intval($x)+1;
$session['x']=$x;
$session->save();
...
Java/Servlet:
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();
...
}
Python/Django:
# 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)
...
C#/ASP.Net:
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);
}
...
}
אפשר לראות את הקוד והדוגמאות המלאות כאן
אני עדיין תוהה עם איזו עוד פלטפורמה כדי לעשות אינטגרציה. בסופו של דבר חשובה לי הפשוטות והנוחות של העבודה.
תגובות
ומה עם תמיכה בגו שאני עובד איתו המון? :P
קודם כל אין ל-go גישה טבעית ל-C (הרי חשפתי ממשק C)? חוץ מזה תראה לי web framework פופולרי ב-go. :P
אגב אולי תעזור לי עם ruby ותכתוב איזה מודול עם שימוש ב-swig ;)
יצרתי API נקי מבוסס C בלבד כדי שלא יהיו בעיות כמו חריגות (exceptions) האופייניות ל-++C וגם כדי שאפשר היה לטעון את הפונקיות ישירות בעזרת dlopen.
מה לגבי חריגות SEH, כמדומני אתה בונה ביחד עם EHsc האם זה לא אומר שאם יש לך חריגה שעולה אתה תרסק את php.exe ?
אני עובד עם חריגות סטנדרטיות ולא המצאות של Microsoft גם C API שאני קורא לא זורק חריגות (הכוונה ל-Structured Exceptions). בפרט /EHsc
במילים אחרות מבחינתי קיימות רק חריגות C++ קוד C הוא שקוף כמו שצריך להיות
כאשר אתה מקמפל עם EHsc אתה לא מונע קיום SEH, אתה רק מגדיר שלא יהיה stack unwind במקרה של SEH .
If you use /EHs or /EHsc, then your catch(...) clause does not catch asynchronous structured exceptions. Access violations and managed System.Exception exceptions are not caught, and objects in scope when an asynchronous exception is generated are not destroyed even if the asynchronous exception is handled.
בהנחה ואין לך מקטעי קוד המכילים try ו catch ואתה לא מפעיל se_translator ככל הנראה כאשר תתרחש תקלה שתומר ל SEH (חוסר זיכרון,קידום סוף איטרטור 'null deref או כל דבר בסגנון) ה dtors שלך לא יפעלו וייתכן ש ה SEH יטפס למעלה.
זה מצב כללי שב-C++ התוכנה עפה כמו ב-Linux עם SIGSEGV או עם SIGFPE וכד'. (אגב חוסר זיכרון אתה כן מקבל std::bad_alloc של C++)
שוב - השימוש ב-/EHsc זה סטנדרטי בעולם C++. לדוגמה ב-MinGW זו אותה התנהגות
יש לגו גישה, אבל לא כזו טבעית. יש תת קומפיילר בשם cgo, שצריך לכתוב עבורו דברים. ד"א לדעתי אתה מאוד תאהב את השפה, היא מאוד פשוטה אבל עם המון כוח.
מעולם לא עבדתיעם swig, ברובי אני תרגמתי דברים באמצעות FFI.
מעולה, כי האמת חשבתי ש-ruby-ffi זאת ספריה שולית ולא נפוצה. שום חיפושים לא החזירו לי תשובה נורמלית.
ברור ש-ffi עדיפה (למעשה python-ctags ו-Java JNA שניהם משתמשים ב-FFI) כי לא צריך לשבור את הראש עם קומפילציה של מודולים וכד'.
תודה! (אם כי עדיין לא יודע מתי אגש לזה כי רובי היא די סינית בשבילי)
הוסף תגובה:
חובה לאפשר JavaScript כדי להגיב.