35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
讲述:冯永吉
时长10:11大小9.32M
为什么在发送数据时,会先尝试通过 socket 直接发送,再由框架接管呢?
关于回调函数的设计
tcp_connection 对象设计的想法是什么,和 channel 有什么联系和区别?
主线程等待子线程完成的同步锁问题
关于 channel_map 的设计,特别是内存方面的设计。
总结
赞 5
提建议
精选留言(32)
- 酸葡萄2019-11-30为什么在发送数据时,会先尝试通过 socket 直接发送,再由框架接管呢? 老师你好,这个问题中,发送缓冲区有数据说明发送效率低(数据多,网络差等原因导致),没有注册WRITE事件是什么意思呢?(感觉这时一个基础问题[小尴尬])
作者回复: 有两种发送数据的方式,第一种是通过注册WRITE事件,等待reactor来驱动我们把数据发送出去;第二种是不需要reactor驱动,直接往套接字上发送。这里的解释是说,在大部分情况下,为了效率,直接往套接字上发送,当一次解决不了时,再通过reactor来驱动数据发送。
9 - Geek_63bb292020-07-30谢谢盛老师,链接是关于实战代码的流程图 https://app.yinxiang.com/fx/7e601cad-6501-4fe7-8e4e-f0fbd9d02c4b
作者回复: ������
共 2 条评论5 - 范闲2020-03-27这是我改造的c++版本,目前还在调试中 https://gitee.com/willam-dialogue/net_reactor 调试过程中遇到了几个问题: 1.在telnet以后, 客户端第一次发送消息,可以正常收到消息. 客户端第二次发送消息,会导致server coredump. 目前初步定位到问题是发生在TcpConnection.h中的sendData函数,更具体的原因没有找到 2.如果将回调函数注册为如下方式: TcpConnection 公有云继承了enabled_shared_from_this typedef std::shared_ptr<TcpConnection> TcpConnectionPtr; typedef std::function<void (const TcpConnectionPtr &)> ConnCompleteCallBack; typedef std::function<void (const TcpConnectionPtr &)> ConnCloseCallBack; typedef std::function<void (const TcpConnectionPtr &)> WriteCompleteCallBack; typedef std::function<void (Buffer*, const TcpConnectionPtr &)> MessageCallBack; 在TcpConnection调用ConnCompleteCallBack就没有问题. 但是在channel中绑定了TcpConnection.h 中的handleRead和handleWrite的回调,在调试过程中会报weak_ptr的相关错误.实际定位发现在handleRead里面调用了MessageCallBack的回调,MessageCallBack的入参是shared_from_this(),weak_ptr的错误由这个产生的,目前还在看是否因为使用方法的原因引起的. 希望同学们能够一起帮忙看看 这些问题, 我还没找到好的方法.邮箱[email protected]展开
作者回复: 给你顶一下,大家一起帮忙看(PS:我最近有点忙,闲下来也来帮你一起看)
共 2 条评论5 - 王小白白白2020-08-01首先非常感谢老师的课程,系统的学习了网络编程相关知识,受益匪浅。 这里是我改写的c++ epoll服务器版本,https://github.com/wangxiaobai-dd/BowServer 主要改动有: 1,使用c++语法,智能指针,variant,std::mutex,std::thread等,代码结构有些改变 2,消除一些内存泄漏,(buffer相关待做) 3,加入一个事件队列channel_queue,这样event_dispatch可以改为非阻塞,取消唤醒机制 4, 有新连接时选择任务数最少的连接 5,epoll del ,update 接口有所修改 会持续完善优化项目,一起学习进步~ 有帮助的话点个星星嘻嘻展开
作者回复: 赞,已点。
4 - 范闲2020-03-27https://gitee.com/willam-dialogue/net_reactor 这是目前我改造的cpp版本,正在调试中。 调试过程中遇到一些问题 1.telnet连接以后,第一次发送消息正常。但是第二次发送消息就会coredump, 初步定位到问题出在TcpConnection.h 中的sendData函数中,具体原因还在排查 2.如果将回调函数改成 typedef std::shared_ptr<TcpConnection> TcpConnectionPtr; typedef std::function<void (const TcpConnectionPtr &)> ConnCompleteCallBack; typedef std::function<void (const TcpConnectionPtr &)> ConnCloseCallBack; typedef std::function<void (const TcpConnectionPtr &)> WriteCompleteCallBack; typedef std::function<void (Buffer*, const TcpConnectionPtr &)> MessageCallBack;展开
作者回复: C++的模板太强大了,不过也很复杂,加油~
2 - yusuf2019-11-26// add event read for the new connection struct channel *channel1 = channel_new(connected_fd, EVENT_READ, handle_read, handle_write, tcpConnection); 请问这里第4个参数设置了handle_write函数,为什么第2个参数没有设置EVENT_WRITE呢? 原本以为这个地方是漏掉了EVENT_WRITE,可添加上EVENT_WRITE后,发现tcp服务器收到数据后会一直打印,而http服务器响应一次请求后会崩溃。这又是为什么呢?展开
作者回复: 这里是向reactor注册了数据可读的事件,注意这个时候缓冲区是没有写入的需求的,如果注册了可写事件,相当于这个事件是肯定会发生的(因为套接字写缓冲区都是空的,可以往里写),所以这个时候你会看到一直会打印。 也就是说,只有在真正有数据需要发送的时候,才需要注册EVENT_WRITE,让reator驱动把需要发送的数据发送完。
2 - CofCai2022-01-22我最开始是直接一头代码的细节里面去,没先从宏观上有个把握,然后读的很痛苦。于是自己就借助一些工具,比如思维导图画一下函数调用关系、各种结构体对象的关系,总算有一点头绪了。贴上我的学习笔记(笔记是边读源码边写的,有的理解后来觉得不对,但可能没来得及修改,希望各位伙伴带着思考): 各种结构体对象关系:https://www.processon.com/view/link/5ead14555653bb6efc7cbe59展开
作者回复: 强大,请问我可以引用么?
共 3 条评论1 - 日就月将2021-10-20老师 您写的代码好像没有加内存释放处理
作者回复: 有可能遗漏的,能帮忙在githut提MR么?
2 - 范龙dragon2020-07-15哪位大神回答下,框架中哪里有释放tcp_connection和channel资源的地方,从代码中看到这两个对象都是malloc出来的,但没找到在哪里free的,求指教!
作者回复: 我的锅,应该在tcp_connection_shutdown函数里面释放channel和tcp_connection两个对象。抽空改下。
共 3 条评论1 - bbbi2020-03-26老师,好像您封装的这个框架跟netty神似
作者回复: 嗯,应该说基于事件分发机制的框架,大致的思想都是相同的,不信你可以再去看看ACE, libevent或者其他的库。
共 2 条评论1 - 范闲2020-03-22老师你好,最近我在做这部分的C++代码的改造,但是再改造过程中有几个疑问点需要像您请教下。 1.关于channel的设计,channel 对象里面定义了一个void *data用来转换成EventLoop指针,在channel_write_event_enable 和 channel_write_event_disable用来改变事件的状态,而且内部的实现都是调用了EventLoop的event_loop_update_channel_event这个函数。为什么不直接将loop 指针透传呢? 2.关于TcpConnection的设计里面的一个成员是EventLoop指针,在tcp_connection_new中会调用channel_new, channel_new的赋值成了tcpConnection, 即data = ttcpConnection。但是在调用channel_write_event_enable 转换成的是EventLoop指针,所以在TcpConnection中EventLoop放第一个是为了这个调用? 3.在Reactor中,做事件分发的是Acceptor,但是在我们这个代码里实际做事件分发的应该是TcpServer啊~handle_connection_established中分发到了不同的线程上展开
作者回复: 1.应该直接透传了event_loop指针的,不是太明白你的问题; 2.保留event_loop对象到tcp_connection里面,是为了后面将这个connection对应的事件和event_loop绑定,就是你说的那部分理解; 3.handle_connection_established就是acceptor线程所要干的事情,也就是你说的从线程池event_loop数组里面获取一个去处理。
1 - Ray_h2019-11-11非常感谢老师的付出!前面基础篇和提升篇的课程可以很快消化。实践篇里面的内容我则需要花比较多的时间去梳理对象之间的关系,然后才能弄清楚运行时各个对象之间的联系。正如前面有同学说老师虽然是用C语言写的代码,但是处处是面向对象的思想。 我认为tcp-server与http-server是基类和子类关系;channel和acceptor也是基类与子类的关系。当然里面还存在大量的包含关系。tcp-connection类继承自channel,但是tcp-connection与eventloop的关系我就不是很确定。还想请老师或者其他同学能够指点迷津,最终很想将老师的c代码改写成c++风格,希望能跟各位同学相互讨论。email: [email protected]展开
作者回复: 非常支持c++改造,可以贴出代码地址大家一起review。
共 3 条评论1 - Running man2022-09-02 来自浙江event_loop_channel_buffer_nolock中申请channelElement并添加到eventLoop链表中,但代码中并没有看到释放该对象堆栈资源,这里是不是一处内存泄露?应该需要在event_loop_handle_pending_channel中去处理释放这部分内存吧?
- 功夫熊猫2022-07-30 来自江苏谢谢老师,目前已经照着老师的思路重新拿c语言实现了一遍。然后现在准备利用go的channel,interface和协程重构一遍,有些语言上的支持感觉会方便很多。我自己现在准备往老师的思路里添加时间轮或时间堆来增加这个框架的功能。1
- 有手也不行2021-09-14为什么在发送数据时,会先尝试通过 socket 直接发送,再由框架接管呢? 老师关于这个问题我还有一点问题就是,假如此时这个channel没有注册write事件,那么我们直接通过write发送内容,但是此时的数据量很大,只发送了一部分,那么框架将接管剩余没发送的数据的,将之注册到channel中,那么我们在接收端收到的数据不就成了两部分了,接收端如何连接这两部分(或者说如何辨认这两部数据是属于一个完整信息的呢)
作者回复: 第一个问题,文章中已经解释了,为了性能。 第二个问题,代码可以说明一切。(把没有发送的数据拷贝到缓冲区中) f (!fault && nleft > 0) { //拷贝到Buffer中,Buffer的数据由框架接管 buffer_append(output_buffer, data + nwrited, nleft); if (!channel_write_event_is_enabled(channel)) { channel_write_event_enable(channel); } }
- 小家伙542021-03-27老师,您最后的服务器代码是那个lib文件夹里的内容吗?我刚开始学这方面的内容,不知道怎么运行,您能说一下吗?
作者回复: 看一下README。整个工程使用CMake编译。
- Steiner2021-02-17如果用C++编写的话,这个channel_map可以用map<int, channel>对象来表示 fd 与 channel的映射吗
作者回复: 可以用std:map的。
- Geek_de83f62021-01-21代码看了两周终于看完了,没感觉架构哪里好,把挺简单的事情写的这么复杂。。。
作者回复: 额,仁者见仁,智者见智吧。个人觉得架构的好处是,可以快速进行业务逻辑的编写。
共 2 条评论 - 刘忽悠2020-07-12直接往socket发送是不是就是线程池里面业务线程处理完成之后,在线程里面直接write,这样做的话有一部分I/O业务相当于在业务线程里面发送,没有进行分离;交给框架去做的话,那么I/O就全部交给负责I/O的线程去处理,清晰一些;不过在业务线程里面直接发,效率高一些,也能理解,所以先去尝试直接发送,不成功再交给框架去处理,不知道理解对不对
作者回复: 基本正确吧。
- 游走2020-05-21学到了关于网络库的新知识
作者回复: 可以跟大家分享一下么?有你的评论更精彩