大型C++项目必须注意的几个小问题

有些问题对于小型的C++项目来说可能无关紧要,但对于大中型C++项目来讲,这些问题却成了大问题。什么样的项目算是小型项目呢,什么样的算是大中型项目呢,我认为10万LOC以下为小型项目,10-50万LOC为中型项目,50万LOC以上为大型项目。当然,不能单纯地以代码行数作为衡量标准,前几天产品重构,我用四二三十行代码换掉了原来的三四千行代码,那这个项目的规模是用这二三十行来计算呢,还是用那三四千行算呢?软件很难有一个准确的度量标准,暂以行数作为一种参考性标准吧。

    当项目较大量一些在小型项目中不需要考虑的问题也变得非常重要了,对这些问题还是要认真考虑的。

1. 目录结构

    小型项目可能一个目录就够了,把所有头文件和源代码及相关的工程文件放到一起,但项目较在时目录中的文件也就多了起来,文件一多查看就很不方便,如果对各种文件执照功能或类别进行一些分组就会方便很多,比如查找某个文件也很迅速。这一点,我觉得boost和chrome做得是很好的,包括它们的命名规则都很统一,而ACE做得就不是太好,所有文件放到一个目录里,好几百个文件,如果安装了CVS或SVN的windows客户端,在打开资源管理器时还要扫描一下文件是否与版本库关联,使得反应其慢,感觉不是太好。

2. 模块划分

    一个大型的项目不可能当成一个整体工程一下子编译完的,如果有几百个文件,使用一个Makefile或者VS的工程,每编译一次都会很耗时,如果需要经常调试的话,编译就会很消耗时间。如果把整个工程划分成多个模块,不同模块以静态库或者动态库的方式进行组织,各模块不仅可以分别编译,单独测试,而且对于多个人协作的并行开发是非常有利的。因为不论是VSS、CVS还要SVN等这些主流的版本管理软件对于并行开发都不能做到尽善尽美,其实这个也不可能,因为软件本身不可能识别程序,像人一样去合并程序代码,如果几个人会经常修改同一个模块的代码时冲突可能就比较多,解决冲突的次数多了不仅影响工作效率,而且还影响心情。

3. 头文件层次

   为什么把头文件的层次结构单独分出来而不是直接把模块划分放到同一个问题里呢?就是要突出其重要性。如果在划分模块时没有把模块的组织方式设计合理,很有可能导致头文件循环引用的情况,这样程序就无法编译。

    有一些特殊情况就更需要注意,比如windows中的winsock2.h和windows.h存在冲突,如果先引用windows.h再引用winsock2.h就会出现重定义的情况。如果想使用Win32 Socket API 2,则必须先引用winsock2.h然后引用windows.h,可是如果在多个文件里引用windows,如果顺序控制不好就有可能导致混乱,从而程序无法编译。个人认为最好的方法就是把该引用放入一个头文件,比如叫sysinc.h,然后所有编译单元的头文件都在第一行引用sysinc.h,然后所有的.cpp文件在第一行引用对应的头文件,这样即可保证winsock2.h和windows.h的引用顺序。

4. 命名空间

   C语言是不支持命名空间的,以前人们为了避免标识符冲突都是加前缀或者后缀,但这样会使得标识符很长,看起来不舒服,而且写起来很麻烦(虽然现在IDE对智能感知的支持已经使这个变得很方便),还老感觉怪怪的,不够自然。所以,后来的语言都对命名空间(或类似功能)进行了支持,如C++、C#、Java、Python等,所以在大型的C++项目为什么不用这个新的特性呢?

    使用不同命名空间中的标识符识不要图省事使用using namespace ns; 这样的语句把整个ns命令空间内的标识符都引进来,这样便失去了命名空间的作用。因为这样做可行是需要一个前提的,就是知道这个不会冲突,可是谁能预知未来不会冲突呢?前些天就遇到在WINDOWS、Linux、AIX三个平台编译都没有问题,可是到了Solaris就出了问题,原因是在系统的头文件if.h中有一个结构定义struct map,这个跟C++标准库中的模块map冲突,导致无法编译。

    在这方面,boost的作者们不愧是C++的专家,这方面做得很好,而ACE就不是太好,所有标识符通过前缀ACE_来标识,保必呢,而且是大写,看起来很不舒服,如果要用呢只能忍了,或者自己单独写一个头文件,使用typedef或者用宏定义把标识符都给改了,然后把新定义的标识符封装到一个ace命令空间中,这也是个不错的主意。

5. 代码注释

      时间长了谁也难保证记住每一段代码的用途,特别是一些特别技巧,一定要随手写下来,为以后自己维护或与同事合作都是很必要的,不要怕别人看懂自己的代码对自己有威胁,开放一些,只有互相学习才能促进发展,你把代码给了他人,他人也会给你建议帮你进步的,中国前一百年的历史就是固步自封的沉痛教训,国家如此,个人亦如此。

6.版本管理工具

   如果几个人同时维护几十万行代码都没有使用版本管理工具的话,那你每天肯定只能有一种感觉,那就是在浪费生命。浪费自己的生命也就算了,如果作为一个领导者不对此做点改变的话,那就是浪费别人的生命,也就是谋财害命。

7. 代码规范

   每个人可能都有自己的编码风格,但是同一个项目,应该只有一种风格,否则可能会影响工作,虽然程序本身的性能和功能都没有会有问题,但对于产品的实际开发过程会有影响,因为它会影响到一些有想法的工程师的心情,当然就会影响工作效率,因为他们老感觉那些代码不够规范,总想去修改这些细节,当改过后可能会被另外的工程师改成另一种风格。所以必须有统一的风格,以统一团队每一位成员的行为。

   有一个细节,我还是有体会的。项目中一些旧的代码写得很乱,有的一个函数都能写几百行,而且代码块的花括号{ 和}都是另起一行if-else都是在单独的行,这样着满屏都是花括号和if - else,看起来代码很不紧凑,比如:
if( cond 1 )
{
    line …
    line …
}
else
{
   line …
}
改成
if( cond 1 ){
   line …
   line …
}else{
  line …
}

看起来就紧凑多了。
   还有一些代码层次很深,一块代码可能都有四层if条件嵌套,看起来是很费劲的,如果在每一次条件判断都进行退出的话,可读性将会有很大的改善,如
  if( cond 1 ){
      if( cond 2 ){
            if( cond 3){
                  work line
            }
      }
  }
如果改成下面代码,看起来会更舒服些
if( ! cond 1)
      return –1;
if ( ! cond 2 )
      return –2;
if( !cond 3 )
    return –3;
work line

当然写成
if( cond 1 && cond 2 && cond 3 ){
   work line
}
更好,但有些时候并没有这么方便。

8. 自动化构建

    如果一个大型项目包含几十个小模块,每次都要手工编译依赖项的话,工作效率是低下的,如果能够自动构建必要的模块将可以大大提高工作效率,即使每次能少输入几个字符也是必要的。
    比如我们的一个项目需要在windows、Linux、AIX、HPUX、Solaris五个平台上运行,如果每次编译前都执行configure,然后再执行make,也不是很方便,如果公共的部分写到一个Makefile.comm中,然后为每一个平台的平台依赖项分别写一个Makfile.win,Makefile.linux, Makefile.aix,Makefile.hpux, Makefile.solaris,这样每次只需要输入make –f Makefile.os来编译,但这样要输入的要挺多的,可以再建一个Makefile文件,然后自动检测系统平台,然后调用对应的Makefile,以后只需要输入make就行了,再想简单,可以写个小脚本,文件名为m,里面调用make指令,这样每次只需要输入./m即可,如果还嫌输入的字符太多的话,可以把.加到你的PATH变量中,以后直接输入m就可以了。如果再想简单呢?那只能使用特异功能,玩儿心灵感应了,我实在是想不到其它的办法了。
   只是个例子而已,可能实际开发过程中需要更多其它的工作,但当你发现你做某件事时不再是第一次,你就可以考虑下次遇到后如何简化了,这也是“程序修炼之道”中提到的DRY(Don’t Repeat Yourself)法则。

9. 自动化测试

   对于一个大型项目,每次构建后都进行手动测试的工作量是很大的。如果公司没有研发团队配备测试人员,而且测试部不能给及时测试的话,研发力会将会有大量的人力浪费在测试上,所以构建自己的自动化测试系统是一项至关重要的内容。

暂时谈到这儿吧,想到再记下来,有机会与网友探讨,各位晚安。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WEBCRAWLER 网络爬虫实训项目 1 WEBCRAWLER 网 络 爬 虫 实 训 项 目 文档版本: 1.0.0.1 编写单位: 达内IT培训集团 C++教学研发部 编写人员: 闵卫 定稿日期: 2015年11月20日 星期五WEBCRAWLER 网络爬虫实训项目 2 1. 项目概述 互联网产品形形色色,有产品导向的,有营销导向的,也有技术导向的,但是 以技术见长的互联网产品比例相对小些。搜索引擎是目前互联网产品中最具技 术含量的产品,如果不是唯一,至少也是其中之一。 经过十几年的发展,搜索引擎已经成为互联网的重要入口之一,Twitter联合创 始人埃文•威廉姆斯提出了“域名已死论”,好记的域名不再重要,因为人们会 通过搜索进入网站。搜索引擎排名对于中小网站流量来说至关重要。了解搜索 引擎简单界面背后的技术原理其实对每一个希望在互联网行业有所建树的信息 技术人员都很重要。 1.1. 搜索引擎 作为互联网应用中最具技术含量的应用之一,优秀的搜索引擎需要复杂的架构 和算法,以此来支撑对海量数据的获取、 存储,以及对用户查询的快速而准确 地响应。 从架构层面,搜索引擎需要能够对以百亿计的海量网页进行获取、 存 储、 处理的能力,同时要保证搜索结果的质量。 如何获取、 存储并计算如此海WEBCRAWLER 网络爬虫实训项目 3 量的数据?如何快速响应用户的查询?如何使得搜索结果尽可能满足用户对信 息的需求?这些都是搜索引擎的设计者不得不面对的技术挑战。 下图展示了一个通用搜索引擎的基本结构。商业级别的搜索引擎通常由很多相 互独立的模块组成,各个模块只负责搜索引擎的一部分功能,相互配合组成完 整的搜索引擎: 搜索引擎的信息源来自于互联网网页,通过“网络爬虫” 将整个“互联网” 的 信息获取到本地,因为互联网页面中有相当大比例的内容是完全相同或者近似 重复的,“网页去重”模块会对此做出检测,并去除重复内容。 在此之后,搜索引擎会对网页进行解析,抽取网页主体内容,以及页面中包含 的指向其它页面的所谓超链接。 为了加快用户查询的响应速度,网页内容通过 “倒排索引”这种高效查询数据结构来保存,而网页之间的链接关系也会予以 保存。之所以要保存链接关系,是因为这种关系在网页相关性排序阶段是可利 用的,通过“链接分析”可以判断页面的相对重要性,对于为用户提供准确的 搜索结果帮助很大。 由于网页数量太多,搜索引擎不仅需要保存网页的原始信息,还要保存一些中 间处理结果,使用单台或者少量的计算机明显是不现实的。 Google等商业搜索 引擎提供商,为此开发了一整套云存储与云计算平台,使用数以万计的普通PCWEBCRAWLER 网络爬虫实训项目 4 搭建了海量信息的可靠存储与计算架构,以此作为搜索引擎及其相关应用的基 础支撑。优秀的云存储与云计算平台已经成为大型商业搜索引擎的核心竞争 力。 以上所述是搜索引擎如何获取并存储海量的网页相关信息。这些功能因为不需 要实时计算,所以可以被看作是搜索引擎的后台计算系统。搜索引擎的首要目 标当然是为用户提供准确而全面的搜索结果,因此响应用户查询并实时提供准 确结果便构成了搜索引擎的前台计算系统。 当搜索引擎接收到用户的查询请求后,首先需要对查询词进行分析,通过与用 户信息的结合,正确推导出用户的真实搜索意图。 此后,先在“Cache系统” 所维护的缓存中查找。搜索引擎的缓存存储了不同的搜索意图及其相对应的搜 索结果。如果在缓存中找到满足用户需求的信息,则直接将搜索结果返回给用 户。这样既省掉了重复计算对资源的消耗,又加快了整个搜索过程的响应速 度。而如果在缓存中没有找到满足用户需求的信息,则需要通过“网页排 序”,根据用户的搜索意图,实时计算哪些网页是满足用户需求的,并排序输 出作为搜索结果。 而网页排序最重要的两个参考因素,一个是“内容相似 性”,即哪些网页是和用户的搜索意图密切相关的;一个是网页重要性,即哪 些网页是质量较好或相对重要的,而这往往可以从“链接分析”的结果中获 得。综合以上两种考虑,前台系统对网页进行排序,作为搜索的最终结果。 除了上述功能模块,搜索引擎的“反作弊”模块近年来越来越受到重视。搜索 引擎作为互联网用户上网的入口,对于网络流量的引导和分流至关重要,甚至 可以说起着决定性的作用。因此,各种“作弊”方式也逐渐流行起来,通过各 种手段将网页的搜索排名提前到与其网页质量不相称的位置,这会严重影响用 户的搜索体验。所以,如何自动发现作弊网页并对其给于相应的惩罚,就成了 搜索引擎非常重要的功能之一。 1.2. 网络爬虫 通用搜索引擎的处理对象是互联网网页,截至目前的网页数量数以百万计,所 以搜索引擎首先面临的问题就是如何能够设计出高效的下载系统,将如此海量 的网页数据传送到本地,在本地形成互联网网页的镜像备份。 网络爬虫即扮演 如此角色。 它是搜索引擎中及其关键的基础构件。WEBCRAWLER 网络爬虫实训项目 5 网络爬虫的一般工作原理如下图所示:  从互联网网页中选择部分网页的链接作为“种子URL”,放入“待抓取URL 队列”;  爬虫从“待抓取URL队列”中依次“读取URL”;  爬虫通过“DNS解析” 将读到的URL转换为网站服务器的IP地址;  爬虫将网站服务器的IP地址、通信端口、网页路径等信息交给“网页下载” 器;  “网页下载”器负责从“互联网”上下载网页内容;  对于已经下载到本地的网页内容,一方面将其存储到“下载页面库” 中,等 待建立索引等后续处理,另一方面将其URL放入“已抓取URL队列”,后者显 然是为了避免网页被重复抓取;  对于刚刚下载到本地的网页内容,还需要从中“抽取URL”;  在“已抓取URL队列”中检查所抽取的URL是否已被抓取过;  如果所抽取的URL没有被抓取过,则将其排入“待抓取URL队列” 末尾,在 之后的抓取调度中重复第步,下载这个URL所对应的网页。 如此这般,形成WEBCRAWLER 网络爬虫实训项目 6 循环,直到“待抓取URL队列”空,这表示爬虫已将所有能够被抓取的网页尽 数抓完,完成一轮完整的抓取过程。 以上所述仅仅是网络爬虫的一般性原理,具体实现过程中还可以有很多优化的 空间,比如将“网页下载”以多线索(进程或线程)并发的方式实现,甚至将 “DNS解析”也处理为并发的过程,以避免爬虫系统的I/O吞吐率受到网站服 务器和域名解析服务器的限制。而对于“已抓取URL队列”则可以采用布隆排 重表的方式加以优化,以降低其时间和空间复杂度。 2. 总体架构 本项目总体架构如下图所示: 配置器 Configurator 超文本传输协议响应 HttpResponse 日志 Log 主线程 main 多路输入输出 MultiIo 插件管理器 PluginMngr 套接字 Socket 字符串工具包 StrKit 统一资源定位符队列 UrlQueues 网络爬虫 WebCrawler 原始统一资源定位符 RawUrl 超文本传输协议响应包头 HttpHeader 域名解析线程 DnsThread 解析统一资源定位符 DnsUrl 接收线程 RecvThread 布隆过滤器 BloomFilter 哈希器 Hash 最大深度插件 MaxDepth 域名限制插件 DomainLimit 超文本传输协议响应包头过滤器插件 HeaderFilter 超文本标记语言文件存储插件 SaveHTMLToFile 图像文件存储插件 SaveImageToFile 发送线程 SendThreadWEBCRAWLER 网络爬虫实训项目 7 2.1. 基础设施 2.1.1. 字符串工具包(StrKit) 常用字符串处理函数。 2.1.2. 日志(Log) 分等级,带格式的日志文件打印。 2.1.3. 配置器(Configurator) 从指定的配置文件中加载配置信息。 2.1.4. 多路输入输出(MultiIo) 封装epoll多路I/O系统调用,提供增加、删除和等待操作接口。 2.1.5. 插件管理器(PluginMngr) 加载插件并接受其注册,维护插件对象容器并提供调用其处理函数的外部接 口。 2.2. 网络通信 2.2.1. 哈希器(Hash) 封装各种哈希算法函数。 2.2.2. 布隆过滤器(BloomFilter) 基于布隆算法,对欲加入队列的原始统一资源定位符进行过滤,以防止已被抓 取过的URL再次入队,降低冗余开销同时避免无限循环。 2.2.3. 原始统一资源定位符(RawUrl) 提供原始形态的统一资源定位符字符串的简单包装,以及规格化等辅助支持。 2.2.4. 解析统一资源定位符(DnsUrl) 将原始形态的统一资源定位符字符串,解析为服务器域名、资源路径、服务器 IP地址,乃至服务器通信端口等。WEBCRAWLER 网络爬虫实训项目 8 2.2.5. 统一资源定位符队列(UrlQueues) 封装原始统一资源定位符队列和解析统一资源定位符队列,提供线程安全的入 队、出队操作,通过统一资源定位符过滤器排重,同时支持基于正则表达式的 统一资源定位符抽取功能。 2.2.6. 套接字(Socket) 发送/接收超文本传输协议请求/响应,发送成功将套接字描述符加入多路I/O, 接收成功抽取统一资源定位符压入队列。 2.2.7. 超文本传输协议响应包头(HttpHeader) 状态码和内容类型等关键信息。 2.2.8. 超文本传输协议响应(HttpResponse) 服务器统一资源定位符和超文本传输协议包头、包体及长度的简单封装。 2.3. 流程控制 2.3.1. 域名解析线程(DnsThread) 从原始统一资源定位符队列中弹出RawUrl对象,借助域名解析系统(DNS)获 取服务器的IP地址,构造DnsUrl对象压入解析统一资源定位符队列。 2.3.2. 发送线程(SendThread) 通过WebCrawler对象启动新的抓取任务,从解析统一资源定位符队列中弹出 DnsUrl对象,向HTTP服务器发送HTTP请求,并将套接字描述符放入MultiIo 对象。 2.3.3. 接收线程(RecvThread) 由WebCrawler对象在从MultiIo对象中等到套接字描述符可读时动态创建,通 过Socket对象接收超文本传输协议响应。WEBCRAWLER 网络爬虫实训项目 9 2.3.4. 网络爬虫(WebCrawler) 代表整个应用程序的逻辑对象,构建并维护包括日志、配置器、多路I/O、插件 管理器、统一资源定位符队列、域名解析线程等在内的多个底层设施,提供诸 如初始化、执行多路输入输出循环、启动抓取任务等外部接口。 2.3.5. 主线程(main) 主函数,处理命令行参数,初始化应用程序对象,进入多路I/O循环。 2.4. 外围扩展 2.4.1. 最大深度插件(MaxDepth) 根据配置文件的MAX_DEPTH配置项,对被抓取超链接的最大递归深度进行限 制。 2.4.2. 域名限制插件(DomainLimit) 根据配置文件的INCLUDE_PREFIXES和EXCLUDE_PREFIXES配置项,对被抓取 超链接的前缀进行限制。 2.4.3. 超文本传输协议响应包头过滤器插件(HeaderFilter) 根据配置文件的ACCEPT_TYPE配置项,对超文本传输协议响应的内容类型进行 限制。 2.4.4. 超文本标记语言文件存储插件(SaveHTMLToFile) 将用超文本标记语言描述的页面内容保存到磁盘文件中。 2.4.5. 图像文件存储插件(SaveImageToFile) 将页面内容中引用的图像资源保存到磁盘文件中。 3. 工作流程 3.1. 主事件流 进程入口函数在进行必要的命令行参数处理和系统初始化以后,进入网络爬虫 的多路输入输出循环,一旦发现某个与服务器相连的套接字有数据可读,即创WEBCRAWLER 网络爬虫实训项目 10 建接收线程,后者负责抓取页面内容,而前者继续于多路输入输出循环中等待 其它套接字上的I/O事件。 3.2. 解析事件流 独立的域名解析线程实时监视原始统一资源定位符队列的变化,并将其中的每 一条新近加入的原始统一资源定位符,借助域名解析系统转换为解析统一资源 定位符,并压入解析统一资源定位符队列。 3.3. 发送事件流 不断从解析统一资源定位符队列弹出解析统一资源定位符,创建套接字,根据 服务器的IP地址和通信端口发起连接请求,建立TCP连接,发送超文本传输协 议请求包,并将套接字放入多路输入输出对象,由主事件流等待其数据到达事 件。 3.4. 接收事件流 每个超文本传输线程通过已明确有数据可读的套接字接收来自服务器的超文本 传输协议响应,并交由统一资源定位符队列进行超链接抽取和布隆排重过滤, 直至压入原始统一资源定位符队列。在压入原始统一资源定位符队列之前,以 及接收到超文本传输协议包头和包体之后,分别执行统一资源定位符插件、超 文本传输协议包头插件和超文本标记语言插件的处理过程。 以上四个事件流,需要平行且独立地并发运行,并在共享资源和执行步调上保 持适度的同步。 4. 目录结构 本项目的目录结构如下所示: WebCrawler/ ├── bin/ │ ├── WebCrawler │ ├── WebCrawler.cfg │ └── WebCrawler.scr ├── docs/ │ ├── 概要设计.pdfWEBCRAWLER 网络爬虫实训项目 11 │ └── 详细设计.pdf ├── download/ ├── plugins/ │ ├── DomainLimit.cpp │ ├── DomainLimit.h │ ├── DomainLimit.mak │ ├── DomainLimit.so │ ├── HeaderFilter.cpp │ ├── HeaderFilter.h │ ├── HeaderFilter.mak │ ├── HeaderFilter.so │ ├── MaxDepth.cpp │ ├── MaxDepth.h │ ├── MaxDepth.mak │ ├── MaxDepth.so │ ├── SaveHTMLToFile.cpp │ ├── SaveHTMLToFile.h │ ├── SaveHTMLToFile.mak │ ├── SaveHTMLToFile.so │ ├── SaveImageToFile.cpp │ ├── SaveImageToFile.h │ ├── SaveImageToFile.mak │ ├── SaveImageToFile.so │ └── mkall └── src/ ├── BloomFilter.cpp ├── BloomFilter.h ├── Configurator.cpp ├── Configurator.h ├── DnsThread.cpp ├── DnsThread.h ├── Hash.cpp ├── Hash.h ├── Http.h ├── Log.cpp ├── Log.h ├── Main.cpp ├── Makefile ├── MultiIo.cpp ├── MultiIo.h ├── Plugin.h ├── PluginMngr.cpp ├── PluginMngr.h ├── Precompile.h ├── RecvThread.cpp ├── RecvThread.h ├── SendThread.cpp ├── SendThread.h ├── Socket.cpp ├── Socket.hWEBCRAWLER 网络爬虫实训项目 12 ├── StrKit.cpp ├── StrKit.h ├── Thread.cpp ├── Thread.h ├── Url.cpp ├── Url.h ├── UrlFilter.h ├── UrlQueues.cpp ├── UrlQueues.h ├── WebCrawler.cpp └── WebCrawler.h 其中bin目录存放可执行程序文件、启动画面文件和配置文件,docs目录存放 项目文档,download目录存放爬虫下载的网页文件和图像文件,plugins目录 存放扩展插件的源代码和共享库文件,src目录存放项目主体部分的源代码文 件。 在教学环境下,以上目录结构可分别放在teacher和student两个子目录中。其 中teacher目录包含完整的程序源码和资料文档,以为学生开发时提供参考和借 鉴。 student目录中的源代码是不完整的,部分类或者函数的实现只给出了基 本框架,但代码中的注释和teacher目录下对应的部分完全相同,其中缺失的内 容,需要学生在理解整体设计思路和上下文逻辑的前提下予以补全。需要学生 参与补全的源代码文件详见开发计划。 5. 开发计划 本项目拟在四个工作日内完成: 工作日 模块 子模块 代码文件 第一天 基础设施 预编译头 Precompile Precompile.h 字符串工具包 StrKit StrKit.h StrKit.cpp 日志 Log Log.h Log.cpp 配置器 Configurator Configurator.h Configurator.cppWEBCRAWLER 网络爬虫实训项目 13 多路输入输出 MultiIo MultiIo.h MultiIo.cpp 插件接口 Plugin Plugin.h 插件管理器 PluginMngr PluginMngr.h PluginMngr.cpp 第二天 网络通信 哈希器 Hash Hash.h Hash.cpp 统一资源定位 符过滤器接口 UrlFilter UrlFilter.h 布隆过滤器 BloomFilter BloomFilter.h BloomFilter.cpp 原始统一资源定位符 RawUrl Url.h 解析统一资源定位符 DnsUrl Url.cpp 统一资源定位符队列 UrlQueues UrlQueues.h UrlQueues.cpp 套接字 Socket Socket.h Socket.cpp 超文本传输协 议响应包头 HttpHeader Http.h 超文本传输协议响应 HttpResponse 第三天 流程控制 线程 Thread Thread.h Thread.cpp 域名解析线程 DnsThread DnsThread.h DnsThread.cpp 发送线程 SendThread SendThread.h SendThread.cppWEBCRAWLER 网络爬虫实训项目 14 接收线程 RecvThread RecvThread.h RecvThread.cpp 网络爬虫 WebCrawler WebCrawler.h WebCrawler.cpp 主线程 main Main.cpp 构建脚本 Makefile Makefile 第四天 外围扩展 最大深度插件 MaxDepth MaxDepth.h MaxDepth.cpp MaxDepth.mak 域名限制插件 DomainLimit DomainLimit.h DomainLimit.cpp DomainLimit.mak 超文本传输协议响 应包头过滤器插件 HeaderFilter HeaderFilter.h HeaderFilter.cpp HeaderFilter.mak 超文本标记语言 文件存储插件 SaveHTMLToFile SaveHTMLToFile.h SaveHTMLToFile.cpp SaveHTMLToFile.mak 图像文件存储插件 SaveImageToFile SaveImageToFile.h SaveImageToFile.cpp SaveImageToFile.cpp 构建脚本 mkall mkall 其中被突出显示的代码文件中,包含需要学生添加的内容,注意源文件中形 如“// 此处添加代码”的注释。WEBCRAWLER 网络爬虫实训项目 15 6. 知识扩展 为了能在实训环节,进一步强化学生独立思考、独立解决问题的能力,本项目 有意涵盖了一些前期课程中不曾涉及或只作为一般性了解的知识和技巧。具体 包括:  预编译头文件  std::string  变长参数表  基于epoll的多路I/O  哈希算法和布隆表  URL、 DNS、 HTTP和HTML  正则表达式  线程封装  精灵进程和I/O重定向  Makefile 对于上述内容,建议项目指导教师根据学生的接受能力,结合项目中的具体应 用,在项目正式启动之前,先做概要性介绍,同时提供进一步详细学习和研究 的线索,包括man手册、参考书、网络链接或其它媒体资源,尽量让学生通过 自己的实践和探索找到解决问题的方法,这才是项目实训的意义所在!
这是一个发生在平行宇宙的事情,那个世界同样有太阳系,同样有地球,也同样有中国和美国,最重要的,他们也使用计算机,也用源代码来生成机器码。 在这个平行宇宙的地球上,有一个叫做Miracle的公司,有一个叫Master的产品,这个产品使用UPM公司出品的DirtyUnpack作为管理源代码的­工具,这个源代码管理工具支持一种叫做Sight的快照方式,在正确配置了BOV的情况下,Sight的大小为400多GB。这400GB中包括源文件,第三方­工具,第三方库,多语言工具,各种脚本,各种语言的资源文件(LMX格式),也就是说,安装了操作系统和基本开发工具之后,有了这个Sight之后就可以完成所­有的开发事项了。 在某一个Sight下,源代码大小11,180,425,539 Bytes(在不重要的数据位上加了扰动,导致这个值比真实数据要略小一些),这个数据是在统计.h和.cpp文件后,用bc计算得到的。其中有少量文件是sy­mbol link,懒得做uniq了,因为我也没有统计.c文件,其它的脚本文件等也都没有统计,原因很简单:DirtyUnpack这个源代码管理工具运行得太慢了,­仅仅统计这两项就花了1/365光年(注意:这是平行宇宙,光年在他们那里是时间单位,嘻嘻) 现在来考查一下巨大的源码带来的问题 1、IDE问题 和咱们的地球一样,在那个平行宇宙里,程序员中的大多数还是喜欢IDE。这么多的源码文件,没有办法全加到project中。比如HugeHard公司出产的B­lindWorkshop开发工具,如果添加这么多文件进入工程文件,会几个小时也无法打开工程——也就是说,当你上班到了公司时,打开这个工程,等到工程打开­了,你就可以去吃中午饭了;下班时关闭工程后,你得加班4小时等着它正常关闭。 2、编译问题 10GB的源码,编译起来是个大问题,要非常慎重地在根目录对clean目标进行make。编译的依赖性问题在这种情况下,也成了一个问题(如:规划哪些源码­需要重新编译)。另外,如何加快编译速度,也算是一个不小的挑战。 3、阅读问题 如果你想要修复Flies(平行宇宙里不叫Bug),或者想要添加新功能,肯定都需要先读懂原有的(部分)代码。设想一下:为了完成某一项任务,你需要有一个必­须要理解的内容集合,包括:源文件,架构,接口等。如何让这个集合的内容最小化,形式上更合理化,也是一个不小的挑战。设想一下极端的情况,如果想要修改一个小­东西,得要先读懂全部这10GB的源码才能动手——这种情况是肯定不能接受的。但有没有可能将这个集合限制在一个相当小的范围内呢?比如:一个文件夹内?甚至一­个文件内? 4、查找问题 在那个平行宇宙中,有一个非常强大的CloseBinary的CLI,叫做Linics。许多在这个地球中存在的工具,在这个CLI下面也有。比如find|x­args,再比如cat/cut/grep/sed等。 有时候,可能会需要在源码中遍历查找些东西,比如,当你在修改常量前,往往会想先知道这个常量在哪里正在被使用,而这个常量是在一个非常公共的头文件中定义的。­于是你需要查找大量的文件来定位这个符号。但是,这个DirtyUnpack的Sight,是一个从网络访问的动态Sight(snapshot类型的太大了,­要400GB,软盘没那么大空间),这种动态的sight速度是不够快的,如果在较高层次的目录上进行查找,往往需要1/356光年的时间才能完成。(前面已经­说了,平行宇宙,“光年”是时间单位) …… 嗯,暂时先说这几条。其实这几条已经能够让人感觉到非常不爽了,有时候某些原来很容易做到的事情却变成了“不可能完成的任务”,比如:一条几个小时才能完成的g­rep命令,在我看来,如非特别必要,一般是不想去跑了。 说这些的东西的目的也很simpe,很naive:对于大型项目,有些理念是不一样的,在接触了那样的限制条件后,才能体会得到。在这些项目中,从架构设计到­编码规范,都和平时接触的小项目有所区别(我原先工作的都是30-60MB源码这样的小项目)。这里的许多东西,不是故意为了特立独行而发明的,实属是被逼无奈­才不得已而为之。“历史的包袱”在这种生存期长达10几光年的项目中也很沉重(为什么C++的编译器不能随便抛弃东西?)。 嗯,期待“结论”或者“启示”的TX们可以关窗口了,本文就是讲故事,到此为止了。某些怀疑论者也不要找我求证了,都说了这是平行宇宙发生的事情了…… P.S. 还得多说一句,“大”不代表“好”,只不过有时候,这个“大”是一个既定事实,你不得不去面对。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值