爲何你學了N遍Springboot,至今仍是學生項目?你的問題都在這裏!

爲何你學了N遍Springboot,至今仍是學生項目?你的問題都在這裏!

爲何你學了n遍《1天精通Spring Boot》,至今仍是不精通Spring Boot,甚至仍是停留在學生項目?真正要作項目就應該一步到位,半吊子半桶水是不行的。一個實戰項目須要充分考慮狀態碼、異常處理、日誌處理、性能監控、數據安全、部署等等因素,而不是急於求成,爲了達到1天精通的目標而糊弄過去。前端

筆者在大學時也經歷過學了不少Spring Boot的教材,可是比起外包(哪怕是個小程序),總以爲比真實項目缺了點什麼,項目跑起來也是分分鐘解體。本篇適合有必定後端開發基礎(不涉及中間件,會在Spring Cloud講),可是又停留於學生項目的讀者(不一樣語言均有借鑑意義)。java

問題分析:程序員


項目監控spring


項目能跑就行!管他什麼項目監控,項目問題統統等用戶反饋,掛了就重啓!SQL慢用戶就等着,我怎麼知道哪裏執行慢?項目訪問不了就是用戶網絡問題!用戶遇到問題就是一清緩存、二換瀏覽器、三重啓大法好!你怕是沒通過社會的毒打,KPI分分鐘墊底,再過2個月就能夠實現家裏蹲的願望了。sql

這裏拿alibaba的druid作個例子,druid能夠監控如下幾個部分:小程序

  1. 數據源:顯示當前項目下全部數據源,多數據源的項目纔會用到。固然了,確定不會顯示密碼。
  2. SQL監控:監控全部執行的SQL語句,包括耗時,參數等等。
  3. SQL防火牆:druid提供了黑白名單的訪問,能夠清楚的看到SQL防禦狀況。
  4. Web應用:能夠看到目前運行的Web程序的併發數,事務書等等詳細信息。
  5. URI監控:能夠監控到全部的請求路徑的請求次數、請求時間等參數。
  6. Session監控:監控session情況,建立時間、最後活躍時間、請求次數、請求時間等參數。

集成完druid,項目立刻提升一個檔次,能夠在boss面前打開頁面吹噓一把。最最關鍵的是,集成druid是分分鐘的事情,幾乎沒有代碼的侵入性,要改造也只需添加個bean,改下配置文件。詳細可參考springboot集成druid,絕對是排查慢SQL、優化性能、監控項目居家必備良藥!後端

85a6c86f243e38ff61cd458b972fcb66.jpeg


日誌瀏覽器


日誌記錄操做軌跡、監控系統運行狀態、回溯系統故障,保留系統故障現場,方便程序員快定位問題。生產環境不一樣於學生項目,隨時有問題就重啓debug,在公司裏,開發人員你碰服務器的資格都沒有,因此保留日誌成了排查Bug惟一的方法。這裏就談談容易被忽視的幾點:緩存

  1. 等級:DEBUG<INFO<WARN<ERROR<FATAL,程序員應該預先判斷好日誌等級。不是全部日誌一打就是log.error(),比方說業務異常每每只須要經過引導用戶正確操做就能夠解決,那麼應該將他歸爲log.warn()級別。若是一股腦所有日誌都打成log.error(),error日誌幾萬行(裏面99%都只是校驗輸入出錯),看到就頭疼,誰去排查error日誌上的問題?
  2. 按期巡檢:初級項目每每是用戶反饋了故障才進行問題排查。而標準的項目應該按期巡檢error日誌,由於②中已經作好的分類,error必定是須要人爲介入的,將這些error日誌逐條排查即可以提早發現項目潛在風險。
  3. 日誌完整性:初學者每每會打出log.error(e.getMessage)或log.error("錯誤信息:" + e)的日誌,這種寫法將吃掉全部的堆棧信息!這是很是很是嚴重的事情,正確的寫法應該是logger.error("第x部分出錯 " + e)。
  4. 日誌配置:初學者每每並不關心日誌文件存放在哪裏,日誌文件保留多久,日誌文件按等級生成等配置細節,而做爲一名架構師,你須要充分評估項目的需求,對這些進行自定義的配置。筆者也曾由於不關心這些配置,致使日誌爆滿撐死服務器!
  5. 日誌框架分爲日誌門面、日誌實現庫、日誌適配器三類。JCL、SLF4j、Jboss-logging這些框架都是日誌門面;而真實幹活的人則是日誌實現庫,常見的Log4j、Log4j二、Logback、JUL;當門面與實現庫不匹配時則須要日誌適配器。固然如今百分之90的項目使用的都是slf4j + logback,能夠不太關心日誌框架的選型。可是若是你立志成爲一名架構師,那框架的選型就是一門必修課,仍是得去了解


狀態碼安全


e2a67098312d64c2c8b431c7b72bacc1.jpeg


對比一下上圖有什麼區別?很簡單,就是多了個狀態碼。有沒有這個狀態碼彷佛對前端請求的數據也沒什麼影響,不加狀態碼前端仍是能夠正常顯示。確實,在覈心數據上是不會有任何影響的,可是一旦出現一些業務異常,返回的數據就會以下圖所示:

{
  "timestamp": "2020-04-25T03:55:26.179+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "\n### Error querying database.  Cause: java.sql.SQLException: sql injection violation, syntax error: syntax error, error in :\u0027 asd from product_info where produc\u0027, expect IDENTIFIER, actual IDENTIFIER pos 12, line 1, column 13, token IDENTIFIER null : select * asd from ...}

若是前端沒有能經過狀態碼進行不一樣返回請求的處理,返回異常數據時,前端代碼仍舊直接獲取data數據,那誰知道前端會出現什麼樣子的異常呢?

若是能與前端妹子好好商量一下狀態碼的規則,那麼前端就能夠經過不一樣的狀態碼,判斷不一樣的請求響應等級,例如:

  • 當接受到200時,說明正常,直接取data數據,進行正常的展現;
  • 若是遇到1000~2000的,說明只是普通的警告,調用⚠️的彈窗,顯示一下msg就能夠了;
  • 若是遇到2000+的說明時是重大問題,須要獲取msg做爲彈窗title,並從data中獲取異常信息……

否則你就爲所欲爲拋異常,想怎麼返回就怎麼返回,前端妹子提刀就來找你了。想一想本身爲何仍是單身?或許就是由於沒跟前端妹子在大明湖畔約定好狀態碼吧。


異常處理

學生項目每每就是不到IDE報沒法編譯,不到萬不得已堅定不進行異常處理!而異常處理又相當重要!那就大體來了解一下吧。

全部異常都是Throwable的子類,分爲Error致命異常和Exception非致命異常。

  • Error異常每每是StackOverflowError、OutOfMemoryError這類根本無能爲力的異常,既然無能爲力了,咱們也不須要太關心了,只須要記錄日誌,也只能到時候排查了。
  • Exception這類異常又分爲checked和RuntimeException、checked是須要顯示處理的異常,好比io異常等,你不處理就編譯不了,也就是上面說的不到萬不得已堅定不進行異常處理的異常,這類由於每次Idea都會提醒。

而咱們關心的是RuntimeException,這類,這類異常每每是業務異常,正常的作法是封裝一個AppException繼承自RuntimeException,在發現業務異常時,配合狀態碼拋出對應異常:

@Getterpublic class APIException extends RuntimeException {
    private int code;
    private String msg;

    // 手動設置異常    public APIException(StatusCode statusCode, String message) {
        // message用於用戶設置拋出錯誤詳情,例如:當前價格-5,小於0        super(message);
        // 狀態碼        this.code = statusCode.getCode();
        // 狀態碼配套的msg        this.msg = statusCode.getMsg();
    }

    // 默認異常使用APP_ERROR狀態碼    public APIException(String message) {
        super(message);
        this.code = AppCode.APP_ERROR.getCode();
        this.msg = AppCode.APP_ERROR.getMsg();
    }}

那麼誰來拋出異常,誰又來處理異常呢?

一般來講,通常拋出業務異常的是在service層或者相關的邏輯業務層進行,而且配合狀態碼進行拋出:

throw new APIException(AppCode.PRODUCT_NOT_EXIST, "上架商品中沒法查詢到:" + orderDetail.getProductId());

而後在調用者層進行處理,這裏的調用者通常指最上層的controller層也就是控制層,進行處理,固然springboot有統一處理的功能,本質是經過AOP對controller層進行攔截異常。有想深刻了解的能夠參考:《正規軍springboot如何處理:參數校驗、統一異常、統一響應》。

@RestControllerAdvice@Slf4jpublic class ControllerExceptionAdvice {

    @ExceptionHandler({BindException.class})
    public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {
        // 從異常對象中拿到ObjectError對象        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return new ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());
    }

    @ExceptionHandler(APIException.class)
    public ResultVo APIExceptionHandler(APIException e) {
        log.error(e.getMessage(), e);
        return new ResultVo(e.getCode(), e.getMsg(), e.getMessage());
    }}


單元測試


單元測試的意義並不只僅在於發現現有代碼Bug。一個好的單元測試能夠做爲測試用例固化在項目中,方便程序員進行重構和修改。如上圖,本身均可能出現不敢刪掉冗餘代碼的狀況,更況且是重構代碼?修改代碼?甚至是接手別人的代碼?在公司中,正常一套代碼會有AB角之分,保證其中一方跑路跳槽,項目也不會出現人員斷層,確保項目能正常進行:

  1. A角經歷開發的完整週期,對功能潛在bug每每比B角清楚的多。當A角能寫好單元測試,B角只需重構修改好代碼,再從新運行一遍單元測試即可以安心上線
  2. 而當A角徹底不寫單元測試,B角接手後大概就是。「臥靠,這段tm能不能改啊?」,「這不會有特殊狀況吧?」,「這其餘地方不會用到吧?」,「這tm該不會是業務需求討論,爲後續模塊預留的吧?」……

若是說,公司某個A角邀請你籤他的AB角生死狀,請先看一下他的單元測試覆蓋率等指標,若是一個單元測試都不寫的,打死都不籤!否則改完代碼一上線極可能又是解體操做!


服務器


這點筆者深有體會,在大學期間,老是聽人說服務器就是一臺電腦。可是你說任你說,我仍是以爲服務器是一個高端大氣又特別神祕的東西!本身的代碼永遠都運行在IDEA上,哪怕我知道部署項目的全部步驟,卻一直止步不前,總以爲少了點什麼。直到第一次接觸到服務器,才發現本來覺得這貨真的是個電腦!這貨真的就是個Linux!並且經過公網IP、域名真的能夠訪問到本身的項目!


原做者:bugpool
原文連接: 爲何你學了N遍Springboot,至今仍是學生項目?你的問題都在這裏!_前端_bugpool的博客-CSDN博客
原出處:CSDN

8498e61271744e9b7e2fd0dabff5a521.jpeg

相關文章
相關標籤/搜索