RPC分布式网络通信框架项目

文章目录

      • 对比单机聊天服务器、集群聊天服务器以及分布式聊天服务器
      • RPC通信原理
      • 使用Protobuf做数据的序列化,相比较于json,有哪些优点?
      • 环境配置使用
        • 项目代码工程目录
        • vscode远程开发Linux项目
        • muduo网络库编程示例
        • CMake构建项目集成编译环境
        • Linux环境下搭建muduo网络库
        • 网络I/O模型介绍
      • Protobuf安装配置
        • ubuntu protobuf环境搭建
      • protobuf 实践讲解
      • 如何将本地服务发布成rpc服务?

对比单机聊天服务器、集群聊天服务器以及分布式聊天服务器

(1) 单机聊天服务器
在这里插入图片描述
首先让我们来看单机聊天服务器会遇到的一些性能瓶颈的问题:

  1. 聊天服务器所能承受的用户的并发量受限于硬件资源;
  2. 任何模块的修改,都会导致整个项目代码重新进行编译和部署;
  3. 系统中,有些模块是属于CPU密集型的,有些模块是属于I/O密集型的,造成各模块对于硬件资源的需求是不相同的。所以,将各模块部署在一台服务器上时,没办法按照各模块对资源的需求部署在特定的硬件资源上。

(2)集群聊天服务器
在这里插入图片描述

集群聊天服务器相对于单机聊天服务器,做了哪些优化,还存在哪些性能瓶颈的问题?
优点:
用户的并发量提升、部署集群服务器比较简单。
缺点
1、项目代码还是需要整体重新编译,而且需要进行多次部署;
2、对于某些模块根本不需要高并发(比如上方服务器中的后台管理模块),而使用集群聊天服务器后,在每一台服务器上都部署了相同的模块,可能会造成服务器资源的浪费。

集群:每一台服务器独立运行一个工程的所有模块。
分布式:一个工程拆分了很多模块,每一个模块独立部署运行在一个或多台服务器主机上,所有服务器协同工作共同提供服务,每一台服务器称作分布式的一个节点,根据节点的并发要求,对一个节点可以再做节点模块集群部署。
在这里插入图片描述
当引入了分布式部署聊天服务器的概念后,我们把每个模块按需部署在一台或多台服务器上,使得服务器的资源利用更加充分。同时,如果只是改动了某一个模块的代码,不用将所有的服务器代码都重新编译和部署,只需要重新编译相对应的模块代码。

但是,引入分布式服务器后,会遇到哪些难题需要我们解决呢?

  1. 我们应该如何对大系统的软件模块进行划分呢?怎么划分才能使得各模块的重复代码更少呢?
  2. 当模块被部署在不同的服务器上时,各模块之间如何进行访问呢?比如下图的机器1上的模块如何去调用机器2上的模块的一个业务方法呢?机器1上的模块进程1怎么调用机器1上的模块进程2里面的一个业务方法呢?

在这里插入图片描述
第1个问题通过软件设计师的经验来进行模块的划分,而第2个问题则是通过我们设计的RPC分布式网络框架来实现远程方法的调用,使得客户端基于此框架,能让不同网络结点之间的服务调用像调用本地服务一样简单。


RPC通信原理

RPC(Remote Procedure Call Protocol)远程过程调用协议
在这里插入图片描述
黄色部分:设计rpc方法参数的打包和解析,也就是数据的序列化和反序列化,使用Protobuf
绿色部分:网络部分,包括寻找rpc服务主机,发起rpc调用请求和响应rpc调用结果,使用muduo网络库和zookeeper服务配置中心(专门做服务发现)。
mprpc框架主要包含以上两个部分的内容。


使用Protobuf做数据的序列化,相比较于json,有哪些优点?

  1. Protobuf 是使用二进制存储数据的,而xmljson都是文本存储的;
  2. Protobuf 不需要存储额外的信息,而json是以键值对的方式来存储数据。

环境配置使用

项目代码工程目录

bin:可执行文件
build:项目编译文件
lib:项目库文件
src:源文件
test:测试代码
example:框架代码使用范例
CMakeLists.txt:顶层的cmake文件
README.md:项目自述文件
autobuild.sh:一键编译脚本

vscode远程开发Linux项目
muduo网络库编程示例
CMake构建项目集成编译环境
Linux环境下搭建muduo网络库

muduo库的安装需要依赖boost库,具体安装配置步骤参考博客:
Linux环境下搭建muduo网络库

网络I/O模型介绍
  • accept + read/write
    不是并发服务器
  • accept + fork - process-pre-connection
    适合并发连接数不大,计算任务工作量大于fork的开销
  • accept + thread thread-pre-connection
    比方案2的开销小了一点,但是并发造成线程堆积过多
  • muduo的设计:reactors in threads - one loop per thread
    方案的特点是one loop per thread,有一个main reactor(I/O)负载accept连接,然后把连接分发到某个sub reactor(Worker),该连接的所用操作都在那个sub reactor所处的线程中完成,多个连接可能被分派到多个线程中,以充分利用CPU。
    如果有过多的耗费CPU I/O的计算任务,可以创建新的线程专门处理耗时的计算任务。
  • reactors in process - one loop pre process
    nginx服务器的网络模块设计,基于进程设计,采用多个Reactors充当I/O进程和工作进程,通过一把accept锁,完美解决多个Reactors的“惊群现象”。

Protobuf安装配置

protobuf(protocol buffer)是google 的一种数据交换的格式,它独立于平台语言。
google 提供了protobuf多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。
由于它是一种二进制的格式,比使用 xml(20倍) 、json(10倍)进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

ubuntu protobuf环境搭建

见项目资料下载地址或者在github源代码下载地址:https://github.com/google/protobuf
源码包中的src/README.md,有详细的安装说明,安装过程如下:
1、解压压缩包:unzip protobuf-master.zip
2、进入解压后的文件夹:cd protobuf-master
3、安装所需工具:sudo apt-get install autoconf automake libtool curl make g++ unzip
4、自动生成configure配置文件:./autogen.sh
5、配置环境:./configure
6、编译源代码(时间比较长):make
7、安装:sudo make install
8、刷新动态库:sudo ldconfig


protobuf 实践讲解

首先在服务端编写proto配置文件,比如测试代码中的test.proto,配置文件中包含需要需要进行序列化的请求消息和响应消息,以及在protobuf中定义了描述rpc方法的服务类型,传入相应的rpc请求方法名以及相应的参数,返回参数。

syntax = "proto3"; // 声明了protobuf的版本package fixbug; // 声明了代码所在的包(对于C++来说是namespace)// 定义下面的选项,表示生成service服务类和rpc方法描述,默认不生成
option cc_generic_services = true;message ResultCode
{int32 errcode = 1;bytes errmsg = 2;
}// 数据   列表   映射表
// 定义登录请求消息类型  name   pwd
message LoginRequest
{bytes name = 1;bytes pwd = 2;
}// 定义登录响应消息类型
message LoginResponse
{ResultCode result = 1;bool success = 2;
}message GetFriendListsRequest
{uint32 userid = 1;
}message User
{bytes name = 1;uint32 age = 2;enum Sex{MAN = 0;WOMAN = 1;}Sex sex = 3;
}message GetFriendListsResponse
{ResultCode result = 1;repeated User friend_list = 2;  // 定义了一个列表类型
}// 在protobuf里面怎么定义描述rpc方法的类型 - service
service UserServiceRpc
{rpc Login(LoginRequest) returns(LoginResponse);rpc GetFriendLists(GetFriendListsRequest) returns(GetFriendListsResponse);
}

通过在终端中执行protoc test.proto --cpp_out=./即可在当前文件夹中生成test.pb.cctest.pb.h,其中massage以及service生成了相应的类,且都是通过继承得到的,class LoginRequest: public::google::protobuf::Message,而UserServiceRpc服务生成了两个类:class UserServiceRpc: public google::protobuf::Service提供给服务提供者进行调用、class UserService_Stub: public UserServiceRpc提供给服务消费者进行调用。如下所示:
在这里插入图片描述在这里插入图片描述
在服务提供者方,由于生成的UerServiceRpc类从public google::protobuf::Service继承而来,而类中的Login()方法和GetFriendLists()方法都是虚函数,因此服务提供者需要对相应的方法进行重写。
在服务消费者方,我们可以发现生成的UserServiceRpc_Stub类没有提供默认的构造函数,而只提供了一个含有google::protobuf::RpcChannel* channe参数的构造函数,而且类中的Login()方法和GetFriendLists方法也都是虚函数,在两个方法的底层都调用了RpcChannel类的CallMethod方法。由于RpcChannel 类中的CallMethod方法是一个纯虚函数,因此我们需要重新定义一个类MyRpcChannel继承RpcChannel,在派生类MyRpcChannel中对继承而来的CallMethod方法进行重写。在构造UserServiceRpc_Stub类时,直接向其传递一个MyRpcChannel对象,即可调用派生类中重写函数。


如何将本地服务发布成rpc服务?

用户要将本地服务发布成一个可分布式部署的rpc服务,首先需要通过在user.proto配置文件中写出描述这个rpc方法的方法名字、参数类型以及返回值的响应类型。然后通过在终端执行protoc user.proto --cpp_out=./生成user.pb.ccuser.pb.h,这样就相当于rpc调用方和rpc提供方就生成了一个协议。在rpc方法的提供方,我们可以将本地服务UserServiceUerServiceRpc继承而来,然后重写Login()方法,重写好之后由框架对Login()方法进行直接调用。最后在服务发布方初始化调用的框架MprpcApplication::Init(argc, argv);,定义一个rpc网络服务对象RpcProvider provider;,把UserService对象发布到rpc节点上provider.NotifyService(new UserService());,启动一个rpc服务发布节点provider.Run();Run以后,进程进入阻塞状态,等待远程的rpc调用请求,这样就完成了将本地服务发布成rpc远程服务。

example/user.proto

syntax = "proto3";package fixbug;option cc_generic_services = true;message ResultCode
{int32 errcode = 1; bytes errmsg = 2;
}message LoginRequest
{bytes name = 1;bytes pwd = 2;
}message LoginResponse
{ResultCode result = 1;bool sucess = 2;
}message RegisterRequest
{uint32 id = 1;bytes name = 2;bytes pwd = 3;
}message RegisterResponse
{ResultCode result = 1;bool sucess = 2;
}service UserServiceRpc
{rpc Login(LoginRequest) returns(LoginResponse);rpc Register(RegisterRequest) returns(RegisterResponse);
}

example/callee/usrservice.cc

#include <iostream>
#include <string>
#include "user.pb.h"
#include "mprpcapplication.h"
#include "rpcprovider.h"/*
UserService原来是一个本地服务,提供了两个进程内的本地方法,Login和GetFriendLists
*/
class UserService : public fixbug::UserServiceRpc // 使用在rpc服务发布端(rpc服务提供者)
{
public:bool Login(std::string name, std::string pwd){std::cout << "doing local service: Login" << std::endl;std::cout << "name:" << name << " pwd:" << pwd << std::endl;  return false;}bool Register(uint32_t id, std::string name, std::string pwd){std::cout << "doing local service: Register" << std::endl;std::cout << "id:" << id << "name:" << name << " pwd:" << pwd << std::endl;return true;}/*重写基类UserServiceRpc的虚函数 下面这些方法都是框架直接调用的1. caller   ===>   Login(LoginRequest)  => muduo =>   callee 2. callee   ===>    Login(LoginRequest)  => 交到下面重写的这个Login方法上了*/void Login(::google::protobuf::RpcController* controller,const ::fixbug::LoginRequest* request,::fixbug::LoginResponse* response,::google::protobuf::Closure* done){// 框架给业务上报了请求参数LoginRequest,应用获取相应数据做本地业务std::string name = request->name();std::string pwd = request->pwd();// 做本地业务bool login_result = Login(name, pwd); // 把响应写入  包括错误码、错误消息、返回值fixbug::ResultCode *code = response->mutable_result();code->set_errcode(0);code->set_errmsg("");response->set_sucess(login_result);// 执行回调操作   执行响应对象数据的序列化和网络发送(都是由框架来完成的)done->Run();}void Register(::google::protobuf::RpcController* controller,const ::fixbug::RegisterRequest* request,::fixbug::RegisterResponse* response,::google::protobuf::Closure* done){uint32_t id = request->id();std::string name = request->name();std::string pwd = request->pwd();bool ret = Register(id, name, pwd);response->mutable_result()->set_errcode(0);response->mutable_result()->set_errmsg("");response->set_sucess(ret);done->Run();}
};int main(int argc, char **argv)
{// 调用框架的初始化操作MprpcApplication::Init(argc, argv);// provider是一个rpc网络服务对象。把UserService对象发布到rpc节点上RpcProvider provider;provider.NotifyService(new UserService());// 启动一个rpc服务发布节点   Run以后,进程进入阻塞状态,等待远程的rpc调用请求provider.Run();return 0;
}

当完成了rpc服务提供方的代码后,接下来我们就来看看如何设计和编写的底层框架的代码的,首先关注框架的初始化操作MprpcApplication::Init(argc, argv);,我们在src目录下创建mprpcapplication.hmprpcapplication.cc
mprpcapplication.h

#pragma once#include "mprpcconfig.h"
#include "mprpcchannel.h"
#include "mprpccontroller.h"// mprpc框架的基础类,负责框架的一些初始化操作
class MprpcApplication
{
public:static void Init(int argc, char **argv);static MprpcApplication& GetInstance();static MprpcConfig& GetConfig();
private:static MprpcConfig m_config;MprpcApplication(){}MprpcApplication(const MprpcApplication&) = delete;MprpcApplication(MprpcApplication&&) = delete;
};

mprpcapplication.cc

#include "mprpcapplication.h"
#include <iostream>
#include <unistd.h>
#include <string>MprpcConfig MprpcApplication::m_config;void ShowArgsHelp()
{std::cout<<"format: command -i <configfile>" << std::endl;
}void MprpcApplication::Init(int argc, char **argv)
{if (argc < 2){ShowArgsHelp();exit(EXIT_FAILURE);}int c = 0;std::string config_file;while((c = getopt(argc, argv, "i:")) != -1){switch (c){case 'i':config_file = optarg;break;case '?':ShowArgsHelp();exit(EXIT_FAILURE);case ':':ShowArgsHelp();exit(EXIT_FAILURE);default:break;}}// 开始加载配置文件了 rpcserver_ip=  rpcserver_port   zookeeper_ip=  zookepper_port=m_config.LoadConfigFile(config_file.c_str());// std::cout << "rpcserverip:" << m_config.Load("rpcserverip") << std::endl;// std::cout << "rpcserverport:" << m_config.Load("rpcserverport") << std::endl;// std::cout << "zookeeperip:" << m_config.Load("zookeeperip") << std::endl;// std::cout << "zookeeperport:" << m_config.Load("zookeeperport") << std::endl;
}MprpcApplication& MprpcApplication::GetInstance()
{static MprpcApplication app;return app;
}MprpcConfig& MprpcApplication::GetConfig()
{return m_config;
}

当获取到配置文件以后,加载配置文件的函数LoadConfigFile()以及查询配置项信息的函数Load()mprpcconfig.hmprpcconfig.cc中定义和实现。
mprpcconfig.h

#pragma once#include <unordered_map>
#include <string>// rpcserverip   rpcserverport    zookeeperip   zookeeperport
// 框架读取配置文件类
class MprpcConfig
{
public:// 负责解析加载配置文件void LoadConfigFile(const char *config_file);// 查询配置项信息std::string Load(const std::string &key);
private:std::unordered_map<std::string, std::string> m_configMap;// 去掉字符串前后的空格void Trim(std::string &src_buf);
};

mprpcconfig.cc

#include "mprpcconfig.h"#include <iostream>
#include <string>// 负责解析加载配置文件
void MprpcConfig::LoadConfigFile(const char *config_file)
{FILE *pf = fopen(config_file, "r");if (nullptr == pf){std::cout << config_file << " is note exist!" << std::endl;exit(EXIT_FAILURE);}// 1.注释   2.正确的配置项 =    3.去掉开头的多余的空格 while(!feof(pf)){char buf[512] = {0};fgets(buf, 512, pf);// 去掉字符串前面多余的空格std::string read_buf(buf);Trim(read_buf);// 判断#的注释if (read_buf[0] == '#' || read_buf.empty()){continue;}// 解析配置项int idx = read_buf.find('=');if (idx == -1){// 配置项不合法continue;}std::string key;std::string value;key = read_buf.substr(0, idx);Trim(key);// rpcserverip=127.0.0.1\nint endidx = read_buf.find('\n', idx);value = read_buf.substr(idx+1, endidx-idx-1);Trim(value);m_configMap.insert({key, value});}fclose(pf);
}// 查询配置项信息
std::string MprpcConfig::Load(const std::string &key)
{auto it = m_configMap.find(key);if (it == m_configMap.end()){return "";}return it->second;
}// 去掉字符串前后的空格
void MprpcConfig::Trim(std::string &src_buf)
{int idx = src_buf.find_first_not_of(' ');if (idx != -1){// 说明字符串前面有空格src_buf = src_buf.substr(idx, src_buf.size()-idx);}// 去掉字符串后面多余的空格idx = src_buf.find_last_not_of(' ');if (idx != -1){// 说明字符串后面有空格src_buf = src_buf.substr(0, idx+1);}
}

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

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

相关文章

【MVC】C# MVC基础知识点、原理以及容器和管道

给自己一个目标&#xff0c;然后坚持一段时间&#xff0c;总会有收获和感悟&#xff01; 国庆假期马上结束&#xff0c;闲暇时间&#xff0c;重温一遍C#关于MVC的技术&#xff0c;控制器、视图、模型&#xff0c;知识点和原理&#xff0c;小伙伴们还记得吗 目录 一、MVC知识点1…

linux常见命令以及jdk,tomcat环境搭建

目录 Is pwd cd touch cat echo vim 复制粘贴 mkdir rm cp jdk部署 1. yum list | grep jdk进行查找​编辑 2.安装​编辑 3.再次确认 4.判断是否安装成功 tomcat安装 1.下载压缩包&#xff0c;把压缩包上传至linux(可能需要yum install lrzsz) 2.解压缩unzip 压缩包名&…

asp.net闲置物品购物网系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net闲置物品购物网系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net 闲置物品购物网 二、功…

Trie树(字典树)C++详解

字典树的定义 字典树是一个用来快速查找和存储字符串集合的数据结构。 字典树的形状 假设我们字典树里有以下5个单词&#xff1a; akio&#xff0c;akno&#xff0c;cspj&#xff0c;csps&#xff0c;trie 那么字典树长这样&#xff1a; trie 的结构非常好懂&#xff0c;我们…

013-第二代上位机开发环境搭建

第二代上位机开发环境搭建 文章目录 第二代上位机开发环境搭建项目介绍虚拟机安装Debian 10文件传输远程调试VNCrsync下载安装验证 配置远程调试环境配置远程设备配置 kitsCompilers配置Qtversions配置kits 测试 总结一下 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4…

一个月软考信息安全工程师考前攻略!

一、考试报名时间 信安考试一年就一次&#xff0c;11月4日考试。千万别错过考试哦&#xff01;也别太紧张&#xff01; 二、考试科目设置 (1)网络信息安全基础知识和技术&#xff0c;考试时间为150分钟&#xff0c;笔试&#xff0c;选择题&#xff1b; (2)网络信息安全工程与…

对干扰SAY NO!GNSS模拟将提供伽利略OS-NMA功能(一)

新一代卫星应用程序依赖灵活且准确的GNSS信号作为许多关键项目的重点要素&#xff0c;以确保高精度的定位、导航和授时&#xff08;PNT&#xff09;数据。虹科Safran提供了GNSS仿真测试解决方案&#xff0c;旨在确保复杂GNSS应用系统的高性能、高弹性和高准确性。 现状 GNSS技…

【网络安全---XSS漏洞(1)】XSS漏洞原理,产生原因,以及XSS漏洞的分类。附带案例和payload让你快速学习XSS漏洞

一&#xff0c;什么是XSS漏洞&#xff1f; XSS全称&#xff08;Cross Site Scripting&#xff09;跨站脚本攻击&#xff0c;为了避免和CSS层叠样式表名称冲突&#xff0c;所以改为了XSS&#xff0c;是最常见的Web应用程序安全漏洞之一&#xff0c;位于OWASP top 10 2013/2017年…

发布以太坊测试网络中的第一笔交易

1.安装以太坊钱包 要想发送发布以太坊测试网络中的第一笔交易&#xff0c;首先需要创建一个管理账户的钱包&#xff0c;这个钱包可以理解为管理私钥的容器&#xff0c;具体按照步骤为&#xff1a;打开Chrome浏览器应用商店搜索MetaMask&#xff0c;选择对应的钱包添加至Chrome…

实时选品系统实现的难点

实时选品系统是一个涉及到多个领域的复杂系统&#xff0c;需要兼顾数据挖掘和分析、推荐算法、机器学习、大数据处理等多方面的技术&#xff0c;才能实现高效、准确和可扩展的功能。 以下是实现实时选品系统时可能遇到的难点&#xff1a; 数据量大&#xff1a;实时选品系统需要…

python matplotlib 显示中文

最近再弄新的服务器&#xff0c;结果上面画图的时候没有中文。 用下面这段代码看一下是不是支持呢&#xff1f; from matplotlib.font_manager import FontManager import subprocess fm FontManager() mat_fonts set(f.name for f in fm.ttflist)print (mat_fonts) output…

第四课 递归、分治

文章目录 第四课 递归、分治lc78.子集--中等题目描述代码展示 lc77.组合--中等题目描述代码展示 lc46.全排列--中等题目描述代码展示 lc47.全排列II--中等题目描述代码展示 lc226.翻转二叉树--简单题目描述代码展示 lc98.验证二叉搜索树--中等题目描述代码展示 lc104.二叉树的最…

CSS学习小结

css的两种使用方式&#xff1a; ①内嵌样式表 ②导入外部样式表&#xff08;实际开发常用&#xff09;<link href"...." rel"stylesheet"/> 选择器&#xff1a; ①标签选择器&#xff1a;通过标签种类决定 ②类选择器&#xff1a;class"..…

STM32F030在使用内部参考电压 (VREFINT)时与STM32G070的区别

背景&#xff1a; 之前使用过STM32G070的内部参考电压来提升ADC采集的准确度&#xff08;STM32使用内部参考电压提高ADC采集准确度&#xff09;&#xff0c;所以本次使用STM32F030的芯片时直接把之前G070的代码拿过来用了&#xff0c;但是出现了问题。 查找资料发现两者不同&am…

学习搜狗的workflow,MacBook上如何编译

官网说可以在MacBook上也可以运行&#xff0c;但是编译的时候却有找不到openssl的错误&#xff1a; 看其他博客也有类似的错误&#xff0c;按照类似的思路去解决 问题原因和解决办法 cmake编译的时候&#xff0c;没有找到openssl的头文件&#xff0c;需要设置cmake编译环境下…

Datawhale团队第十期录取名单!

Datawhale团队 公示&#xff1a;Datawhale团队成员 Datawhale成立四年了&#xff0c;从一开始的12个人&#xff0c;学习互助&#xff0c;到提议成立开源组织&#xff0c;做更多开源的事情&#xff0c;帮助更多学习者&#xff0c;也促使我们更好地成长。于是有了我们的使命&…

韦东山老师 RTOS 入门课程(二)理解任务的创建,切换过程

RTOS 的核心实现&#xff1a;保存&#xff0c;恢复现场 接下来开始尝试实现 RTOS。当然我们开发的时候其实不用这样做&#xff0c;现在尝试实现只是为了更好地理解原理。 RTOS 的核心就是刚才在研究的问题&#xff1a;保存和恢复现场。再追其本质&#xff0c;其实就是所有寄存…

102-视频与网络应用篇-环境搭建

1.开发环境 本栏目开发所采集的操作系统是windows10Vmware上安装的Ubuntu18&#xff0c;关于Vmware和Ubuntu操作系统的安装过程本文不详细描述&#xff0c;具体安装步骤大家可以自己百度&#xff0c;有很丰富的安装教程。 本栏目查看资料、代码编辑、均在windows端&#xff0c;…

【文末送书】Python数据分析

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

基于树种优化的BP神经网络(分类应用) - 附代码

基于树种优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于树种优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.树种优化BP神经网络3.1 BP神经网络参数设置3.2 树种算法应用 4.测试结果&#xff1a;5.M…