Java項(xiàng)目啟動(dòng)成功、失敗信息實(shí)時(shí)反饋提醒問(wèn)題(郵件或者短信)
在開(kāi)發(fā)的流程中必須涉及的項(xiàng)目的部署發(fā)布,這個(gè)過(guò)程也肯定會(huì)有成功失敗,在不同的公司里采用的提醒方式不同。
在說(shuō)明下面的方案前,需要首先說(shuō)明一下基本應(yīng)用的場(chǎng)景。
場(chǎng)景說(shuō)明
1、純后臺(tái)應(yīng)用,現(xiàn)在項(xiàng)目基本都是前后端分離,因?yàn)橐粋€(gè)前端服務(wù)可能會(huì)對(duì)應(yīng)多個(gè)后端服務(wù)的支持,隨著整個(gè)開(kāi)發(fā)大環(huán)境的完善和技術(shù)的成熟,之前那種前后端耦合的應(yīng)用很少,這里不在做具體的考慮。
2、項(xiàng)目的基本架構(gòu)是Spring+Spring MVC+Mybatis,其實(shí)主要是Spring+Spring MVC,因?yàn)橄旅娴膶?shí)現(xiàn)方式在這種模式下模擬,其他環(huán)境可以根據(jù)這個(gè)模式仿照,但是照搬可能存在問(wèn)題,因?yàn)楝F(xiàn)在很多公司都開(kāi)始使用SpringBoot相關(guān)的前沿技術(shù)。
3、環(huán)境說(shuō)明
- 本地測(cè)試環(huán)境,就是IDEA的啟動(dòng)測(cè)試,不多說(shuō);
- 開(kāi)發(fā)測(cè)試環(huán)境,這個(gè)環(huán)境基本用來(lái)聯(lián)調(diào),開(kāi)發(fā)人員發(fā)布項(xiàng)目使用的;
- 測(cè)試環(huán)境,這個(gè)是測(cè)試人員來(lái)用,將開(kāi)發(fā)的代碼拉到測(cè)試環(huán)境,進(jìn)行各種姿勢(shì)的測(cè)試;
- 演示環(huán)境,這個(gè)環(huán)境看各個(gè)公司的定義可能不同,也就是上線(xiàn)前的最后一個(gè)環(huán)境,基本模擬線(xiàn)上環(huán)境,最終驗(yàn)證項(xiàng)目的完整性,有時(shí)候所說(shuō)的灰度、冒煙測(cè)試都會(huì)在這個(gè)環(huán)境執(zhí)行;
- 線(xiàn)上環(huán)境,不解釋。
4、下面的內(nèi)容數(shù)據(jù)自己歪歪,如果存在問(wèn)題,歡迎提建議。
簡(jiǎn)單實(shí)現(xiàn)的幾種方式
在項(xiàng)目中添加一個(gè)主頁(yè),當(dāng)發(fā)布完成后,訪(fǎng)問(wèn)該頁(yè)面是OK就表示發(fā)布成功,反之就是失敗,但是這樣存在很多問(wèn)題,比如在線(xiàn)上環(huán)境,這個(gè)頁(yè)面可能就訪(fǎng)問(wèn)不到(純后臺(tái)應(yīng)用可能不會(huì)提供這個(gè)訪(fǎng)問(wèn)功能)。在本地測(cè)試和開(kāi)發(fā)環(huán)境可以湊合使用。
定時(shí)任務(wù)請(qǐng)求項(xiàng)目,這個(gè)不僅能監(jiān)控到項(xiàng)目啟動(dòng)是否成功,也可以監(jiān)控服務(wù)器宕機(jī)問(wèn)題,但是也存在問(wèn)題,那就是要重新開(kāi)一個(gè)項(xiàng)目,用于發(fā)送請(qǐng)求,另外當(dāng)項(xiàng)目啟動(dòng)的時(shí)候,剛好定時(shí)任務(wù)發(fā)起,此時(shí)是請(qǐng)求不通的,系統(tǒng)會(huì)誤報(bào)啟動(dòng)失敗或者宕機(jī),其實(shí)不是這樣。
日志掃描,通過(guò)對(duì)日志的分析查看日志中的異常,并做分析,給予開(kāi)發(fā)或者維護(hù)人員一個(gè)通知,可以通過(guò)Python腳本等方式執(zhí)行,設(shè)計(jì)到整體項(xiàng)目架構(gòu)的問(wèn)題,不做太多的討論,也不是這個(gè)博客的主要討論范疇,不做太多贅述。
項(xiàng)目中添加邏輯代碼,用于捕獲項(xiàng)目啟動(dòng)加載是否正常,從而判斷項(xiàng)目是否啟動(dòng)成功。(主要討論)
項(xiàng)目啟動(dòng)反饋提醒實(shí)現(xiàn)
web.xml文件中的配置
<servlet> <servlet-name>spring-dispatcher</servlet-name> <servlet-class>com.minuor.service.notice.LocalDispatcherServletDemo</servlet-class> <!-- 配置SpringMVC需要加載的配置文件 spring-xxx.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-dispatcher</servlet-name> <!--默認(rèn)匹配所有的請(qǐng)求 --> <url-pattern>/</url-pattern> </servlet-mapping>
正常應(yīng)該配置spring默認(rèn)的DispatcherServlet,這里需要改為加載重新后的LocalDispatcherServletDemo。
LocalDispatcherServletDemo類(lèi)代碼
package com.minuor.service.notice; import com.minuor.common.utils.JavaMailSendUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.context.i18n.LocaleContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.net.InetAddress; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; /** * @Author: Joker * @Date: 2018/3/19 * @Desc: 重寫(xiě)DispatcherServlet,做系統(tǒng)啟動(dòng)成功失敗監(jiān)控并通知 */ @Slf4j public class LocalDispatcherServletDemo extends DispatcherServlet { public LocalDispatcherServletDemo() { super(); } public LocalDispatcherServletDemo(WebApplicationContext webApplicationContext) { super(webApplicationContext); } @Override protected WebApplicationContext initWebApplicationContext() { WebApplicationContext webApplicationContext; try { webApplicationContext = super.initWebApplicationContext(); sendMsg(Boolean.TRUE); log.info(">>>>>>>webApplicationContext初始化成功~"); } catch (Exception e) { sendMsg(Boolean.FALSE); webApplicationContext = new XmlWebApplicationContext();//設(shè)置臨時(shí)值,避免重復(fù)初始化 log.info(">>>>>>>webApplicationContext初始化失敗~"); } return webApplicationContext; } /** * 發(fā)送郵件 * * @param flag */ private void sendMsg(boolean flag) { try { JavaMailSendUtil sender = new JavaMailSendUtil(); //成功失敗信息 String result = "FAIL"; if (flag) result = "SUCCESS"; //模塊名 String ip = InetAddress.getLocalHost().getHostAddress(); String userDir = System.getProperty("user.dir"); String tempStr = userDir.substring(0, userDir.indexOf(File.separator + "bin")); String userDirModel = tempStr.substring(tempStr.lastIndexOf(File.separator) + 1); //組裝并推送信息 String mailTest = "發(fā)布系統(tǒng):" + "Minuor個(gè)人博客系統(tǒng)" + "<br/>" + "發(fā)布環(huán)境:" + ip + "<br/>" + "模塊名稱(chēng):" + userDirModel + "<br/>" + "發(fā)布時(shí)間:" + new SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new Date()) + "<br/>" + "發(fā)布結(jié)果:" + result + "<br/>" + "溫馨提示:" + "此郵件僅為系統(tǒng)發(fā)布通知郵件,請(qǐng)勿回復(fù)!"; sender.sendEmail("個(gè)人博客系統(tǒng)發(fā)布結(jié)果提醒", mailTest, "xxxx@163.com"); } catch (Exception e) { log.info(">>>>>個(gè)人博客系統(tǒng)發(fā)布結(jié)果預(yù)警郵件推送失敗!異常信息:{}", e); } } @Override public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) { super.setDetectAllHandlerMappings(detectAllHandlerMappings); } @Override protected void initFrameworkServlet() throws ServletException { super.initFrameworkServlet(); } @Override public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) { super.setDetectAllHandlerAdapters(detectAllHandlerAdapters); } @Override public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) { super.setDetectAllHandlerExceptionResolvers(detectAllHandlerExceptionResolvers); } @Override public void setDetectAllViewResolvers(boolean detectAllViewResolvers) { super.setDetectAllViewResolvers(detectAllViewResolvers); } @Override public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) { super.setThrowExceptionIfNoHandlerFound(throwExceptionIfNoHandlerFound); } @Override public void setCleanupAfterInclude(boolean cleanupAfterInclude) { super.setCleanupAfterInclude(cleanupAfterInclude); } @Override protected void onRefresh(ApplicationContext context) { super.onRefresh(context); } @Override protected void initStrategies(ApplicationContext context) { super.initStrategies(context); } @Override protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) { return super.getDefaultStrategy(context, strategyInterface); } @Override protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { return super.getDefaultStrategies(context, strategyInterface); } @Override protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { return super.createDefaultStrategy(context, clazz); } @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { super.doService(request, response); } @Override protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { super.doDispatch(request, response); } @Override protected LocaleContext buildLocaleContext(HttpServletRequest request) { return super.buildLocaleContext(request); } @Override protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { return super.checkMultipart(request); } @Override protected void cleanupMultipart(HttpServletRequest request) { super.cleanupMultipart(request); } @Override protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { return super.getHandler(request); } @Override protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { super.noHandlerFound(request, response); } @Override protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { return super.getHandlerAdapter(handler); } @Override protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { return super.processHandlerException(request, response, handler, ex); } @Override protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { super.render(mv, request, response); } @Override protected String getDefaultViewName(HttpServletRequest request) throws Exception { return super.getDefaultViewName(request); } @Override protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { return super.resolveViewName(viewName, model, locale, request); } }
重新所有的方法,直接調(diào)用父類(lèi)DispatcherServlet內(nèi)的邏輯,實(shí)際LocalDispatcherServletDemo中沒(méi)有具體的邏輯,只有構(gòu)造方法、initWebApplicationContext、sendMsg短信發(fā)送邏輯的修改和創(chuàng)建。
JavaMailSendUtil郵件發(fā)送邏輯代碼
package com.minuor.common.mail; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.mail.*; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; /** * @Author: Joker * @Date: 2018/3/19 * @Desc: 發(fā)送郵件服務(wù) */ @Slf4j public class JavaMailSendUtil { /** * 發(fā)送郵件 */ public void sendEmail(String subject, String mailText, String rStr) { try { // 1.創(chuàng)建一個(gè)程序與郵件服務(wù)器會(huì)話(huà)對(duì)象 Session Properties props = new Properties(); props.setProperty("mail.smtp.host", "smtp.163.com"); props.setProperty("mail.smtp.port", "25"); // 指定驗(yàn)證為true props.setProperty("mail.smtp.auth", "true"); // 驗(yàn)證賬號(hào)及密碼,密碼需要是第三方授權(quán)碼 Authenticator auth = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication( "xxxx@163.com", "123456"); } }; Session session = Session.getInstance(props, auth); // 2.創(chuàng)建一個(gè)Message,它相當(dāng)于是郵件內(nèi)容 Message message = new MimeMessage(session); // 設(shè)置發(fā)送者 message.setFrom(new InternetAddress("xxxxx@163.com")); // 設(shè)置發(fā)送方式與接收者 if (StringUtils.isBlank(rStr)) return; String[] rStrs = rStr.split(","); InternetAddress[] address = new InternetAddress[rStrs.length]; int index = 0; for (String str : rStrs) { address[index] = new InternetAddress(str); index++; } message.setRecipients(MimeMessage.RecipientType.TO, address); // 設(shè)置主題 message.setSubject(subject); // 設(shè)置內(nèi)容 message.setContent(mailText, "text/html;charset=utf-8"); // 3.創(chuàng)建 Transport用于將郵件發(fā)送 Transport.send(message); log.info(">>>>>>>發(fā)送郵件成功<<<<<<<"); } catch (Exception e) { log.error(">>>>>>>發(fā)送郵件異常:{}", e); } } }
封裝的最簡(jiǎn)單的發(fā)送方式,只為實(shí)現(xiàn)簡(jiǎn)單的郵件推送功能。
具體說(shuō)明
在重新initWebApplicationContext內(nèi),對(duì)調(diào)用父類(lèi)的initWebApplicationContext方法加了異常捕捉,在catch捕捉中添加了郵件處理邏輯,也將異常吃點(diǎn)不再打印出來(lái);
郵件推送分為兩個(gè)部分,一個(gè)是項(xiàng)目啟動(dòng)成功,一個(gè)是項(xiàng)目啟動(dòng)失敗,兩個(gè)部分都會(huì)給開(kāi)發(fā)或者維護(hù)人員提供相應(yīng)的郵件提醒;
最主要的一點(diǎn),這里涉及到的郵件發(fā)送工具類(lèi),重寫(xiě)類(lèi)LocalDispatcherServletDemo都不能交給spring管理,因?yàn)樵趕pring加載失敗的時(shí)候,這些類(lèi)對(duì)應(yīng)的bean是不能創(chuàng)建成功的,更不用說(shuō)對(duì)象的注入以及具體邏輯的實(shí)現(xiàn)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- java項(xiàng)目啟動(dòng)失敗的問(wèn)題及解決
- 解決Java項(xiàng)目啟動(dòng)報(bào)錯(cuò):Logback?configuration?error?detected:問(wèn)題
- 解決JAVA項(xiàng)目啟動(dòng)卡住,無(wú)任何異常信息的問(wèn)題
- Javaweb項(xiàng)目啟動(dòng)Tomcat常見(jiàn)的報(bào)錯(cuò)解決方案
- java 實(shí)現(xiàn)web項(xiàng)目啟動(dòng)加載properties屬性文件
- Java web項(xiàng)目啟動(dòng)Tomcat報(bào)錯(cuò)解決方案
相關(guān)文章
webuploader 實(shí)現(xiàn)圖片批量上傳功能附實(shí)例代碼
這篇文章主要介紹了webuploader 實(shí)現(xiàn)圖片批量上傳功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11詳解Java如何實(shí)現(xiàn)數(shù)值校驗(yàn)的算法
給定一個(gè)字符串如何判斷它是否為數(shù)值類(lèi)型?本文將帶著大家學(xué)習(xí)一下如何利用Java實(shí)現(xiàn)這個(gè)判斷算法,感興趣的小伙伴可以學(xué)習(xí)一下2022-04-04java自定義動(dòng)態(tài)鏈接數(shù)據(jù)庫(kù)示例
這篇文章主要介紹了java自定義動(dòng)態(tài)鏈接數(shù)據(jù)庫(kù)示例,需要的朋友可以參考下2014-02-02淺談Spring框架中@Autowired和@Resource的區(qū)別
最近review別人代碼的時(shí)候,看到了一些@Autowired不一樣的用法,覺(jué)得有些意思,下面這篇文章主要給大家介紹了關(guān)于Spring框架中@Autowired和@Resource區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-10-10SpringBoot利用validation實(shí)現(xiàn)優(yōu)雅的校驗(yàn)參數(shù)
數(shù)據(jù)的校驗(yàn)是交互式網(wǎng)站一個(gè)不可或缺的功能,如果數(shù)據(jù)庫(kù)中出現(xiàn)一個(gè)非法的郵箱格式,會(huì)讓運(yùn)維人員頭疼不已。本文將介紹如何利用validation來(lái)對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),感興趣的可以跟隨小編一起學(xué)習(xí)一下2022-06-06SpringBoot整合FastDFS中間件實(shí)現(xiàn)文件分布管理
FastDFS是一個(gè)開(kāi)源的輕量級(jí)分布式文件系統(tǒng),它對(duì)文件進(jìn)行管理,功能包括:文件存儲(chǔ)、文件同步、文件上傳、文件下載等,解決了大容量存儲(chǔ)和負(fù)載均衡的問(wèn)題,本文介紹了SpringBoot整合FastDFS中間件實(shí)現(xiàn)文件分布管理,需要的朋友可以參考下2024-08-08Java HttpURLConnection超時(shí)和IO異常處理
這篇文章主要介紹了Java HttpURLConnection超時(shí)和IO異常處理的相關(guān)資料,需要的朋友可以參考下2016-09-09