【微服务】springboot整合skywalking使用详解

news2025/2/28 19:53:16

目录

一、前言

二、SkyWalking介绍

2.1 SkyWalking是什么

2.2 SkyWalking核心功能

2.3 SkyWalking整体架构

2.4 SkyWalking主要工作流程

三、为什么选择SkyWalking

3.1 业务背景

3.2 常见监控工具对比

3.3 为什么选择SkyWalking

3.3.1 代码侵入性极低

3.3.2 功能丰富

3.3.3 产品完善度高

四、SkyWalking环境搭建

4.1 下载安装包

4.2 部署服务

4.2.1 部署oap(apm)服务

4.2.2 web-ui界面访问

4.3 数据持久化配置

4.3.1 修改配置文件

4.3.2 上传mysql驱动包

4.3.3 重启服务

五、springboot接入Skywalking

5.1 准备一个测试使用的接口

5.2 获得Java Agent包

5.3 参数化启动项目

5.4 接口访问测试

六、dubbo接入Skywalking

6.1 搭建三个微服务模块

6.2 api模块

6.3 dubbo-provider模块

6.3.1 添加核心依赖

6.3.2 添加核心配置文件

6.3.3 添加dubbo接口实现类

6.3.4 服务启动类

6.4 dubbo-consumer模块

6.4.1 添加核心依赖

6.4.2 添加核心配置文件

6.4.3 添加接口类

6.5 功能测试

6.6 接入Skywalking

七、写在文末


一、前言

随着分布式应用大规模部署,应用可观测性从理论到落地已经在众多大型互联网应用中得到实践,经过多年沉淀,行业内也逐步形成了相应的一整套解决方案,比如针对日志可视化ELK解决方案,分布式链路追踪APM解决方案SkyWalking,可视化监控工具Prometheus等等。今天将详细介绍下APM解决方案中一款重要工具SkyWalking的使用。

二、SkyWalking介绍

2.1 SkyWalking是什么

 SkyWalking是一个开源的可观测平台,用于从服务和云原生等基础设施收集、分析、聚合以及可视化数据。官网地址:Apache SkyWalking

SkyWalking 提供了一种简便的方式来清晰地观测分布式系统。相比较zipkin而言,skywalking利用agent字节码增强技术实现代码无侵入,通信方式采用GRPC,性能较好,实现方式是java探针,支持告警,支持JVM监控,支持全局调用统计,UI界面更加强大等优点。

2.2 SkyWalking核心功能

SkyWalking 有哪些功能呢?下面列举了其主要的功能点:

  • 监控手段丰富,可以通过语言探针和 service mesh 获得监控是数据;

  • 多种类型的语言自动探针,包括 Java,.NET Core 和 Node.JS;

  • 轻量高效,无需大数据平台,和大量的服务器资源;

  • 模块化,UI、存储、集群管理都有多种机制可选;

  • 支持各类告警机制;

  • 提供优秀的可视化解决方案,支持各类监控数据的可视化;

2.3 SkyWalking整体架构

下面是SkyWalking的整体架构图

从官网提供的Skywalking架构图来看,分成几个部分,简单来说,可以分成下面几个部分

OAP后端

OAP负责接收 Agent 发送的 Tracing 和Metric的数据信息,然后进行分析Analysis Core,存储到外部存储器 Storage ,最终提供查询Query 功能

WEB-UI

UI负责提供web控制台,查看链路,查看各种指标,性能等

Agent探针

Agent负责收集日志数据:Agent以探针的方式,进行请求链路的数据采集,并向OAP服务器上报

Storage存储

数据的存储层,支持ElasticSearch、Mysql、H2多种方式

2.4 SkyWalking主要工作流程

官网关于SkyWalking的详细工作流程图如下

如果在实际应用中,简化上面的流程之后其核心工作流程分为下面几步:

  • 数据采集 ,服务通过探针的方式接入数据采集的功能;
  • 上报服务器 ,请求链路的相关处理行为会上报到OAP服务中;
  • 数据存储 ,进行数据的聚合管理和分析,并存储在持久层;
  • 数据展现 ,通过UI界面进行可视化呈现收集的数据;

三、为什么选择SkyWalking

3.1 业务背景

随着业务规模的不断增长,应用的微服务数量也在随着增长,当微服务的数量越来越大时,一旦系统中出现某个问题,如果不借助工具或其他监测手段,解决问题是耗时耗力的,尤其是N多个微服务之间的调用链路很长,调用关系非常复杂的时候,对于排查、定位分析和解决问题将变得异常困难。因此急需一种可视化工具,可以追踪分布式调用的完整链路的信息,并提供可视化分析界面。

3.2 常见监控工具对比

市面上提供的可用于微服务应用监控工具有很多,下面列举一些常用的解决方案:

  • Zipkin:Twitter公司开源的一个分布式追踪工具,被Spring Cloud Sleuth集成,使用广泛而稳定,需要在应用程序中埋点,对代码侵入性强;

  • Cat:美团大众点评开源的一款分布式链路追踪工具。需要在应用程序中埋点,对代码侵入性强;

  • Pinpoint:一个韩国团队开源的产品,探针收集的数据粒度非常细,但性能损耗大,因其出现的时间较长,完成度很高;

  • SkyWalking:中国人吴晟(华为)开源的一款分布式追踪,分析,告警的工具,现在是Apache旗下开源项目,对云原生支持,目前增长势头强劲,社区活跃,中文文档没有语言障碍;

  • OpenTelemetry ,是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型、采集、处理、导出等的标准化问题,提供与三方 vendor 无关的服务;

  • Grafana,是一个监控仪表系统,由Grafana Labs公司开源的的一个系统监测 (System Monitoring) 工具。帮助用户简化监控的复杂度,用户只需要提供需要监控的数据,它就可以生成各种可视化仪表。同时它还支持报警功能,可以在系统出现问题时通知用户;

  • Prometheus,主要用于对基础设施的监控,包括服务器(CPU、MEM等)、数据库(MYSQL、PostgreSQL等)、Web服务等,几乎所有东西都可以通过Prometheus进行监控。而它的数据,则是通过配置,建立与数据源的联系来获取的;

3.3 为什么选择SkyWalking

相比于其他框架,SkyWalking有自己的优势,主要体现在下面几点:

3.3.1 代码侵入性极低

Skywalking采用字节码增强的技术实现,微服务以java-agent的方式集成,业务代码本身无侵入,像Zipkin代码侵入性就比较高。

3.3.2 功能丰富

1)链路追踪、拓扑分析能力强,采用先进的流式拓扑分析设计;

2)Skywalking功能比较丰富,插件丰富,报表统计,UI界面更加人性化,更符合国人的使用习惯;

3.3.3 产品完善度高

Java生态、功能丰富,国人开发、社区活跃,迭代迅速。

四、SkyWalking环境搭建

官网安装包下载地址:Downloads | Apache SkyWalking

4.1 下载安装包

为了后面的演示,即将springboot服务接入到Skywalking,需要部署OAP服务,以及Java Agent,对应的安装包如下:

下载oap安装包

oap即Skywalking服务端,如下进到官网后,选择下面这里的包,选择合适的版本进行下载,这里我选择9.3.0的版本;

下载Java Agent包

即服务端agent,用于收集来自客户端的端点信息和指标信息,然后上报到oap服务端

4.2 部署服务

4.2.1 部署oap(apm)服务

oap的服务运行起来很简单,最直接的方式就是解压后进入到bin目录直接使用脚本启动即可

tar -zxvf apache-skywalking-apm-9.3.0.tar.gz

cd apache-skywalking-apm-bin

cd bin

启动脚本说明:

1、oapService.sh,oap的服务启动脚本;

2、webappService.sh,web-ui服务启动脚本;

3、startup.sh,同时包含了启动上面两个服务的脚本

实际使用时,为了方便直接使用 startup.sh启动即可

启动成功后会有两个服务,如上所示:

  • skywalking-oap-server:暴露11800和12800两个端口,分别为收集监控数据的端口11800和接受前端请求的端口12800,修改端口可以修改config/applicaiton.yml;
  • skywalking-web-ui:服务会占用 8080 端口, 修改端口可以修改webapp/webapp.yml;

4.2.2 web-ui界面访问

启动成功后,可以直接访问web-ui界面,访问地址:IP:8080,如果需要修改ui界面的访问端口,直接修改web-app目录下的配置文件中的端口即可;

访问的界面如下

4.3 数据持久化配置

默认情况下如果不做任何配置,Skywalking运行过程中产生的数据将会存储在H2,即内存数据库,一旦服务宕机或被重启,数据将丢失,Skywalking提供了多种持久化数据存储的方式,可以选择mysql,es等,下面使用mysql来存储数据。

4.3.1 修改配置文件

找到config目录下的application.yml配置文件,然后找到下面配置的地方,改为mysql

修改相关的mysql连接参数,指向你自己的连接,注意提前创建一个数据库

4.3.2 上传mysql驱动包

将mysql的连接jar包上传到oap-libs目录下,下载链接:文件分享

4.3.3 重启服务

不管是使用哪种数据持久化方式,一定要上传对应的jar包之后再重启服务,否则会报连接驱动找不到的问题

然后刷新页面,就可以看到数据库下生成了很多Skywalking相关的数据表

五、springboot接入Skywalking

在项目中,如何将springboot接入Skywalking,从而通过Skywalking来监控服务呢?下面来看具体的操作步骤

5.1 准备一个测试使用的接口

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/getById")
    public User find(@RequestParam String userId) {
        return new User(userId, "jerry");
    }

}

5.2 获得Java Agent包

通过上面下载的Agent的包解压到本地目录(如果是服务器同样的操作)

5.3 参数化启动项目

本地启动项目时,添加如下启动参数

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=sky-boot -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=服务端IP:11800

在idea中配置上面的这段启动参数

5.4 接口访问测试

启动成功后,浏览器访问上面提供的测试接口

然后再在skywalking的web-ui界面上就能看到当前监控到的服务信息了

继续点进去,可以看到详细的各种监控指标信息

也可以点击其他的监控指标进行多维度查看

六、dubbo接入Skywalking

dubbo在微服务治理中也是经常使用的,下面通过实例演示下如何利用Skywalking监控dubbo服务

6.1 搭建三个微服务模块

工程目录结构如下

顶层pom依赖

<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>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.6.14</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

6.2 api模块

该模块主要定义公共的实体类,服务接口

定义一个实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    private String id;
    private String userName;

}

定义一个服务接口

public interface UserService {
    User getByUserId(String userId);
}

发布api模块jar包

通过maven命令,将api模块的jar包发布到本地仓库以供其他模块依赖

6.3 dubbo-provider模块

模块结构如下

6.3.1 添加核心依赖

这里注册中心使用zk

    <dependencies>

       <dependency>
            <groupId>com.congge</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
        </dependency>

    </dependencies>

6.3.2 添加核心配置文件

server:
  port: 8083

dubbo:
  application:
    name: sky-dubbo-provider
  protocol:
    name: dubbo
    port: -1

  registry:
    address: zookeeper://IP地址:2181
    protocol: zookeeper

6.3.3 添加dubbo接口实现类

import com.congge.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;

@Slf4j
@DubboService
public class UserServiceImpl implements UserService{

    @Override
    public User getByUserId(String userId) {
        log.info("[ServiceProvider] 根据 id 查询用户:{}", userId);
        return new User(userId, "jerry");
    }
}

6.3.4 服务启动类

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableDubbo
@SpringBootApplication
public class ProviderApp {

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

6.4 dubbo-consumer模块

模块结构如下

6.4.1 添加核心依赖

    <dependencies>

        <dependency>
            <groupId>com.congge</groupId>
            <artifactId>api</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
        </dependency>

    </dependencies>

6.4.2 添加核心配置文件

server:
  port: 8082

dubbo:
  application:
    name: sky-dubbo-consumer
  protocol:
    name: dubbo
    port: -1
  registry:
    address: zookeeper://IP地址:2181
    protocol: zookeeper

6.4.3 添加接口类

为了方便测试,自定义一个接口,在接口中调用dubbo服务

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @DubboReference
    private UserService userService;

    //http://localhost:8082/user/getByUserId?userId=001
    @GetMapping("/getByUserId")
    private User getByUserId(@RequestParam String userId) {
        User user = userService.getByUserId(userId);
        log.info("调用 dubbo 服务成功,获取用户信息:{}", JSON.toJSONString(user));
        return user;
    }
}

6.5 功能测试

分别启动provider和consumer两个微服务模块,然后调用一下上面的接口,确认看到下面的结果后说明服务可以正常调通(前提:确保连接的zk服务可正常使用

6.6 接入Skywalking

按照上述接入springboot的方式,idea启动的时候修改下启动配置参数,provider和consumer两个模块启动时分别添加如下参数:

provider配置的启动参数

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=dubbo-provider -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=IP地址:11800

consumer配置的启动参数

-javaagent:E:\code-self\skywalking-agent\skywalking-agent.jar -DSW_AGENT_NAME=dubbo-consumer -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=IP地址:11800

配置之后再次启动两个模块的服务,再次访问接口,仍然能够得到期望的结果

此时再次到web-ui界面上看到在服务列表中出现了dubbo的两个服务信息

如果切换到拓扑图,也能够清晰看到两个服务的调用链路

七、写在文末

本文详细介绍了Skywalking从搭建到使用的详细流程,事实上Skywalking的功能远不止这些,它不仅能够监控微服务的调用,还能监控像前端,PHP,nginx等众多其他的中间件,甚至还能与其他的可视化展示工具进行对接,可以说功能非常完善,如果整合你的项目在技术选项中需要集成一款链路监控,链路追踪以及可视化监控指标展示的工具,Skywalking也许是一个不错的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1346815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算器写作文

一起来交流编程吧【CSDN app】&#xff1a; http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kx9pL9ccIHGKNLE0CWviAqQ_q6HzxomLW&authKeyVslKe623ptw8VRepda%2Bh0Ttr8Ruz8v%2FBW5HpVzyTWU7ECwpHIZpULMj6qIHYZBVb&noverify0&gro 计算器写作文 题目描述 众所周知&a…

大数据应用领域:数据驱动一切

大数据出现的时间只有十几年&#xff0c;被人们广泛接受并应用只有几年的时间&#xff0c;但就是这短短几年的时间&#xff0c;大数据呈现出爆炸式增长的态势。在各个领域&#xff0c;大数据的身影几乎无处不在。今天我们通过一些大数据典型的应用场景分析&#xff0c;一起来看…

初始Web服务器

一、web服务器 1、什么是web服务器&#xff1f; web服务器就是web项目的容器&#xff0c;我们将开发好的web项目部署到web容器中&#xff0c;才能使用网络中的用户通过浏览器进行访问。 一张图带你了解web服务器有啥作用&#xff1a; 在我的电脑上有一个已经做好的项目&#…

【实用工具】Tmux使用指南

Tmux 三个重要概念 session&#xff08;会话&#xff09;、window&#xff08;窗口&#xff09;、pane&#xff08;面板&#xff09; 一个会话可以有多个窗口&#xff0c;一个窗口可以划分为多个面板 注意在tmux中使用快捷命令的话&#xff0c;需要加上前缀ctrlb 关于session的…

01. BI - Project one, 员工离职预测

文章目录 项目概要数据集处理LRSVM Hi&#xff0c;你好。我是茶桁。 又是开始了一个新的阶段。我不建议一些没基础的同学直接从这里开始&#xff0c;还是要先去之前的课程里补补基础。有的时候即便依葫芦画瓢的把代码写出来了&#xff0c;但是基本原理不清楚。而有的时候&#…

【华为机试】2023年真题B卷(python)-关联子串

一、题目 题目描述&#xff1a; 给定两个字符串str1和str2&#xff0c; str1进行排列组合只要有一个为str2的子串则认为str1是str2的关联子串&#xff0c; 请返回子串在str2的起始位置&#xff0c;若不是关联子串则返回-1。 二、示例 示例1 输入输出示例仅供调试&#xff0c;后…

WSL使用VsCode运行cpp文件

文章目录 缘起主要步骤参考 缘起 今天在阅读《C20设计模式-可复用的面向对象设计方法&#xff08;原书第2版&#xff09;》的时候&#xff0c;遇到代码想要运行一下&#xff0c;于是决定使用wsl下的vscode配置cpp的环境。 主要步骤 1.安装gcc和g编译器 打开命令行输入wsl&am…

C++_模板

目录 1、函数模板 1.2 模板原理 2、多个模板参数 3、模板的显示实例化 4、模板的匹配 5、类模板 结语&#xff1a; 前言&#xff1a; 在C中&#xff0c;模板分为函数模板和类模板&#xff0c;而模板的作用就是避免了重复的工作&#xff0c;把原本是程序员要做的重复工作交…

JavaFX:MVC模式学习01-使用PropertyValueFactory将模型与视图绑定

PropertyValueFactory类是“TableColumn cell value factory”,绑定创建列表中的项。示例如下&#xff1a; TableColumn<Person,String> firstNameCol new TableColumn<Person,String>("First Name");firstNameCol.setCellValueFactory(new PropertyVal…

C语言实验4:指针

目录 一、实验要求 二、实验原理 1. 指针的基本概念 1.1 指针的定义 1.2 取地址运算符&#xff08;&&#xff09; 1.3 间接引用运算符&#xff08;*&#xff09; 2. 指针的基本操作 2.1 指针的赋值 2.2 空指针 3. 指针和数组 3.1 数组和指针的关系 3.2 指针和数…

CentOS 7 实战指南:目录操作命令详解

写在前面 想要在 CentOS 7 系统下更高效地进行目录操作吗&#xff1f;不要犹豫&#xff0c;在这里我为你准备了一篇精彩的技术文章&#xff01;这篇文章将带您深入了解 CentOS 7 下目录操作相关命令的使用方法。无论您是新手还是有一定经验的用户&#xff0c;这篇文章都将为您…

vue-springboot基于JavaWeb的家装一体化商城平台guptn

针对用户需求开发与设计&#xff0c;该技术尤其在各行业领域发挥了巨大的作用&#xff0c;有效地促进了家装一体化的发展。然而&#xff0c;由于用户量和需求量的增加&#xff0c;信息过载等问题暴露出来&#xff0c;为改善传统线下管理中的不足&#xff0c;本文将提出一套基于…

区块链的三难困境是什么,如何解决?

人们需要保持社交、工作和睡眠之间的平衡&#xff0c;并且努力和谐相处。同样的概念也反映在区块链的三难困境中。 区块链三难困境是一个术语&#xff0c;指的是现有区块链的局限性&#xff1a;可扩展性、安全性和去中心化。这是一个存在了几十年的设计问题&#xff0c;其问题的…

学习动态规划解决不同路径、最小路径和、打家劫舍、打家劫舍iii

学习动态规划|不同路径、最小路径和、打家劫舍、打家劫舍iii 62 不同路径 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量dp[i][j] dp[i-1][j] dp[i][j-1] import java.util.Arrays;/*** 路径数量* 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量…

【深度解析C++】const成员函数

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;Cthis指针&#xf…

野火霸道-V2+3.2寸屏+FreeRTOS+LVGL移植

摘要 基于野火霸道-V23.2寸屏的开发板&#xff0c;下载器为STLINK分为两个版本&#xff0c;FreeRTOS和裸机版本 裸机 裸机准备 lvgl v8.2版本的源码野火的《触摸画板-3.2寸》与《基本定时器》的代码例程 移植 将基本定时器代码移植到触摸画板-3.2寸的例程中&#xff0c;…

java springboot将接口查询数据放在系统中 一小时系统更新一次 避免用户访问接口查询数据库缓慢

真到了公司 很多数据库表 特别是常用的功能业务对应的 都是几百万条起步的数据 查询会比较缓慢 那么 我们就可以不用每次都真的查询数据库 例如 我这里有一个接口 通过 封装的 IBookService.list 函数去查询数据库 接口返回是这样的 我们先在启动类 条件装配上 这个接口所在的…

MySQL所有常见问题

一、事务 定义:一组操作要么全部成功,要么全部失败,目的是为了保证数据最终的一致性 在MySQL中,提供了一系列事务相关的命令: start transaction | begin | begin work:开启一个事务commit:提交一个事务rollback:回滚一个事务事务的ACID 原子性(Atomicity):当前事…

信号与线性系统翻转课堂笔记20——系统函数与信号流图

信号与线性系统翻转课堂笔记20——系统函数与信号流图 The Flipped Classroom20 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff09;了解信…

CSU计算机学院2021年C语言期末题目思路分享(后两道题)

文章目录 E: 实数相加——大数加法的拓展原题题目描述输入输出样例输入样例输出 题目思路实现步骤代码和注释 F: 谍影寻踪——链表的思想和运用原题题目描述输入输出样例输入样例输出 题目思路 一点感想 E: 实数相加——大数加法的拓展 原题 题目描述 C语言就要期末考试了&a…