שרת מזח משובץ בג'אווה

1. סקירה כללית

במאמר זה נבחן את ה- מֵזַח סִפְרִיָה. Jetty מספק שרת אינטרנט שיכול לרוץ כמכולה מוטבע ומשתלב בקלות עם ה- javax.servlet סִפְרִיָה.

2. תלות Maven

כדי להתחיל, נוסיף תלות של Maven לספריות שרתים ומזחים:

 org.eclipse.jetty jetty-server 9.4.3.v20170317 org.eclipse.jetty jetty-servlet 9.4.3.v20170317 

3. הפעלת שרת מזח עם סרוולט

הפעלת המכולה המשובצת במזח היא פשוטה. עלינו ליצור חדש שרת התנגד והגדר אותו להתחיל ביציאה נתונה:

מחלקה ציבורית JettyServer {שרת שרת פרטי; התחלה בטלנית ציבורית () זורקת חריג {שרת = שרת חדש (); מחבר ServerConnector = ServerConnector חדש (שרת); connector.setPort (8090); server.setConnectors (מחבר חדש [] {connector}); }

בואו נגיד שאנחנו רוצים ליצור נקודת קצה שתגיב עם קוד מצב HTTP של 200 אם הכל ילך כשורה ועומס מטען פשוט של JSON.

ניצור כיתה המרחיבה את ה- HttpServlet כיתה לטיפול בבקשה כזו; מחלקה זו תהיה מושחלת וחוסמת עד לסיומה:

מחלקה ציבורית BlockingServlet מרחיבה את HttpServlet {מוגן void doGet (HttpServletRequest בקשה, HttpServletResponse תגובה) זורק ServletException, IOException {respons.setContentType ("יישום / json"); response.setStatus (HttpServletResponse.SC_OK); response.getWriter (). println ("{\" סטטוס \ ": \" בסדר \ "}"); }}

לאחר מכן, עלינו לרשום את ה- BlockingServlet בכיתה ב סרוולטהנדלר אובייקט באמצעות addServletWithMapping () שיטה והפעל את השרת:

servletHandler.addServletWithMapping (BlockingServlet.class, "/ status"); server.start ();

אם אנו רוצים לבדוק את ההיגיון שלנו ב- Servlet, עלינו להפעיל את השרת שלנו באמצעות הקוד שנוצר בעבר JettyServer מחלקה שהיא עטיפה של מופע שרת ה- Jetty בפועל במסגרת הגדרת הבדיקה:

@ לפני התקנת הריק הציבורי () זורקת חריג {jettyServer = JettyServer חדש (); jettyServer.start (); }

לאחר תחילת העבודה, אנו נשלח בקשת HTTP לבדיקה אל ה- /סטָטוּס נקודת סיום:

מחרוזת url = "// localhost: 8090 / status"; לקוח HttpClient = HttpClientBuilder.create (). Build (); HttpGet בקשה = HttpGet חדש (url); תגובה HttpResponse = client.execute (בקשה); assertThat (response.getStatusLine (). getStatusCode ()). isEqualTo (200);

4. סרוולטים שאינם חוסמים

למזח תמיכה טובה בעיבוד בקשות אסינכרוניות.

בואו נגיד שיש לנו משאב עצום שהוא קלט / פלט אינטנסיבי שלוקח הרבה זמן לטעון וחוסם את שרשור הביצוע למשך זמן ניכר. עדיף שאפשר לשחרר את החוט הזה לטפל בבקשות אחרות בינתיים, במקום לחכות למשאב קלט / פלט כלשהו.

כדי לספק היגיון כזה עם המזח, אנו יכולים ליצור סרוולט שישתמש ב- AsyncContext בכיתה על ידי קריאה ל- startAsync () שיטה על HttpServletRequest. קוד זה לא יחסום את השרשור המבצע אלא יבצע את פעולת הקלט / פלט בשרשור נפרד שיחזיר את התוצאה כשהוא מוכן לשימוש ב- AsyncContext.complete () שיטה:

מחלקה ציבורית AsyncServlet מרחיב HttpServlet {מחרוזת סטטית פרטית HEAVY_RESOURCE = "זהו משאב כבד כלשהו שיוגש בצורה לא סינכרונית"; ריק מוגן doGet (בקשת HttpServletRequest, תגובה HttpServletResponse) זורק ServletException, IOException {ByteBuffer content = ByteBuffer.wrap (HEAVY_RESOURCE.getBytes (StandardCharsets.UTF_8)); AsyncContext async = request.startAsync (); ServletOutputStream out = response.getOutputStream (); out.setWriteListener (WriteListener חדש () {@Override public void onWritePossible () זורק IOException {while (out.isReady ()) {if (! content.hasRemaining ()) {response.setStatus (200); async.complete () ; להחזיר;} out.write (content.get ());}} @ ביטול חלל ציבורי onError (Throwable t) {getServletContext (). log ("שגיאת Async", t); async.complete ();}}) ; }}

אנו כותבים את ByteBuffer אל ה OutputStream, וברגע שכל המאגר נכתב אנו מאותתים שהתוצאה מוכנה לחזור ללקוח על ידי הפעלת ה- לְהַשְׁלִים() שיטה.

לאחר מכן, עלינו להוסיף את AsyncServlet כמיפוי סרבל של מזח:

servletHandler.addServletWithMapping (AsyncServlet.class, "/ heavy / async");

כעת אנו יכולים לשלוח בקשה אל / כבד / אסינכרון נקודת קצה - בקשה זו תטופל על ידי המזח בצורה אסינכרונית:

מחרוזת url = "// localhost: 8090 / כבד / async"; לקוח HttpClient = HttpClientBuilder.create (). Build (); HttpGet בקשה = HttpGet חדש (url); תגובה HttpResponse = client.execute (בקשה); assertThat (response.getStatusLine (). getStatusCode ()) .isEqualTo (200); מחרוזת responseContent = IOUtils.toString (r esponse.getEntity (). GetContent (), StandardCharsets.UTF_8); assertThat (responseContent) .isEqualTo ("זהו משאב כבד כלשהו שיוגש באופן אסינכרוני");

כאשר היישום שלנו מטפל בבקשות בצורה אסינכרונית, עלינו להגדיר את מאגר החוטים באופן מפורש. בחלק הבא, נגדיר מֵזַח להשתמש במאגר פתילים מותאם אישית.

5. תצורת מזח

כאשר אנו מריצים את יישום האינטרנט שלנו בהפקה, נרצה אולי לכוון את אופן עיבוד שרת ה- Jetty. זה נעשה על ידי הגדרת מאגר האשכולות והחלתו על שרת המזח שלנו.

לשם כך, יש לנו שלוש הגדרות תצורה שנוכל להגדיר:

  • maxThreads - כדי לציין את מספר האשכולות המרבי שג'טי יכולה ליצור ולהשתמש בבריכה
  • מיני אשכולות - כדי לקבוע את המספר הראשוני של האשכולות בבריכה בהן ג'טי ישתמש
  • idleTimeout - ערך זה באלפיות השנייה מגדיר כמה זמן חוט יכול להיות סרק לפני שהוא נעצר ומוסר ממאגר החוטים. מספר הנושאים שנותרו בבריכה לעולם לא יירד מתחת ל minThreads הגדרה

בעזרת אלה נוכל להגדיר את המוטבע מֵזַח שרת באופן פרוגרמטי על ידי העברת מאגר החוטים שהוגדר ל - Windows שרת בַּנַאִי:

int maxThreads = 100; int minthreads = 10; int idleTimeout = 120; QueuedThreadPool threadPool = חדש QueuedThreadPool (maxThreads, minThreads, idleTimeout); שרת = שרת חדש (threadPool);

ואז, כשאנחנו מתחילים את השרת שלנו הוא ישתמש בשרשור ממאגר שרשורים ספציפי.

6. מסקנה

במדריך מהיר זה ראינו כיצד לשלב שרתים משובצים עם Jetty ובדקנו את יישום האינטרנט שלנו.

כמו תמיד, הקוד זמין ב- GitHub.