מדדים עבור ה- REST API שלך באביב

REST למעלה

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

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

במדריך זה נשלב מדדים בסיסיים לממשק REST API של Spring.

נבנה את הפונקציונליות המדדית תחילה באמצעות מסנני Servlet פשוטים, ואז באמצעות מפעיל אתחול האביב.

2. ה web.xml

נתחיל ברישום מסנן - “MetricFilter" - לתוך ה web.xml של האפליקציה שלנו:

 metricFilter org.baeldung.web.metric.MetricFilter metricFilter / * 

שים לב כיצד אנו ממפים את המסנן כדי לכסות את כל הבקשות שנכנסות - “/*” - שכמובן ניתן להגדרה מלאה.

3. מסנן סרוולט

עכשיו - בואו ניצור את המסנן המותאם אישית שלנו:

מחלקה ציבורית MetricFilter מיישמת את המסנן {metricService הפרטי MetricService; @Override init ריק ריק (FilterConfig config) זורק ServletException {metricService = (MetricService) WebApplicationContextUtils .getRequiredWebApplicationContext (config.getServletContext ()) .getBean ("metricService"); } @Override public void doFilter (בקשת ServletRequest, ServletResponse, שרשרת FilterChain) זורק java.io.IOException, ServletException {HttpServletRequest httpRequest = (((HttpServletRequest) בקשה); מחרוזת req = httpRequest.getMethod () + "" + httpRequest.getRequestURI (); chain.doFilter (בקשה, תגובה); int status = ((HttpServletResponse) תגובה). getStatus (); metricService.increaseCount (req, status); }}

מכיוון שהמסנן אינו שעועית רגילה, אנחנו לא הולכים להזריק את metricService אלא במקום לאחזר אותו ידנית - דרך ה- ServletContext.

שים לב גם שאנו ממשיכים בביצוע שרשרת המסננים על ידי קריאה ל doFilter ממשק API כאן.

4. מדד - ספירת קוד סטטוס

הבא - בואו נסתכל על הפשטות שלנו שירות MetricService:

@Service בכיתה ציבורית MetricService {private ConcurrentMap statusMetric; ציבור MetricService () {statusMetric = חדש ConcurrentHashMap (); } ריבוי חלל ציבורי (בקשת מחרוזת, מצב int) {מספר שלם statusCount = statusMetric.get (סטטוס); אם (statusCount == null) {statusMetric.put (status, 1); } אחר {statusMetric.put (status, statusCount + 1); }} מפה ציבורית getStatusMetric () {return statusMetric; }}

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

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

@RequestMapping (value = "/ status-metric", method = RequestMethod.GET) @ResponseBody מפה ציבורית getStatusMetric () {return metricService.getStatusMetric (); }

והנה תגובה לדוגמא:

{ "404":1, "200":6, "409":1 }

5. מדד - קודי סטטוס לפי בקשה

הבא - בואו נרשום מדדים לספירות לפי בקשה:

@Service בכיתה ציבורית MetricService {פרטי ConcurrentMap פרטי metricMap; ריבוי חלל ציבוריCount (בקשת מחרוזת, סטטוס int) {ConcurrentHashMap statusMap = metricMap.get (בקשה); אם (statusMap == null) {statusMap = ConcurrentHashMap חדש (); } ספירת מספרים שלמים = statusMap.get (סטטוס); if (count == null) {count = 1; } אחר {count ++; } statusMap.put (סטטוס, ספירה); metricMap.put (בקשה, statusMap); } מפה ציבורית getFullMetric () {return metricMap; }}

אנו נציג את תוצאות המדדים דרך ה- API:

@RequestMapping (value = "/ metric", method = RequestMethod.GET) @ResponseBody מפה ציבורית getMetric () {return metricService.getFullMetric (); }

כך נראים המדדים האלה:

{"GET / משתמשים": {"200": 6, "409": 1}, "GET / משתמשים / 1": {"404": 1}}

על פי הדוגמה לעיל ל- API הייתה הפעילות הבאה:

  • בקשות "7" ל- "GET / משתמשים
  • "6" מהם הביאו לתגובות קוד מצב "200" ורק אחת ל"409 "

6. מדד - נתוני סדרות זמן

ספירות כלליות מועילות במקצת ביישום, אך אם המערכת פועלת במשך זמן משמעותי - קשה לדעת מה המשמעות של המדדים הללו.

אתה צריך את הקשר הזמן על מנת שהנתונים יהיו הגיוניים ויתפרשו בקלות.

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

@Service בכיתה ציבורית MetricService {פרטי ConcurrentMap פרטי timeMap; פרטי סטטי SimpleDateFormat dateFormat = חדש SimpleDateFormat ("yyyy-MM-dd HH: mm"); רווח ציבורי ריבוי מספר (בקשת מחרוזת, מצב int) {מחרוזת זמן = dateFormat.format (תאריך חדש ()); ConcurrentHashMap statusMap = timeMap.get (time); אם (statusMap == null) {statusMap = ConcurrentHashMap חדש (); } ספירת מספרים שלמים = statusMap.get (סטטוס); if (count == null) {count = 1; } אחר {count ++; } statusMap.put (סטטוס, ספירה); timeMap.put (time, statusMap); }}

וה getGraphData ():

אובייקט ציבורי [] [] getGraphData () {int colCount = statusMetric.keySet (). size () + 1; הגדר allStatus = statusMetric.keySet (); int rowCount = timeMap.keySet (). size () + 1; אובייקט [] [] תוצאה = אובייקט חדש [rowCount] [colCount]; תוצאה [0] [0] = "זמן"; int j = 1; עבור (int status: allStatus) {result [0] [j] = status; j ++; } int i = 1; ConcurrentMap tempMap; עבור (כניסה entry: timeMap.entrySet ()) {result [i] [0] = entry.getKey (); tempMap = entry.getValue (); עבור (j = 1; j <colCount; j ++) {תוצאה [i] [j] = tempMap.get (תוצאה [0] [j]); אם (תוצאה [i] [j] == null) {תוצאה [i] [j] = 0; }} i ++; } להחזיר תוצאה; }

כעת אנו ממפים זאת לממשק ה- API:

@RequestMapping (value = "/ metric-graph-data", method = RequestMethod.GET) @ResponseBody אובייקט ציבורי [] [] getMetricData () {return metricService.getGraphData (); }

ולבסוף - אנו הולכים לעבד את זה באמצעות תרשימים של גוגל:

  גרף מטרי google.load ("ויזואליזציה", "1", {חבילות: ["corechart"]}); function drawChart () {$ .get ("/ metric-graph-data", function (mydata) {var data = google.visualization.arrayToDataTable (mydata); var options = {title: 'מדד אתרים', hAxis: {title : 'זמן', titleTextStyle: {color: '# 333'}}, vAxis: {minValue: 0}}; var chart = new google.visualization.AreaChart (document.getElementById ('chart_div')); chart.draw ( נתונים, אפשרויות);}); } 

7. שימוש באביב 1.x מפעיל

בחלקים הבאים נעבור לפונקציונליות המפעיל ב- Spring Boot כדי להציג את המדדים שלנו.

ראשית - נצטרך להוסיף את תלות המפעיל שלנו pom.xml:

 org.springframework.boot spring-boot-starter-actuator 

7.1. ה MetricFilter

הבא - אנחנו יכולים להפוך את MetricFilter - לפולי אביב אמיתיים:

@Component בכיתה ציבורית MetricFilter מיישם את המסנן {@ MetricService הפרטי האוטומטי; @Override public void doFilter (בקשת ServletRequest, ServletResponse, שרשרת FilterChain) זורק java.io.IOException, ServletException {chain.doFilter (בקשה, תגובה); int status = ((HttpServletResponse) תגובה). getStatus (); metricService.increaseCount (סטטוס); }}

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

7.2. באמצעות שירות דלפק

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

@Service בכיתה הציבורית MetricService {@ מונה CounterService פרטי מאושר; רשימת פרטי רשימת status; חלל ציבורי ריבוי (מעמד int) {counter.increment ("status." + status); אם (! statusList.contains ("counter.status." + status)) {statusList.add ("counter.status." + status); }}}

7.3. ייצוא מדדים באמצעות מאגר נתונים

לאחר מכן - עלינו לייצא את המדדים - באמצעות ה- מאגר מטרי:

@Service בכיתה ציבורית MetricService {@ רישום מטרי פרוטוקול אוטומטי; רשימה פרטית statusMetric; רשימת פרטי רשימת status; @Scheduled (fixedDelay = 60000) exportMetrics ריק () מדד מדד; ArrayList statusCount = ArrayList חדש (); עבור (סטטוס מחרוזת: statusList) {metric = repo.findOne (status); אם (מדד! = null) {statusCount.add (metric.getValue (). intValue ()); repo.reset (סטטוס); } אחר {statusCount.add (0); }} statusMetric.add (statusCount); }}

שים לב שאנחנו מאחסנים ספירות של קודי סטטוס לדקה.

7.4. מגף אביב PublicMetrics

אנחנו יכולים גם להשתמש באביב המגף PublicMetrics לייצא מדדים במקום להשתמש במסננים שלנו - כדלקמן:

ראשית, יש לנו את המשימה המתוזמנת שלנו מדדי יצוא לדקה:

@ MetricReaderPublicMetrics פרטיים אוטומטיים publicMetrics; רשימה פרטית statusMetricsByMinute; רשימת פרטי רשימת status; סופי סטטי פרטי SimpleDateFormat dateFormat = חדש SimpleDateFormat ("yyyy-MM-dd HH: mm"); @Scheduled (fixedDelay = 60000) exportMetrics ריק () {ArrayList lastMinuteStatuses = initializeStatuses (statusList.size ()); עבור (Metric counterMetric: publicMetrics.metrics ()) {updateMetrics (counterMetric, lastMinuteStatuses); } statusMetricsByMinute.add (lastMinuteStatuses); }

אנו, כמובן, צריכים לאתחל את רשימת קודי הסטטוס של HTTP:

פרטי ArrayList initializeStatuses (גודל int) {counterList ArrayList = ArrayList חדש (); עבור (int i = 0; i <size; i ++) {counterList.add (0); } להחזיר counterList; }

ואז באמת נעדכן את המדדים ספירת קוד סטטוס:

update void privateMetrics (Metric counterMetric, ArrayList statusCount) {String status = ""; מדד int = -1; int oldCount = 0; אם (counterMetric.getName (). מכיל ("counter.status.")) {status = counterMetric.getName (). substring (15, 18); // דוגמה 404, 200 appendStatusIfNotExist (status, statusCount); אינדקס = statusList.indexOf (סטטוס); oldCount = statusCount.get (index) == null? 0: statusCount.get (אינדקס); statusCount.set (אינדקס, counterMetric.getValue (). intValue () + oldCount); }} בטל פרטי appendStatusIfNotExist (סטטוס מחרוזת, סטטוס ArrayList) {אם (! statusList.contains (סטטוס)) {statusList.add (סטטוס); statusCount.add (0); }}

ציין זאת:

  • PublicMetics שם מונה סטטוס התחל עם "counter.status" לדוגמה "counter.status.200.root
  • אנו רושמים רשימת ספירת סטטוסים לדקה ברשימה שלנו statusMetricsByMinute

אנו יכולים לייצא את הנתונים שנאספו כדי לצייר אותם בתרשים - כדלהלן:

אובייקט ציבורי [] [] getGraphData () {תאריך נוכחי = תאריך חדש (); int colCount = statusList.size () + 1; int rowCount = statusMetricsByMinute.size () + 1; אובייקט [] [] תוצאה = אובייקט חדש [rowCount] [colCount]; תוצאה [0] [0] = "זמן"; int j = 1; עבור (סטטוס מחרוזת: statusList) {result [0] [j] = status; j ++; } עבור (int i = 1; i <rowCount; i ++) {result [i] [0] = dateFormat.format (תאריך חדש (current.getTime () - (60000 * (rowCount - i)))); } רשימת minuteOfStatuses; רשימה אחרונה = ArrayList חדש (); עבור (int i = 1; i <rowCount; i ++) {minuteOfStatuses = statusMetricsByMinute.get (i - 1); עבור (j = 1; j = j? last.get (j - 1): 0); } בעוד (j <colCount) {תוצאה [i] [j] = 0; j ++; } last = minuteOfStatuses; } להחזיר תוצאה; }

7.5. צייר גרף באמצעות מדדים

לבסוף - בואו נציג את המדדים הללו באמצעות מערך דו מימד - כדי שנוכל לגרף אותם:

אובייקט ציבורי [] [] getGraphData () {תאריך נוכחי = תאריך חדש (); int colCount = statusList.size () + 1; int rowCount = statusMetric.size () + 1; אובייקט [] [] תוצאה = אובייקט חדש [rowCount] [colCount]; תוצאה [0] [0] = "זמן"; int j = 1; עבור (סטטוס מחרוזת: statusList) {result [0] [j] = status; j ++; } זמני ArrayList; עבור (int i = 1; i <rowCount; i ++) {temp = statusMetric.get (i - 1); תוצאה [i] [0] = dateFormat.format (תאריך חדש (current.getTime () - (60000 * (rowCount - i)))); עבור (j = 1; j <= temp.size (); j ++) {result [i] [j] = temp.get (j - 1); } בעוד (j <colCount) {תוצאה [i] [j] = 0; j ++; }} להחזיר תוצאה; }

והנה שיטת הבקר שלנו getMetricData ():

@RequestMapping (value = "/ metric-graph-data", method = RequestMethod.GET) @ResponseBody אובייקט ציבורי [] [] getMetricData () {return metricService.getGraphData (); }

והנה תגובה לדוגמא:

[["זמן", "counter.status.302", "counter.status.200", "counter.status.304"], ["2015-03-26 19:59", 3,12,7], ["26/03/2015 20:00", 0,4,1]]

8. שימוש באביב 2.x מפעיל

ב- Spring Boot 2, ממשקי ה- API של Spring Actuator היו עדים לשינוי משמעותי. המדדים של אביב עצמם הוחלפו ב מִיקרוֹמֶטֶר. אז בואו נכתוב את אותה דוגמה למדדים לעיל עם מִיקרוֹמֶטֶר.

8.1. החלפה שירות דלפק עם מד רישום

מכיוון שיישום ה- Boot Boot שלנו כבר תלוי במתנע המפעיל, מיקרומטר כבר מוגדר אוטומטית. אנחנו יכולים להזריק מד רישום במקום שירות דלפק. אנו יכולים להשתמש בסוגים שונים של מטר כדי לתפוס מדדים. ה דֶלְפֵּק הוא אחד המטרים:

@ רישום MeterRegistry פרטי מאושר; רשימת statuslist רשימה פרטית; @ ביטול הרווח הציבורי ריבוי הספירה (סטטוס int סופי) {String counterName = "counter.status." + סטטוס; registry.counter (counterName) .increment (1); אם (! statusList.contains (counterName)) {statusList.add (counterName); }}

8.2. ייצוא ספירות באמצעות מד רישום

במיקרומטר נוכל לייצא את דֶלְפֵּק ערכים באמצעות מד רישום:

@Schedched (fixedDelay = 60000) exportMetrics ריק () {ArrayList statusCount = ArrayList חדש (); עבור (סטטוס מחרוזת: statusList) {חיפוש חיפוש = registry.find (סטטוס); אם (חיפוש! = null) {מונה מונה = חיפוש.מונה (); statusCount.add (counter! = null? ((int) counter.count ()): 0); registry.remove (counter); } אחר {statusCount.add (0); }} statusMetricsByMinute.add (statusCount); }

8.3. פרסום מדדים באמצעות מטר

כעת אנו יכולים גם לפרסם מדדים באמצעות מטרים של מד רישום:

@Scheduled (fixedDelay = 60000) exportMetrics ריק () {ArrayList lastMinuteStatuses = initializeStatuses (statusList.size ()); עבור (Meter counterMetric: publicMetrics.getMeters ()) {updateMetrics (counterMetric, lastMinuteStatuses); } statusMetricsByMinute.add (lastMinuteStatuses); } update void privateMetrics (סופי Meter counterMetric, סופי ArrayList statusCount) {מחרוזת status = ""; מדד int = -1; int oldCount = 0; אם (counterMetric.getId (). getName (). מכיל ("counter.status.")) {status = counterMetric.getId (). getName (). substring (15, 18); // דוגמה 404, 200 appendStatusIfNotExist (status, statusCount); אינדקס = statusList.indexOf (סטטוס); oldCount = statusCount.get (index) == null? 0: statusCount.get (אינדקס); statusCount.set (אינדקס, (int) ((Counter) counterMetric) .count () + oldCount); }}

9. מסקנה

במאמר זה בחנו כמה דרכים פשוטות לבניית כמה יכולות מדדים בסיסיות ליישום אינטרנט של Spring.

שימו לב שהדלפקים אינם בטוחים בחוטים - כך שהם אולי לא יהיו מדויקים בלי להשתמש במשהו כמו מספרים אטומיים. זה היה מכוון רק בגלל שהדלתא צריכה להיות קטנה ודיוק של 100% הוא לא המטרה - אלא לאתר מגמות מוקדמות.

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

היישום המלא של מאמר זה נמצא בפרויקט GitHub.

REST תחתון

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

$config[zx-auto] not found$config[zx-overlay] not found