(十)nodejs循序渐进-高性能游戏服务器框架pomelo之介绍和安装篇

目录

Pomelo

安装Pomelo

创建demoserver项目

pomelo命令

项目结构说明

pomelo框架

架构

服务器实现

客户端请求与响应、广播的抽象介绍


Pomelo

pomelo是一个快速、可扩展、Node.js分布式游戏服务器框架,对游戏服务器开发感兴趣的同学可以关注关注。

之前做页游,端游和手游一直都是用的C++,或者C++ + lua,C++ + golang的方式来开发,说实话,C++用起来得心用手,有多少坑自己心里有点逼数,用的大都是腾讯系的框架库,要么是盛大系的框架库,底层比较稳定,即使如此,但一旦有个项目要立项,之前代码不可换皮的情况下,你发现用C++开发还真的是笨重,因为你可能又得重新编一大堆的库,或许编完就得大半天功夫,有些工具类库可能还得自己去写。

后来在2018年接手国外的一个游戏项目,老外借助gameSparks平台开发,用的nodejs,这个时候我才关注nodejs,到现在已经2年了我不算是nodejs的开发老人,但也算是用nodejs开发了不少游戏项目,那么这里我给大家分享一个比较热门,活跃的服务器框架pomelo。

用它的主要好处:

1. 入门简单,有比较丰富的文档和示例(虽然现在看版本也比较老了,但是入门没什么问题)

2.分布式多进程且扩展简单(单进程多线程,每个服务器都是一个Node进程,通过配置文件就可以管理集群)

3.可以不去关注底层和网络相关逻辑,聚焦业务逻辑的处理,对于有Web服务器开发经验却没有游戏服务器开发经验来说还是比较友好的

4.提供了很多工具和客户端支持(像IOS、Android & Java、Javascript、C、Cocos2d-x、U3D等)

 

安装Pomelo

安装要求 

 不管是windows还是linux 我更喜欢用源码安装,毕竟源码时最新的,而且我也可以选择版本来更新。

比如用命令简单傻瓜操作:

npm install pomelo -g

如果用源码安装:

git clone https://github.com/NetEase/pomelo.git
cd pomelo
npm install -g

安装完之后看到我的版本信息:

说明:Pomelo安装可能出现各种失败

    1. 回头去检查一下,Python是否2.5 < version < 3.0和VC++编辑器是否有问题

    2.如果以前全局安装过Pomelo,最好删除掉 “C:\Users\当前用户\AppData\Roaming\npm\node_modules”目录下Pomelo文件夹和“C:\Users\当前用户\AppData\Roaming\npm-cache”目录下Pomelo开头的文件夹

    3.如果并不报错,npm卡住不动,多数是网络原因,重复多安几次;或者打开FQ工具试试;也可以用淘宝镜像 cnpm 安装.

创建demoserver项目

$ pomelo init ./demoserver

或者使用命令

$ mkdir demoserver
$ cd demoserver
$ pomelo init

在初始化项目过程中需要你选择网络连接协议:

Please select underly connector, 1 for websocket(native socket), 2 for socket.io, 3 for wss, 4 for socket.io(wss), 5 for udp, 6 for mqtt

除了5 for udp,其它都是长连接,我们接下来选择1,至于这些协议之间有什么区别,请各位自行查找资料,这里不赘述了。

如果你不小心选择网络协议错了,不要紧,你仍然有机会修改app.js的配置 :

如果你需要替换成wss,那么你只需要参考app.js.wss里的配置,修改里边对应的位置:

项目初始化成功后转到项目根目录,执行安装项目执行 npm-install.bat 依赖项 (其它平台执行npm-install.sh),接下来我有必要说一下pomelo的命令,因为你在目前或者后期会经常和一些命令打交道。

pomelo命令

命令行工具pomelo是Pomelo框架提供的一个小工具,该工具能够帮助开发者更便捷、更有效率地进行应用开发。该工具包括的命令支持绝大多数的应用开发操作,包括创建初始项目、启动应用、停止应用、关闭应用等。

init
init: 创建一个新项目,该项目中包含创建pomelo应用的基本文件及pomelo应用的简单示例。支持相对路径和绝对路径。默认情况下为当前路径,项目名称为当前文件夹名称。

pomelo init  projectname

在创建新项目时,需要选择新项目使用的与客户端通信时使用的connector,1代表Websocket(native socket),2代表 socket.io。

start
start: 启动应用及服务器。

pomelo start [-e,–env ] [-d,–directory ] [-D,–daemon] 命令格式

其中,-e 用来选择启动时使用的env,如production,development,stress-test等; -d 用来指定项目目录; -D 用来开启daemon模式启动,如果开启了daemon,那么进程将转入后台运行, 所有的日志将不再打印到console上,只能通过对应的日志文件查看日志。

list

list: 列出当前应用开启的所有服务器的信息,包括服务器Id、服务器类型、pid、堆使用情况、启动时长。

pomelo list [-u,–username ] [-p,–password ] [-h,–host ] [-P,–port ] 命令格式

当应用启动后,该命令列出所有服务器信息。由于当执行此操作时,pomelo是作为监控管理框架的一个客户端的,在连接注册到master上的时候,需要进行身份验证。默认生成的项目中,有一个默认的用户名admin,口令也为admin,因此在不指定用户名和口令的时候,默认使用的用户名和口令均为admin,下面的stop命令和kill命令均需要使用用户名和口令验证,默认值与此处相同。应用的管理用户可以通过修改config/adminUser.json文件进行配置;
执行本命令时,还需要指定master服务器的ip和port, 这样可以是的pomelo list可以在任意地方执行。pomelo stop/kill/add等也同样需要指定master服务器的ip和port,默认使用127.0.0.1:3005作为master服务器的地址。

4 . pomelo命令:stop

stop: 关闭应用及服务器或者停止指定的服务器。

pomelo stop [-u,–username ] [-p,–password ] [-h,–host ] [-P,–port ] […] 命令格式

stop用来停止当前应用,优雅地关闭应用。和kill命令不同,这种关闭首先会切断客户端与服务器的连接,然后逐一关闭所有服务器。如果指定了服务器serverId的话,则会关闭特定的服务器,而不是关闭所有的服务器。与list命令一样,需要权限验证,默认的用户名和密码均为admin,也需要指定master服务器的位置, 跟pomelo list一样,默认使用127.0.0.1:3005。

kill
该命令需在项目的根目录或game-server下使用;
kill: 强制关闭应用及服务器。

pomelo kill [-u,–username ] [-p,–password ] [-h,–host ] [-P,–port ] [-f,–force]

该命令强制关闭应用。在本地进行应用开发过程中,如果遇到kill之后还有服务器进程没有关闭的情况,可以增加–force选项,强制关闭所有服务器进程。该操作相当地暴力,可能产生数据丢失等不好的影响,可以在开发调试时使用,不推荐在线上使用该命令。该命令同样也需要进行身份验证以及指定master服务器的位置,具体方式同list和stop。

6 . pomelo命令:add

add: 运行时动态添加服务器。pomelo add也需要身份验证以及指定master服务器的地址。

pomelo add [-u,–username ] [-p,–password ] [-h,–host ] [-P,–port ] […]

args参数是用来指定新增服务器的参数的,包括服务器类型,服务器id等, 支持一次增加一台或多台同类型的服务器;示例如下:

pomelo add host=127.0.0.1 port=8000++ clientPort=9000++ frontend=true clusterCount=3 serverType=connector
pomelo add host=127.0.0.1 port=8000 clientPort=9000 frontend=true serverType=connector id=added-connector-server

masterha

masterha: 当启用masterha高可用的时候,用来启动master服务器的slave节点。需要在game-server/config目录下配置masterha.json。其他的命令行参数类似于pomelo start;

pomelo masterha [-d,–direcotry ]

其他命令
–version:列出当前使用pomelo的版本信息。
–help:列出所有pomelo支持的命令及使用说明。

项目结构说明

  game-server :  游戏服务器,所有游戏服务器功能和逻辑都在此目录下

    game-server/app.js:入口文件

    game-server/app: 存放游戏逻辑和功能相关代码都这个子目录下,servers目录下可以新建多个目录来创建不同类型的服务器,在pomelo中,使用路径来区分服务器类型

    game-server/config:存放游戏服务器配置文件目录,像日志、服务器、数据库等几乎所有配置文件都可以存放到此目录下

    game-server/logs:日志目录,存放游戏服务器所有日志文件

  web-server:  web服务器(如果你是个H5游戏,这里就是Web客户端,如果是IOS、Andriod客户端,这目录就没什么用)

  shared:公共代码存放处,这里要以放一些共用代码

 所有依赖项安装成功后,开始启动项目

   启动game-server

cd game-server
pomelo start

 

测试连接

  1.启动web-server

cd web-server
node app

修改web-server里的app.js :

打开浏览器:

测试gameserver

这里可以用我刚才说的pomelo命令查看下当前的服务器:

 

好了,你现在可以运行demo了,那么接下来就得开始循序渐进熟悉框架了

pomelo框架

架构

该架构把游戏服务器做了抽象, 抽象成为两类:前端服务器和后端服务器, 如图:

该架构把游戏服务器做了抽象, 抽象成为两类:前端服务器和后端服务器

前端服务器(frontend)的职责:

>负责承载客户端请求的连接

>维护session信息

>把请求转发到后端

>把后端需要广播的消息发到前端

后端服务器(backend)的职责:

>处理业务逻辑, 包括RPC和前端请求的逻辑

>把消息推送回前端

服务器实现

动态语言的面向对象有个基本概念叫鸭子类型 服务器的抽象也同样可以比喻为鸭子, 服务器的对外接口只有两类, 一类是接收客户端的请求, 叫做handler, 一类是接收RPC请求, 叫做remote, handler和remote的行为决定了服务器长什么样子。 因此我们只要定义好handler和remote两类的行为, 就可以确定这个服务器的类型。

服务器抽象的实现

利用目录结构与服务器对应的形式, 可以快速实现服务器的抽象。

以下是示例图:

利用目录结构与服务器对应的形式, 可以快速实现服务器的抽象。

图中的connector, connector, gate三个目录代表三类服务器类型, 每个目录下的handler与remote决定了这个服务器的行为(对外接口)。 开发者只要往handler与remote目录填代码, 就可以实现某一类的服务器。这让服务器实现起来非常方便。 让服务器动起来, 只要填一份配置文件servers.json就可以让服务器快速动起来。 配置文件和对应的进行架构如下所示:

1 . gate服务器
一个应用的gate服务器,一般不参与rpc调用,也就是说其配置项里可以没有port字段,仅仅有clientPort字段,它的作用是做前端的负载均衡。客户端往往首先向gate服务器发出请求,gate会给客户端分配具体的connector服务器。具体的分配策略一般是根据客户端的某一个key做hash得到connector的id,这样就可以实现各个connector服务器的负载均衡。

2 . connector服务器
connector服务器接收客户端的连接请求,创建与客户端的连接,维护客户端的session信息。同时,接收客户端对后端服务器的请求,按照用户配置的路由策略,将请求路由给具体的后端服务器。当后端服务器处理完请求或者需要给客户端推送消息的时候,connector服务器同样会扮演一个中间角色,完成对客户端的消息发送。connector服务器会同时拥有clientPort和port,其中clientPort用来监听客户端的连接,port端口用来给后端提供服务。

目前pomelo提供了hybridconnector和sioconnector,其中hybridconnector支持tcp,websocket; sioconnector支持socket.io。但是实际编程中,只有这些connector可能还无法满足我们的需求,我们可能需要自己定制自己的connector,pomelo提供了定制connector的接口,我们将会在后边的项目实例种用到。

3 . 应用逻辑服务器
gate服务器和connector服务器又都被称作前端服务器,应用逻辑服务器是后端服务器,它完成实际的应用逻辑,提供服务给客户端,当然客户端的请求是通过前端服务器路由过来的。后端服务器之间也会通过rpc调用而有相互之间的交互。由于后端服务器不会跟客户端直接有连接,因此后端服务器只需监听它提供服务的端口即可。

4 . master服务器
master服务器加载配置文件,通过读取配置文件,启动所配置的服务器集群,并对所有服务器进行管理。

5 . rpc调用
pomelo中使用rpc调用进行进程间通信,在pomelo中rpc调用分为两大类,使用namespace进行区分,namespace为sys的为系统rpc调用,它对用户来说是透明的,目前pomelo中系统rpc调用有:
1.后端服务器向前端服务器请求session信息
2.后端服务器通过channel推送消息时对前端服务器发起的rpc调用
3.前端服务器将用户请求路由给后端服务器时也是sys rpc调用
除了系统rpc调用外,其余的由用户自定义的rpc调用属于user namespace的rpc调用,需要用户自己完成rpc服务端remote的handle代码,并由rpc客户端显式地发起调用

6 . route,touter
route用来标识一个具体服务或者客户端接受服务端推送消息的位置,对服务端来说,其形式一般是…,例如"chat.chatHandler.send", chat就是服务器类型,chatHandler是chat服务器中定义的一个Handler,send则为这个Handler中的一个handle方法。对客户端来说,其路由一般形式为onXXX,当服务端推送消息时,客户端会有相应的回调。 一般来说具体的同类型应用服务器都会有多个,当客户端请求到达后,前端服务器会将用户客户端请求派发到后端服务器,这种派发需要一个路由函数router,可以粗略地认为router就是根据用户的session以及其请求内容,做一些运算后,将其映射到一个具体的应用服务器id。可以通过application的route调用给某一类型的服务器配置其router。如果不配置的话,pomelo框架会使用一个默认的router。pomelo默认的路由函数是使用session里面的uid字段,计算uid字段的crc32校验码,然后用这个校验码作为key,跟同类应用服务器数目取余,得到要路由到的服务器编号。注意这里有一个陷阱,就是如果session没有绑定uid的话,此时uid字段为undefined,可能会造成所有的请求都路由到同一台服务器。所以在实际开发中还是需要自己来配置router。

7 . Channel
channel可以看作是一个玩家id的容器,主要用于需要广播推送消息的场景。可以把某个玩家加入到一个Channel中,当对这个Channel推送消息的时候,所有加入到这个Channel的玩家都会收到推送过来的消息。一个玩家的id可能会被加入到多个Channel中,这样玩家就会收到其加入的Channel推送过来的消息。需要注意的是Channel都是服务器本地的,应用服务器A和B并不会共享Channel,也就是说在服务器A上创建的Channel,只能由服务器A才能给它推送消息。

8 . request, response, notify, push
pomelo中有四种消息类型的消息,分别是request,response,notify和push,客户端发起request到服务器端,服务器端处理后会给其返回响应response;notify是客户端发给服务端的通知,也就是不需要服务端给予回复的请求;push是服务端主动给客户端推送消息的类型。

9 . filter
filter分为before和after两类,每类filter都可以注册多个,形成一个filter链,所有的客户端请求都会经过filter链进行一些处理。before filter会对请求做一些前置处理,如:检查当前玩家是否已登录,打印统计日志等。after filter是进行请求后置处理的地方,如:释放请求上下文的资源,记录请求总耗时等。after filter中不应该再出现修改响应内容的代码,因为在进入after filter前响应就已经被发送给客户端。

10 . handler
handler是实现具体业务逻辑的地方,在请求处理流程中,它位于before filter和after filter之间,handler的接口声明如下:

handler.methodName = function(msg, session, next) {
// …
}

参数含义与before filter类似。handler处理完毕后,如有需要返回给客户端的响应,可以将返回结果封装成js对象,通过next传递给后面流程。

11 . error handler
error handler是一个处理全局异常的地方,可以在error handler中对处理流程中发生的异常进行集中处理,如:统计错误信息,组织异常响应结果等。error handler函数是可选的,格式如下:

app.set(‘errorHandler’, handleFunc);
来向pomelo框架进行注册,函数声明如下:
errorHandler = function(err, msg, resp, session, next) {
// …
}
err是前面流程中发生的异常;resp是前面流程传递过来,需要返回给客户端的响应信息。

12 . component
pomelo 框架是由一些松散耦合的component组成的,每个component完成一些功能。每个component往往有start,afterStart,stop等调用,用来完成生命周期管理。

13 . admin client, monitor, master
monitor运行在各个应用服务器中,它会向master注册自己,向master上报其服务器的信息,当服务器群有变化时,接收master推送来的变化消息,更新其服务器上下文。
master运行在应用服务器中,它会收集整个服务器群的信息,有变化时会将变化推送到各个monitor;同时,master还接受admin client的请求,按照client发出的命令,执行对应的操作。
client独立运行自己的进程,它会发起到master的连接,然后通过对master发出请求或者命令,来管理整个服务器群。

14 . admin module
在pomelo中,module特指服务器监控管理模块,实现的是监控逻辑,比如收集进程状态等。用户在使用时,可以通过application的registerAdmin注册管理模块,实现自己定制的监控管理功能。每一个module中都会定义可选的四种回调函数:

  1. masterHandler(agent, msg, cb) 当有应用服务器给master发监控数据时,这个回调函数会由master进程进行回调,完成应用服务器的消息处理;
  2. monitorHandler(agent, msg, cb) 当有master请求应用服务器的一些监控信息时,由应用服务器进行回调,完成对master请求的处理;
  3. clientHandler(agent, msg, cb)当由管理客户端向master请求服务器群信息时,由master进程进行回调处理客户端的请求。
  4. start(cb) 当admin module,注册加载完成后,这个回调会被执行,在这里可以做一些初始化工作。

客户端请求与响应、广播的抽象介绍

所有的web应用框架都实现了请求与响应的抽象。尽管游戏应用是基于长连接的, 但请求与响应的抽象跟web应用很类似。 下图的代码是一个request请求示例:

深入浅出node.js游戏服务器开发——Pomelo框架的设计动机与架构介绍

请求的api与web应用的ajax请求很象,基于Convention over configuration的原则, 请求不需要任何配置。 如下图所示,请求的route字符串:chat.chatHandler.send, 它可以将请求分发到chat服务器上chatHandler文件定义的send方法。

Pomelo的框架里还实现了request的filter机制,广播/组播机制,详细介绍见pomelo框架参考。

服务器间RPC调用的抽象介绍

架构中各服务器之间的通讯主要是通过底层RPC框架来完成的,该RPC框架主要解决了进程间消息的路由和RPC底层通讯协议的选择两个问题。 服务器间的RPC调用也实现了零配置。实例如下图所示:

深入浅出node.js游戏服务器开发——Pomelo框架的设计动机与架构介绍

上图的remote目录里定义了一个RPC接口: chatRemote.js,它的接口定义如下:

chatRemote.kick = function(uid, player, cb) {}

其它服务器(RPC客户端)只要通过以下接口就可以实现RPC调用:

app.rpc.chat.chatRemote.kick(session, uid, player, function(data){});

 这个调用会根据特定的路由规则转发到特定的服务器。(如场景服务的请求会根据玩家在哪个场景直接转发到对应的server)。

rpc的使用远比其它rpc框架简单好多,因为我们无需写任何配置文件,也无需生成stub。因为我们服务器抽象的实现的方式,使得rpc客户端可以在应用启动时扫描服务器目录自动生成stub对象。

完成了以上三个目标, 一个实时的分布式应用框架的轮廓就搭出来了。接下来我们在下一章节里说明下在当前demoserver的基础上不断地扩充,丰富它的功能。

 

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

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

相关文章

leetcode344. 反转字符串 史上最简单力扣题

编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。…

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

游戏启动过程 启动入口 在使用pomelo进行游戏开发时&#xff0c;工程目录下的app.js是整个游戏服务器的启动运行入口。app.js中创建项目&#xff0c;进行默认配置并启动服务器的代码如下&#xff1a; var pomelo require(pomelo); var app pomelo.createApp(); app.set(na…

(十二)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---构造函数析构函数

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