ApiBoot Logging可以无缝整合SpringCloud来采集请求日志,目前支持RestTemplate、Openfeign两种方式,我们本章来讲解下在使用Openfeign完成服务之间请求相互调用的一条链路请求日志是否可以都采集到。
搭建Eureka Server
我们先来搭建一个Eureka Server,请访问【搭建服务注册中心Eureka Server】文章内容查看具体搭建流程。
搭建Logging Admin
我们需要搭建一个Logging Admin用于接收Logging Client上报的请求日志,请访问【ApiBoot Logging整合SpringCloud Eureka负载均衡上报日志】查看具体的搭建流程。
我们本章来模拟提交订单的业务逻辑,涉及到两个服务,分别是:商品服务、订单服务,接下来我们需要来创建这两个服务。
添加ApiBoot & SpringCloud统一版本
由于是采用Maven 多模块项目,存在继承关系,我们只需要在root模块添加版本依赖即可,其他子模块就可以直接使用,如下所示:
1.82.1.5.RELEASEGreenwich.SR3org.minbox.frameworkapi-boot-dependencies${api.boot.version}pomimportorg.springframework.cloudspring-cloud-dependencies${spring.cloud.version}pomimport
创建公共Openfeign接口定义
学习过Openfeign的同学应该都知道,Openfeign可以继承实现,我们只需要创建一个公共的服务接口定义,在实现该接口的服务进行业务实现,在调用该接口的地方直接注入即可。
下面我们创建一个名为common-openfeign的公共依赖项目,pom.xml添加依赖如下所示:
org.springframework.bootspring-boot-starter-webtrueorg.springframework.cloudspring-cloud-starter-openfeigntrue
在提交订单时我们简单模拟需要获取商品的单价,所以在common-openfeign项目内我们要提供一个查询商品单价的服务接口,创建一个名为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;
/**
* 商品服务接口定义
*
* @author 恒宇少年
*/
@FeignClient(name = "good-service")
@RequestMapping(value = "/good")
public interface GoodClient {
/**
* 获取商品价格
*
* @param goodId 商品编号
* @return
*/
@GetMapping(value = "/{goodId}")
Double getGoodPrice(@PathVariable("goodId") Integer goodId);
}
注解解释:
-
@FeignClient:SpringCloud Openfeign提供的接口客户端定义注解,通过value或者name来指定GoodClient访问的具体ServiceID,这里我们配置的value值为good-service项目spring.application.name配置参数(ServiceID=spring.application.name)。
这样当我们通过注入GoodClient接口调用getGoodPrice方法时,底层通过Openfeign的Http代理访问good-service的对应接口。
创建商品服务
下面我们再来创建一个名为good-service的SpringBoot项目。
添加相关依赖
在pom.xml项目配置文件内添加如下依赖:
org.minbox.frameworkapi-boot-starter-loggingorg.springframework.bootspring-boot-starter-weborg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.minbox.chaptercommon-openfeign0.0.1-SNAPSHOT
可以看到我们在good-service项目依赖内添加了我们在上面创建的common-openfeign依赖模块,因为GoodClient服务接口的实现是在good-service项目内,我们需要添加common-openfeign依赖后创建对应的XxxController并实现GoodClient接口完成对应的业务逻辑实现。
商品业务实现
这里我们简单做个示例,将价格固定返回,实现GoodClient的控制器如下所示:
package org.minbox.chapter.good.service;
import org.minbox.chapter.common.openfeign.GoodClient;
import org.springframework.web.bind.annotation.RestController;
/**
* 商品服务接口实现
*
* @author 恒宇少年
* @see GoodClient
*/
@RestController
public class GoodController implements GoodClient {
@Override
public Double getGoodPrice(Integer goodId) {
if (goodId == 1) {
return 15.6;
}
return 0D;
}
}
注册到Eureka Server
我们需要将good-service注册到Eureka Server,修改application.yml配置文件如下所示:
# ServiceID
spring:
application:
name: good-service
# 端口号
server:
port: 8082
# Eureka Config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10000/eureka/
instance:
prefer-ip-address: true
配置上报的Logging Admin
我们需要将good-service的请求日志上报到Logging Admin,采用SpringCloud ServiceID的方式配置,修改application.yml配置文件如下所示:
api:
boot:
logging:
# 控制台打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服务编号
discovery:
service-id: logging-admin
启用Eureka Client & Logging
最后我们在XxxApplication入口类添加注解来启用Eureka Client以及Logging Client,如下所示:
/**
* 商品服务
*
* @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("{}服务启动成功.", "商品");
}
}
至此我们的商品服务已经准备完成.
创建订单服务
创建一个名为order-service的SpringBoot项目(建议参考源码,本章采用Maven多模块创建)。
添加相关依赖
修改pom.xml添加相关依赖如下所示:
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
订单业务实现
我们来模拟一个提交订单的场景,创建一个名为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;
}
}
注册到Eureka Server
将我们创建的order-service注册到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
配置上报的Logging Admin
我们需要将order-service的请求日志上报到Logging Admin,采用SpringCloud ServiceID的方式配置,修改application.yml配置文件如下所示:
api:
boot:
logging:
# 控制台打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服务编号
discovery:
service-id: logging-admin
启用Eureka Client & Logging
修改order-service入口类OrderServiceApplication,添加启用Eureka Client、Logging Client的注解,如下所示:
/**
* 订单服务
*
* @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("{}服务启动成功.", "");
}
}
注解解释:
-
@EnableFeignClients:该注解是Openfeign提供的启用自动扫描Client的配置,我们通过basePackages(基础包名)的方式进行配置扫描包下配置@FeignClient注解的接口,并为每个接口生成对应的代理实现并添加到Spring IOC容器。org.minbox.chapter.common.openfeign包名在common-openfeign项目内。
运行测试
依次启动项目,eureka-server > logging-admin > good-service > order-service。
通过curl命令访问order-service内的提交订单地址:/order,如下所示:
➜ ~ curl -X POST http://localhost:8081/order/?goodId/=1/&buyCount/=3
订单提交成功,订单总金额:46.8
可以看到我们已经可以成功的获取订单的总金额,我们在/order请求方法内调用good-service获取商品的单价后计算得到订单总金额。
测试点:链路信息传递
我们通过控制台输出的日志信息来确认下链路信息(traceId、spanId)的透传是否正确。
收到order-service上报的日志
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上报的日志
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"
}
]
结果分析:
-
请求日志的入口为
order-service所以并不存在parentSpanId(上级单元编号),而spanId(单元编号)、traceId(链路编号)也是新生成的。 -
本次请求会经过
good-service服务,因此parentSpanId则是order-service生成的spanId,traceId同样也是order-service生成的,透传HttpHeader方式进行传递,表示在同一条请求链路上。
敲黑板,划重点
ApiBoot Logging支持使用Openfeign传递链路信息,内部通过Openfeign拦截器实现,源码详见:org.minbox.framework.logging.client.http.openfeign.LoggingOpenFeignInterceptor。
将traceId(链路编号)、parentSpanId(单元编号)通过HttpHeader的形式传递到目标访问服务,服务通过请求日志拦截器进行提取并设置链路绑定关系。
-
traceId传递时HttpHeader名称为:minbox-logging-x-trace-id. -
parentSpanId传递时HttpHeader名称为:minbox-logging-x-parent-span-id
审核编辑 黄昊宇 -
JAVA
+关注
关注
20文章
3005浏览量
116820 -
spring
+关注
关注
0文章
341浏览量
16048 -
MySQL
+关注
关注
1文章
930浏览量
29740 -
人脸识别
+关注
关注
77文章
4130浏览量
88733
发布评论请先 登录
EDAS再升级!全面支持Spring Cloud应用
Dubbo Cloud Native 之路的实践与思考
ApiBoot Logging Admin可视化界面管理日志教程
ApiBoot Logging使用Rest Template透传链路信息
ApiBoot Logging整合Spring Cloud Eureka负载均衡上报日志
ApiBoot Logging忽略路径不进行采集日志的教程
修改ApiBoot Logging日志采集前缀的教程
Spring Cloud Function基于Spring Boot的函数计算框架
RabbitRpc基于spring cloud的微服务rpc调用
ApiBoot Logging使用Spring Cloud Openfeign透传链路信息
评论