招聘网站手机端源码分享 招聘手机版

大家好,关于招聘网站手机端源码分享很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于招聘手机版的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!

第7章大型网站开发范式

概括来讲,任何一个网站,归根到底,在技术层面通常有三大关键页面,分别是:首页、列表页、详情页。虽然不同的产品,不同的平台,在业务上的定义和名称不尽相同,但基本上由这三大页面构成了核心的流程,即从首页到列表页,再从列表页到详情页。

这三个页面,最终又可以归结为网站页面,但是它们之间有着微妙的区别。不同的页面,所面临的场景是不一样的,需要呈现的数据以及交互方式、着重点也不尽相同。功能上的差异性反向决定了技术实现上的挑战、约束、非功能性要求也不尽相同。那么它们分别有哪些开发范式?这一章即将揭晓。

7.1首页开发范式

首页,是产品的门面,也是大部分用户访问的第一个页面。它的重要性,不言而喻。

若想获得用户的青睐,提升用户体验,有力支撑业务功能,设计首页时有一定的开发范式。首页主要关注的是对分类信息的展示,以及热门数据信息的推荐,最为重要的是需要提供列表页的访问入口。

为了更具体地理解首页开发范式在实际业务场景的应用,我们以开发招聘网站为例,结合其业务进行说明。不难发现,当应聘者进入我们的招聘网站时,他会首先进入首页,定位到自己所在的城市,并点击与自己相关的职位,然后进行到招聘列表页。接着再从列表页点击感兴趣的招聘岗位进入详情页,最后投递自己精心制作的简历,期待面试的通知和安排。这就是我们招聘网站的主要业务,项目名称就叫:迷你招聘。

7.1.1前期准备工作

在接下来的实现过程中,虽然是从零到一开发,但我们暂时不使用任何框架,而是基于前面介绍的、已实现的3+1套数据模板为核心技术,塑造招聘网站企业级系统的雏形,从而窥探抽象的范式、思想和概念如何落地到业务项目中、如何有机融入现有系统。

迷你招聘网站项目PHP源代码:https://gitee.com/dogstar/job

在前期,需要完成一些基本的项目准备工作。在这里,我们创建了一个Git代码仓库,存放项目的相关代码。通过Nginx在本地配置搭建了一个站点,网站地址是:http://dev.job.com/。因为这一节重点关注的是企业级网站数据的规划与实现,以及首页的开发范式,所以对业务数据的最终来源和深入的领域规则也暂时放一边,取而代之的是使用模拟的数据来表示。

最终,迷你招聘首页的实现效果截图如下:

图7-1迷您招聘网站首页

7.1.2规划你的数据

在招聘网站的首页,从上到下,主要有以下三部分数据:

顶部广告职位分类热门职位

根据数据稳定-访问象限分布图,以及结合前面所学的3+1套数据模板,我们稍微分析一下,对数据进行规划设计。

顶部广告,是属于稳定且低频访问的数据,虽然首页的流量也很高,但前面的顶部广告只是首页这一个页面使用到,并不是全部页面都需要,因此相对来说是低频访问的数据。顶部广告数据是稳定的,是因为通常内部广告的更新速度不会非常频繁,即便有更新,也可以接受一定时间内的延迟,例如几分钟的缓存时间。因此,顶部广告适宜采用轻量级数据模板。此外,这部分的广告信息由管理后台录入,并更新到数据库,前台通过数据库查询可获取此部分数据。

职位分类,也是属于稳定且低频访问的数据。它与顶部广告的性质类似。它之所以也是稳定的,是因为招聘类的职位分类一旦确定后,市场内在短时间内很少会有新的专业出现。但随时着公司的业务发展,可能会在不同的阶段拓展更多的职位。整体而言,这部分的数据也是稳定的。而且也是通过管理后台录入,前台从数据库查询即可。职位分类也适宜采用轻量级数据模板。

最后是我们的热门职位模块,这部分的数据与前面两份数据不同。它是非常活跃的,并且由大数据推荐算法根据用户画像实时提供。由于是千人千面的差异化推荐,因为热门职位适宜采用实时数据模板。

再用表格来总结整理,对于我们本次的招聘网站首页的数据规则,就更为明确了。

表7-1迷你招聘首页的数据规划

首页数据

采用的数据模板

最终数据来源

顶部广告

轻量级数据模板

数据库

职位分类

轻量级数据模板

数据库

热门职位

实时数据模板

大数据推荐算法,远程接口

关于轻量级数据模板和实时数据模板,我们在前面均已实现并已掌握,接下来就是关于如何使用了。

7.1.3基本实现思路

首页查询对象

在访问页面时,需要用到一些用户参数。这些输入的参数,可能是出现在链接后面,以GET参数表示,又或者是保存在用户浏览器,隐藏在COOKIE中。对于招聘首页,需要用到的参数有用户选择的城市city参数。为此,我们针对首页先创建IndexQuery查询类,并在里面添加城市city参数。

<?php\n//./data/AdData.php文件\nrequire_oncedirname(__FILE__).&39;;\n\nclassIndexQueryextendsDataQuery{\n//所在城市,如:gz=广州,sz=深圳\npublic$city;\n}

基于数据模板的实现

接下来,分别是首页三部分数据基于数据模板的实现。先来看顶部广告数据。创建AdData广告数据类,并继承轻量级数据模板LightWeightDataProvider基类,实现具体获取广告数据的细节,并指定缓存的key。

<?php\n//./data/AdData.php文件\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\n\nclassAdDataextendsLightWeightDataProvider{\nprotectedfunctiondoGetData(DataQuery$query,&$trace=&39;){\n//全球追踪器:数据库\n$trace.=&39;;\n\n$model=newAdModel();\nreturn$model->getTopBanner($query->city);\n}\n\nprotectedfunctiongetCacheKey(DataQuery$query){\nreturn&39;.$query->city;\n}\n}

可以说,数据类是基于数据规划和管理策略的代理层。这一层是我们重点关注的。至于数据库Model模型层的实现,以及背后的数据库表设计与数据库操作,暂且通过模拟的数据来表示。以下是AdModel类的实现代码。

<?php\n//./model/AdModel.php文件\nclassAdModel/**extendsModel**/{\npublicfunctiongetTopBanner($city){\n//假设从数据库获取的数据如下……\nreturnarray(\n&39;=>&39;,\n&39;=>&39;,\n&39;=>&39;,\n&39;=>&39;,\n);\n}\n}

准备好广告数据后,就可以按MVC模式,在控制层获取数据并渲染视图。

<?php\n//./controller/IndexController.php文件\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\n\nclassIndexController{\npublicfunctionindex(){\n//首页查询参数对象\n$query=newIndexQuery();\n\n//顶部广告\n$adData=newAdData();\n$ad=$adData->getData($query);\n\ninclude(dirname(__FILE__).&39;);\n}\n}

作为完整的实现过程,再来看下视图模板./view/index.html的代码。

<!–顶部广告–>\n<divclass=&34;>\n<divclass=&34;>\n<br/>\n\n<h2><?phpecho$ad[&39;];?></h2>\n<p><?phpecho$ad[&39;];?></p>\n<p><aclass=&34;href=&39;link&34;role=&34;><?phpecho$ad[&39;];?>?</a></p>\n</div>\n</div>

以首页查询参数为起点,从数据层,到模型层,再到控制层,最后到首页的视图模板,经过这一系列快速的、基本的实现,再次访问迷你招聘网站的首页,可以呈现以下运行效果。

图7-2招聘首页的顶部广告

职位分类的实现过程和顶部广告的实现类似,这里不再赘述。至于热门职位,实现也和此类似,唯一不同的,热门职位使用的是实时数据模板。所以,在创建HotJobData热门数据类后,需要继承RealTimeDataProvider类。同时,在标识全球追踪器时,用字母A表示从远程接口获取。

//./data/HotJobData.php文件\n<?php\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\n\nclassHotJobDataextendsRealTimeDataProvider{\nprotectedfunctiondoGetData(DataQuery$query,&$trace=&39;){\n//全球追踪器:远程接口\n$trace.=&39;;\n\n$model=newJobModel();\nreturn$model->getHotJob($query->city);\n}\n}

完整的核心实现

完成顶部广告AdData、职位分类CategoryData、热门职位HotJobData这三分部的数据规划与实现后,在控制层,再把这些网站数据元素综合串联起来,就能实现首页功能的有机组合。结合全球追踪器,可以得到更加完备的体系。虽然目前还只是一个首页,但正所谓,麻雀虽小,五脏俱全。在首页的背后,是设计巧妙的微型系统,基于此微架构,可以延伸出大比例的结构,逐步演进,快速迭代。一环扣一环,环环相扣。我们先来讲首页控制层黏合后的实现代码,后面再来探讨在这基础上可以进行哪些扩展。

如果一个系统是经过精心设计的,那么它的代码是优雅、清晰,并且是简单的。它是那么容易理解,以致于不用过多解释。

<?php\n//./controller/IndexController.php文件\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\n\nclassIndexController{\npublicfunctionindex(){\n//首页查询参数对象\n$query=newIndexQuery();\n\n//全球追踪器\n$trace=&39;;\n\n//顶部广告\n$trace.=&39;;\n$adData=newAdData();\n$ad=$adData->getData($query,$trace);\n\n//职位分类\n$trace.=&39;;\n$cateData=newCategoryData();\n$category=$cateData->getData($query,$trace);\n\n//热门职位\n$trace.=&39;;\n$hotData=newHotJobData();\n$job=$hotData->getData($query,$trace);\n\n//通过头部输出全球追踪器\nheader(&39;.$trace);\n\n//视图渲染\ninclude(dirname(__FILE__).&39;);\n}\n}

就如上述首页控制层IndexController类的代码,遵循程序“输入-处理-输出”这三大模块的顺序,在index()方法的最前面,我们创建了一个首页查询参数对象,完成初始化后,用户选择的城市会存放在IndexQuery::$city类属性。注意这个查询对象,是针对整个首页都适用的,可以把全部的参数扩展到IndexQuery,如果确实有需要,也可以进行查询子类的细分。

对比前面顶部广告单一模块的调用代码,当时没有使用全球追踪器。回顾前面的代码,是这样的,没有进行数据的追踪。

//顶部广告\n$adData=newAdData();\n$ad=$adData->getData($query);

这是因为当时我们关注是局部的实现,而现在则是通盘的考虑。为了观察全部数据经过的节点,可视化还原其链路,我们引入了全球追踪器$trace,并逐层往下传递收集。升级后的调用代码如下:

//顶部广告\n$trace.=&39;;\n$adData=newAdData();\n$ad=$adData->getData($query,$trace);

只需要在原来的基础上,把数据的名称累加起来,并往下传递,晚点就可以收割成果了。

//通过头部输出全球追踪器\nheader(&39;.$trace);

当底层已经支持,业务上层也实现对其调用后,再次访问首页,在浏览器内按F12进行调试,并切换到网络,可以发现在响应头部有以下追踪的信息:

TRACE:&34;

根据这一条追踪信息,我们可以从专业的角度进行现场解说:你看啊,对于网站首页,顶部广告数据先从轻量级模板获取,因为没有缓存,穿透到了数据库查询。接着是职位分类信息,也是先从轻量级模板获取,后穿透到数据库获取。而最后的热门职位,则每次都是实时从远程接口获取到个性化推荐的数据。

不言而喻!

当这一切妥当后,我们就能看到了前面首页的运行效果。是不是感觉很奇妙,很简单?啊哈,软件开发,本来就很简单。

为了给读者一个更加整体的轮廓,我把本次项目的目录结构,以及相关的注释补充如下。

job$tree\n.\n├──controller首页控制器\n├──data顶部广告数据\n│├──CategoryData.php数据供给器\n││├──DataProvider.php\n││├──DataQuery.php\n││├──LightWeightDataProvider.php\n││└──RealTimeDataProvider.php\n│├──HotJobData.php首页查询对象\n├──model对外访问入口\n│├──css\n││└──bootstrap.min.css\n│├──index.php视图层\n└──index.html

这只是一个开始,下面内容更精彩。

7.1.4从完成到完善

至此为止,我们已经完成了招聘首页的功能开发。注意,这里只是说完成,而不是完善。上面一系列的开发,构建了一个初级的网站,纵使引入了一些往常极少应用到的新技艺,但离完善的企业网站还有一段很长的距离。那怎样做才更完善呢?

很多技术类的书籍,都会对PHP的基础语法,以及基本的项目开发作详细的介绍和说明,但对于如何有效地综合应用这些基础追求更完善的系统,则鲜有记载。我们不缺乏核心的技术、高级的技术,但缺少把这些技术恰当融入到项目业务中,产生更大的价值。

如何才能达到完善的状态呢?首先要从完善的定义开始。只有清楚知道一个完善的系统需要哪些特性时,我们才能更好地去构建它。这需要一些思维的转变。那就是考虑我们的用户需要什么,他们的诉求是什么,以及他们期待什么。

正如绝大部分的网站一样,它的用户其实不止一种。就迷你招聘网站而言,众所周知的是,它的最终用户是应聘者,那些想找工作的人。这部分是招聘网站的主要用户人群,也是核心的人群。但除此之外呢?还有就是我们这些软件开发工程师,我们需要开发、调试、排查故障,满足这些需求应该提供哪些技术支持呢?又比如企业内部的运营部门,在修改首页顶部广告时,他们希望能够在正式发布前可以先进行预览,以校对投放的广告信息在首页最终呈现的效果是否正确。但首页现在是没有预览功能的。

针对预览这一诉求,让我们向完善迈出第一步。

预览功能

很多网站,都需要能够提前预览的功能。预览的功能,仿佛能给人一种预知未来的能力,穿越到未来的某个时刻,看到将来的网站。当具备以下这几个条件时,我们离预览功能就远了。

第一,要支持指定访问参数的设置第二,要支持当前时间的设定第三,要支持缓存使用与否的控制

以招聘首页的预览功能为例,支持指定访问参数的设置,即意味着我们可以通过某种方式轻松切换当前选择的城市,即便初始时已确定当前的城市。为简单起见,假设选择的城市保存在浏览器COOKIE中,预览时则可以通过GET参数来进行覆盖。以下升级后的代码体现了这一点。

classIndexQueryextendsDataQuery{\n//所在城市,如:gz=广州,sz=深圳\npublic$city;\n\npublicfunction__construct(){\n//默认城市\n$this->city=&39;;\n//用户真实选择的城市\nif(!empty($_COOKIE[&39;])){\n$this->city=$_COOKIE[&39;];\n}\n//预览时指定的城市\nif(!empty($_GET[&39;])){\n$this->city=$_GET[&39;];\n}\n\nparent::__construct();\n}\n}

在很多行业中,其领域业务都是与时间有密切关系的。例如电商平台的商品,只有到了开售时间才能开始售卖。又如电影票的购买也要等到开映前的一段时间才能开始。但迷你招聘网站暂时还没有和时间有关的业务场景,暂时跳过。至于缓存使用与否的控制,则是绝大部分系统都需要的基础特性。关于这一点,我们前面没有实现。但没关系,我们拥有架构提升的能力,可以通过修改底层源代码来优化。

首先,对于查询对象的基类,添加两个开关,一个是读缓存开关cacheWrite,一个是写缓存开关cacheRead。默认情况都是开启状态。

classDataQuery{\npublic$cacheWrite=true;//缓存读开关\npublic$cacheRead=true;//缓存写开关\n}

同样地,我们可以通过GET参数为预览时指定缓存使用与否的控制。为DataQuery添加构造函数,并在里面实现此逻辑。

classDataQuery{\npublic$cacheWrite=true;//缓存读开关\npublic$cacheRead=true;//缓存写开关\n\npublicfunction__construct(){\nif(isset($_GET[&39;])){\n$this->cacheWrite=$_GET[&39;]==0?false:true;\n}\n\nif(isset($_GET[&39;])){\n$this->cacheRead=$_GET[&39;]==0?false:true;\n}\n}\n}

只有当明确指定不需要缓存时,才把读写缓存的开关设置为关闭。

至此,我们可以阶段性快速验证目前所做的修改是否对已有的功能有影响,并且核对新增的功能是否能正常工作。临时修改IndexController首页控制器,将查询对象打印出来。

classIndexController{\npublicfunctionindex(){\n//首页查询参数对象\n$query=newIndexQuery();\n//TODO:临时调试,调完即删\nvar_dump($query);die();\n\n……\n}\n}

再次访问前面,并指定预览时的城市,以及缓存开关的控制状态。例如访问:

http://dev.job.com/?city=sz&cacheWrite=0&cacheRead=0

可以得到输出的结果是:

object(IndexQuery)34;city&34;sz&34;cacheWrite&34;cacheRead&2(3){\n[&34;]=>\nstring(2)&34;\n[&34;]=>\nbool(true)\n[&34;]=>\nbool(true)\n}

通过增加city、cacheWrite、cacheRead这三个参数在预览时动态设置,我们可以指定网站访问的上下文,分别从业务维度、技术维度,甚至时间维度框定系统所运行的时机和场合。通俗一点来讲,就是我们通过仿真的手段,塑造了期待发生的场景。接下来,就是如何把演员放进来,让他们按照我们新编排的剧本来演。

打开编辑器,进入到DataProvider数据供给器,修改既有的代码,把缓存读写开关的控制扩展进去。

abstractclassDataProvider{\npublicfunctiongetData(DataQuery$query,&$trace=&39;){\n……\n//从缓存中获取(追加读开关控制)\n$data=$query->cacheRead?$cache->get($key):NULL;\nif($data===NULL){\n……\n//写入到缓存(追加写开关控制)\nif($query->cacheWrite){\n$cache->set($key,$data,$this->getCacheExpireTime($query));\n}\n}\n……\n}\n}

完成对底层数据供给器的优化后,通过预览参数访问首页,如果在响应头部看到的追踪信息TRACE类似如下,则说明预览功能升级完毕!

TRACE:&34;

预览功能虽然说是一个小功能,但能基于现有的系统平滑升级而不至于需要大动干弋,不会牵一发而动全身,做到这一点就很难了。另一个视角,作为不懂任何编程技术的运营部门,他们不关心技术本身的难易程度,只会关心他们想得到的效果是否可以满足。只有设身处地地为他人考虑,为用户着想,为其他合作部门考虑,我们开发和维护的系统,才能更加完善。

数据预热

与预览功能相对的一面是,我们希望系统可以尽量使用高效缓存,因为在高并发时,哪怕一次数据库的穿透操作都有可能引起“雪崩”的发生。这时采用的策略就是进行数据预热,将所需要用到的数据提前生成并同步到缓存中。

数据预热,并不是指把数据从数据源获取出来然后写入到缓存中这么简单。对于大型企业级网站系统而言,要全面考虑,精心设计,按一定的设计和计划严格实施和执行,并思考由此解决方案而触发的新问题和新挑战,以及对于新问题的下一步应对措施是什么。从问题域到解空间,再从解空间到问题域,如此循环,不断精进、不断收敛,方能造就更加完善的系统。

各位从事软件开发的同学,应该意识到,好的设计是友好的。它不会通过否定过去来抬高自己,也不会以损坏、排斥既有的功能、组件或模块来将新的元素强硬融入到系统中。恰恰相反,它会兼顾并传承古老的文化,进而创新。当它消失在历史舞台时,系统也不会因为它的离去而阵痛不已,反而它离去时仍然会留下芬香。Linux世界中的shell命令就是一个好设计,shell命令通过管道相互协作,并且可以通过简单的、既有的命令,衍生更加强大的命令。在自然界中,这样的例子更是随处可见。一如海洋中波涛汹涌的海浪,每一片浪花的倒下,都是为后面的浪花作铺垫。于是有了“长江后浪推前浪”。

回到正题,为了实现数据预览,新的设计方案是什么?我们又怎么基于既有的系统进行升级和扩展?由此引发的新问题是什么,又该如何面对和解决?答案即将揭晓。

为了能提前生成数据,并进行数据预热,避免数据库穿透,我们需要准备两份缓存数据。一份缓存数据是当前用户正在访问使用的缓存数据,一份是用于保存提前生成的缓存数据。并且这两份缓存数据,随着时间的推移,循环交替切换。再具体一点,就是以10分钟为间隔,依次轮询切换这两份缓存。

此时,要把时间这一因素扩展到数据供给器。实现起来,可以分为两步。第一步,修改查询对象基类DataQuery,添加当前请求时间这一类属性。同时,在构造函数初始化时,对当前请求时间进行默认初始化和预览时的初始化。

classDataQuery{\n……\npublic$nowTime;//当前请求时间戳\n\npublicfunction__construct(){\n……\n//默认&预览初始化\n$this->nowTime=$_SERVER[&39;];\nif(isset($_GET[&39;])){\n$this->nowTime=$_GET[&39;];\n}\n}\n}

注意到,默认初始化时,使用的是$_SERVER[&39;]变量,而不是time()函数。这样做有两个好处,一个是性能上会更优,一个是变量更容易修改和模拟。

接下来第二步,是修改数据供给器的底层代码,让它支持两份缓存数据能随时间推移不断循环切换。

abstractclassDataProvider{\npublicfunctiongetData(DataQuery$query,&$trace=&39;){\n……\n$key=$this->getCacheKey($query);\n\n//为缓存添加后缀,支持每10分钟,切换一份缓存\n$key.=&39;.intval(date(&39;,$query->nowTime)/10%2);\n\n//从缓存中获取(追加读开关控制)\n$data=$query->cacheRead?$cache->get($key):NULL;\n……\n}\n}

大家不要着急往下看,而是应该细细品读新增的代码,它的写法,以及它的意思。因为这些代码都是曾经惨痛的项目经验换来的。以前,最初的时候,我们是这样实现的:

//为缓存添加后缀,支持每10分钟,切换一份缓存\n$key.=&39;.date(&39;).intval(date(&39;)/10).&39;

得到的缓存后缀类似是:201807010000、201807010010、201807010020、201807010030、……、201807012350。即每隔10分钟,缓存的后缀版本会不断累加。到最后,因为当时使用的是Memcached缓存,而缓存key是不断增加的,导致Memcached的空间使用率非常高。假设使用的是文件缓存,同样会存在类似的问题,如果不定期清理的话,缓存的文件会越来越多。相比于Memcached,文件缓存还相对容易清理,而Memcached的清理则麻烦一些。基于这个历史教训,我们改进了原来的缓存后缀版本号,改为只有0和1这两个后缀。这样就非常简单了。

以首页顶部广告数据为例,AdData::getCacheKey()方法中提供的缓存key是:&39;.$query->city。那么添加缓存key后缀后,最终的key为top_banner_ad_gz_0或top_banner_ad_gz_1。

classAdDataextendsLightWeightDataProvider{\nprotectedfunctiongetCacheKey(DataQuery$query){\nreturn&39;.$query->city;\n}\n}

完成台前的准备工作后,跟着就是幕后的筹备工作了。

假设当前时间是2018年7月2号16点40分01秒,应聘者访问了我们的迷你招聘网站,此时缓存的后缀版本号为数字0,包括顶部广告、职位分类这两份数据。由于热门职位是实时获取的,不需要缓存。但正在使用的这两份数据,顶部广告top_banner_ad_gz_0和职位分类category_job_type_0,是在用户访问之前就已经预热好了。让时光倒流10分钟,回到16点30分,并把镜头定位到服务器的计划任务上,我们就知道在幕后发生了什么事情。

在16点30分时,网站用户访问和使用的数据是顶部广告top_banner_ad_gz_1和职位分类category_job_type_1,注意这时的缓存后缀版本号是数字1。通过后台定时计划任务,我们可以提前生成下一个轮询版本的缓存数据,而不会影响当前正在使用的数据。这也是符合关注点分离的原则,把终端用户使用数据与技术上准备的数据分离,互不干扰。

此时,轻量级数据模板也要细化一下。考虑到数据要提前10分钟生成,接着又有10分钟的访问期,而轻量级数据模板原来的缓存时间默认统一为10分钟,明显是不够的。因此,要加时,额外追加10分钟,即变成20分钟,才能保证提前生成的数据,到用户访问时仍然保持有效。

abstractclassLightWeightDataProviderextendsDataProvider{\nprotectedfunctiongetCacheExpireTime(DataQuery$query){\nreturn1200;//备注:从原来10分钟,调整成20分钟\n}\n}

与此同时,使用适配者模式,把轻量级缓存类LightWeightCache继承于简单实现的文件缓存类FileCache。文件缓存的实现没有什么难度,这里只是作为演示说明使用。例如,顶部广告的缓存文件路径为./cache/top_banner_ad_gz_0.dat,保存的数据需要序列化后再存储,并且最前面的数字表示有效的时间戳。

$cat./cache/top_banner_ad_gz_0.dat\n1530522938a:4:{s:5:&34;;s:21:&34;;s:4:&34;;s:63:&34;;s:6:&34;;s:12:&34;;s:4:&34;;s:11:&34;;}

在命中了缓存的情况下,全球追踪器在响应头部的输出类似如下,没有D节点标识,因为没有穿透到数据库。

TRACE:&34;

但是要注意的是,如果你在分布式集群服务器上使用文件缓存,要考虑到缓存文件同步的问题。此时可以使用scp的方式来同步到各应用服务器。

完成了对底层升级改造这一阶段性的操作后,就要开始实现计划任务的脚本编写了。思路很简单,对于全部的城市进行遍历,然后提前生成顶部广告和职位分类的数据。前面我们已经做了很多铺垫,打下了坚实的基础,再来实现数据预热的功能就很简单了。创建对首页数据进行提前预热的计划任务脚本./crontab/build_index_data.php,并放置以下PHP代码。

<?php\n//./crontab/build_index_data.php文件\n/**\n*对首页数据进行提前预热\n*/\n\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\nrequire_oncedirname(__FILE__).&39;;\n\n//穿越到未来,往前推移10分钟\n$_SERVER[&39;]=time()+600;\n//全部城市,例如只有广州gz和深圳sz\n$allCitys=array(&39;,&39;);\n//待提前预热的数据\n$dataList=array(\nnewAdData(),//顶部广告\nnewCategoryData(),//职位分类\n);\n\nforeach($allCitysas$city){\n//首页查询参数对象\n$query=newIndexQuery();\n//指定所在城市\n$query->city=$city;\n//不读缓存,但提前写入缓存\n$query->cacheRead=false;\n$query->cacheWrite=true;\n\n//提前预热数据\nforeach($dataListas$obj){\n$obj->getData($query);\n}\n}\n\necho&39;,PHP_EOL;

上面代码非常容易理解。在手动引入必要的类文件后,我们准备了业务上需要的全部城市列表,同时也准备了技术上需要的全部待提前预热的数据对象。接下来就是一个业务循环,以及嵌套一个技术循环,将相关的业务参数和技术参数设定后,就可以进行数据的预热了。非常简单!

假设当前时间为2018年7月2号18点整,此时访问迷你招聘首页后,可以在缓存文件目录看到有两份缓存数据。

$tree./cache/\n./cache/\n├──category_job_type_0.dat\n└──top_banner_ad_gz_0.dat\n\n0directories,2files

此时,如果执行刚编写完成的计划任务脚本build_index_data.php,例如:

$php./crontab/build_index_data.php\ndone!

再次对比文件缓存目录下的内容,就可以发现提前生成了三份数据:category_job_type_1、top_banner_ad_gz_1和top_banner_ad_sz_1。是不是很有趣?

$tree./cache/\n./cache/\n├──category_job_type_0.dat\n├──category_job_type_1.dat由计划任务生成\n└──top_banner_ad_sz_1.dat39;REQUEST_TIME&39;2018-07-0219:30:00'\ndone!

就可以达到想要的效果啦!

到目前为止,我们的招聘首页,从简单到复杂,从完成到完善。除了完成基本的业务功能外,我们还考虑到了性能上的优化,紧急刷新的应急处理方案,页面预览功能的支持。纵使如此,有待挖掘的贴心设计还有很多。书不尽言,暂且告一段落。

7.1.5世界杯,缓存开关与完美

在编写这一小节时,正值2018俄罗斯世界杯直播中。与此同时,在场外,有很多世界杯积分竞猜的活动,对于我这个足球门汉外来说,足球比赛的结果本质上就是两个比分,但基于这两个比分的竞猜玩法却有很多种。比如猜输赢、猜比分、猜让球……这让我联想到,如果我们对于软件开发,也能怀着一颗这样“爱玩”的心,估计我们设计和开发的系统,功能会更多样,也会更有趣。

就像在查询对象里面的读缓存开关和写缓存开关,虽说是两个开关,但两两组合起来就有四种情况。罗列出来,似乎没什么特别之处,无非就是:

读缓存、写缓存读缓存、不写缓存不读缓存、写缓存不读缓存、不写缓存

甚至连小学生都能把全部的组合回答出来。但作为专业的软件开发工程师,我们有没想过,有没深入思考过,这四种组合背后的意义?答案在前面章节中已经提供了。

当读缓存的开关和写开关都开启时,那就是我们最终用户正常访问的场景;当不读缓存却有写缓存时,则是数据预热的时机,这时会把数据提前生成并放入缓存;当缓存既不读也不写时,则是预览的时候,是为了让业务运营团队能够提前预览核对,也是为了让技术开发人员能在线上进行故障排查。这一微妙之处,还须我们自己慢慢体会。如人饮水,冷暖自知。

在Facebook的文化,有句话是:完成,胜于完美。

诚然,完美遥不可及,但我们不应由此而放弃对完美的追求。但要知道的是,即便达到了完美的境界,那也只是一瞬间的状态。更何况网站系统开发本来就没有完美这一说。而通过不断思考、不断实践、不断验证,我们越完善,就能离完美越靠近。

魔鬼隐藏在细节中,而天使就站在魔鬼的背后。

文章分享结束,招聘网站手机端源码分享和招聘手机版的答案你都知道了吗?欢迎再次光临本站哦!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平