项目第十弹:连接管理模块与服务器模块

ops/2024/9/23 4:49:03/

项目第十弹:连接管理模块与服务器模块

  • 一、连接管理模块的设计
    • 1.连接模块的设计
    • 2. 连接管理模块的设计
      • 1.连接ID? TcpConnectionPtr!!
  • 二、连接管理模块的实现
    • 1.连接模块的实现
    • 2.连接管理模块的实现
  • 三、服务器模块的设计
    • 1.前言
    • 2.设计
  • 四、服务器模块的实现
  • 五、服务器源文件编写,编译与运行

一、连接管理模块的设计

我们之前说:RabbitMQ是将连接细分为信道,从而复用TCP连接
这个连接就是我们今天要实现的连接模块

muduo库当中有一个TcpConnection连接类,它是对底层TCP套接字的封装,不是我们要实现的业务上的连接模块

我们要实现的业务上的连接模块要怎么设计呢?

1.连接模块的设计

信道是连接的细分,因此连接的任务就是:

  1. 打开/关闭信道
  2. 获取信道

而且声明/删除信道都是接收Request,响应Response

成员:

  1. 信道管理模块句柄
  2. muduo网络通信连接TcpConnectionPtr
  3. ProtobufCodecPtr
  4. 信道模块构造所需资源:
  5. 虚拟机管理模块句柄
  6. 消费者管理模块句柄
  7. 异步工作线程池句柄

依旧是无需互斥锁

2. 连接管理模块的设计

增、删、查

1.连接ID? TcpConnectionPtr!!

我们肯定是需要将连接放到哈希表当中组织起来的
问题是应该让谁当key呢?

跟信道一样,搞一个“连接ID”??
是可以的,只不过要给所有的请求都在加上一个连接ID,而我们说信道是用户视角中的通信通道,现在如果要给所有的请求都加上一个连接ID,未免有点“违背”了这个信道的概念,而且很不优雅

那怎么办呢?
有没有什么优雅一些的方法?
在这里插入图片描述
接口:

  1. 创建连接
  2. 销毁连接
  3. 获取连接

成员:
4. 互斥锁
5. unordered_map<TcpConnectionPtr,Connection::ptr> _connection_map;

注意:

unordered_map 直接对 shared_ptr 的底层原始指针进行哈希,而不是对 shared_ptr 对象本身进行哈希

二、连接管理模块的实现

1.连接模块的实现

因为信道是连接的细分,所以对应的信道管理模块也由连接所创建,所独占,所管理

using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;using OpenChannelRequestPtr = std::shared_ptr<OpenChannelRequest>;
using CloseChannelRequestPtr = std::shared_ptr<CloseChannelRequest>;class Connection
{
public:using ptr = std::shared_ptr<Connection>;//因为信道是连接的细分,所以对应的信道管理模块也由连接所创建,所独占,所管理Connection(const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec, const ConsumerManager::ptr &consumer_manager_ptr,const VirtualHostManager::ptr &vhost_manager_ptr, const threadpool::ptr &pool_ptr): _conn(conn), _codec(codec), _consumer_manager_ptr(consumer_manager_ptr), _vhost_manager_ptr(vhost_manager_ptr), _pool_ptr(pool_ptr), _channel_manager_ptr(std::make_shared<ChannelManager>()) {}void openChannel(const OpenChannelRequestPtr &req){// 1. 处理请求_channel_manager_ptr->OpenChannel(req->channel_id(), _conn, _codec, _consumer_manager_ptr, _vhost_manager_ptr, _pool_ptr);// 2. 返回响应basicResponse(req->channel_id(), req->req_id(), true);}void closeChannel(const CloseChannelRequestPtr &req){// 1. 处理请求_channel_manager_ptr->CloseChannel(req->channel_id());// 2. 返回响应basicResponse(req->channel_id(), req->req_id(), true);}Channel::ptr getChannel(const std::string &channel_id){return _channel_manager_ptr->getChannel(channel_id);}private:void basicResponse(const std::string &channel_id, const std::string &req_id, bool ret){BasicCommonResponse resp;resp.set_channel_id(channel_id);resp.set_req_id(req_id);resp.set_ok(ret);_codec->send(_conn, resp);}muduo::net::TcpConnectionPtr _conn;ProtobufCodecPtr _codec;ConsumerManager::ptr _consumer_manager_ptr;VirtualHostManager::ptr _vhost_manager_ptr;threadpool::ptr _pool_ptr;ChannelManager::ptr _channel_manager_ptr;
};

2.连接管理模块的实现

就是增、删、查而已

class ConnectionManager
{
public:using ptr = std::shared_ptr<ConnectionManager>;void createConnection(const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec,const ConsumerManager::ptr &consumer_manager_ptr, const VirtualHostManager::ptr &vhost_manager_ptr,const threadpool::ptr &pool_ptr){std::unique_lock<std::mutex> ulock(_mutex);if (_connection_map.count(conn))return;_connection_map.insert(std::make_pair(conn, std::make_shared<Connection>(conn, codec, consumer_manager_ptr, vhost_manager_ptr, pool_ptr)));}void destroyConnection(const muduo::net::TcpConnectionPtr &conn){std::unique_lock<std::mutex> ulock(_mutex);_connection_map.erase(conn);}Connection::ptr getConnecion(const muduo::net::TcpConnectionPtr &conn){std::unique_lock<std::mutex> ulock(_mutex);auto iter = _connection_map.find(conn);if (iter == _connection_map.end()){default_warning("未找到该TcpConnectionPtr对象所关联的连接对象");return Connection::ptr();}return iter->second;}private:std::mutex _mutex;std::unordered_map<muduo::net::TcpConnectionPtr, Connection::ptr> _connection_map;
};

三、服务器模块的设计

1.前言

在完成了信道管理模块和连接管理模块之后,即具体的TCP连接所对应的网络服务模块已经完成了之后

下面我们就可以直接搭建服务器
我们之前使用过muduo库来搭建基于protobuf的网络服务器和客户端,知道muduo库是基于事件注册与响应机制的
都已经写过了,所以怎么设计,为何要这么写我就不赘述了,用法都是一样的

2.设计

注册的事件响应函数:

  1. 打开/关闭信道
  2. 声明/删除虚拟机
  3. 声明/删除交换机
  4. 声明/删除队列
  5. 绑定/解绑队列
  6. 订阅/取消订阅队列
  7. 发布/确认消息

注册的连接响应函数:
OnConnectionCallback

对外接口:
构造
start开启服务函数(死循环进行循环监听)

成员:

  1. muduo库网络通信模块:
    EventLoop
    TcpServer
  2. muduo库基于protobuf的协议处理模块
    ProtobufDispatcher
    ProtobufCodec
  3. 连接所需资源模块句柄
    VirtualHostManager::ptr
    ConsumerManager::ptr
    threadpool::ptr
  4. 连接管理模块句柄
    ConnectionManager::ptr

四、服务器模块的实现

其实那些注册的函数就是先获取连接,后获取信道
然后复用信道的接口即可

注意:虚拟机在创建的时候要恢复历史消息,恢复历史消息时顺便初始化对应队列的消费者管理结构

using namespace ns_helper;namespace ns_google
{using MessagePtr = std::shared_ptr<google::protobuf::Message>;
}
const std::string vhost_dbfile = "main.db";
const int default_thread_num = 5;class Server
{
public:Server(uint16_t port, const std::string &dbfile = vhost_dbfile, int thread_num = default_thread_num): _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "Server", muduo::net::TcpServer::kReusePort), _dispatcher(std::bind(&Server::OnUnknownCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))), _consumer_manager_ptr(std::make_shared<ConsumerManager>()), _vhost_manager_ptr(std::make_shared<VirtualHostManager>(dbfile)), _pool_ptr(std::make_shared<threadpool>(thread_num)), _connection_manager_ptr(std::make_shared<ConnectionManager>()){// 0.针对历史消息中的所有队列,初始化队列的消费者管理结构VirtualHostMap vhmap = _vhost_manager_ptr->getAllVirtualHost();for (auto &vhost_kv : vhmap){MsgQueueMap mqmap = _vhost_manager_ptr->getAllMsgQueue(vhost_kv.first);for (auto &q_kv : mqmap){_consumer_manager_ptr->initQueueConsumerManager(vhost_kv.first,q_kv.first);}}// 1. 注册_dispatcher的事件响应回调函数// 1. 打开/关闭信道_dispatcher.registerMessageCallback<OpenChannelRequest>(std::bind(&Server::OnOpenChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<CloseChannelRequest>(std::bind(&Server::OnCloseChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 2. 声明/删除虚拟机_dispatcher.registerMessageCallback<DeclareVirtualHostRequest>(std::bind(&Server::OnDeclareVirtualHost, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<EraseVirtualHostRequest>(std::bind(&Server::OnEraseVirtualHost, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 3. 声明/删除交换机_dispatcher.registerMessageCallback<DeclareExchangeRequest>(std::bind(&Server::OnDeclareExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<EraseExchangeRequest>(std::bind(&Server::OnEraseExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 4. 声明/删除队列_dispatcher.registerMessageCallback<DeclareMsgQueueRequest>(std::bind(&Server::OnDeclareMsgQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<EraseMsgQueueRequest>(std::bind(&Server::OnEraseMsgQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 5. 绑定/解绑队列_dispatcher.registerMessageCallback<BindRequest>(std::bind(&Server::OnBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<UnbindRequest>(std::bind(&Server::OnUnbind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 6. 订阅/取消订阅队列_dispatcher.registerMessageCallback<BasicConsumeRequest>(std::bind(&Server::OnBasicConsume, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<BasicCancelRequest>(std::bind(&Server::OnBasicCancel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 7. 发布/确认消息_dispatcher.registerMessageCallback<BasicPublishRequest>(std::bind(&Server::OnBasicPublish, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<BasicAckRequest>(std::bind(&Server::OnBasicAck, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 2. 注册_server的事件回调函数和连接回调函数_server.setConnectionCallback(std::bind(&Server::OnConnection, this, std::placeholders::_1));_server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));}void start(){// 开始监听_server.start();// 开始事件死循环监控_baseloop.loop();}// 回调接口
private:void OnConnection(const muduo::net::TcpConnectionPtr &conn){if (conn->connected()){// 创建连接_connection_manager_ptr->createConnection(conn, _codec, _consumer_manager_ptr, _vhost_manager_ptr, _pool_ptr);default_info("连接建立成功");}else{// 删除连接_connection_manager_ptr->destroyConnection(conn);default_info("连接断开成功");}}void OnUnknownCallback(const muduo::net::TcpConnectionPtr &conn, const ns_google::MessagePtr &message, muduo::Timestamp){default_warning("非法请求,即将断开连接");if (conn->connected()){conn->shutdown();}}// 1. 打开/关闭信道void OnOpenChannel(const muduo::net::TcpConnectionPtr &conn, const OpenChannelRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("打开信道时,没有找到连接对应的Connection对象");return;}myconn->openChannel(req);}void OnCloseChannel(const muduo::net::TcpConnectionPtr &conn, const CloseChannelRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("关闭信道时,没有找到连接对应的Connection对象");return;}myconn->closeChannel(req);}// 2. 声明/删除虚拟机void OnDeclareVirtualHost(const muduo::net::TcpConnectionPtr &conn, const DeclareVirtualHostRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("声明虚拟机时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("声明虚拟机失败,因为获取信道失败");return;}mychannel->declareVirtualHost(req);}void OnEraseVirtualHost(const muduo::net::TcpConnectionPtr &conn, const EraseVirtualHostRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("删除虚拟机时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("删除虚拟机失败,因为获取信道失败");return;}mychannel->eraseVirtualHost(req);}// 3. 声明/删除交换机void OnDeclareExchange(const muduo::net::TcpConnectionPtr &conn, const DeclareExchangeRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("声明交换机时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("声明交换机失败,因为获取信道失败");return;}mychannel->declareExchange(req);}void OnEraseExchange(const muduo::net::TcpConnectionPtr &conn, const EraseExchangeRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("删除交换机时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("删除交换机失败,因为获取信道失败");return;}mychannel->eraseExchange(req);}// 4. 声明/删除队列void OnDeclareMsgQueue(const muduo::net::TcpConnectionPtr &conn, const DeclareMsgQueueRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("声明队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("声明队列失败,因为获取信道失败");return;}mychannel->declareMsgQueue(req);}void OnEraseMsgQueue(const muduo::net::TcpConnectionPtr &conn, const EraseMsgQueueRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("删除队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("删除队列失败,因为获取信道失败");return;}mychannel->eraseMsgQueue(req);}// 5. 绑定/解绑队列void OnBind(const muduo::net::TcpConnectionPtr &conn, const BindRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("绑定队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("绑定队列失败,因为获取信道失败");return;}mychannel->bind(req);}void OnUnbind(const muduo::net::TcpConnectionPtr &conn, const UnbindRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("解除绑定队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("解除绑定队列失败,因为获取信道失败");return;}mychannel->unBind(req);}// 6. 订阅/取消订阅队列void OnBasicConsume(const muduo::net::TcpConnectionPtr &conn, const BasicConsumeRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("订阅队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("订阅队列失败,因为获取信道失败");return;}mychannel->basicConsume(req);}void OnBasicCancel(const muduo::net::TcpConnectionPtr &conn, const BasicCancelRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("取消订阅队列时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("取消订阅队列失败,因为获取信道失败");return;}mychannel->basicCancel(req);}// 7. 发布/确认消息void OnBasicPublish(const muduo::net::TcpConnectionPtr &conn, const BasicPublishRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("发布消息时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("发布消息失败,因为获取信道失败");return;}mychannel->basicPublish(req);}void OnBasicAck(const muduo::net::TcpConnectionPtr &conn, const BasicAckRequestPtr &req, muduo::Timestamp){// 1. 先看有无该连接Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);if (myconn.get() == nullptr){default_info("确认消息时,没有找到连接对应的Connection对象");return;}// 2. 获取信道Channel::ptr mychannel = myconn->getChannel(req->channel_id());if (mychannel.get() == nullptr){default_info("确认消息失败,因为获取信道失败");return;}mychannel->basicAck(req);}muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server;ProtobufDispatcher _dispatcher;ProtobufCodecPtr _codec;ConsumerManager::ptr _consumer_manager_ptr;VirtualHostManager::ptr _vhost_manager_ptr;threadpool::ptr _pool_ptr;ConnectionManager::ptr _connection_manager_ptr;
};

五、服务器源文件编写,编译与运行

#include "broker.hpp"int main()
{ns_mq::Server server(8888);server.start();return 0;
}

注意:因为muduo_base库要依赖muduo_net库,所以要先连接muduo_net,后连接muduo_base

server:server.cc ../mqthird/include/proto/codec.cc ../mqcommon/mq_msg.pb.cc ../mqcommon/mq_proto.pb.ccg++ -o $@ $^ -std=c++11 -L ../mqthird/lib -I ../mqthird/include -lprotobuf -pthread -lmuduo_net  -lmuduo_base -lsqlite3 -lz 
.PHONY:clean
clean:rm -f server

在这里插入图片描述
编译并运行成功
在这里插入图片描述
套接字的确处于监听状态
等到我们写完客户端之后在进行一次功能联合大测试,到时候统一排查BUG

在这里插入图片描述
至此,服务器大功告成,总共3347行代码

以上就是项目第十弹:连接管理模块与服务器模块的全部内容


http://www.ppmy.cn/ops/114587.html

相关文章

go webapi上传文件 部属到linux

go厉害的地方&#xff0c;linux服务器上无需安装任务依赖就可以运行&#xff0c;大赞&#xff01; 一、编译 #在Goland中cmd中执行 go env -w GOARCHamd64 go env -w GOOSlinux go build main.go # 切换回来 否则无法运行 go env -w GOOSwindows go run main.go 拷贝到linux服…

ALSA ubuntu 编译

1、下载tar包:alsa-lib、alsa-utils GitHub - alsa-project/alsa-lib: The Advanced Linux Sound Architecture (ALSA) - library&#xff08;核心库&#xff09; GitHub - alsa-project/alsa-utils: The Advanced Linux Sound Architecture (ALSA) - utilities(工具库) 2、…

基于Java的SSM(Spring、Spring MVC、MyBatis)框架构建的远程诊断系统

基于Java的SSM&#xff08;Spring、Spring MVC、MyBatis&#xff09;框架构建的远程诊断系统&#xff0c;适用于医疗、工业设备监测等多个领域。这样的系统通常需要具备实时数据采集、数据分析、故障诊断等功能。下面是一个简化的系统设计方案&#xff0c;以及一些关键组件和技…

C++primer第十一章使用类(矢量随机游走实例)

操作符重载 操作符重载(operator overoading)是一种形式的 C多态。 第8章介绍了C是如何使用户能够定义多个名称相同但特征标(参数列表)不同的函数的。这被称为函数重载(function overloading)或函数多态(functional polymorphism)&#xff0c;旨在让您能够用同名的函数来完成…

Ubuntu 安装和使用 Fcitx 中文输入法;截图软件flameshot

一、Ubuntu 安装和使用 Fcitx 中文输入法 在 Ubuntu 上安装和使用 Fcitx 输入法框架是一个常见的选择&#xff0c;特别是对于需要中文输入的用户。以下是详细的步骤来安装和配置 Fcitx 输入法&#xff1a; 1. 安装 Fcitx 和相关输入法 首先&#xff0c;更新你的包列表并安装…

伊犁云计算创建ftp

1 yum 安装不再说&#xff0c; 2 有局域网搭建不再说 参考之前的文档 我们直接干ftp 先装ftp 服务 进入 etc/vsftpd/vsftpd.conf 修改如上图 看一下ftp 21 端口是不是在监听 开始测试

2016年国赛高教杯数学建模A题系泊系统的设计解题全过程文档及程序

2016年国赛高教杯数学建模 A题 系泊系统的设计 近浅海观测网的传输节点由浮标系统、系泊系统和水声通讯系统组成&#xff08;如图1所示&#xff09;。某型传输节点的浮标系统可简化为底面直径2m、高2m的圆柱体&#xff0c;浮标的质量为1000kg。系泊系统由钢管、钢桶、重物球、…

python爬虫初体验(二)

在Python中&#xff0c;每个模块都有一个内置的变量 name&#xff0c;用于表示当前模块的名称。当一个Python文件被执行时&#xff0c;Python解释器会首先将该文件作为一个模块导入&#xff0c;并执行其中的代码。此时&#xff0c;__name__的值为模块的名称。 作用 模块可被导…