热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用

应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性。
一、高可用的Session服务器场景简介

1.1 应用服务器的无状态特性

  应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性。

http

PS:提到无状态特性,不得不说下Http协议。我们常常听到说,Http是一个无状态协议,同一个会话的连续两个请求互相不了解,他们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的所有信息外,该环境不保存与会话有关的任何信息。之所以我们在使用ASP.NET WebForm开发中会感觉不到Http的无状态特性,完全是因为Microsoft帮我们实现了ViewState,它是ASP.NET WebForm中保存页面信息的基本单位,本质是一个HTML中的隐藏域,回调时会将这个隐藏域中的数据提交到服务器端。 

  在很多场景中,用户都需要和我们的网站系统进行多次的信息交互,这时就需要一种解决方案来克服无状态特性所带来的困境。还好,在巨人的肩膀上,我们已经有了很好的解决方案,那就是浏览器端的COOKIE和服务器端的Session。在一般的单机开发中(这里一般是指只有一台Web服务器的情况),服务器端我们通常使用Session来存储用户登录状态(一般是一个自定义对象实例),在多数的管理信息系统开发中(毕竟内部系统用户量不多,一台Web服务器既提供Web服务又存储Session对象内存还算是够用的)这是很常见的。

  但是,在大用户量下,单机版的Session就会显得效率低下,甚至会拖累Web服务器的性能。这是因为:每个用户的Http请求发到服务器端后,每台Web服务器的服务器软件(例如:IIS、Tomcat等)都会为该请求创建一个线程来进行处理和响应,但是一台服务器同一时间可以接收的请求数毕竟是有限的(这个根据服务器的配置而定,例如CPU中i3、i5和i7类型分别可以创建的线程数都各不相同),当某个时间段出现高并发请求数的时候(比如:网购秒杀系统中经常同一时间会出现海量的并发数),那这台应用服务器将会接收前所未有的请求负载,最终可能会因为承受不了高负载而导致宕机,网站不得不停止服务。


  这时,又想起了那句话:当一头牛拉不动车的时候,不要去寻找一头更强壮的牛,而是用两头牛来拉车。于是,我们可以采用服务器集群的技术来对Web服务器进行改进,增加N台Web服务器部署相同的Web应用构成Web服务器集群来对外提供服务,通过负载均衡设备或软件将海量的并发请求数平均地分摊到每台Web服务器,例如:假设某系统在促销活动期间同一时刻涌入了10万个请求,而服务器集群中有5台Web服务器同时提供服务,这时负载均衡设备就将这个10万请求通过某种算法较为均衡地分配给其中的Web服务器,平均下来每台服务器最多就只承担2万个请求。

  通过服务器集群,已经较好地解决了请求负载问题,这时新的问题又来了:由于Session默认是属于进程内(InProc)的,也就是说它是存储在Web服务器的内存里边的。当构建好集群之后,用户的Session会建立在负载均衡设备所分配的其中一台Web服务器里边。但是当用户下一次访问或者访问系统中的其他子系统(比如:我首先在百度百科进行登录了,然后访问百度贴吧),由于Session会话还存储在上一次提供服务的Web服务器里边,系统校验规则(现在这台Web服务器里边检测到没有该用户的Session)会造成用户的重复登录(比如:都是在百度的网页,它却让你登录好几次,你爽吗?很明显,不爽吧)。这时,就需要我们解决Web服务器集群的Session管理,下面我们就来看看如何进行Web服务器集群的Session管理。

1.2 应用服务器集群的Session管理

  我们现在来看看在集群环境中,Session管理的几种常见手段:

  ①Session复制:该方案简单易行,集群中的几台服务器之间同步Session对象,任何一台服务器宕机都不会导致Session对象的丢失,服务器也只需要从本机获取即可。但是,该方案只适合集群规模较小的情况下。当规模较大时,大量的Session复制操作会占用服务器和网络的大量资源,系统不堪重负。

Session复制

  ②Session绑定:利用负载均衡的源地址Hash算法,总是将源于同一IP地址的请求分发到同一台服务器上。这样的话,在整个会话期间,用户所有的请求都在同一台服务器上进行处理,即Session绑定在某台特定服务器上,保证Session总能在这台服务器上获取。(这种方案又叫做会话粘滞)。

Session绑定

  但是,这种方案不符合高可用的需求。因为一旦某台服务器宕机,那么该机器上得Session也就不复存在了,用户请求切换到其他机器后因为没有Session而无法完成业务处理。因此,很少有网站采用此方案进行Session管理。

  ③COOKIE记录Session:利用浏览器支持的COOKIE记录Session简单易行,可用性高,并且支持服务器的线性伸缩,因此,许多网站都或多或少地使用了COOKIE来记录Session。但是COOKIE记录Session有缺点:比如受COOKIE大小限制、每次请求响应都要传输COOKIE影响性能、用户关闭了COOKIE会造成访问不正常等。

COOKIE记录Session

  ④Session服务器:利用独立部署的Session服务器(集群)统一管理Session,应用服务器每次读写Session时,都访问Session服务器。这种方案实际上是将应用服务器的状态分离,分为无状态的应用服务器和有状态的Session服务器。

Session服务器


  从上面的几种方式来看,各有利弊,但Session服务器是最符合高可用需求的方案,也是企业中经常用到的方案。那么,对于有状态的Session服务器,一种较简单的方法是利用分布式缓存(如Memcached、Redis等,有关Redis的简单介绍可以阅读我的博文:NoSQL初探之人人都爱Redis)、数据库等,在这些产品的基础上进行封装,使其符合Session的存储和访问要求。综合上述介绍,我们今天就采用Memcached来构建我们的Session服务器,解决Web服务器集群的Session的共享访问。

PS:为什么要采用分布式缓存方案而不采用数据库来存储Session?这个就得要分析一下数据访问的性能瓶颈了,一般来说,磁盘IO读写的速度是最慢的,因为数据库数据其实是存储在文件中的,虽然目前大多数的数据库都采用了B+树结构,读取一条数据最多都还是需要4次的数据读写(三次磁盘访问获得数据索引及行ID,一次数据文件读操作,终于知道数据库操作多麻烦了)。而分布式缓存例如Memcached是以Key/Value这种简单的形式存储在服务器的内存里边的,内存的随机读写速度是完爆磁盘IO的,因此内网+内存的双内模式是比较完美的方案。

磁盘又分为两种类型:

  ①机械硬盘:通过马达驱动磁头臂,带动磁头到指定的磁盘位置访问数据。它能够实现快速顺序读写,慢速随机读写。

  ②固态硬盘(又称SSD):无机械装置,数据存储在可持久记忆的硅晶体上,因此可以像内存一样快速随机访问。

  在目前的网站应用中,大部分应用访问数据都是随机的,这种情况下SSD具有更好的性能表现,但是性价比有待提升(蛮贵的,么么嗒)。

二、Memcached实现Session的分布式存储

2.0 案例总体预览

  (1)模拟的登录案例场景

    假设我们有一个基于ASP.NET的信息系统,这个系统使用一个统一的系统登录页面进行用户登录,登陆后默认跳转到一个用户中心主页,并显示:欢迎您,{用户账号名称}。

    ①系统登录页面效果:

    ②用户主页效果:

  (2)模拟的技术体系选择

    ASP.Net MVC+EF Code First+MySQL+Memcached

2.1 初始准备工作

  (1)新建一个ASP.NET MVC4的空项目,视图引擎选择为“Razor”即可;

  (2)在项目中新建一个文件夹,取名为“Lib”,主要存放一些必要的DLL文件;

  (3)在项目中添加对这几个DLL的引用,注意这里引入EntityFramework.dll是为了支持后面的CodeFirst开发方式,EF版本必须在4.1及以上。PS:你也可以通过Package Manager来安装EntityFramework。到此,我们的准备工作就做好了,接下来就可以开始正式的工作了。

2.2 借助EF CodeFirst生成MySQL数据库

  首先,EF是一种ORM(Object-relational mapping)框架,它能把我们在编程时使用对象映射到底层的数据库结构。ORM框架负责把从数据库传回的记录集转换为对象,也可以依据对象当前所处的具体状态生成相应的SQL命令发给数据库,完成数据的存取工作(常见的数据存取操作可简称为CRUD:Create、Read、Update、Delete)。

  EF给数据库应用系统开发带来了更高的效率,使用它能更容易地写出易维护、易扩展的系统,而且性能虽然比不上ADO.NET,但也足够好,能满足大多数开发场景的需求。与ADO.NET不一样,EF的抽象层次较高:它把数据库映射为DbContext,把数据库中存取的数据直接映射为实体(Entity)对象,屏蔽了底层的数据库内部结构,无需直接使用下层数据存取引擎所提供的底层对象(比如ADO.NET所提供的DbConnection,DbCommands等)完成CRUD。

  EF支持三种开发模式:Code First、Database First和Model First。这里我们使用Code First模式,它能帮助我们实现快速开发迭代的目标。最后,EF不是本文的重点,如果你还不了解EF或者Code First,可以参阅金旭亮老师的《EF走马观花》系列文章,这里就不再赘述了。

  (1)在Models文件夹新建一个类,取名为“UserInfo”。它作为我们的实体类,映射到MySQL数据库中的UserInfo表(这里MySQL数据库中还未创建这样的数据表)

 View Code

  (2)再在Models文件夹新建一个类,取名为“MyDbContext”,使其继承于DbContext,作为EF操作的数据库上下文使用。需要注意的是:这里的name=MySqlDemo,MySqlDemo为数据库的名字。而这里this.Database.CreateIfNotExists()方法则用于判断MySqlDemo这个数据库是否已存在?如果不存在,那么就创建一个。

 View Code

  (3)在Web.config中新增一个数据库连接字符串,database设置为“MySqlDemo”,与上面的MyDbContext中的构造函数中的name保持一致。

 View Code

  至此,因为我们只用到UserInfo这一个表,所以现在我们有关数据库与EF的代码就到此结束。

2.3 自己封装Memcached帮助类对外提供服务接口

  (1)在Web.config中新增一个AppSetting,保存Memcached服务器的地址列表:

    
    

  (2)在Models中新建一个类,取名为“MemcachedHelper”,我们将其设置为静态类,所以方法全是静态的,我们无需实例化便可直接调用。可以看到,我们这里使用了静态构造函数来初始化全局静态对象,它不属于任何一个实例,所以这个构造函数只会被执行一次,而且是在创建此类的第一个实例或引用任何静态成员之前,由.NET自动调用。

 View Code

  这里我们没有对SockIOPool进行一列的配置,采用其默认的配置即可。但是要注意的是:SockIOPool的PoolName和MemcacheClient的PoolName必须保持一致。

2.4 用户登录时调用Memcached帮助类接口存储用户登录状态

  (1)在Controller中新建一个控制器,取名为“LogonController”。主要用来显示系统登陆页和进行用户验证的AJAX操作(将用户Session存入Memcached也在此操作中)。至于登录页面的HTML和JS脚本在此就不再赘述,请自行下载Demo文件查看。

 View Code

  现在来看下这个控制器的核心代码:

  ①Index这个Action主要用于显示登录视图,页面代码不再贴出来,只看看下面这个AJAX请求代码:借助JQuery AJAX向ValidateUserInfo这个Action提交用户名和密码,如果服务器端校验成功则返回一个JSON对象,浏览器端判断success属性是否为true,如果为true则跳转到系统主页。

 View Code

  ②ValidateUserInfo这个Action则是根据浏览器提交过来的AJAX请求,判断用户名和密码是否正确。这里用到了我们刚刚写的MyDbContext,首先对它进行实例化。(这一步非常重要,这时我们的MySQL数据库中还木有MySqlDemo这个数据库,当第一次实例化MyDbContext时,EF会帮我们在MySQL中创建MySqlDemo这个数据库,其本质其实就是帮我们生成一串:crate database mysqldemo;之类的SQL语句),然后使用Lambda表达式这种优美的语法去进行校验。如果你对Lambda表达式不熟悉,可以参阅MSDN的《Lambda表达式(C#编程指南)》一文来学习下。

  ③就是最核心的部分,我们采用Guid(保证作为Key的唯一性)作为SessionId写入浏览器端的COOKIE中,并以此作为Key存入Memcached分布式缓存中,还给它设置了默认失效时间(这里为20分钟)。之后,每次浏览器向服务器端提交请求时,在HTTP报文中都会附带上这个COOKIE,服务器端就可以通过这个COOKIE作为Key去Memcached服务器中查找Session对象。

PS:一般来说,Memcached的数据Key的命名是有讲究的,这里传智的老马推荐了一个命名规则:{命名空间}-{部门名称}-{项目名称}。我们这里就简单一点,使用Guid来作为Key,因为每次生成的Guid是唯一的。

2.5 封装BaseController解决Action触发前的校验规则

  (1)在以往的信息系统项目开发中,我们在系统里边会做一个全局的校验器,判断用户的每次操作请求是否具有相应的权限,这里我们主要是校验用户是否登录,以便我们在具体的模块中获取用户的Session对象。单机情况下,我们一般就存入本机进程内的Session,因此就主要判断Session中是否存在登录状态。这里我们使用Memcached来存储Session对象,那么我们就在每个Action执行前加一段规则:判断Memcached中是否有当前用户的登录状态,如果有,则继续执行Action。如果木有,那么对不起,请进行登录。

  (2)在WebForm中我们可以通过写一个BasePage,使其继承于Page,重写OnLoad事件,再让其他页面继承于BasePage,就可以对用户是否登录这个校验应用到所有继承了BasePage的Page类中。那么,在MVC模式中,请求对象不再是xxx.aspx页面类型,而是/ControllerName/ActionName的路由,因此我们需要寻找一种针对Action的全局过滤方法。额,在此我们不禁想到了一个高大上的英文:AoP(Aspect Oriented Programming),它是软件开发中一个较为热门的话题,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。因此,我们对Action增加全局校验规则就是将Action的校验方法与Action的业务处理相分离,使其耦合度降低,也提高了Action的校验方法的重用性,符合了AoP的思想。那么,扯了大半天,在ASP.NET MVC中到底如何实现呢?别担心,我们可以像写BasePage一样,写一个BaseController来进行处理,使其继承于Controller,然后将其他需要使用用户登录状态校验的Controller都继承于BaseController即可。

 View Code

  (3)现在来看看上面这段代码:

  ①从浏览器提交过来的HTTP请求报文中取得COOKIE(也就是SessionId),如果没有COOKIE那么肯定还没有登录,将进行重定向到登陆页面;

  ②如果有COOKIE那么就去Memcached服务器中查询是否有这个Key的数据内容,如果没有没有内容,那么还是重定向到登陆页面;如果有内容,则返回这个对象,由于返回的是Object类型,所以需要我们转换一下赋给全局的用户对象实例。

  ③获取到UserInfo对象之后,再次将其重新存储到Memcached中,这里其实是将Session延长失效时间,实现了一个Session的滑动时间机制的效果。

PS:因为我们在登录验证的Action里面给这个缓存设置的过期时间是一个绝对的时间,而非滑动过期时间。所谓绝对时间是指到了指定时间以后便会失效,而滑动时间是指在指定时间内无访问请求便失效。

2.6 测试运行用户登录与Session存储

  (1)到此,大部分的代码都已完毕。这里我们需要进行一个小测试,新建一个Controller,取名为“HomeController”,将Index页面用作登陆后的主页,显示Session对象中的UserName属性。代码很简单,只有两行:

        public ActionResult Index()
        { // 从Memcached中获取Session并传给View ViewBag.UserName = CurrentUser.UserName; return View();
        } 

  至于View,请参考Demo文件,此处就不在赘述了,就一堆HTML而已。

  (2)①开启虚拟机中的两台Windows Server服务器,确保Memcached服务都已成功开启。(如果你是装在本机的,直接开启服务即可)

      ②开启MySQL数据库服务,这里我的MySQL是装在本机的,所以只需要开启服务即可。

  (3)点击调试,开始运行IIS Express服务器,进行测试吧骚年(这里需要说明的是:你需要首先随便输入一个账号和密码进行数据库的创建,因为我们采用的是Code First的方式,然后再进入MySQL添加几行测试用户数据,这里我已经添加了几行,开始进行测试。)我在数据库里边添加了一行{账号:edisonchou,密码:123456},因此我输入账号和密码,点击登录,会出现一个友好的提示:“正在验证中,请稍候...”(如果你是第一次运行,那么需要创建数据库,这个时间会比较长,不要担心)。

  (4)等待系统验证完成后,便会跳转到系统主页,显示欢迎界面。可以看到,下图中的两处是根据Session的UserName来显示的。

  (5)现在我们手动修改URL,输入/Logon/Index,跳转到登陆页面。然后,再修改URL为/Home/Index,看看是否需要重新登录,由于我们已经将Session存入了Memcached,所以再次进入/Home/Index这个Action时会进行判断,通过浏览器端传过去的COOKIE去Memcached中取Session,如存在则继续执行。这里,我们之前已经登录了,所以不再需要重新登录。下面是再次跳转/Home/Index的HTTP报文信息,可以看出登录之后已经写入了COOKIE,之后的请求都会带上COOKIE传递到服务器端,服务器端也就可以通过COOKIE作为Key获取Session对象。

  (6)新开一个浏览器,再使用另一个用户数据进行登录:

  (7)登陆后,查看HTTP报文,获取COOKIE中的sessionId:

  (8)根据两个用户的sessionId作为Key,在Memcached服务器中查看存储情况:经查看,两个Session都存储到了192.168.80.11这台服务器里边。为何没有平均分配,这是因为算法是根据计算Hash值来的,刚好这两个sessionId计算后的Hash值都是要分配到192.168.80.11这台服务器里边,所以就都存到了这里边。(需要注意的是:这里仅仅是在我的机器上跑的测试结果,网友们的测试结果可能会跟我的不一样,这是正常的)

  至此,我们的小测试到此结束,我们的案例也到此为止。

三、学习小结

  本篇我首先通过花大力气对Session服务器场景的简介引出分布式缓存对于构建Session服务器的可行性,然后使用ASP.NET MVC+EF Code First+MySQL+Memcached来模拟了一个信息系统登录场景,其中借助了Memcached来存储Session对象,并通过Action的过滤器来实现对用户登录状态的校验。最后通过一个小测试,来验证Memcached是否存储了我们的Session对象。

  当然,此案例只是一个玩具级别的Demo,还需要经过很多次性能测试和优化才能应用到实际开发中。但是,我们可以从中了解到一些有益的思想,理解这些思想对于我们的实际开发能力有所裨益的。最后,希望我的文章能够给更多的像我一样的菜鸟开发者带来一点光明,如果你觉得有用,那就麻烦点一下“推荐”吧,谢谢!

参考文献

    (1)李智慧,《大型网站技术架构-核心原理与案例分析》,http://item.jd.com/11322972.html

  (2)马伦,《Memcached公开课》,http://bbs.itcast.cn/thread-14836-1-1.html

  (3)旋风,《Memcached分布式缓存替代Session解决方案》,http://www.cnblogs.com/xuanfeng/archive/2009/06/04/1494735.html

  (4)金旭亮,《Entity Framework走马观花》,http://blog.csdn.net/bitfan/article/details/12779517

  (5)MSDN,《静态类和静态成员(C#编程指南)》,http://msdn.microsoft.com/zh-cn/library/79b3xss3.aspx

  (6)痞子一毛,《Memcached+COOKIE替代Session解决方案(MVC版)》,http://www.cnblogs.com/piziyimao/archive/2013/01/29/2882236.html

  (7)瞌睡龙飞,《.NET中Cache的用法》,http://blog.csdn.net/ttotcs/article/details/7476234

  (8)lulu Studio,《ASP.NET MVC:Action过滤器(Filtering)》,http://www.cnblogs.com/QLeelulu/archive/2008/03/21/1117092.html

附件下载

  (1)MemcachedMvcDemo:http://pan.baidu.com/s/1gd3RvKB


推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 本文介绍了Hive常用命令及其用途,包括列出数据表、显示表字段信息、进入数据库、执行select操作、导出数据到csv文件等。同时还涉及了在AndroidManifest.xml中获取meta-data的value值的方法。 ... [详细]
  • 大厂首发!思源笔记docker
    JVMRedisJVM面试内存模型以及分区,需要详细到每个区放什么?GC的两种判定方法GC的三种收集方法:标记清除、标记整理、复制算法的 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文介绍了在Ubuntu下制作deb安装包及离线安装包的方法,通过备份/var/cache/apt/archives文件夹中的安装包,并建立包列表及依赖信息文件,添加本地源,更新源列表,可以在没有网络的情况下更新系统。同时提供了命令示例和资源下载链接。 ... [详细]
  • 本文讨论了读书的目的以及学习算法的重要性,并介绍了两个算法:除法速算和约瑟夫环的数学算法。同时,通过具体的例子和推理,解释了为什么x=x+k序列中的第一个人的位置为k,以及序列2和序列3的关系。通过学习算法,可以提高思维能力和解决问题的能力。 ... [详细]
  • REVERT权限切换的操作步骤和注意事项
    本文介绍了在SQL Server中进行REVERT权限切换的操作步骤和注意事项。首先登录到SQL Server,其中包括一个具有很小权限的普通用户和一个系统管理员角色中的成员。然后通过添加Windows登录到SQL Server,并将其添加到AdventureWorks数据库中的用户列表中。最后通过REVERT命令切换权限。在操作过程中需要注意的是,确保登录名和数据库名的正确性,并遵循安全措施,以防止权限泄露和数据损坏。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 面试经验分享:华为面试四轮电话面试、一轮笔试、一轮主管视频面试、一轮hr视频面试
    最近有朋友去华为面试,面试经历包括四轮电话面试、一轮笔试、一轮主管视频面试、一轮hr视频面试。80%的人都在第一轮电话面试中失败,因为缺乏基础知识。面试问题涉及 ... [详细]
  • 本文讨论了在使用Git进行版本控制时,如何提供类似CVS中自动增加版本号的功能。作者介绍了Git中的其他版本表示方式,如git describe命令,并提供了使用这些表示方式来确定文件更新情况的示例。此外,文章还介绍了启用$Id:$功能的方法,并讨论了一些开发者在使用Git时的需求和使用场景。 ... [详细]
author-avatar
手机用户2602936797
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有