ApiBoot Logging
可以無(wú)縫整合SpringCloud
來(lái)采集請(qǐng)求日志,目前支持RestTemplate
、Openfeign
兩種方式,我們本章來(lái)講解下在使用Openfeign
完成服務(wù)之間請(qǐng)求相互調(diào)用的一條鏈路請(qǐng)求日志是否可以都采集到。
搭建Eureka Server
我們先來(lái)搭建一個(gè)Eureka Server
,請(qǐng)?jiān)L問(wèn)【搭建服務(wù)注冊(cè)中心Eureka Server】文章內(nèi)容查看具體搭建流程。
搭建Logging Admin
我們需要搭建一個(gè)Logging Admin
用于接收Logging Client
上報(bào)的請(qǐng)求日志,請(qǐng)?jiān)L問(wèn)【ApiBoot Logging整合SpringCloud Eureka負(fù)載均衡上報(bào)日志】查看具體的搭建流程。
我們本章來(lái)模擬提交訂單的業(yè)務(wù)邏輯,涉及到兩個(gè)服務(wù),分別是:商品服務(wù)
、訂單服務(wù)
,接下來(lái)我們需要來(lái)創(chuàng)建這兩個(gè)服務(wù)。
添加ApiBoot & SpringCloud統(tǒng)一版本
由于是采用Maven 多模塊
項(xiàng)目,存在繼承關(guān)系,我們只需要在root
模塊添加版本依賴即可,其他子模塊就可以直接使用,如下所示:
1.82.1.5.RELEASEGreenwich.SR3org.minbox.frameworkapi-boot-dependencies${api.boot.version}pomimportorg.springframework.cloudspring-cloud-dependencies${spring.cloud.version}pomimport
創(chuàng)建公共Openfeign接口定義
學(xué)習(xí)過(guò)Openfeign
的同學(xué)應(yīng)該都知道,Openfeign
可以繼承實(shí)現(xiàn),我們只需要?jiǎng)?chuàng)建一個(gè)公共的服務(wù)接口定義,在實(shí)現(xiàn)該接口的服務(wù)進(jìn)行業(yè)務(wù)實(shí)現(xiàn),在調(diào)用該接口的地方直接注入即可。
下面我們創(chuàng)建一個(gè)名為common-openfeign
的公共依賴項(xiàng)目,pom.xml
添加依賴如下所示:
org.springframework.bootspring-boot-starter-webtrueorg.springframework.cloudspring-cloud-starter-openfeigntrue
在提交訂單時(shí)我們簡(jiǎn)單模擬需要獲取商品的單價(jià),所以在common-openfeign
項(xiàng)目?jī)?nèi)我們要提供一個(gè)查詢商品單價(jià)的服務(wù)接口,創(chuàng)建一個(gè)名為GoodClient
的接口如下所示:
package org.minbox.chapter.common.openfeign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 商品服務(wù)接口定義
*
* @author 恒宇少年
*/
@FeignClient(name = "good-service")
@RequestMapping(value = "/good")
public interface GoodClient {
/**
* 獲取商品價(jià)格
*
* @param goodId 商品編號(hào)
* @return
*/
@GetMapping(value = "/{goodId}")
Double getGoodPrice(@PathVariable("goodId") Integer goodId);
}
注解解釋:
-
@FeignClient
:SpringCloud Openfeign
提供的接口客戶端定義注解,通過(guò)value
或者name
來(lái)指定GoodClient
訪問(wèn)的具體ServiceID
,這里我們配置的value
值為good-service
項(xiàng)目spring.application.name
配置參數(shù)(ServiceID
=spring.application.name
)。
這樣當(dāng)我們通過(guò)注入GoodClient
接口調(diào)用getGoodPrice
方法時(shí),底層通過(guò)Openfeign
的Http
代理訪問(wèn)good-service
的對(duì)應(yīng)接口。
創(chuàng)建商品服務(wù)
下面我們?cè)賮?lái)創(chuàng)建一個(gè)名為good-service
的SpringBoot
項(xiàng)目。
添加相關(guān)依賴
在pom.xml
項(xiàng)目配置文件內(nèi)添加如下依賴:
org.minbox.frameworkapi-boot-starter-loggingorg.springframework.bootspring-boot-starter-weborg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.minbox.chaptercommon-openfeign0.0.1-SNAPSHOT
可以看到我們?cè)?code>good-service項(xiàng)目依賴內(nèi)添加了我們?cè)谏厦鎰?chuàng)建的common-openfeign
依賴模塊,因?yàn)?code>GoodClient服務(wù)接口的實(shí)現(xiàn)是在good-service
項(xiàng)目?jī)?nèi),我們需要添加common-openfeign
依賴后創(chuàng)建對(duì)應(yīng)的XxxController
并實(shí)現(xiàn)GoodClient
接口完成對(duì)應(yīng)的業(yè)務(wù)邏輯實(shí)現(xiàn)。
商品業(yè)務(wù)實(shí)現(xiàn)
這里我們簡(jiǎn)單做個(gè)示例,將價(jià)格固定返回,實(shí)現(xiàn)GoodClient
的控制器如下所示:
package org.minbox.chapter.good.service;
import org.minbox.chapter.common.openfeign.GoodClient;
import org.springframework.web.bind.annotation.RestController;
/**
* 商品服務(wù)接口實(shí)現(xiàn)
*
* @author 恒宇少年
* @see GoodClient
*/
@RestController
public class GoodController implements GoodClient {
@Override
public Double getGoodPrice(Integer goodId) {
if (goodId == 1) {
return 15.6;
}
return 0D;
}
}
注冊(cè)到Eureka Server
我們需要將good-service
注冊(cè)到Eureka Server
,修改application.yml
配置文件如下所示:
# ServiceID
spring:
application:
name: good-service
# 端口號(hào)
server:
port: 8082
# Eureka Config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10000/eureka/
instance:
prefer-ip-address: true
配置上報(bào)的Logging Admin
我們需要將good-service
的請(qǐng)求日志上報(bào)到Logging Admin
,采用SpringCloud ServiceID
的方式配置,修改application.yml
配置文件如下所示:
api:
boot:
logging:
# 控制臺(tái)打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服務(wù)編號(hào)
discovery:
service-id: logging-admin
啟用Eureka Client & Logging
最后我們?cè)?code>XxxApplication入口類添加注解來(lái)啟用Eureka Client
以及Logging Client
,如下所示:
/**
* 商品服務(wù)
*
* @author 恒宇少年
*/
@SpringBootApplication
@EnableLoggingClient
@EnableDiscoveryClient
public class GoodServiceApplication {
/**
* logger instance
*/
static Logger logger = LoggerFactory.getLogger(GoodServiceApplication.class);
public static void main(String[] args) {
SpringApplication.run(GoodServiceApplication.class, args);
logger.info("{}服務(wù)啟動(dòng)成功.", "商品");
}
}
至此我們的商品服務(wù)已經(jīng)準(zhǔn)備完成.
創(chuàng)建訂單服務(wù)
創(chuàng)建一個(gè)名為order-service
的SpringBoot
項(xiàng)目(建議參考源碼,本章采用Maven多模塊創(chuàng)建)。
添加相關(guān)依賴
修改pom.xml
添加相關(guān)依賴如下所示:
org.minbox.frameworkapi-boot-starter-loggingorg.springframework.bootspring-boot-starter-weborg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.cloudspring-cloud-starter-openfeignorg.minbox.chaptercommon-openfeign0.0.1-SNAPSHOT
訂單業(yè)務(wù)實(shí)現(xiàn)
我們來(lái)模擬一個(gè)提交訂單的場(chǎng)景,創(chuàng)建一個(gè)名為OrderController
的控制器,如下所示:
/**
* 訂單控制器
*
* @author 恒宇少年
*/
@RestController
@RequestMapping(value = "/order")
public class OrderController {
/**
* 商品接口定義注入
* {@link GoodClient}
*/
@Autowired
private GoodClient goodClient;
@PostMapping
public String submit(Integer goodId, Integer buyCount) {
Double goodPrice = goodClient.getGoodPrice(goodId);
Double orderAmount = goodPrice * buyCount;
//...
return "訂單提交成功,訂單總金額:" + orderAmount;
}
}
注冊(cè)到Eureka Server
將我們創(chuàng)建的order-service
注冊(cè)到Eureka Server
,修改application.yml
配置文件如下所示:
spring:
application:
name: order-service
server:
port: 8081
# Eureka Config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10000/eureka/
instance:
prefer-ip-address: true
配置上報(bào)的Logging Admin
我們需要將order-service
的請(qǐng)求日志上報(bào)到Logging Admin
,采用SpringCloud ServiceID
的方式配置,修改application.yml
配置文件如下所示:
api:
boot:
logging:
# 控制臺(tái)打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服務(wù)編號(hào)
discovery:
service-id: logging-admin
啟用Eureka Client & Logging
修改order-service
入口類OrderServiceApplication
,添加啟用Eureka Client
、Logging Client
的注解,如下所示:
/**
* 訂單服務(wù)
*
* @author 恒宇少年
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableLoggingClient
@EnableFeignClients(basePackages = "org.minbox.chapter.common.openfeign")
public class OrderServiceApplication {
/**
* logger instance
*/
static Logger logger = LoggerFactory.getLogger(OrderServiceApplication.class);
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
logger.info("{}服務(wù)啟動(dòng)成功.", "");
}
}
注解解釋:
-
@EnableFeignClients
:該注解是Openfeign
提供的啟用自動(dòng)掃描Client
的配置,我們通過(guò)basePackages
(基礎(chǔ)包名)的方式進(jìn)行配置掃描包下配置@FeignClient
注解的接口,并為每個(gè)接口生成對(duì)應(yīng)的代理實(shí)現(xiàn)
并添加到Spring IOC
容器。org.minbox.chapter.common.openfeign
包名在common-openfeign
項(xiàng)目?jī)?nèi)。
運(yùn)行測(cè)試
依次啟動(dòng)項(xiàng)目,eureka-server
> logging-admin
> good-service
> order-service
。
通過(guò)curl
命令訪問(wèn)order-service
內(nèi)的提交訂單地址:/order
,如下所示:
? ~ curl -X POST http://localhost:8081/order/?goodId/=1/&buyCount/=3
訂單提交成功,訂單總金額:46.8
可以看到我們已經(jīng)可以成功的獲取訂單的總金額,我們?cè)?code>/order請(qǐng)求方法內(nèi)調(diào)用good-service
獲取商品的單價(jià)后計(jì)算得到訂單總金額。
測(cè)試點(diǎn):鏈路信息傳遞
我們通過(guò)控制臺(tái)輸出的日志信息來(lái)確認(rèn)下鏈路信息(traceId、spanId)的透?jìng)魇欠裾_。
收到order-service上報(bào)的日志
Receiving Service: 【order-service -> 127.0.0.1】, Request Log Report,Logging Content:[
{
"endTime":1573009439840,
"httpStatus":200,
"requestBody":"",
"requestHeaders":{
"host":"localhost:8081",
"user-agent":"curl/7.64.1",
"accept":"*/*"
},
"requestIp":"0:0:0:0:0:0:0:1",
"requestMethod":"POST",
"requestParam":"{/"buyCount/":/"3/",/"goodId/":/"1/"}",
"requestUri":"/order",
"responseBody":"訂單提交成功,訂單總金額:46.8",
"responseHeaders":{},
"serviceId":"order-service",
"serviceIp":"127.0.0.1",
"servicePort":"8081",
"spanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"startTime":1573009439301,
"timeConsuming":539,
"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
}
]
收到good-service上報(bào)的日志
Receiving Service: 【good-service -> 127.0.0.1】, Request Log Report,Logging Content:[
{
"endTime":1573009439810,
"httpStatus":200,
"parentSpanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"requestBody":"",
"requestHeaders":{
"minbox-logging-x-parent-span-id":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"minbox-logging-x-trace-id":"3e20cc72-c880-4575-90ed-d54a6b4fe555",
"host":"10.180.98.156:8082",
"connection":"keep-alive",
"accept":"*/*",
"user-agent":"Java/1.8.0_211"
},
"requestIp":"10.180.98.156",
"requestMethod":"GET",
"requestParam":"{}",
"requestUri":"/good/1",
"responseBody":"15.6",
"responseHeaders":{},
"serviceId":"good-service",
"serviceIp":"127.0.0.1",
"servicePort":"8082",
"spanId":"6339664e-097d-4a01-a734-935de52a7d44",
"startTime":1573009439787,
"timeConsuming":23,
"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
}
]
結(jié)果分析:
-
請(qǐng)求日志的入口為
order-service
所以并不存在parentSpanId
(上級(jí)單元編號(hào)),而spanId
(單元編號(hào))、traceId
(鏈路編號(hào))也是新生成的。 -
本次請(qǐng)求會(huì)經(jīng)過(guò)
good-service
服務(wù),因此parentSpanId
則是order-service
生成的spanId
,traceId
同樣也是order-service
生成的,透?jìng)鱄ttpHeader方式進(jìn)行傳遞,表示在同一條請(qǐng)求鏈路上。
敲黑板,劃重點(diǎn)
ApiBoot Logging
支持使用Openfeign
傳遞鏈路信息,內(nèi)部通過(guò)Openfeign
攔截器實(shí)現(xiàn),源碼詳見(jiàn):org.minbox.framework.logging.client.http.openfeign.LoggingOpenFeignInterceptor
。
將traceId
(鏈路編號(hào))、parentSpanId
(單元編號(hào))通過(guò)HttpHeader
的形式傳遞到目標(biāo)訪問(wèn)服務(wù),服務(wù)通過(guò)請(qǐng)求日志攔截器進(jìn)行提取并設(shè)置鏈路綁定關(guān)系。
-
traceId
傳遞時(shí)HttpHeader名稱為:minbox-logging-x-trace-id
. -
parentSpanId
傳遞時(shí)HttpHeader名稱為:minbox-logging-x-parent-span-id
審核編輯 黃昊宇
-
JAVA
+關(guān)注
關(guān)注
19文章
2967瀏覽量
104763 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14344 -
MySQL
+關(guān)注
關(guān)注
1文章
811瀏覽量
26580 -
人臉識(shí)別
+關(guān)注
關(guān)注
76文章
4012瀏覽量
81911
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論