javascript
基于docker部署的微服务架构(九): 分布式服务追踪 Spring Cloud Sleuth
為什么80%的碼農都做不了架構師?>>> ??
前言
微服務架構中完成一項功能經常會在多個服務之間遠程調用(RPC),形成調用鏈。每個服務節點可能在不同的機器上甚至是不同的集群上,需要能追蹤整個調用鏈,以便在服務調用出錯或延時較高時準確定位問題。
以下內容引用 Dapper,大規模分布式系統的跟蹤系統 譯文 ,介紹了分布式服務追蹤的重要性以及設計原則:
當代的互聯網的服務,通常都是用復雜的、大規模分布式集群來實現的。互聯網應用構建在不同的軟件模塊集上,這些軟件模塊,有可能是由不同的團隊開發、可能使用不同的編程語言來實現、有可能布在了幾千臺服務器,橫跨多個不同的數據中心。因此,就需要一些可以幫助理解系統行為、用于分析性能問題的工具。
舉一個跟搜索相關的例子,這個例子闡述了Dapper可以應對哪些挑戰。比如一個前段服務可能對上百臺查詢服務器發起了一個Web查詢,每一個查詢都有自己的Index。這個查詢可能會被發送到多個的子系統,這些子系統分別用來處理廣告、進行拼寫檢查或是查找一些像圖片、視頻或新聞這樣的特殊結果。根據每個子系統的查詢結果進行篩選,得到最終結果,最后匯總到頁面上。我們把這種搜索模型稱為“全局搜索”(universal search)。總的來說,這一次全局搜索有可能調用上千臺服務器,涉及各種服務。而且,用戶對搜索的耗時是很敏感的,而任何一個子系統的低效都導致導致最終的搜索耗時。如果一個工程師只能知道這個查詢耗時不正常,但是他無從知曉這個問題到底是由哪個服務調用造成的,或者為什么這個調用性能差強人意。首先,這個工程師可能無法準確的定位到這次全局搜索是調用了哪些服務,因為新的服務、乃至服務上的某個片段,都有可能在任何時間上過線或修改過,有可能是面向用戶功能,也有可能是一些例如針對性能或安全認證方面的功能改進。其次,你不能苛求這個工程師對所有參與這次全局搜索的服務都了如指掌,每一個服務都有可能是由不同的團隊開發或維護的。再次,這些暴露出來的服務或服務器有可能同時還被其他客戶端使用著,所以這次全局搜索的性能問題甚至有可能是由其他應用造成的。舉個例子,一個后臺服務可能要應付各種各樣的請求類型,而一個使用效率很高的存儲系統,比如Bigtable,有可能正被反復讀寫著,因為上面跑著各種各樣的應用。
上面這個案例中我們可以看到,對Dapper我們只有兩點要求:無所不在的部署,持續的監控。無所不在的重要性不言而喻,因為在使用跟蹤系統的進行監控時,即便只有一小部分沒被監控到,那么人們對這個系統是不是值得信任都會產生巨大的質疑。另外,監控應該是7x24小時的,畢竟,系統異常或是那些重要的系統行為有可能出現過一次,就很難甚至不太可能重現。那么,根據這兩個明確的需求,我們可以直接推出三個具體的設計目標:
在 spring cloud 技術棧中, spring cloud Sleuth 借鑒了 Google Dapper 的實現, 提供了分布式服務追蹤的解決方案。
引入 Spring Cloud Sleuth 追蹤系統
Spring Cloud Sleuth 提供了兩種追蹤信息收集的方式,一種是通過 http 的方式,一種是通過 異步消息 的方式,這里以生產環境常用的 異步消息 的收集方式為例。
在之前創建的項目上做修改,增加 Spring Cloud Sleuth 分布式服務追蹤功能。
修改 add-service-demo 的 pom.xml 文件,增加相關依賴:
spring-cloud-starter-sleuth 引入 sleuth 基礎jar包, spring-cloud-sleuth-stream 和 spring-cloud-stream-binder-rabbit 引入通過 異步消息 收集追蹤信息的相關jar包,spring-cloud-starter-feign 引入了 feign,用來遠程調用別的服務(在 基于docker部署的微服務架構(二): 服務提供者和調用者 中有介紹),稍后會創建一個提供隨機數的服務,用來展示服務調用鏈。
然后修改 log4j2.xml 配置文件, 修改日志格式為:
在日志信息中增加用來追蹤的 TraceId、SpanId ,Export 表示是否導出到 zipkin 。
之前在 基于docker部署的微服務架構(四): 配置中心 的內容中已經配置了 rabbitmq,用于 spring cloud bus,所以這里就不用再配消息隊列了,用之前配置的 rabbitmq 就可以了。
這時候啟動 add-service-demo 工程,可以看到控制臺輸出的日志信息增加了 TraceId、SpanId 的相關信息,INFO [add-service-demo,,,] 18668,但是現在還沒有具體的內容,因為沒有發生服務調用。
創建一個新的工程 random-service-demo,用來生成一個隨機整數。新建 maven 項目,修改 pom.xml 文件,引入相關依賴:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.4.2.RELEASE</version> </parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>0.10.0.1</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-amqp</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-sleuth-stream</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency> </dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Camden.SR2</version><type>pom</type><scope>import</scope></dependency></dependencies> </dependencyManagement><properties><!-- 指定java版本 --><java.version>1.8</java.version><!-- 鏡像前綴,推送鏡像到遠程庫時需要,這里配置了一個阿里云的私有庫 --><docker.image.prefix>registry.cn-hangzhou.aliyuncs.com/ztecs</docker.image.prefix><!-- docker鏡像的tag --><docker.tag>demo</docker.tag><!-- 激活的profile --><activatedProperties></activatedProperties><kafka.bootstrap.servers>10.47.160.238:9092</kafka.bootstrap.servers> </properties>這里同樣引入了 Sleuth 相關內容。
創建啟動入口類 RandomServiceApplication.java :
resources 中的配置文件可以完全復用 add-service-demo 中的配置,因為最終的配置是從 配置中心 中拉取的, resources 只需要配置 config-server 的相關內容即可。
在 git 倉庫中增加 random-service-demo-dev.yml 配置文件,內容:
配置了端口和消息隊列。
創建一個 RandomController.java 對外提供隨機數服務:
@RestController@RefreshScopepublic class RandomController {private static final Logger logger = LoggerFactory.getLogger(RandomController.class);@RequestMapping(value = "/random", method = RequestMethod.GET)public Integer random() {logger.info(" >>> random");Random random = new Random();return random.nextInt(10);}}業務邏輯很簡單,生成一個 0 ~ 10 的隨機整數并返回。
接下來在 add-service-demo 工程中增加一個隨機數相加的接口,調用 random-service-demo 生成隨機數,并把隨機數相加作為結果返回。
在 AddServiceApplication.java 中增加 @EnableFeignClients 注解,開啟 feign 客戶端遠程調用。
增加 RandomService.java 用來遠程調用 random-service-demo 中的接口:
在 AddController.java 中增加 randomAdd 方法,并對外暴露接口。在方法中兩次調用 random-service-demo 生成隨機數的接口,把隨機數相加作為結果返回:
@Autowired private RandomService randomService;private static Logger logger = LoggerFactory.getLogger(AddController.class);@RequestMapping(value = "/randomAdd", method = RequestMethod.GET) public Map<String, Object> randomAdd() {logger.info(">>> randomAdd");Integer random1 = randomService.random();Integer random2 = randomService.random();Map<String, Object> returnMap = new HashMap<>();returnMap.put("code", 200);returnMap.put("msg", "操作成功");returnMap.put("result", random1 + random2);return returnMap; }修改服務網關 service-gateway-demo 引入 sleuth, 修改 pom.xml 引入依賴(參照 add-service-demo ),修改 log4j2.xml 中的日志格式(參照 add-service-demo )。
啟動 add-service-demo 、 random-service-demo 、 service-gateway-demo ,通過網關調用接口 http://localhost/add-service/randomAdd。查看日志可以發現 從 service-gateway-demo 到 add-service-demo 再到 random-service-demo 中輸出的日志信息,包含相同的 TraceId ,表明處于一個調用鏈。
使用zipkin收集追蹤信息并展現
通過上邊的配置,在服務調用的過程中 spring cloud sleuth 自動幫我們添加了 TraceId 、 SpanId 等服務追蹤需要的內容。現在還需要集中收集這些信息,并提供可視化界面把這些信息展示出來。
Zipkin 是 Twitter 的一個開源項目,允許開發者收集各個服務上的監控數據,并提供查詢接口。spring cloud sleuth 對 zipkin 做了封裝,提供了兩種數據保存方式:內存和 mysql ,這里以生產環境中使用的 mysql 持久化方式為例。
創建一個 maven 工程 zipkin-server-demo,修改 pom.xml 文件增加相關依賴:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.4.2.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>0.10.0.1</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-sleuth-zipkin-stream</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency><dependency><groupId>io.zipkin.java</groupId><artifactId>zipkin-autoconfigure-ui</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Camden.SR2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><properties><!-- 指定java版本 --><java.version>1.8</java.version><!-- 鏡像前綴,推送鏡像到遠程庫時需要,這里配置了一個阿里云的私有庫 --><docker.image.prefix>registry.cn-hangzhou.aliyuncs.com/ztecs</docker.image.prefix><!-- docker鏡像的tag --><docker.tag>demo</docker.tag><!-- 激活的profile --><activatedProperties></activatedProperties><kafka.bootstrap.servers>10.47.160.238:9092</kafka.bootstrap.servers></properties>簡單說明下引入的依賴:spring-cloud-sleuth-zipkin-stream 引入了通過消息驅動的方式收集追蹤信息所需要的 zipkin 依賴, spring-cloud-starter-sleuth、spring-cloud-stream-binder-rabbit,這兩個和之前項目中引入的一樣,都是消息驅動的 sleuth 相關依賴。zipkin-autoconfigure-ui 引入了 zipkin 相關依賴,最后引入了 mysql 和 jdbc 的依賴,用于保存追蹤數據。
在 resources 目錄中新建配置文件 application.yml :
server:port: 9411spring:profiles:active: @activatedProperties@rabbitmq:host: 10.47.160.114port: 5673username: guestpassword: guestdatasource:schema: classpath:/mysql.sqlurl: jdbc:mysql://10.47.160.114:3306/sleuth_logusername: soapassword: 123456initialize: truecontinueOnError: truesleuth:enabled: falseoutput:ansi:enabled: ALWAYSzipkin:storage:type: mysql配置了 zipkin web頁面的端口 9411 ,配置 mysql 和初始化腳本, 并指定 zipkin.storage.type 為 mysql。
在 resources 目錄中創建 mysql 初始化腳本 mysql.sql :
此腳本初始化了 zipkin 保存追蹤數據需要的表。
新建 log4j2.xml 配置文件,可以把其他項目中的復制過來( add-service-demo 等),內容都是一樣的。
創建啟動入口類 ZipkinServerApplication.java :
@SpringBootApplication@EnableZipkinStreamServerpublic class ZipkinServerApplication {public static void main(String[] args) {SpringApplication.run(ZipkinServerApplication.class, args);}}運行 main 方法啟動 zipkin,訪問 http://localhost:9411 打開頁面。
有可能在 zipkin 中查詢不到數據,這是因為 sleuth 有一個采樣率的概念,并不會發送所有的數據,可以通過配置 spring.sleuth.sampler.percentage 指定數據采樣的百分比。
重復多次訪問 http://localhost/add-service/randomAdd 調用接口,就能在 zipkin 中查詢到數據了。
還可以查看服務間的調用鏈:
使用docker-maven-plugin打包并生成docker鏡像
這部分內容和前面幾篇文章基本相同,都是把容器間的訪問地址和 --link 參數對應,不再贅述。
demo源碼 spring-cloud-4.0目錄
grok插件解析日志內容
如果使用 ELK 進行日志分析的話,可以使用 grok 插件解析 spring cloud sleuth 追蹤系統的日志信息(關于 ELK 系統的部署,可以參閱 基于docker部署的微服務架構(七): 部署ELK日志統計分析系統 )。
修改 logstash 的配置文件,增加 grok filter:
這樣就可以解析日志信息了。
最后
分布式服務追蹤在微服務架構中是非常重要的一部分,在發生異常時需要通過追蹤系統來定位問題。Spring Cloud Sleuth 基于 Google Dapper 提供了一個簡單易用的分布式追蹤系統。
在生產環境中,只有追蹤系統還不夠,在服務調用發生錯誤時,比如:網絡延時、資源繁忙等,這種錯誤往往會造成阻塞,造成后續訪問困難,在高并發情況下,調用服務失敗時如果沒有隔離措施,會波及到整個服務端,進而使整個服務端崩潰。
所以還需要一個熔斷系統,對服務依賴做隔離和容錯。下一篇將會介紹 hystrix 熔斷系統。
轉載于:https://my.oschina.net/lhztt/blog/795307
總結
以上是生活随笔為你收集整理的基于docker部署的微服务架构(九): 分布式服务追踪 Spring Cloud Sleuth的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NOIP2016提高组复赛解题报告
- 下一篇: Ajax实现--jQuery