Java生鮮電商平臺-監(jiān)控模塊的設(shè)計與架構(gòu)
來源:https://www.cnblogs.com/jurendage/p/9070442.html
Java開源生鮮電商平臺-監(jiān)控模塊的設(shè)計與架構(gòu),我們談到監(jiān)控,一般設(shè)計到兩個方面的內(nèi)容:
服務(wù)器本身的監(jiān)控。(比如:linux服務(wù)器的CPU,內(nèi)存,磁盤IO等監(jiān)控)
業(yè)務(wù)系統(tǒng)的監(jiān)控. (比如:業(yè)務(wù)系統(tǒng)性能的監(jiān)控,SQL語句的監(jiān)控,請求超時的監(jiān)控,用戶輸入的監(jiān)控,整個請求過程時間的監(jiān)控,優(yōu)化等等)
1. 服務(wù)器本身的監(jiān)控
說明:由于Java開源生鮮電商平臺采用的是阿里云的linux CentOS服務(wù)器,由于阿里云本身是有監(jiān)控預(yù)警的,但是我們不可能時刻去看,最好有集成自己的系統(tǒng)監(jiān)控,
最終在各種系統(tǒng)對比的過程中,選擇了netdata這個工具,當(dāng)然有一些軟件比如:zabbix,negios等等都是可以的,但是我們服務(wù)器壓力不算大,最終采用了更加輕量級的解決方案。
相關(guān)的安裝與使用,大家自行百度處理,我這邊就不列舉出來了。
以下是相關(guān)的實際運營截圖:

??
?
2. 業(yè)務(wù)監(jiān)控
說明:任何一個業(yè)務(wù)系統(tǒng)都需要采用業(yè)務(wù)監(jiān)控,拋異常,有error日志,短信預(yù)警,推送等等
Java內(nèi)存 JavaCPU使用情況 用戶Session數(shù)量 JDBC連接數(shù) http請求、sql請求、jsp頁面與業(yè)務(wù)接口方法(EJB3、Spring、 Guice)的執(zhí)行數(shù)量,平均執(zhí)行時間,錯誤百分比等
最終,業(yè)務(wù)代碼中采用了Spring AOP進(jìn)行日志攔截,把請求方法超過了1500秒的方法進(jìn)行了error日志的輸出:
業(yè)務(wù)代碼如下:
import?org.apache.commons.lang.time.StopWatch;
import?org.aspectj.lang.JoinPoint;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.After;
import?org.aspectj.lang.annotation.AfterReturning;
import?org.aspectj.lang.annotation.AfterThrowing;
import?org.aspectj.lang.annotation.Around;
import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Before;
import?org.aspectj.lang.annotation.Pointcut;
import?org.slf4j.Logger;
import?org.slf4j.LoggerFactory;
import?org.springframework.stereotype.Component;
/**
?*?聲明一個切面,記錄每個Action的執(zhí)行時間
?*/
@Aspect
@Component
public?class?LogAspect?{
????
????private?static?final?Logger?logger=LoggerFactory.getLogger(LogAspect.class);
????
????/**
?????*?切入點:表示在哪個類的哪個方法進(jìn)行切入。配置有切入點表達(dá)式
?????*/
????@Pointcut("execution(*?com.netcai.admin.controller.*.*.*(..))")
????public?void?pointcutExpression()?{
????????logger.debug("配置切入點");
????}
????
????/**
?????*?1?前置通知
?????*?@param?joinPoint
?????*/
????@Before("pointcutExpression()")
????public?void?beforeMethod(JoinPoint?joinPoint)?{
????????logger.debug("前置通知執(zhí)行了");
????}
????
????/**
?????*?2?后置通知
?????*?在方法執(zhí)行之后執(zhí)行的代碼.?無論該方法是否出現(xiàn)異常
?????*/
????@After("pointcutExpression()")?
????public?void?afterMethod(JoinPoint?joinPoint)?{
????????logger.debug("后置通知執(zhí)行了,有異常也會執(zhí)行");
????}
????
????/**
?????*?3?返回通知
?????*?在方法法正常結(jié)束受執(zhí)行的代碼
?????*?返回通知是可以訪問到方法的返回值的!
?????*?@param?joinPoint
?????*?@param?returnValue
?????*/
????@AfterReturning(value?=?"pointcutExpression()",?returning?=?"returnValue")
????public?void?afterRunningMethod(JoinPoint?joinPoint,?Object?returnValue)?{
????????logger.debug("返回通知執(zhí)行,執(zhí)行結(jié)果:"?+?returnValue);
????}
????/**
?????*?4?異常通知
?????*?在目標(biāo)方法出現(xiàn)異常時會執(zhí)行的代碼.
?????*?可以訪問到異常對象;?且可以指定在出現(xiàn)特定異常時在執(zhí)行通知代碼
?????*?@param?joinPoint
?????*?@param?e
?????*/
????@AfterThrowing(value?=?"pointcutExpression()",?throwing?=?"e")
????public?void?afterThrowingMethod(JoinPoint?joinPoint,?Exception?e)
????{
????????logger.debug("異常通知,?出現(xiàn)異常?"?+?e);
????}
????
????/**
?????*?環(huán)繞通知需要攜帶?ProceedingJoinPoint?類型的參數(shù).?
?????*?環(huán)繞通知類似于動態(tài)代理的全過程:?ProceedingJoinPoint?類型的參數(shù)可以決定是否執(zhí)行目標(biāo)方法.
?????*?且環(huán)繞通知必須有返回值,?返回值即為目標(biāo)方法的返回值
?????*/
????@Around("pointcutExpression()")
????public?Object?aroundMethod(ProceedingJoinPoint?pjd)
????{
????????StopWatch?clock?=?new?StopWatch();
????????//返回的結(jié)果
????????Object?result?=?null;
????????//方法名稱
????????String?className=pjd.getTarget().getClass().getName();
????????
????????String?methodName?=?pjd.getSignature().getName();
????????
????????try?
????????{
????????????//?計時開始
????????????clock.start();?
????????????//前置通知
????????????//執(zhí)行目標(biāo)方法
????????????result?=?pjd.proceed();
????????????//返回通知
????????????clock.stop();
????????}?catch?(Throwable?e)?
????????{
????????????//異常通知
????????????e.printStackTrace();
????????}
????????//后置通知
????????if(!methodName.equalsIgnoreCase("initBinder"))
????????{
????????????long?constTime=clock.getTime();
????????????
????????????logger.info("["+className+"]"+"-"?+"["+methodName+"]"+"?花費時間:?"?+constTime+"ms");
????????????
????????????if(constTime>500)
????????????{
????????????????logger.error("["+className+"]"+"-"?+"["+methodName+"]"+"?花費時間過長,請檢查:?"?+constTime+"ms");
????????????}
????????}
????????return?result;
????}
}
補(bǔ)充說明:這個方法記錄那個類,那個方法執(zhí)行的時間多少,超過設(shè)置的閥值,那么就打印error日志,需要我們每天進(jìn)行查看與針對性的優(yōu)化。
3. 對于整個業(yè)務(wù)線的監(jiān)控,我們采用了另外一種開源的監(jiān)控:javamelody
相關(guān)的配置與處理如下:
POM文件中設(shè)置:
<dependency>
????<groupId>net.bull.javamelodygroupId>
????<artifactId>javamelody-coreartifactId>
????<version>1.68.1version>
dependency>
<dependency>
????<groupId>org.jrobingroupId>
????<artifactId>jrobinartifactId>
????<version>1.5.9version>
dependency>
web.xml文件中處理
<context-param>
????<param-name>contextConfigLocationparam-name>
????<param-value>
????????classpath*:config/applicationContext.xml
????????classpath*:net/bull/javamelody/monitoring-spring.xml
????????classpath*:net/bull/javamelody/monitoring-spring-datasource.xml
????????classpath*:net/bull/javamelody/monitoring-spring-aspectj.xml
????param-value>
context-param>
<filter>
????<filter-name>monitoringfilter-name>
????<filter-class>net.bull.javamelody.MonitoringFilterfilter-class>
????<async-supported>trueasync-supported>
????<init-param>
????????<param-name>logEnabledparam-name>
????????<param-value>trueparam-value>
????init-param>
filter>
<filter-mapping>
????<filter-name>monitoringfilter-name>
????<url-pattern>/*url-pattern>
filter-mapping>
<listener>
????<listener-class>net.bull.javamelody.SessionListenerlistener-class>
listener>
最終運營效果如下:


?
?
?
?
總結(jié):最終可以形成一套基于自己的監(jiān)控系統(tǒng),當(dāng)然還有類似的更加強(qiáng)大的監(jiān)控系統(tǒng),比如:連接池方面druid,系統(tǒng)方面zabbix,業(yè)務(wù)方面可以用cat等等,甚至開發(fā)采用自己的監(jiān)控系統(tǒng)也是可以的。也是支持二次開發(fā)的。
