(十一)nodejs循序渐进-高性能游戏服务器框架pomelo之启动流程和组件

游戏启动过程

启动入口

在使用pomelo进行游戏开发时,工程目录下的app.js是整个游戏服务器的启动运行入口。app.js中创建项目,进行默认配置并启动服务器的代码如下:

var pomelo = require('pomelo');
var app = pomelo.createApp();
app.set('name', 'nameofproject');
app.start();

从上面的代码可以看出,用户首先需要在项目中引入pomelo,然后创建application的实例app,接着完成一些基本的应用配置,最后应用就可以启动了。当app.js运行起来后,pomelo会根据游戏的配置启动不同的相关组件及服务器。

服务器与组件

服务器启动流程的主要工作就是逐一启动app.load注册的组件。组件是连接pomelo框架和当前服务器所依赖的服务之间的桥梁。不同的服务器可以选择加载不同的组件。Pomelo提供了一些系统默认的组件,主要包括:handler, filter, master, monitor, proxy, remote, server, sync, connection。开发者也可以根据需要,开发自己的组件,并加载到服务器进程中。

组件同时是具有生命周期的,其生命周期可以包括start, after start, stop等。在组件中可以实现这些方法,应用服务器会在不同的运行阶段执行组件不同生命周期的方法。

启动流程详述

应用创建及启动

所有服务器的启动都是从运行app.js开始。每一个服务器的启动都首先创建一个全局唯一的application对象,该对象中挂载了所在服务器的所有信息,包括服务器物理信息、服务器逻辑信息、以及pomelo的组件信息等。同时,该对象还提供应用管理和配置等基本方法。 在app.js中调用app.start()方法后,application对象首先会通过loadDefaultComponents方法加载默认的组件。

组件加载

在加载组件时,系统会根据application对象中服务器的信息,针对不同的服务器加载不同的组件,从而使得不同服务器进程对外提供不同个服务。对于master服务器,主要加载的组件是master组件。Master组件主要负责根据根据servers.json文件中的配置信息和启动参数去启动其他服务器。对于其它服务器默认加载proxy、channel、sync、backendSession和server组件,特定的服务器还需要加载特定的组件,例如前端服务器会加载统计客户端连接数量的connection组件。具体组件的说明如下:

  • master: master组件主要负责启动master服务器。
  • monitor: monitor组件主要负责启动各个服务器的monitor服务,该服务负责收集服务器的信息并定期向master进行消息推送,保持master与各个服务器的心跳连接。
  • proxy: proxy组件主要负责生成服务器rpc客户端,由于系统中存在多个服务器进程,不同服务器进程之间相互通信需要通过rpc调用(master服务器除外)。
  • remote: remote组件主要负责加载后端服务器的服务并生成服务器rpc服务端。
  • server:server组件主要负责启动所有服务器的用户请求处理服务。
  • connector: connector组件主要负责启动前端服务器的session服务和接收用户请求。
  • sync: sync组件主要负责启动数据同步模块并对外提供数据同步功能。
  • connection: connection组件主要负责启动用户连接信息的统计服务。
  • channel: channel组件主要负责启动channelService服务,该服务主要提供channel相关的功能包括创建channel,通过channel进行消息推送等。
  • session: session组件主要负责启动sessionService服务,该服务主要用来对前端服务器的用户session进行统一管理。
  • backendSession: backendSession组件主要负责启动backendSession服务,该服务主要负责维护服务器本地session并与前端服务器进行交互。
  • dictionary: dictionary组件主要负责生成handler的字典。
  • protobuf: protobuf组件主要负责解析服务端和客户端的proto buffer的定义,从而对客户端和服务端的通信内容进行压缩。

组件概述

我们知道,pomelo的应用程序执行的全部过程,就是对其相应组件的生命周期的管理,实际的所有逻辑功能均由pomelo组件提供。pomelo内建提供了十多个组件,这些组件适用于不同的服务器,提供不同的功能。有些组件提供的功能比较复杂,有些则比较简单。下面我们将以提供的功能为主线来阐述pomelo提供的内建组件。

master组件

master组件仅仅由master服务器加载,它主要的功能包括启动所有的应用服务器、管理和监控所有的应用服务器和接受管理客户端的请求与响应。

master服务器总是率先启动,master组件在其start调用的最后才会调用Starter.start,Starter.start才会启动所有的应用服务器,因此Master组件总是最先start。在Master组件的start调用中,会完成以下几步:

  • 加载注册Module到MasterConsoleService,Module的导出方式有两种,可以导出工厂函数,也可以导出对象,如果导出工厂函数的话,其签名应该是 FacFunc(opts, ConsoleServicee),其中opts是用户调用app.registerAdmin的时候传入的,ConsoleService则是具体的加载注册Module的MasterConsoleService。

  • 在加载注册完所有的Module后,会开启MasterAgent对端口的监听,此时,master就已经可以接收来自monitor和client的request和notify了。

  • 开启监听后,MasterConsoleService会enable所有的module,这步操作主要是看看有没有module配置了周期性地拉去monitor信息,也就是module的配置中有type选项和interval选项,且type的值为'pull',interval指定了周期,则认为其配置了周期性监控操作,此时会完成周期性事件的调度,使得master可以周期性地获取监控信息。

  • 最后如果有Module定义了start回调,将会在这里调用,一般在start回调里会做一些初始化信息。 经历了这些步骤后,master完成启动。

在master组件的start方法里,会根据用户提供的服务器配置信息,启动用户配置的所有的具体应用服务器。

当master组件start结束后,他将开启一个socket监听端口,接受应用服务器和监控客户端的连接和注册,收集应用服务器上报的监控信息,给应用服务器推送一些消息,并对管理客户端发出的管理请求给予响应。管理客户端如pomelo-cli可能发出的请求包括查看某个服务器进程状态,增加一个服务器,停掉一个服务器等。以增加一个服务器为例,当管理客户端发出增加服务器请求时,会提供相应的服务器参数,如服务器类型,主机ip,开启的端口等。此时,master组件接受后,会启动相应的服务器,并将新增加的服务器信息广播通知给其他已经启动的服务器。

master组件无配置项。

monitor组件

monitor组件由所有的包括master服务器在内的服务器都会加载,它的主要功能就是与master建立连接进行通信,从而对整个应用服务器群进行管理和监控。master服务器本身也会加载monitor服务器,因为master服务器也会收集其本身自己的监控信息。

由于应用服务器是在master组件启动后期才创建,因此monitor总是后于master启动。monitor的启动过程与master类似,唯一不同的就是,monitor会发起到master的连接,而不是监听接口。monitor中同样也会使用与master完全相同的方式,加载注册Module,如果有Module配置了周期性地推送监控数据到master的话,即其配置type的值为'push',这里也会调度对应的事件,使得能够周期地推送数据。最后如果有Module定义了start的话,则会回调start。Monitor的启动过程与master基本一致。

monitor会通过master接受一些命令,比如关闭整个服务器等。对于一些周期性监控的信息,pomelo提供了两种收集方式,即pull方式和push方式。pull方式要求master周期地去与monitor通信,拉取相应的监控信息;push方式,则是由monitor周期地主动地向master报告其监控信息。

monitor组件无配置项。

filter

pomelo内建了常见的一些filter,用户可以通过如下的方式启用:

app.filter(pomelo.filters.<filterName>(<args>));

下面介绍一下这几个fitler:

serial

这个filter是用来对用户请求做串行化的,可以使得用户的请求只有在第一个请求被处理完后,才会处理第二个请求。serial中使用了一个taskManager,当用户请求到来时,在beforeFilter中,将用户的请求放到taskManager中,taskManager中维护着一个task队列。在对应的afterFilter中,如果taskManager还有未处理的请求,将会处理其请求,即在一个请求的afterFilter里启动在taskManager中还没处理的下一个请求,这样就实现了请求的序列化。

timeout

这个filter是用来对服务端处理超时进行警告的,在beforeFilter中会启动一个定时器,在afterFilter中清除。如果在其定时器时间内,afterFilter被调用,定时器将会被清除,因此不会出现超时警告。如果定时器超时时,afterFilter还没有执行到,则会引发超时警告,并记录日志。默认的处理超时是3秒,可以在加载timeout的时候作为参数传入。

time

这个filter使用来记录服务器处理时间的,在beforeFilter中会记录一下当前的时间戳,在afterFilter中再次获取当前的时间戳,然后两个时间戳相减,得到整个处理时间,然后记录日志。

toobusy

这个filter中,一旦检测到node.js中事件循环的请求等待队列过长,超过一个阀值时,就会触发toobusy。一旦触发了toobusy,那么toobusy的filter中将终止此请求处理链,并在next调用中,传递错误参数。

connector组件

connector组件是一个重量级的组件,它会依赖于session组件,server组件,pushScheduler组件和connection组件。connector组件仅仅被前端服务器加载,它主要用来管理客户端的连接。connector组件会加载底层的connector,创建端口监听,绑定事件响应。当有客户端连接请求时,connector组件会请求session组件,获得当前连接的session,如果session组件中没有相应的session的话,session组件会为这个新连接创建新的session,并维护相应的连接;然后connector组件还会向connection组件上报连接信息,供统计使用;最后,将拿到的session以及客户端的请求,一起抛给server组件,由server组件进行请求处理。当server组件处理完请求后,又会通过connector组件将响应返回给客户端。在返回响应给客户端的时候,connector组件做了一个缓存选择,这个缓存实现依赖于pushScheduler组件,也就是说connector组件并不是直接将响应发给客户端,而是将响应给pushScheduler组件。pushScheduler组件根据相应调度策略,可能不缓存直接通过session组件维护的连接,将响应发出去,也可能进行缓存,并按时flush。这是可以配置的。

connector组件支持如下配置项:

  • connector: 底层使用的通信connector,不配置的话,会默认使用sioconnector;
  • useProtobuf: 目前仅仅支持connector配置使用hybridconnector的情况,配置其为true,将开启消息的protobuf功能;
  • useDict: 目前仅仅支持connector配置使用hybridconnector的情况,配置其为true时,将会开启基于字典的路由消息压缩;
  • useCrypto: 目前仅仅支持connector配置为hybridconnector的情况,配置其为true时,将会启用通信时的数字签名;
  • encode/decode: 消息的编码解码方式,如果不配置的话,将会默认使用connector配置中,底层connector提供的相应的编码解码函数。
  • transports:这个配置选项是用于sioconnector的,因为socket.io的通信方式可能会有多种,如websocket,xhr-polling等等。通过这个配置选项可以选择需要的方式。

配置connector组件,通过调用如下方式进行:

app.set('connectorConfig', opts);

session组件

session组件跟connector相关,也是仅仅被前端服务器加载,为sessionService提供一个组件包装, 加载session组件后,会在app的上下文中增加sessionService,可以通过app.get('sessionService')获取。它主要用来维护客户端的连接信息,以及生成session并维护session。如果与经典TCP进行类比的话,那么session中维护的连接就可以粗略地认为就是TCP服务器端accept返回的socket句柄。一个连接与一个session对应,同时session组件还维护具体登录用户与session的绑定信息。一个用户可以有多个客户端登录,对应于多个session。当需要给客户端推送消息或者给客户端返回响应的话,必须通过session组件拿到具体的客户端连接来进行。

session组件支持如下配置项:

  • singleSession: 如果这个配置项配置为true的话,那么将将不允许一个用户同时绑定到多个session,在绑定用户一次后,后面的绑定将会失败。

配置session组件,通过调用如下方式进行:

app.set('sessionConfig', opts);

connection组件

connection组件是一个功能相对简单的组件,也是仅仅被前端服务器加载,为connectionService提供一个组件包装,他主要进行连接信息的统计,connector组件接收到客户端连接请求以及有客户端离线时,以及用户登录下线等等情况,都会向其汇报。

connection组件无配置项。

server组件

server组件也是一个功能比较复杂的组件,它被除master外的服务器加载。server组件会加载并维护自身的Filter信息和Handler信息。server组件会从connector组件的回调里获得到相应的客户端请求或者通知,然后会使用自己的before filters对其消息进行过滤,再次调用自己的相应Handler进行请求的逻辑处理,然后将响应通过回调的方式发给connector处理。最后调用after filters进行一些清理处理。

当然,如果客户请求的服务本来就是前端服务器提供的话,会是上面的那种处理流程。如果客户请求的服务是后端服务器提供的服务的话,则将不是上面的那种处理流程,此时会出现sys rpc调用。前面那种前端服务器自己处理的情况具体调用为doHandle,而发起rpc调用的情况则为doForward。这两种处理流程的不同点是,对于自身的请求,调用自己的filter-handler链进行处理,对于不是前端服务器自己提供的服务,则是发起一个sys rpc,然后将rpc调用的结果作为响应,发给connector进行处理。关于这个rpc调用则是pomelo内建的msgRemote实现的。

对于后端服务器来说,其客户请求不是直接来源于真实的客户端,而是来源于前端服务器对其发起的sys rpc调用,这个rpc调用的实现就是pomelo内建的msgRemote,在msgRemote的实现里,会将来自前端服务器的sys rpc调用请求派发给后端服务器的server组件,然后后端服务器会启用filter-handler链对其进行处理,最后通过rpc调用的返回将具体的响应返回给前端服务器。

在前端服务器将客户端请求向后端服务器分派时,由于同类型的后端服务器往往有很多,因此需要一个路由策略router,一般情况下用户通过Application.route调用为后端服务器配置router。

server组件无配置项。

pushScheduler组件

pushScheduler组件也是一个功能较为简单的组件,它仅仅被前端服务器加载,与connector组件的关系密切。当connector组件收到server组件的对客户端请求的响应后,connector并不直接将此响应返回给客户端,而是将这个给客户端发送响应的操作调度给scheduler组件。pushScheduler组件完成最后通过session组件拿到具体的客户端连接并将请求的响应发送给客户端的任务。因此,通过pushScheduler组件可以对发给用户的响应进行缓冲,从而提高通信效率。pomelo实现了两种调度策略,一种是不进行任何缓冲,直接将响应发送给客户端,一种是进行缓冲,并定时地将已缓冲的响应发送给对应的客户端。

pushScheduler配置项:

  • scheduler: scheduler组件的具体调度策略配置,默认的是直接将响应发给客户端,同时pomelo还提供了有缓冲并且定时刷新的调度策略。用户也可以自定义自己的调度策略。

配置pushScheduler组件,通过调用如下:

app.set('pushSchedulerConfig', opts);

如果要启用使用缓冲的scheduler的话,可以在app.js中增加:

app.set('pushSchedulerConfig', {scheduler: pomelo.pushSchedulers.buffer, flushInterval: 20});

flushInterval是刷新周期,默认为20毫秒。

proxy组件

proxy组件是一个重量级的组件,它被除master外的所有服务器加载。proxy组件会扫描具体应用服务器的目录,抽取其中的remote部分,由于javascript语言的动态性,可以很轻易地获得到remote中的关于远程调用的元信息,生成stub,并将这些调用都挂到app.rpc下面,当用户发起rpc调用时,proxy组件会查看其扫描到的stub信息,以此决定此远程调用是否合法。同时,proxy又会创建一个RpcClient,当发起远程调用时,负责与远端的remote进行通信,并得到远程调用的结果供调用者使用。当进行远程调用时,由于同类型的远程服务器可能有多个,所以这里同样需要配置相应的router。

proxy的配置项:

  • cacheMsg, 配置cacheMsg为true的话,将开启rpc调用时的对消息的缓冲,而不是直接一旦有rpc请求就发出。
  • interval, 与配置参数cacheMsg配合使用,设置flush缓存的周期
  • mailBoxFactory, rpc底层实现需要的,用户可以定义自己的mailBoxFactory,我们将在rpc原理里面详述。

另外,可以开启rpc的调用日志,通过如下的调用:

app.enable('rpcDebugLog');

配置proxy使用:

app.set('proxyConfig', opts);

remote组件

remote组件是与proxy组件对等的组件,它用来提供rpc调用服务。rpc组件完成对当前服务器的remote的加载,并开启监听端口,等待rpc客户端的连接及相应的rpc调用。当接收到具体的调用请求时,会根据调用请求中描述的调用请求信息,调用相应的remote中的相应方法。然后再将具体的处理结果返回给rpc客户端。rpc服务端还支持对调用请求的filter,也就是说跟server组件处理客户端请求一样,rpc服务端处理具体请求时也会使用filter-remote链进行处理。

remote组件配置项:

  • cacheMsg, 与proxy组件的含义相同
  • interval, 与proxy组件的含义相同
  • acceptorFactory, rpc底层实现需要的,可以认为跟proxy配置中的mailBoxFactory是对等的,我们将在rpc原理里面详述。

跟proxy组件一样,用户可以开启rpcDebugLog来得到所有的rpc调用过程的日志。 配置remote组件使用:

app.set('remoteConfig', opts);

dictionary组件

dictionary组件是一个可选组件,不会被默认加载,只有当connector组件的配置中开启了useDict的时候,此组件才会被加载。此组件会遍历所有handler的route字符串,还会从config/dictionary.json中读取客户端的route字符串,然后对这些字符串进行编码,给予每一个路由赋予一个唯一的小整数,实现route信息压缩,当客户端与前端服务器通信时需要路由信息时,将不会再使用很长的那个字符串,而仅仅使用一个小整数。

dictionary的配置项:

  • dict, 客户端路由字符串文件的位置,默认使用的是config/dictionary.json 配置dictionary组件使用:

    app.set('dictionaryConfig', opts);

protobuf组件

protobuf组件也是一个可选组件,不会被默认加载,只有当connector组件的配置中开启了useProtobuf的时候,此组件才会被加载。此组件会加载对应的proto文件,并完成消息的基于protobuf的编解码。默认的proto文件的配置信息在config/serverProtos.json和config/clientProtos.json中。具体会在详细介绍pomelo-protobuf中详细介绍。

protobuf组件无配置项。

channel组件

channel组件维护channel信息,可以被除了master之外的服务器加载。channel组件可以看作是channelService的组件包装,加载该组件后,会在app上下文中加入channelService,可以通过app.get('channelService')获取。可以认为一个channel就是一个用户的集合,每一个用户大致对应于前端服务器中的一个session,用户可以通过channel组件向一个channel里面的所有用户推送消息。当然,由于后端服务器并不与客户端直接相连,故后端服务器会发起一个sys rpc来表示向客户端推送消息,接受这个远程调用的是pomelo已经实现的ChannelRemote。

channel组件的配置项:

  • broadcastFilter, broadcast的过滤函数。会在执行channel的broadcast的时候,在前端服务器上,在消息发送给每个session之前,进行一个过滤。其函数签名为

    broadcastFilter(session, msg, filterParam)

其中filterParam参数由在channelService的broadcast调用时传入,如下:

channelService.broadcast(type, route, {filterParam: param}, cb);

可以通过如下方式对Channel组件进行配置:

app.set('channelConfig', opts)

backendSession组件

BackendSession组件可以看作是BackendSessionService的组件包装,加载该组件后,会在app的上下文中加入backendSessionService,可以通过app.get('backendSessionService')调用获取。可以被除了master之外的服务器加载。它主要为后端服务器提供BackendSession信息,并通过远程过程调用完成一些比如对原始session绑定uid等操作。

backendSession组件无配置项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444537.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(十二)nodejs循序渐进-高性能游戏服务器框架pomelo之创建一个游戏聊天服务器

上个章节我们简单介绍了下pomelo的安装和目录结构&#xff0c;有读者可能觉得有点吃不消&#xff0c;为什么不再深入讲一讲目录结构和里边的库&#xff0c;这里我就不费口舌了&#xff0c;大家可以去官网参考文档说明&#xff0c;本文只告诉大家如何利用这个框架来开发自己的东…

看这玩意复习你还会挂科?《软件工程篇》

软件工程&#xff1a;是指导软件开发和维护的一门工程学科 三要素方法/工具/开发过程 价值&#xff1a;促进项目成功 现代产品开发三原则&#xff1a;功用性、可行性、称许性 软件过程是软件工程的核心组成部分。 迭代 &#xff1a;反复求精 增量&#xff1a;逐块建造 需…

C++:02---命名空间

一、概念: ①类似于仓库,空间内存储代码,需要用到时调用②也为防止名字冲突提供了更加可控的机制二、命名空间的定义 定义的基本格式如下:namespace 命名空间名 { //一系列声明与定义 };三、命名空间的注意事项 命名空间定义时最后的分号可有可无只要出现在全局作用域中的…

看这玩意复习你还会挂科?《软件工程2篇》

第一章&#xff1a; 软件工程定义&#xff1a; 1968年10月&#xff0c;Fritz Bauer 首次提出了“软件工程”的概念&#xff0c;并将“软件工程”定义为&#xff1a;为了经济地获得能够在实际机器上有效运行的可靠软件&#xff0c;而建立并使用的一系列工程化原则。 1993年IE…

C++:05---命名空间

一、概念: ①类似于仓库,空间内存储代码,需要用到时调用②也为防止名字冲突提供了更加可控的机制二、命名空间的定义 定义的基本格式如下:namespace 命名空间名 { //一系列声明与定义 };三、命名空间的注意事项 命名空间定义时最后的分号可有可无只要出现在全局作用域中的…

C++:04---内联函数

1.概念: 内联类似于宏定义,当程序执行到内联函数时,相当于复制了一份函数代码。牺牲代码空间,赢得了时间 内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求 2.关键字:inline 声明时写了inline,定义时可省略。建议声明和定义都加上inlineinline int add(int…

leetcode86. 分隔链表

给定一个链表和一个特定值 x&#xff0c;对链表进行分隔&#xff0c;使得所有小于 x 的节点都在大于或等于 x 的节点之前。 你应当保留两个分区中每个节点的初始相对位置。 示例: 输入: head 1->4->3->2->5->2, x 3 输出: 1->2->2->4->3->5…

(十三)nodejs循序渐进-高性能游戏服务器框架pomelo之扩展聊天服务器为机器人自动聊天

聊天服务器扩展 大家在上一篇文章里相信已经学会了pomelo框架的基本用法了&#xff0c;那么我们在上一篇文章的代码基础上继续扩展&#xff0c;丰富系统&#xff0c;另外也熟悉下他的更多的用法&#xff0c;这一节我将扩展它&#xff1a;增加一个机器人自动聊天的功能。 目的…

C++:09---类静态成员、类常量成员

一、类静态成员(static) 先介绍一下什么是静态变量、静态函数 静态局部变量:存在域(全局数据区),作用域(块作用域)静态全局变量:存在域(全局数据区),作用域(整个文件)静态函数:存在域(全局数据区),作用域(整个文件)static int a=10;//全局静态变量 static vo…

C++:08---成员变量初始化方式

成员变量初始化有三种方式: 在构造函数体内赋值初始化在自定义的公有函数体中赋值初始化(一般用于成员变量的初始化)在构造函数的成员初始化列表初始化一、构造函数体内初始化 说明:在构造函数体内的初始化方式,本质是是为成员变量赋值,而不是真正意义上的初始化,这点要…

leetcode1290. 二进制链表转整数 刷新认知,最简单算法题

给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。 请你返回该链表所表示数字的 十进制值 。 示例 1&#xff1a; 输入&#xff1a;head [1,0,1] 输出&#xff1a;5 解释&#xff1a;二进制数 (101) 转化为十进…

Redis:02---安装Redis(Linux+Windows+Docker)

Linux安装&#xff1a;一、安装方式1&#xff08;下载源码编译安装&#xff09;第一步&#xff1a;从下面的网址中下载Redis最新稳定版本的源代码sudo wget http://download.redis.io/redis-stable.tar.gz第二步&#xff1a;下载完之后解压&#xff0c;建立一个软链接指向于red…

C++:10---再议拷贝构造函数

一、概念 使用一个已经存在的对象,去构造(初始化)另一个对象二、格式 参数加上const&,因为拷贝构造函数在几种情况下都会被隐式地使用,因此拷贝构造函数不应该是explict的const:防止函数内部修改值&:防止无限循环拷贝类名(类名 const& 参数名) { 函数体 }三、…

人的思维谬误与心理学效应

启发法 用一个容易的问题代替难以回答的真正问题。这个容易的问题的答案就是对真正问题的启发&#xff0c;但启发经常和真正的答案差得很远&#xff0c;而人却往往把启发当成了真正问题的答案。 接下来介绍和启发法相关的心理效应和谬误。每一个谬误都会注明真正的问题是什么…

C++:07---this指针

一、this指针介绍 概念:this指针是成员函数的一个隐式参数,在类中本质上就是对象的指针(常量指针)特点:在成员函数中可通过this指针区别成员变量与形参变量this可以显式调用示例代码:class Cperson { private: int age; float height; public: void InitPerson(int age,flo…

Redis :01---Redis简介和安装

一、Redis简介 Redis官网&#xff1a;https://redis.io/ Redis是一种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库 与很多键值对数据库不同的是&#xff0c;Redis中的值可以是由string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、 list&…

215. 数组中的第K个最大元素 BFPRT最牛解法

在未排序的数组中找到第 k 个最大的元素。请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 示例 1: 输入: [3,2,1,5,6,4] 和 k 2 输出: 5 示例 2: 输入: [3,2,3,1,2,4,5,5,6] 和 k 4 输出: 4 说明: 你可以假设 k 总是…

C++: 06---构造函数析构函数

拷贝构造函数: 用一个已经存在的对象来生成一个相同类型的新对象。(浅拷贝)默认的拷贝构造函数: 如果自定义了拷贝构造函数,编译器就不在生成默认的拷贝构造函数。 如果没有自定义拷贝构造函数,但在代码中用到了拷贝构造函数,编译器会生成默认…

leetcode371. 两整数之和 不用+号做加法

不使用运算符 和 - &#xff0c;计算两整数 ​​​​​​​a 、b ​​​​​​​之和。 示例 1: 输入: a 1, b 2 输出: 3 示例 2: 输入: a -2, b 3 输出: 1 思路&#xff1a;模拟加法器 二进制不考虑进位&#xff1a;000&#xff0c;010&#xff0c;110&#xff0c;是…

C++:05---class和struct

C++被称为“C with class”,可见在C++中class是多么重要,与class类似的一个结构就是struct了,struct最早是在C语言中出现的,在C++中对struct的功能也进行了扩展。 class : public(公有):在类内外、派生类中都可被访问protected(保护):希望与派生类共享但是不想被公共…