記一次介面最佳化操作

一隻愛閱讀的程式設計師發表於2022-12-01

  專案正式上線之後,後期主要是不斷地進行版本迭代,開發新的功能。自己參與

開發的專案正式開始使用後,人數還不少,早上高峰期的時候一個介面一個小時的請求

數達到約3萬。而且這只是部分使用者在進行使用,還沒有大規模地放開,伺服器已經

開始告警,某一個介面的查詢超過四五秒。收到這個資訊後,負責人立馬讓我們檢視

日誌資訊,排查問題。透過命令 grep 介面請求的URL” 日誌檔名,檢視多臺服務

器上面列印的日誌資訊,發現確實有多臺伺服器上列印的介面耗時都超過5S以上。

  檢視日誌的方式是自己在網上搜尋的,可是介面耗時卻是系統中寫的,如何衡量一個

介面的好壞?其中一個指標就是處理請求的能力,可以透過jmeter來做介面的效能測試。

這個在測試階段,專業的測試人員都已經測試過,肯定是符合要求我們才上生產。可是

現在已經是服務在生產上面跑,在生產階段,不能在使用那種方式來處理。對於後端

開發人員來說,可以在需要的介面請求中,列印每一次介面的耗時。簡單處理方式如下:

首先需要定義一個攔截器:

@Slf4j

public class InterfaceInterceptor implements HandlerInterceptor {

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        request.setAttribute("REQUEST_START_TIME", String.valueOf(System.currentTimeMillis()));

        return true;

    }

 

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        String requestURI = request.getRequestURI();

        String requestStartTime = (String)request.getAttribute("REQUEST_START_TIME");

        log.info("介面 {} 請求耗時: {} ms", requestURI, System.currentTimeMillis() - Long.parseLong(requestStartTime));

    }

}

重寫兩個方法,一個方法是在處理請求前呼叫的preHandle方法,處理邏輯很簡單,設定一個固定的變數值,

具體的值設定為當前的時間戳,將其放在請求物件中。介面請求處理完成之後,呼叫afterCompletion方法,

將之前存入的值取出來,轉換為Long型別,然後用當前時間時間戳減去最開始請求的時間戳,就可以計算出單個介面處理的耗時。

接下來寫一個簡單的測試方法,如下

@Slf4j

@RestController

@RequestMapping("/happy/yilang")

public class TestControlelr {

    @GetMapping("/interface")

    @LcloudThreadLimiter(maxThread="max_thread", waitTime="wait_time")

    public String interfaceTest(){

        return "介面耗時統計";

    }

}

最後將攔截器新增到攔截器配置類中,即是註冊攔截器,攔截的指定的路徑為/happy/**,可以

按需要進行靈活的配置,如下

@Configuration

public class WebMvcConfig extends WebMvcConfigurerAdapter {

 

    /**

     *  Function:  addInterceptors

     *  Author :  kaye0110,

 *  Version : 1.0

     *  Description : 註冊攔截器

     *  Param and Description :

     *  @param registry

     */

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(InterfaceInterceptor()).addPathPatterns("/happy/**");

        super.addInterceptors(registry);

    }

    @Bean

    public InterfaceInterceptor InterfaceInterceptor(){

        return  new InterfaceInterceptor();

    }

}

啟動專案,測試結果如下

 

 

 

拿到每一次介面請求的耗時資料,對於效能要求是一個重要的參考指標。每個公司的要求都不一樣,有的可能要求1s內返回,

有的可能要求500ms內返回,有的可能要求20ms內返回。根據公司自己的要求,然後來對比介面的耗時,就可以判斷出這個

介面的效能如何,是否需要最佳化。

  自己在排查日誌的過程中發現,這個介面就是一個簡單的介面,沒有做比較複雜的計算操作,就是根據主鍵ID查詢一條資料

資訊,為什麼會導致這麼慢呢?怎麼進行最佳化呢?之後經過仔細分析,發現在請求高峰期的時候,資料庫的CPU達到90%多,

因為資料庫的效能急劇下降。負責人經過檢視資料庫伺服器的相關資訊,普通開發人員沒許可權看,還發現MQ在快速的大量的

向資料庫中寫資料, 還有定時任務也在頻繁的向資料庫寫資料,所以導致資料庫的效能解決下降。由於起了多臺伺服器,定時

任務的處理就更加地頻繁,如果每間隔兩分鐘處理一次定時任務,5臺伺服器兩分鐘就處理10次。因此就找到對應的解決方案:

.1.擴容資料庫;.2.這個介面實時查詢資料庫修改為從快取中取資料;.3.增加定時任務的處理時間間隔;4.降低MQ訊息消費的流量。

定時將需要查詢的所有資料,預先載入到快取中,然後查詢的時候就直接從快取當中取,不再去查詢資料庫。透過這幾步的最佳化,

最終很好地解決了這個介面的效能問題。

  如果有其他更好建議的小夥伴,歡迎留言討論。

相關文章