linux性能监控项目简历

Linux性能分析监控
技术栈:c++,c++常用特性,docker,grpc,proto,cmake,qt

  1. docker模块:dockerfile指定相应的cmake,grpc,proto等源码和依赖,构建整个项目环境,易于在多台服务器上部署环境,并编写容器操作的脚本指令,易于启动操作项目所依赖的环境。
  2. monitor模块:采用工厂方法,通过构造moniotr的抽象类定义接口,后实现相应的CPU状态,系统负载,软中断,mem,net监控,易于为之后扩展更多系统监控;并为了模拟出真实的性能问题,使用stress工具进行模拟压测,分析相应时刻服务器的cpu状况和中断状况。
  3. 通过grpc框架,构建出相应的server, client;server在所要监控的服务器部署,client生成库供monitor模块和display模块调用,并考虑为了降低耦合性,项目每个模块相互独立,可拆解,只通过调用grpc服务来进行远程连接。
  4. 使用protobuf序列化协议,构建出整个项目的数据结构。
  5. display模块分为两大部分:ui界面的构造,datamodel构造;
    ui界面使用QWidget、QTableView、QStackedLayout、QPushButton等进行构建
    datamodel:通过继承QAbstractTableModel,构建出相应的cpu_model、softirq_model、mem_model等,每3秒刷新一次数据。

项目难点部分:

docker部分(分布式环境搭建)

1、源码安装:这个项目使用了一些grpc等大型的开源源码库,需要对这些源码进行构建(如何构建)

项目想用分布式,是选择自研的rpc框架,还是用goole的还是说其他的rpc框架,我用的是grpc;
所以就要去看grpc的官方文档;去看REMADE.md,看如何使用grpc;
在这里插入图片描述
下面还有源码的位置;
在这里插入图片描述
言归正传,去看C++的如何使用grpc,其实是一个C++的实现,这这里的README中找寻如何使用Cmake进行编译源码(因为我们在项目要使用源码构建),在Cmake这个部分能够看到编译指南

查看编译指南
其中;第一要先安装依赖项(Pre-requistes)(我们的环境是docker,所以这里的安装依赖是卸载dockerfile中的)
在编写dockerfile的时候不是一下子就编写好的,要不断的去补充缺少的依赖;
然后看build构建部分
在这里插入图片描述
然后是安装:
在这里插入图片描述
这里的安装部分其实就是install_grpc.sh部分的命令

比如去看grpc的官方文档,写grpc的构建脚本

2、dockerfile的编写(如何调试dockerfile)

在构建源码的过程中,发现缺少什么报错,分析错误,缺什么就在前面添加什么,dockerfile文件时一步一步完善的,而且源码之间是有逻辑顺序的;dockerfile在构建时是有缓存的,上一次在哪里构建失败,然后我们修改之后,再去构建,修改部分之后的位置就全都没有缓存了,缓存就失效了;所以当发现缺少依赖的时候,要靠近使用该缺少依赖的构建部分去补上;这样不会有大面积的缓存失效;(告诉面试官,这样省时省力,最后确定能够构建出来了,就整理一下就好)
在每个源码安装的脚本中最后都要将源码和源码的压缩包进行删除,防止镜像太大;(告诉面试官:一开始镜像太大,后来删除了,压缩镜像文件大小)

提前cd到这个路径,后面就是相对路径了,就不要那么复杂的写路径了
cd “ ( d i r n a m e " (dirname " (dirname"{BASH_SOURCE[0]}”)"
cd就是到这个路径下/tmp/install/grpc

3、docker_run into.sh的这些设计docker的基本操作指令的脚本编写过程

遇到core dumped需要gdb来调试一下
1、环境变量的问题:例如run.sh中的 -e DISPLAY=$display \ 这个参数,这是用docker安装qt,构建,要有环境变量display;
2、run:先关闭一个容器并删除再开启一个容器(或者重命名,每个容器的命名不能够相同)

echo "stop and rm docker" 
docker stop linux_monitor > /dev/null
docker rm -v -f linux_monitor > /dev/null

3、into:xhost +local:root 1>/dev/null 2>&1 这个一i地那个要添加,否则报错;

4、镜像库的问题

更换镜像源,以下都是操作都是使用的国内镜像源来下载的;

grpc和proto

1、如何使用grpc

去找grpc源码中examples中cpp的例子中的helloworld
阅读这个例子,学习这个例子examples/cpp/helloworld/greeter_server.cc

#include <iostream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"

#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;

ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");

// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloReply* reply) override {
    std::string prefix("Hello ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
};

void RunServer(uint16_t port) {
  std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
  GreeterServiceImpl service;

  grpc::EnableDefaultHealthCheckService(true);
  grpc::reflection::InitProtoReflectionServerBuilderPlugin();
  ServerBuilder builder;
  // Listen on the given address without any authentication mechanism.
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  // Register "service" as the instance through which we'll communicate with
  // clients. In this case it corresponds to an *synchronous* service.
  builder.RegisterService(&service);
  // Finally assemble the server.
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;

  // Wait for the server to shutdown. Note that some other thread must be
  // responsible for shutting down the server for this call to ever return.
  server->Wait();
}

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);
  RunServer(absl::GetFlag(FLAGS_port));
  return 0;
}

代码分析:

  1. 引入必要的库:首先引入了一些标准库和第三方库,包括 gRPC 库和 Abseil 库:
    #include :用于标准输入输出。
    #include :用于智能指针管理。
    #include :用于字符串操作。
    #include “absl/flags/flag.h”:Abseil 库中的标志(flag)处理库,用于处理命令行参数。
    #include “absl/flags/parse.h”:用于解析命令行标志。
    #include “absl/strings/str_format.h”:用于字符串格式化。

2、引入gRPC相关头文件这些头文件与 gRPC 服务的实现相关:
#include <grpcpp/ext/proto_server_reflection_plugin.h>:用于启用 gRPC 服务器反射。
#include <grpcpp/grpcpp.h>:gRPC 的核心库。
#include <grpcpp/health_check_service_interface.h>:用于 gRPC 健康检查服务。

3、引入自动生成的Protoc文件
根据是否使用 Bazel 构建工具,代码引入了自动生成的 gRPC 和 Proto 文件:#include “helloworld.grpc.pb.h”:该文件由 Protocol Buffers 编译器从 helloworld.proto 文件生成,包含 gRPC 服务和消息定义。

4、定义命令行标志
使用 Abseil 库定义一个命令行标志 port,默认值为 50051:
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
5、实现服务逻辑
GreeterServiceImpl 类继承自 Greeter::Service,并重写了 SayHello 方法:
class GreeterServiceImpl final : public Greeter::Service { Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override { std::string prefix("Hello "); reply->set_message(prefix + request->name()); return Status::OK; } }; SayHello 方法接收 HelloRequest 请求,返回 HelloReply 响应,内容为 "Hello " + 请求的名字。Greeter::Service 这个类是从 Protocol Buffers (protobuf) 编译生成的代码中来的。具体来说,它是从您定义的 .proto 文件(通常命名为 helloworld.proto)中生成的。

6、启动grpc服务器
RunServer 函数负责启动 gRPC 服务器:
void RunServer(uint16_t port) { std::string server_address = absl::StrFormat("0.0.0.0:%d", port); GreeterServiceImpl service; grpc::EnableDefaultHealthCheckService(true); grpc::reflection::InitProtoReflectionServerBuilderPlugin(); ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); } 通过 absl::StrFormat 构造服务器地址。
启用默认的健康检查服务和 Proto 反射插件。
创建一个 ServerBuilder 对象来配置和启动服务器。
使用 InsecureServerCredentials 在指定地址监听不安全的连接(不使用 SSL/TLS)。
注册服务实例 service 以处理客户端请求。
启动服务器并等待其关闭。

7、主函数
主函数负责解析命令行参数并启动服务器:
int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); RunServer(absl::GetFlag(FLAGS_port)); return 0; }
该代码实现了一个简单的 gRPC 服务,可以通过命令行参数指定端口(默认 50051)。它定义了一个 SayHello 方法,接受 HelloRequest 请求并返回带有 “Hello” 前缀的响应。通过 gRPC 的 ServerBuilder 构建和启动服务,支持健康检查和反射功能。
学习完成这个例子之后,就可以写项目中的rpc_manager中的server部分;
要进行验证自己写的文件有没有编译问题,或者其他问题,例如将自己编写的server运行起来,然后去查看一下这个监听端口有没有起来;
而客户端我们的项目时封装成了一个库,但最好在封装库的前提下,也生成可执行程序,然后执行client;测试一下客户端和服务端能否进行一个完整的连接测试;

重要
在使用monitor可执行程序(调用的client的库)连接server后,可以从netstat -tap看到产生了一对TCP连接;
其实端口50051是服务器监听的端口,当monitor连接上server之后,就会出现一对TCP连接,当运行了display的可执行程序后,可以看到又出现了一对TCP连接,分析:client和display连接的都是服务器端口50051,二者都是客户端,monitor将数据发送给server,dispaly从server中获取数据;
在这里插入图片描述

2、源码构建的困难

有的没有提供CMake构建的方法;

2、proto中CMakeLists.txt的编写

1、观看源码中examples/protos/helloworld.proto这个写法,但是源码中编译它,使用Bazle来构建的,不是用的Cmake;所以我们无法通过官方源码学习构建;就需要我们找proto如何使用Cmake来构建;

CMakeLists.txt中给生成库添加链接库时要进行试错才能行,当编译报错时,根据提示信息,来一步一步来添加依赖库;

target_link_libraries(monitor_proto
    PUBLIC  # 关键字表示链接的库不仅对当前目标有效,而且对所有链接到该目标的其他目标也有效。
        protobuf::libprotobuf
        gRPC::grpc
        gRPC::grpc++ 
)

2、连接这些库,到哪里去找这些库呢?

CMake 在链接时,会根据你指定的库名和配置路径来查找这些库。具体来说:
1. find_package 命令
在 CMake 文件的开头,你已经使用 find_package 命令找到了 protobuf, gRPC, 和 c-ares 库:
find_package 命令会在系统的预定义路径和 CMake 模块路径中搜索所需的包,并将找到的库、头文件路径和其他相关信息存储在 CMake 变量中。例如,find_package(protobuf CONFIG REQUIRED) 可能会设置 protobuf::libprotobuf 库路径以及 PROTOBUF_INCLUDE_DIRS 头文件路径。
我们是使用的容器,这些库都被装在容器中,基本上都在usr/local中,找不到就会报错,如果没有安装,然后我们就需要修改dockerfile文件了;
2、CMake对于库的查找路径
系统路径:CMake 会在系统的标准库路径(如 /usr/lib, /usr/local/lib 等)中查找库
CMake 安装路径:如果你通过 CMake 安装了这些库,CMake 会在其安装目录下查找库。
自定义路径:你可以通过设置 CMAKE_PREFIX_PATH 或 CMAKE_MODULE_PATH 来指定 CMake 额外查找路径。
环境变量:CMake 还会查找与库相关的环境变量,比如 PROTOBUF_ROOT 或 gRPC_ROOT。

2、protoc的编译问题

get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION) # 这行代码获取 gRPC::grpc_cpp_plugin 的位置,并将其存储在 grpc_cpp_plugin_location 变量中。
protobuf_generate(TARGET monitor_proto LANGUAGE cpp) # 这行代码指示 CMake 生成 Protocol Buffers 文件,目标是 monitor_proto,语言是 cpp(C++)。为了将 .proto 文件编译成对应的 C++ 源文件,并将这些生成的文件添加到已经创建的 monitor_proto 库中。
protobuf_generate(TARGET monitor_proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${grpc_cpp_plugin_location}")

第二条命令是用来生成对应的C++源文件的;
第三条命令才是和grpc相关的,用到了cpp的插件,才会生成和grpc相关的代码;后面实现的时候来继承这里的代码;

3、在private-node/proto/monitor_info.proto文件中有些自定义类型是repeated

比如电脑cpu有多个,所以就是repeated;但是内存是单个,就不用repeated

4、在private-node/proto/monitor_info.proto文件中grpc的结构

service GrpcManager {
  rpc SetMonitorInfo(MonitorInfo) returns (google.protobuf.Empty) {
  }

  rpc GetMonitorInfo(google.protobuf.Empty) returns (MonitorInfo) {
  }
}

a. 消息类型的定义 (monitor.pb.h 和 monitor.pb.cc)
这些文件包含 .proto 文件中定义的消息类型(如 MonitorInfo)的类定义和实现。如果使用了 google.protobuf.Empty,相关的定义通常已经在 Google 提供的 protobuf 库中定义好了。
b. gRPC 服务类 (monitor.grpc.pb.h 和 monitor.grpc.pb.cc)
这些文件包含与 gRPC 服务 GrpcManager 相关的类定义和实现。具体来说:
GrpcManager::Service 类:这是一个纯虚基类,包含了 SetMonitorInfo 和 GetMonitorInfo 方法的虚函数声明。您需要继承这个类并实现这些方法,以定义服务器的实际行为。
GrpcManager::Stub 类:这个类用于客户端调用 gRPC 服务。它包含了 SetMonitorInfo 和 GetMonitorInfo 方法,**客户端可以使用这些方法来向服务器发送请求。**客户端可以创建一个 Stub 实例,并通过调用其方法来与服务器交互。
GrpcManager::AsyncService 类:这个类用于实现异步 gRPC 服务器。它提供了异步版本的 SetMonitorInfo 和 GetMonitorInfo 方法,您可以使用它们来处理非阻塞的请求。

总结protoc

1、准备数据结构,然后将这些数据结构封装到一起(有的需要repeated,有的不需要);也就是整合数据结构
2、写服务的名字和接口;
3、编译模块:CMakeList的编写;

各个模块之间要解耦合

当CMakeLists.txt写了链接库之后的作用

保险期间,还是要设定target_include_directories
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值