這個單體式 Helpdesk 應用程式旨在為手機廠商提供客戶支援服務,採用 Java 7、Spring 3.x、Jersey 1.8 和 Hibernate 等技術構建中間層,前端使用 HTML、JavaScript 和 jQuery,資料函式庫則使用 MySQL 5.x。應用程式採用傳統的三層架構,包含資料函式庫層、業務邏輯/服務層和使用者介面層。其中,認證模組使用基本的使用者名稱和密碼驗證,並透過 Spring 攔截器進行請求攔截和角色授權。帳戶管理模組提供帳戶的增刪改查等功能,產品目錄管理則提供產品資訊的維護,僅限管理員操作。此外,應用程式還提供預約設定、搜尋和訊息板等功能,方便客戶與技術支援人員互動和資訊交流。然而,隨著業務規模的擴大和需求的變化,單體式架構的限制日益顯現。為提升應用程式的靈活性和可擴充套件性,建議將其逐步拆解為微服務架構,並考慮引入 Spring Boot、Kubernetes 和 Prometheus 等現代化技術,以簡化開發、佈署和監控流程。

Prometheus 與 Grafana 的容器監控

在現代雲端環境中,監控容器化應用程式的執行狀態是至關重要的。Prometheus 和 Grafana 這兩個工具結合使用,可以提供強大且直觀的監控解決方案。以下是如何使用這兩個工具來實作叢集範圍的監控。

使用 Prometheus 監控容器

首先,我們可以透過 Prometheus 的 UI 來檢視容器的統計資料。讓我們從開啟 Prometheus 的 UI 開始,網址是 http://localhost:9090。接著,點選執行按鈕旁邊的下拉選單,選擇要檢視的查詢,然後點選執行。

假設我們選擇了 container_cpu_system_seconds_total 這個查詢,結果會顯示所有容器的系統 CPU 時間消耗(以秒為單位)。這樣可以幫助我們瞭解每個容器的 CPU 使用情況。

提升監控畫面美觀度:使用 Grafana

儘管 Prometheus 提供了豐富的統計資料,但其顯示方式可能不夠美觀。這時候,我們可以將 Prometheus 的統計資料匯入到 Grafana 中,以提升監控畫面的美觀度。

開啟 Grafana 的 UI,網址是 http://localhost:3000。使用預設的管理員帳號和密碼(admin/admin)登入。然後點選頂部的下拉選單,選擇資料來源。接著點選儀錶板標籤。

在資料來源列表中,我們會看到一個名為「Prometheus Stats」的條目,這是我們之前組態好的資料來源。點選這個條目的右端的匯入按鈕,Grafana 會自動從 Prometheus 資料函式庫中匯入所有統計資料和事件。這步驟只需要做一次,之後 Grafana 會自動重新整理新資料。

匯入完成後,我們可以點選「Prometheus Stats」來檢視新的、更具吸引力的儀錶板。這些儀錶板不僅美觀,還能提供更直觀的資料展示。

自定義儀錶板:展示容器 CPU 載入

接下來,我們可以建立一個簡單的自定義儀錶板來展示主機上的容器累積 CPU 載入。開啟 Grafana 的 UI,點選左上角的選單,選擇「儀錶板」,然後點選「新增」。選擇「單一統計」。

在組態中輸入以下查詢:

sum(rate(container_cpu_user_seconds_total{image!=""}[1m])) / count(node_cpu{mode="system"}) * 100

此查詢會在給定時間點提取 CPU 資源利用率,並以實時方式顯示結果。

內容解密:

  • sum(rate(container_cpu_user_seconds_total{image!=""}[1m])): 計算每分鐘所有容器使用者模式 CPU 時間總和。
  • count(node_cpu{mode="system"}): 計算系統模式 CPU 數量。
  • * 100: 將結果轉換為百分比。

這樣可以幫助我們實時監控每個容器的 CPU 載入情況。

其他監控指標

除了 CPU 載入之外,我們還可以建立其他類別似的儀錶板來監控記憶體使用情況和系統負載等指標。例如:

sum(container_memory_usage_bytes)
sum(rate(node_load1[5m]))

這些查詢可以幫助我們全面瞭解叢集中的資源使用情況。

整合 Alertmanager

最後,我們可以整合 Alertmanager 來處理警示管理。Alertmanager 是 Prometheus 的警示管理系統,能夠根據 Prometheus 收集到的資料生成警示。

首先在 Docker Compose 檔案中新增 Alertmanager 組態:

alertmanager:
  image: prom/alertmanager
  container_name: alertmanager_pk
  volumes:
    - ./alertmanager/:/etc/alertmanager/
  command:
    - '-config.file=/etc/alertmanager/config.yml'
    - '-storage.path=/alertmanager'
  restart: unless-stopped
  expose:
    - 9093
  ports:
    - 9093:9093
  networks:
    - pk_network

然後在 Prometheus 的服務中新增 Alertmanager 組態:

prometheus:
  image: prom/prometheus
  container_name: Prometheus_pk
  volumes:
    - ./prometheus/:/etc/prometheus/
    - prometheus_data:/prometheus
  command:
    - '-config.file=/etc/prometheus/prometheus.yml'
    - '-storage.local.path=/prometheus'
    - '-alertmanager.url=http://alertmanager:9093'
    - '-storage.local.memory-chunks=100000'

接著建立一個名為 container.rules 的規則檔案:

ALERT tomcat_down
IF absent(container_memory_usage_bytes{name="tomcat"})
FOR 10s
LABELS { severity = "critical" }
ANNOTATIONS {
summary= "tomcat down",
description= "tomcat container is down for more than 10 seconds."
}

這個規則檔案會檢查 Tomcat 的狀態,如果 Tomcat 暫停工作超過 10秒鐘則生成警示。

Alertmanager 組態

  • IF absent(container_memory_usage_bytes{name="tomcat"}): 檢查 Tomcat 的記憶體使用量是否存在。
  • FOR 10s: 持續檢查10秒鐘。
  • LABELS { severity = "critical" }: 警示嚴重性標籤為「critical」。
  • ANNOTATIONS: 警示摘要和描述。

最後在 Prometheus.yml 中載入規則檔案:

rule_files:
- "containers.rules"

重新啟動 Docker Compose 檔案:

docker-compose up –d

開啟 Prometheus UI(http://localhost:9090),點選頂部的警示選單即可看到活動中的警示。這樣就完成了 Alertmanager 的整合。

主題標題:解構與重構單一式Helpdesk應用程式

段落標題:應用程式功能概述

玄貓(BlackCat)將帶領大家探討一個典型的單一式Helpdesk應用程式,這個應用程式專門提供手機廠商的客戶支援服務。這個應用程式涵蓋多項功能,包括但不限於帳戶管理、事件建立與管理、產品目錄管理、預約設定、搜尋功能以及訊息板功能。

段落標題:技術架構概述

在這個案例中,我們使用以下技術來構建這個Helpdesk應用程式:

  • 使用者介面:HTML、JavaScript及JQuery
  • 中間層:Java 7、Spring 3.x、Jersey 1.8及Hibernate
  • 資料函式庫:MySQL 5.x

段落標題:應用程式架構

這個應用程式採用三層架構,分別為資料函式庫層、業務邏輯/服務層以及使用者介面層。讓我們來深入瞭解這些服務的具體功能。

次段落標題:認證、攔截器及授權

這個模組提供使用者認證及授權服務,確保使用者能夠根據其角色存取合適的資訊。為了簡化流程,我們採用基本的認證方式(使用者名稱及密碼)並根據角色進行授權。每次請求都會被Spring攔截器處理,確保請求已經透過認證。

小段落標題:認證服務

認證服務負責驗證使用者的身份,並檢查登入頁面上輸入的使用者名稱及密碼是否比對資料函式庫中的記錄。

@Override
@POST
@Consumes({"application/xml", "application/json"})
@Produces({"application/json"})
@Path("/authenticate/")
public AuthenticationResponse authenticate(
    @Context HttpHeaders headers,
    AuthenticationRequest request) {
    // To-do Implementation
}

次段落標題:內容解密:

在這段程式碼中,我們定義了一個RESTful API端點來處理使用者的認證請求。該端點接受POST請求,並支援兩種內容型別:XML及JSON。它會根據輸入的HttpHeaders及AuthenticationRequest物件來驗證使用者身份。

@Override
@POST
@Consumes({"application/xml", "application/json"})
@Produces({"application/json"})
@Path("/authenticate/")
public AuthenticationResponse authenticate(
    @Context HttpHeaders headers,
    AuthenticationRequest request) {
    // To-do Implementation
}

次段落標題:攔截器

攔截器會攔截所有進入應用伺服器的請求,並執行預處理邏輯。這裡我們使用Spring框架中的攔截器來實作這一功能。

<interceptors>
    <interceptor>
        <mapping path="/*"/>
        <beans:bean class="org.spring.controller.AuthenticationInterceptor"/>
    </interceptor>
</interceptors>

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // To-do Implementation
}

次段落標題:內容解密:

這段程式碼定義了一個Spring攔截器,該攔截器會攔截所有比對「/*.」模式的請求。當請求進入時,preHandle方法會被執行,進行必要的預處理邏輯。

<interceptors>
    <interceptor>
        <mapping path="/*"/>
        <beans:bean class="org.spring.controller.AuthenticationInterceptor"/>
    </interceptor>
</interceptors>

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // To-do Implementation
}

次段落標題:授權

我們的應用程式中包含多種角色,當使用者登入時,他們的角色會被儲存到HTTP session中。授權控制器會從session中提取這些角色資訊進行驗證。

LoginForm userData = (LoginForm) context.getSession().getAttribute("LOGGEDIN_USER");
<%
LoginForm loginform = (LoginForm) session.getAttribute("LOGGEDIN_USER");
String user = loginform.getUsername();
if (session.getAttribute("ACCESS_LEVEL").equals("4")) {
%>

次段落標題:內容解密:

在這裡,我們從session中提取了登入的使用者資訊,並根據其角色來進行授權控制。這樣可以確保只有擁有特定許可權的使用者才能存取特定資源。

LoginForm userData = (LoginForm) context.getSession().getAttribute("LOGGEDIN_USER");
<%
LoginForm loginform = (LoginForm) session.getAttribute("LOGGEDIN_USER");
String user = loginform.getUsername();
if (session.getAttribute("ACCESS_LEVEL").equals("4")) {
%>

段落標題:帳戶管理

帳戶管理模組提供與使用者帳戶相關的各種服務,包括查詢、新增、更新及刪除帳戶等操作。以下是一些主要的API服務:

  • getAccount:取得已註冊使用者的詳細資訊。
  • addAccount:新增一個新的使用者。
  • updateAccount:更新已有的使用者資訊。
  • deleteAccount:移除系統中的某個使用者。
@Component
@Path("/AccountService")
public class AccountServiceImpl implements AccountService {
    // Service methods implementation
}

段落標題:產品目錄管理(僅限管理員)

產品目錄管理模組專門為管理員設計,負責儲存和管理與產品相關的目錄資訊。這些資訊包括產品詳細描述、庫存狀態等。

段落標題:預約設定

這個功能允許客戶設定與技術支援人員的預約時間,方便客戶獲得即時幫助。

段落標題:搜尋功能

搜尋功能允許客戶查詢現有問題及解決方案,以及在產品目錄中查詢特定產品。

段落標題:訊息板

訊息板是一個客戶社群板塊,讓客戶之間可以互相協作和幫助。

段落標題:未來趨勢與改進建議

在當前技術環境下,單一式應用程式已經逐漸被微服務架構所取代。微服務架構提供了更高的靈活性和可擴充套件性,適合於大規模分散式系統。未來可以考慮將這些功能拆分成獨立的微服務,以提升系統的穩定性和維護性。

此外,在技術選型上可以考慮引入更多現代化工具和框架,例如Spring Boot來簡化組態和佈署流程;Kubernetes來進行容器協調;以及Prometheus和Grafana來進行監控和日誌管理。

玄貓(BlackCat)希望透過這次探討幫助大家更好地理解單一式Helpdesk應用程式的設計與實作方法。