1、案例背景
一家互聯網公司需要實時分析其服務器日志、應用日志和用戶行為日志,以快速發現潛在問題并優化系統性能。
2、需求分析
- 目標:實時分析日志數據,快速發現問題并優化系統性能。
- 數據來源:
- 服務器日志:如 Nginx、Tomcat、Docker等日志。
- 應用日志:業務系統的運行日志。
- 用戶行為日志:用戶操作記錄(如點擊、瀏覽、下單等)。
- 輸出:
- 錯誤率、請求延遲、用戶行為路徑等關鍵指標。
- 實時監控儀表盤。
3、解決思路
- 日志采集:使用工具(如Filebeat或Fluentd)將日志數據寫入Kafka。
- 數據存儲與分析:Kafka中的數據被導入到ClickHouse,利用其高效的壓縮和查詢性能進行日志分析。
- 可視化:通過 Grafana 或 Redash 構建儀表盤,展示關鍵指標(如錯誤率、請求延遲等)。
4、技術選型
- 日志采集:Filebeat 或 Fluentd。
- 消息隊列:Kafka(用于緩沖和傳輸日志數據)。
- 存儲與分析:ClickHouse(高性能 OLAP 數據庫)。
- 可視化:Grafana 或 Redash。
5、ClickHouse的作用
- 高效存儲:日志數據量通常非常龐大,ClickHouse的列式存儲和高壓縮比顯著降低了存儲成本。
- 實時分析:支持毫秒級響應的復雜查詢,適合對海量日志進行實時分析。
- 靈活擴展:支持分布式部署,能夠處理PB級別的日志數據。
6、基本實現步驟
(1)、日志采集
1. 安裝 Filebeat:
bash示例:
sudo apt-get install filebeat
2. 配置 Filebeat:
編輯 filebeat.yml 文件,指定日志文件路徑和 Kafka 輸出:
yaml示例:
filebeat.inputs:- type: logpaths:- /var/log/nginx/*.log- /var/log/application/*.logoutput.kafka:hosts: ["kafka-broker:9092"]topic: "logs"
解釋:
Input為采集日志相關配置,如nginx的log日志文件,應用程序的log日志文件,output指定輸出到kafka。
3. 啟動 Filebeat:
bash示例:
sudo service filebeat start
(2)、消息隊列(Kafka)
1. 安裝 Kafka:
bash示例:
wget https://downloads.apache.org/kafka/3.0.0/kafka_2.13-3.0.0.tgz
tar -xzf kafka_2.13-3.0.0.tgz
cd kafka_2.13-3.0.0
2. 啟動 Kafka:
bash示例:
bin/zookeeper-server-start.sh config/zookeeper.properties
bin/kafka-server-start.sh config/server.properties
(3)、數據消費與寫入ClickHouse
1. 創建 ClickHouse 表:
sql示例:
CREATE TABLE logs (timestamp DateTime,level String,message String,source String) ENGINE = MergeTree()ORDER BY (timestamp);
(4)、可視化
1. 安裝 Grafana:
bash示例:
sudo apt-get install grafana
sudo service grafana-server start
2. 配置 ClickHouse 數據源:
在 Grafana 中添加 ClickHouse 數據源,配置連接信息。
3. 構建儀表盤:
創建圖表展示日志的關鍵指標,如錯誤率、請求延遲等。
7、Spring Boot代碼示例
使用Spring Boot消費Kafka數據并寫入 ClickHouse。
(1)、添加依賴
<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- Kafka --><dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency><!-- ClickHouse JDBC --><dependency><groupId>com.clickhouse</groupId><artifactId>clickhouse-jdbc</artifactId><version>0.3.2</version></dependency><!-- Jackson for JSON Parsing --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>
</dependencies>
(2)、配置 Kafka 和 ClickHouse
在 application.yml 中配置 Kafka 和 ClickHouse:
spring:kafka:bootstrap-servers: kafka-broker:9092consumer:group-id: clickhouse-groupauto-offset-reset: earliestdatasource:url: jdbc:clickhouse://clickhouse-server:8123/defaultdriver-class-name: com.clickhouse.jdbc.ClickHouseDriverusername: defaultpassword:
(3)、Kafka 消費者
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;@Service
public class LogConsumer {private final LogRepository logRepository;public LogConsumer(LogRepository logRepository) {this.logRepository = logRepository;}@KafkaListener(topics = "logs", groupId = "clickhouse-group")public void consume(String message) {// 解析日志消息Log log = parseLog(message);// 寫入 ClickHouselogRepository.save(log);}private Log parseLog(String message) {// 假設日志是 JSON 格式ObjectMapper objectMapper = new ObjectMapper();try {return objectMapper.readValue(message, Log.class);} catch (Exception e) {throw new RuntimeException("Failed to parse log message", e);}}
}
(4)、ClickHouse 數據訪問層
創建Repository類。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class LogRepository {private final JdbcTemplate jdbcTemplate;@Autowiredpublic LogRepository(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public void save(Log log) {String sql = "INSERT INTO logs (timestamp, level, message, source) VALUES (?, ?, ?, ?)";jdbcTemplate.update(sql, log.getTimestamp(), log.getLevel(), log.getMessage(), log.getSource());}
}
(5)、日志實體類
import java.time.LocalDateTime;public class Log {private LocalDateTime timestamp;private String level;private String message;private String source;// Getters and Setters
}
(6)、 Service 層(LogService.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;@Service
public class LogService {@Autowiredprivate JdbcTemplate jdbcTemplate;// 查詢最近5分鐘的錯誤率public List<Map<String, Object>> getErrorRate() {String sql = "SELECT program, error_count * 100.0 / total_requests AS error_percent " +"FROM log_errors_mv " +"WHERE minute >= now() - interval 5 minute " +"GROUP BY program";return jdbcTemplate.queryForList(sql);}// 查詢指定時間段的響應時間分布public List<Map<String, Object>> getResponseTimeStats(String startTime, String endTime) {String sql = "SELECT percentileState(upstream_response_time, 0.95) AS p95 " +"FROM log_main " +"WHERE timestamp BETWEEN ? AND ?";return jdbcTemplate.queryForList(sql, startTime, endTime);}
}
(7)、Controller 層(LogController.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;@RestController
public class LogController {@Autowiredprivate LogService logService;@GetMapping("/error-rate")public List<Map<String, Object>> getErrorRate() {return logService.getErrorRate();}@GetMapping("/response-time")public List<Map<String, Object>> getResponseTime(@RequestParam String startTime,@RequestParam String endTime) {return logService.getResponseTimeStats(startTime, endTime);}
}
8、關鍵優化與注意事項
以上僅為簡單的示例,實際生產中每一步都會比較復雜,需要結合實際需求在做詳細的數據庫設計以及接口設計等。這里我們主要是理解做的思路。
(1)、表設計優化
- 分區策略:按 toYYYYMMDD(timestamp) 分區,便于按天清理舊數據。
- 物化視圖:預聚合高頻查詢指標(如錯誤率、響應時間),避免重復計算。
- 索引與排序:在 program 和 timestamp 字段上建立索引,加速過濾查詢。
(2)、ClickHouse 配置優化
- 資源分配:增大 max_threads 和 max_memory_usage,提升并發處理能力。
- 日志壓縮:使用 gzip 或 lz4 壓縮日志數據,減少存儲開銷。
(3)、Spring Boot 性能調優
- 連接池配置:使用 HikariCP 管理數據庫連接(通過 spring.datasource.hikari.* 配置)。
- 緩存機制:對高頻查詢結果使用 Redis 緩存(如錯誤率統計)。
逆風成長,Dare To Be!!!