java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > springcloud集成skywalking

springcloud集成skywalking实现全链路追踪

作者:小码农叔叔

在微服务治理中,springcloud也是技术选型中的一个成熟的解决方案,而且相对dubbo来说,springcloud涉及到的微服务组件更多,调用链路可能更复杂,本文将详细介绍下如何在springcloud中集成skywalking,需要的朋友可以参考下

一、前言

在微服务治理中,springcloud也是技术选型中的一个成熟的解决方案,而且相对dubbo来说,springcloud涉及到的微服务组件更多,调用链路可能更复杂,本文将详细介绍下如何在springcloud中集成skywalking。

二、环境准备

2.1 软件环境

本文springcloud微服务模块需要依赖的外部模块如下:

2.2 微服务模块

服务模块如下:

2.3 环境搭建

2.3.1 下载安装包

像mysql,redis的搭建相信很多同学都非常熟悉了,这里就不再赘述了,快速介绍下nacos的单机搭建流程,nacos下载地址:git下载地址

2.3.2 解压并启动服务

1、解压安装包
tar -zxvf nacos-server-1.4.2.tar.gz
 
2、进入到bin目录使用脚本启动
sh startup.sh -m standalone

2.3.3 访问web界面

服务正常启动后,可以在浏览器访问nacos的ui界面:http://IP:8848/nacos

默认登录账号和密码:nacos/nacos 

三、搭建springcloud微服务

3.1 顶层公共依赖

最外层添加如下依赖

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
        <dubbo.version>3.1.5</dubbo.version>
 
        <spring-cloud.version>2021.0.5</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
 
        <!-- mybatis-plus 版本 -->
        <mybatis-plus.version>3.5.2</mybatis-plus.version>
        <druid.version>1.1.17</druid.version>
 
        <mybatis-boot.version>2.2.2</mybatis-boot.version>
 
    </properties>
 
    <dependencyManagement>
        <dependencies>
 
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
 
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
 
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
 
        </dependencies>
    </dependencyManagement>
 
    <dependencies>
 
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.23</version>
        </dependency>
 
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>8.14.0</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3.2 用户服务模块

模块结构如下

3.2.1 准备测试使用数据库

创建一个数据库,并提前准备一张测试使用的表

CREATE TABLE `t_user` (
  `id` varchar(32) NOT NULL,
  `name` varchar(32) DEFAULT NULL,
  `email` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

添加一条测试使用的数据

INSERT INTO `db-base`.`t_user`(`id`, `name`, `email`) VALUES ('001', 'jerry', 'jerry@163.com');

3.2.2 添加依赖

用户模块的服务将会使用nacos作为注册中心,所以需要添加nacos的依赖,同时,后面的服务调用需要走统一网关,因此gateway的依赖不可少

<dependencies>
 
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <!-- spring data redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
 
        <!--nacos服务发现客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-boot.version}</version>
        </dependency>
 
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
 
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

3.2.3 添加配置文件

server:
  port: 9002
spring:
  application:
    name: user-service
 
  cloud:
    nacos:
      discovery:
        server-addr: nacos的地址:8848
 
  profiles:
    active: dev # 环境标识
 
  datasource:
    url: jdbc:mysql://数据库连接地址:3306/db-base
    driverClassName: com.mysql.jdbc.Driver
    username: root
    password: 123456
 
  redis:
    host: localhost
    port: 6379
 
mybatis:
  mapper-locations: classpath:mapper/*.xml
  #目的是为了省略resultType里的代码量
  type-aliases-package: com.congge.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.2.4 redis的自定义配置类

接口中将会使用redis作为缓存,需要自定义对缓存的序列化

 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
 
@Configuration
public class RedisConfig {
 
    @Bean
    public FastJson2JsonRedisSerializer<Object> fastJson2JsonRedisSerializer() {
        return new FastJson2JsonRedisSerializer<>(Object.class);
    }
 
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(fastJson2JsonRedisSerializer());
        redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer());
        return redisTemplate;
    }
}

自定义序列化类

 
import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
 
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
 
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
 
    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
 
    private Class<T> clazz;
 
    public FastJson2JsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }
 
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t).getBytes(DEFAULT_CHARSET);
    }
 
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
 
        return JSON.parseObject(str, clazz);
    }
}

3.2.5 核心业务实现类

在这段代码中,添加了一个根据ID查询用户详情的方法,第一次未查到将会走数据库,然后放入缓存,以后相同的请求再过来的时候,如果缓存中有数据将会走缓存

 
import com.alibaba.fastjson2.JSON;
import com.congge.entity.User;
import com.congge.mapper.UserMapper;
import com.congge.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
 
import java.util.concurrent.TimeUnit;
 
@Slf4j
@Service
public class UserServiceImpl  implements UserService {
 
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
 
    @Autowired
    private UserMapper userMapper;
 
    @Override
    public User getById(String id) {
        log.info("[用户服务] 基于 id 查询用户信息:{}", id);
        String key = "sw:users:" + id;
        Object json = redisTemplate.opsForValue().get(key);
        if (json != null) {
            log.info("[用户服务] redis 中查询到用户信息:key={}, json={}", key, json);
            return JSON.parseObject(json.toString(), User.class);
        }
 
        User user = userMapper.getById(id);
        if (user != null) {
            log.info("[用户服务] redis 中不存在,从数据库查到数据并缓存:{}", user);
            redisTemplate.opsForValue().set(key, user, 2, TimeUnit.HOURS);
            return user;
        }
 
        log.warn("[用户服务] 基于 id 查询用户失败,用户不存在:{}", id);
        return null;
    }
}

3.2.6 业务测试接口

添加一个接口类

@RestController
@RequestMapping("/user")
public class UserController {
 
    @Autowired
    private UserService userService;
 
    //http:localhost:9002/user/getById?id=001
    @GetMapping("/getById")
    public User getById(@RequestParam String id) {
        return userService.getById(id);
    }
}

3.2.7 启动类

@MapperScan("com.congge.mapper")
@SpringBootApplication
public class UserApp {
    public static void main(String[] args) {
        SpringApplication.run(UserApp.class, args);
    }
 
}

3.2.8 接口模拟测试

启动用户模块的服务,然后调用查询用户的接口,可以正常查到数据库的数据

同时nacos的服务列表中,也能看到当前注册上去的用户服务信息

到这里,用户服务就基本整合完成

3.3 订单服务模块

模块结构如下

3.3.1 引入依赖

订单模块中,将会通过feign的方式调用user模块的服务,所以这里增加了feign的依赖

    <dependencies>
 
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <!--nacos服务发现客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
 
    </dependencies>

3.3.2 添加配置文件

server:
  port: 9003
 
spring:
  application:
    name: order-service
 
  cloud:
    nacos:
      discovery:
        server-addr: nacos的地位:8848

3.3.3 添加feign服务

order模块中对user模块的接口调用,通过下面的接口定义,然后注入到需要调用的类中即可

import com.congge.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
 
@FeignClient(name = "user-service",path = "/user")
public interface UserFeignService {
 
    @GetMapping(value = "/getById")
    User getById(@RequestParam("id") String id);
 
}

3.3.4 添加测试接口

@RequestMapping("/order")
@RestController
public class OrderController {
 
    @Autowired
    private OrderService orderService;
 
    //localhost:9003/order/getById?id=001
    @GetMapping("/getById")
    public Object getById(@RequestParam String id) {
        return orderService.getById(id);
    }
}

接口实现

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
 
    @Autowired
    private  UserFeignService userFeignService;
 
    @Override
    public Map getById(String id) {
        log.info("[订单服务] 基于 id 查询订单详情:{}", id);
 
        Map map = new HashMap();
 
        Order order = new Order();
        order.setOrderId("0002");
        order.setProductId("0001");
        order.setProductName("小米手机");
        map.put("order",order);
 
        User user = userFeignService.getById("001");
        map.put("user",user);
        return map;
 
    }
}

3.3.5 启动类

@EnableFeignClients
@SpringBootApplication
public class OrderApp {
 
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }
 
}

3.3.6 接口模拟测试

启动order模块的服务,然后调用查询订单的接口,可以看到期望的返回结果

同时在nacos的服务列表中存在两个服务信息

3.4 网关服务模块

上面分别完成了两个服务模块的搭建,测试,以及相互之间的调用,但是并没有通过网关,接下来将服务接口的调用通过网关接入

3.4.1 引入依赖

    <dependencies>
 
        <!--gateway网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
 
        <!--nacos客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
 
    </dependencies>

3.4.2 添加配置文件

网关模块的搭建,主要是配置文件中涉及的路由规则的配置,需要搞清楚规则配置中的各项含义,否则调用的时候容易出错

server:
  port: 9001
spring:
  application:
    name: api-gateway
 
  cloud:
    nacos:
      discovery:
        server-addr: nacos的地址:8848
 
    gateway:
      discovery:
        locator:
          enabled: true # 让gateway可以发现nacos中的微服务
 
      routes:
        - id: us_route
          uri: lb://user-service
          predicates:
            - Path=/us/**
          filters:
            - StripPrefix=1
        - id: order_route
          uri: lb://order-service
          predicates:
            - Path=/os/**
          filters:
            - StripPrefix=1
#  profiles:
#    active: dev # 环境标识

3.4.3 启动类

@SpringBootApplication
public class GatewayApp {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayApp.class, args);
    }
 
}

3.4.4 功能测试

启动网关模块的服务,然后在nacos中可以看到网关的服务信息也注册进来了

用户查询接口测试

如果通过网关调用,可以调用接口:localhost:9001/us/user/getById?id=001

订单查询接口测试

如果通过网关调用,可以调用接口:localhost:9001/os/order/getById?id=001

四、springcloud接入SkyWalking

通过上面的步骤,我们完成了springcloud的微服务模块的搭建,和调用效果的测试,接下来,将微服务接入到SkyWalking中,看看SkyWalking是否能够追踪到微服务调用的完整链路信息

4.1 参数准备

对gateway,user,order三个模块,在服务启动时分别添加如下启动参数

gateway模块

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=service-gateway -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=IP服务地址:11800

user模块

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=service-user -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=IP服务地址:11800

order模块

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=service-order -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=IP服务地址:11800

4.2 接口调用

先分别启动user和order模块,然后调用查询order的服务接口

调用成功后,等待监控数据上报到Skywalking,然后去Skywalking观察调用链路的信息

拓扑图展现

从拓扑图上可以看到该接口调用的完整链路信息

Trace的信息展现

调用链路的信息就更加的完整了,正好是获取订单详情接口的完整链路,包括最终获取用户走的是redis缓存

4.3 接入网关

按照上面同样的方式,启动网关服务,然后通过网关调用获取订单详情的接口

接口调用成功,此时再去Skywalking监控界面检查调用的拓扑信息,奇怪的是,在调用链路中,并没有显示调用的起始点是网关,这是怎么回事呢?

4.3.1 问题分析

默认情况下,oap服务并不识别gateway作为服务链路的入口,如果需要支持,可以在下载的Agent的包目录下,找到optional-plugins目录下的gateway的插件包,然后拷贝到plugins目录中

注意,拷贝的jar包版本要与你工程中的包版本对应起来,拷贝完成后,重新重启几个模块的服务

再次调用接口

调用成功后,从Skywalking的拓扑信息,以及Trace链路监控信息来看,此时网关就作为入口能够正常显示出来了

五、写在文末

在生产环境中,随着部署的微服务增多,微服务中引用的中间件的增多,一个接口从开始到响应结果,中间的调用链路可能非常复杂,如果不借助外部的可视化工具进行协助排查,这将是一个非常耗时耗力的过程,所以在此情况下,Skywalking在全链路监控这一块提供了一个非常好的选择,本篇到此结束,感谢观看。

以上就是springcloud集成skywalking实现全链路追踪的详细内容,更多关于springcloud集成skywalking的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文