今天给各位分享alapi网站源码分享的知识,其中也会对api源码php分享进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
前言
本片文章将搭建redis源码debug环境,通过分析main函数流程,了解到redis的网络模型以及事件驱动模型,将redis架构清晰展现。
正文
源码搭建
源码下载地址:redis-github
下载clion然后导入项目,切换redis6分支,创建以下文件:
文件名:CMakeLists.txt
跟目录:
cmake_minimum_required(VERSION3.0FATAL_ERROR)\nproject(redisVERSION4.0)\nset(CMAKE_BUILD_TYPE&34;)\n\nget_filename_component(REDIS_ROOT&34;ABSOLUTE)\n\nadd_subdirectory(deps)\nadd_subdirectory(src/modules)\n\nset(SRC_SERVER_TMP\nsrc/acl.c\nsrc/adlist.c\nsrc/ae.c\nsrc/anet.c\nsrc/ae_kqueue.c\nsrc/dict.c\nsrc/sds.c\nsrc/zmalloc.c\nsrc/lzf_c.c\nsrc/lzf_d.c\nsrc/pqsort.c\nsrc/zipmap.c\nsrc/sha1.c\nsrc/ziplist.c\nsrc/release.c\nsrc/networking.c\nsrc/util.c\nsrc/object.c\nsrc/db.c\nsrc/replication.c\nsrc/rdb.c\nsrc/t_string.c\nsrc/t_list.c\nsrc/t_set.c\nsrc/t_zset.c\nsrc/evict.c\nsrc/defrag.c\nsrc/module.c\nsrc/quicklist.c\nsrc/expire.c\nsrc/childinfo.c\nsrc/redis-check-aof.c\nsrc/redis-check-rdb.c\nsrc/lazyfree.c\nsrc/geohash.c\nsrc/rax.c\nsrc/geohash_helper.c\nsrc/siphash.c\nsrc/geo.c\nsrc/t_hash.c\nsrc/config.c\nsrc/aof.c\nsrc/pubsub.c\nsrc/multi.c\nsrc/debug.c\nsrc/sort.c\nsrc/intset.c\nsrc/syncio.c\nsrc/cluster.c\nsrc/crc16.c\nsrc/endianconv.c\nsrc/slowlog.c\nsrc/scripting.c\nsrc/bio.c\nsrc/rio.c\nsrc/rand.c\nsrc/memtest.c\nsrc/crc64.c\nsrc/bitops.c\nsrc/sentinel.c\nsrc/notify.c\nsrc/setproctitle.c\nsrc/crcspeed.c\nsrc/blocked.c\nsrc/hyperloglog.c\nsrc/latency.c\nsrc/sparkline.c\nsrc/t_stream.c\nsrc/lolwut.c\nsrc/lolwut5.c\nsrc/listpack.c\nsrc/localtime.c\nsrc/timeout.c\nsrc/connection.c\nsrc/tls.c\nsrc/tracking.c\nsrc/lolwut6.c\nsrc/gopher.c\nsrc/sha256.c\n)\nset(SRC_SERVERsrc/server.c${SRC_SERVER_TMP})\n\nset(SRC_CLI\nsrc/anet.c\nsrc/sds.c\nsrc/adlist.c\nsrc/redis-cli.c\nsrc/zmalloc.c\nsrc/release.c\nsrc/anet.c\nsrc/ae.c\nsrc/crc64.c\nsrc/crc16.c\nsrc/dict.c\nsrc/siphash.c\nsrc/crcspeed.c\n)\n\n\nset(EXECUTABLE_OUTPUT_PATHsrc)\nlink_directories(deps/linenoise/deps/lua/srcdeps/hiredis)\n\nadd_executable(redis-server${SRC_SERVER})\ntarget_include_directories(redis-server\nPRIVATE${REDIS_ROOT}/deps/linenoise\nPRIVATE${REDIS_ROOT}/deps/hiredis\nPRIVATE${REDIS_ROOT}/deps/lua/src)\ntarget_link_libraries(redis-server\nPRIVATEpthread\nPRIVATEm\nPRIVATElua\nPRIVATElinenoise\nPRIVATEhiredis)\n\n\nadd_executable(redis-cli${SRC_CLI})\ntarget_include_directories(redis-cli\nPRIVATE${REDIS_ROOT}/deps/linenoise\nPRIVATE${REDIS_ROOT}/deps/hiredis\nPRIVATE${REDIS_ROOT}/deps/lua/src)\n\ntarget_link_libraries(redis-cli\nPRIVATEpthread\nPRIVATEm\nPRIVATElinenoise\nPRIVATEhiredis\n)\n复制代码
deps/CMakeLists.txt
add_subdirectory(hiredis)\nadd_subdirectory(linenoise)\nadd_subdirectory(lua)\n复制代码
deps/hiredis/CMakeLists.txt
add_library(hiredisSTATIC\nhiredis.c\nnet.c\ndict.c\nsds.c\nasync.c\nread.c\n)\n复制代码
deps/linenoise/CMakeLists.txt
add_library(linenoiselinenoise.c)\n复制代码
deps/lua/CMakeLists.txt
set(LUA_SRC\nsrc/lapi.csrc/lcode.csrc/ldebug.csrc/ldo.csrc/ldump.csrc/lfunc.c\nsrc/lgc.csrc/llex.csrc/lmem.c\nsrc/lobject.csrc/lopcodes.csrc/lparser.csrc/lstate.csrc/lstring.c\nsrc/ltable.csrc/ltm.c\nsrc/lundump.csrc/lvm.csrc/lzio.csrc/strbuf.csrc/fpconv.c\nsrc/lauxlib.csrc/lbaselib.csrc/ldblib.csrc/liolib.csrc/lmathlib.c\nsrc/loslib.csrc/ltablib.c\nsrc/lstrlib.csrc/loadlib.csrc/linit.csrc/lua_cjson.c\nsrc/lua_struct.c\nsrc/lua_cmsgpack.c\nsrc/lua_bit.c\n)\n\nadd_library(luaSTATIC${LUA_SRC})\n复制代码
src/modules/CMakeLists.txt
cmake_minimum_required(VERSION3.9)\nset(CMAKE_BUILD_TYPE&34;)\nadd_library(helloworldSHAREDhelloworld.c)\nset_target_properties(helloworldPROPERTIESPREFIX&34;SUFFIX&34;)\n\n\nadd_library(hellotypeSHAREDhellotype.c)\nset_target_properties(hellotypePROPERTIESPREFIX&34;SUFFIX&34;)\n\n\nadd_library(helloblockSHAREDhelloblock.c)\nset_target_properties(helloblockPROPERTIESPREFIX&34;SUFFIX&34;)\n\n\nadd_library(testmoduleSHAREDtestmodule.c)\nset_target_properties(testmodulePROPERTIESPREFIX&34;SUFFIX&34;)\n\n复制代码
修改文件:ae_kqueue.c,将include修改为:
34;unistd.h&include&34;\n34;zmalloc.h&include<sys/types.h>\ninclude<sys/time.h>\n复制代码
执行cmake.(自行安装cmake)执行make等待成功后配置运行
main方法
intmain(intargc,char**argv){\nstructtimevaltv;\nintj;\n\n34;test&34;ziplist&34;quicklist&34;intset&34;zipmap&34;sha1test&34;util&34;endianconv&34;crc64&34;zmalloc&endif\n\n/*Weneedtoinitializeourlibraries,andtheserverconfiguration.*/\nendif\nsetlocale(LC_COLLATE,&34;);\n//设置时间环境变量\ntzset();\n//内存超出的handler(记录日志)\nzmalloc_set_oom_handler(redisOutOfMemoryHandler);\n//随机数初始化\nsrand(time(NULL)^getpid());\n//精确时间\ngettimeofday(&tv,NULL);\ncrc64_init();\n\n//hash算法种子\nuint8_thashseed[16];\ngetRandomBytes(hashseed,sizeof(hashseed));\ndictSetHashFunctionSeed(hashseed);\n//是否以哨兵模式启动\nserver.sentinel_mode=checkForSentinelMode(argc,argv);\n\n//初始化配置文件\ninitServerConfig();\nACLInit();\nmoduleInitModulesSystem();\ntlsInit();\n\n//将执行命令以及参数保存\nserver.executable=getAbsolutePath(argv[0]);\nserver.exec_argv=zmalloc(sizeof(char*)*(argc+1));\nserver.exec_argv[argc]=NULL;\nfor(j=0;j<argc;j++)server.exec_argv[j]=zstrdup(argv[j]);\n\n//初始化哨兵配置\nif(server.sentinel_mode){\ninitSentinelConfig();\ninitSentinel();\n}\n\n//检查aof和rdb错误\nif(strstr(argv[0],&34;)!=NULL)\nredis_check_rdb_main(argc,argv,NULL);\nelseif(strstr(argv[0],&34;)!=NULL)\nredis_check_aof_main(argc,argv);\n复制代码
主要是执行了initServerConfig方法,将服务的配置进行设置了默认值
if(argc>=2){\nj=1;\nsdsoptions=sdsempty();\nchar*configfile=NULL;\n\n//输出版本号以及帮助等\nif(strcmp(argv[1],&34;)==0||\nstrcmp(argv[1],&34;)==0)version();\nif(strcmp(argv[1],&34;)==0||\nstrcmp(argv[1],&34;)==0)usage();\nif(strcmp(argv[1],&34;)==0){\nif(argc==3){\nmemtest(atoi(argv[2]),50);\nexit(0);\n}else{\nfprintf(stderr,&34;);\nfprintf(stderr,&34;);\nexit(1);\n}\n}\n\n//获取配置文件地址\nif(argv[j][0]!=&39;||argv[j][1]!=&39;){\nconfigfile=argv[j];\nserver.configfile=getAbsolutePath(configfile);\nzfree(server.exec_argv[j]);\nserver.exec_argv[j]=zstrdup(server.configfile);\nj++;\n}\n\n//解析参数比如–port6379等一些配置\nwhile(j!=argc){\nif(argv[j][0]==&39;&&argv[j][1]==&39;){\n//跳过–check-rdb没有参数\nif(!strcmp(argv[j],&34;)){\nj++;\ncontinue;\n}\nif(sdslen(options))options=sdscat(options,&34;);\noptions=sdscat(options,argv[j]+2);\noptions=sdscat(options,&34;);\n}else{\n/*Optionargument*/\noptions=sdscatrepr(options,argv[j],strlen(argv[j]));\noptions=sdscat(options,&34;);\n}\nj++;\n}\n//哨兵配置只能从文件读取不允许参数\nif(server.sentinel_mode&&configfile&&*configfile==&39;){\nserverLog(LL_WARNING,\n&34;);\nserverLog(LL_WARNING,\n&34;);\nexit(1);\n}\nresetServerSaveParams();\n//加载配置文件和命令行读入的参数到server配置中\nloadServerConfig(configfile,options);\nsdsfree(options);\n}\n复制代码
然后是解析输入的参数,可以通过:
redis-server../redis.conf–port6379\n复制代码
来指定配置文件的地址,以及可以填写一些参数,最终通过loadServerConfig方法来将配置文件以及自己填的参数解析
//Redis信息\nserverLog(LL_WARNING,&34;);\nserverLog(LL_WARNING,\n&34;,\nREDIS_VERSION,\n(sizeof(long)==8)?64:32,\nredisGitSHA1(),\nstrtol(redisGitDirty(),NULL,10)>0,\n(int)getpid());\n\nif(argc==1){\nserverLog(LL_WARNING,&34;,argv[0],server.sentinel_mode?&34;:&34;);\n}else{\nserverLog(LL_WARNING,&34;);\n}\n\n//后台模式模式\nserver.supervised=redisIsSupervised(server.supervised_mode);\nintbackground=server.daemonize&&!server.supervised;\nif(background)daemonize();\n\n//初始化服务器\ninitServer();\n复制代码
主要是initServer这个方法,inintServer首先初始化了一些list,这一段就不放上来了,继续往下看
//常用字符串等共享对象\ncreateSharedObjects();\n//提高最大打开文件数量\nadjustOpenFilesLimit();\n//创建事件循环\nserver.el=aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);\nif(server.el==NULL){\nserverLog(LL_WARNING,\n&39;%s&34;,\nstrerror(errno));\nexit(1);\n}\n复制代码
可以看到这里调用aeCreateEventLoop创建了服务的事件循环器,这也是整个服务的基石
aeEventLoop
typedefstructaeEventLoop{\nintmaxfd;/*当前最高文件描述符*/\nintsetsize;//event数量\nlonglongtimeEventNextId;//定时任务自增id\ntime_tlastTime;\naeFileEvent*events;//事件数组\naeFiredEvent*fired;/*准备就绪的事件*/\naeTimeEvent*timeEventHead;//定时任务头节点\nintstop;\nvoid*apidata;\naeBeforeSleepProc*beforesleep;\naeBeforeSleepProc*aftersleep;\nintflags;\n}aeEventLoop;\n复制代码
这是事件循环的结构体,主要是*events来存放对应的事件
typedefstructaeFileEvent{\nintmask;/*三种flagsAE_(READABLE|WRITABLE|BARRIER)*/\naeFileProc*rfileProc;\naeFileProc*wfileProc;\nvoid*clientData;\n}aeFileEvent;\n复制代码
aeFileEvent主要是读事件和写事件,根据事件的不同调用方法,按照普通的流程来说应该是先读后写,但是如果mask为AE_BARRIER的时候,表示先写后读
typedefstructaeTimeEvent{\nlonglongid;\nlongwhen_sec;//秒\nlongwhen_ms;//毫秒\naeTimeProc*timeProc;//对应执行方法\naeEventFinalizerProc*finalizerProc;//回收方法\nvoid*clientData;\nstructaeTimeEvent*prev;\nstructaeTimeEvent*next;\nintrefcount;\n}aeTimeEvent;\n复制代码
这是事件循环里面的定时事件,多个定时事件组成一个链表
aeEventLoop*aeCreateEventLoop(intsetsize){\naeEventLoop*eventLoop;\ninti;\n\nif((eventLoop=zmalloc(sizeof(*eventLoop)))==NULL)gotoerr;\neventLoop->events=zmalloc(sizeof(aeFileEvent)*setsize);\neventLoop->fired=zmalloc(sizeof(aeFiredEvent)*setsize);\nif(eventLoop->events==NULL||eventLoop->fired==NULL)gotoerr;\neventLoop->setsize=setsize;\neventLoop->lastTime=time(NULL);\neventLoop->timeEventHead=NULL;\neventLoop->timeEventNextId=0;\neventLoop->stop=0;\neventLoop->maxfd=-1;\neventLoop->beforesleep=NULL;\neventLoop->aftersleep=NULL;\neventLoop->flags=0;\nif(aeApiCreate(eventLoop)==-1)gotoerr;\nfor(i=0;i<setsize;i++)\neventLoop->events[i].mask=AE_NONE;\nreturneventLoop;\n\nerr:\nif(eventLoop){\nzfree(eventLoop->events);\nzfree(eventLoop->fired);\nzfree(eventLoop);\n}\nreturnNULL;\n}\n复制代码
创建事件循环代码,可以看到主要调用aeApiCreate,并且初始化了setsize个事件,setsize表示最大client数量
include&34;\nifdefHAVE_EPOLL\n34;ae_epoll.c&else\ninclude&34;\ninclude&34;\nendif\n39;tcareifthisfails*/\nserver.sofd=anetUnixServer(server.neterr,server.unixsocket,\nserver.unixsocketperm,server.tcp_backlog);\nif(server.sofd==ANET_ERR){\nserverLog(LL_WARNING,&34;,server.neterr);\nexit(1);\n}\nanetNonBlock(NULL,server.sofd);\n}\n\n//没有listen任何端口\nif(server.ipfd_count==0&&server.tlsfd_count==0&&server.sofd<0){\nserverLog(LL_WARNING,&34;);\nexit(1);\n}\n\n复制代码
继续是绑定端口以及unixsocket
//创建16个数据库\nfor(j=0;j<server.dbnum;j++){\nserver.db[j].dict=dictCreate(&dbDictType,NULL);\nserver.db[j].expires=dictCreate(&keyptrDictType,NULL);\nserver.db[j].expires_cursor=0;\nserver.db[j].blocking_keys=dictCreate(&keylistDictType,NULL);\nserver.db[j].ready_keys=dictCreate(&objectKeyPointerValueDictType,NULL);\nserver.db[j].watched_keys=dictCreate(&keylistDictType,NULL);\nserver.db[j].id=j;\nserver.db[j].avg_ttl=0;\nserver.db[j].defrag_later=listCreate();\nlistSetFreeMethod(server.db[j].defrag_later,(void(*)(void*))sdsfree);\n}\n复制代码
初始化数据库
//创建定时器\nif(aeCreateTimeEvent(server.el,1,serverCron,NULL,NULL)==AE_ERR){\nserverPanic(&39;tcreateeventlooptimers.&34;Unrecoverableerrorcreatingserver.ipfdfileevent.&34;Acceptingclientconnection:%s&34;Accepted%s:%d&34;-ERRmaxnumberofclients+cluster&34;connectionsreached\\r\\n&34;-ERRmaxnumberofclientsreached\\r\\n&34;Errorregisteringfdeventforthenewclient:%s(conn:%s)&34;Erroracceptingaclientconnection:%s(conn:%s)”,\nconnGetLastError(conn),connGetInfo(conn,conninfo,sizeof(conninfo)));\nfreeClient(connGetPrivateData(conn));\nreturn;\n}\n}\n复制代码
首先判断了最大客户端数量,超出数量返回失败,然后调用createClient创建了客户端
client*createClient(connection*conn){\nclient*c=zmalloc(sizeof(client));\n\nif(conn){\nconnNonBlock(conn);\nconnEnableTcpNoDelay(conn);\nif(server.tcpkeepalive)\nconnKeepAlive(conn,server.tcpkeepalive);\nconnSetReadHandler(conn,readQueryFromClient);\nconnSetPrivateData(conn,c);\n}\n复制代码
只看这一段,给客户端连接设置了读处理器,readQueryFromClient
staticinlineintconnSetReadHandler(connection*conn,ConnectionCallbackFuncfunc){\nreturnconn->type->set_read_handler(conn,func);\n}\n\n.set_read_handler=connSocketSetReadHandler,\n复制代码
set_read_handler是connSocketSetReadHandler方法
staticintconnSocketSetReadHandler(connection*conn,ConnectionCallbackFuncfunc){\nif(func==conn->read_handler)returnC_OK;\n\nconn->read_handler=func;\nif(!conn->read_handler)\naeDeleteFileEvent(server.el,conn->fd,AE_READABLE);\nelse\nif(aeCreateFileEvent(server.el,conn->fd,\nAE_READABLE,conn->type->ae_handler,conn)==AE_ERR)returnC_ERR;\nreturnC_OK;\n}\n复制代码
看到这里就明白了,redis创建了多个tcp事件,当客户端第一次连接的时候会创建client实例,并且设置ReadHandler,ReadHandler实际上向epoll注册了读事件,当有命令可读的时候调用readQueryFromClient
readQueryFromClient最后会调用到了processCommand方法也就是执行命令,同时在redis6里面是支持多线程的,但是通过前面的源码可以看出来:dict并没有进行加锁,那么这个多线程是怎么回事呢?看下回分解redis多线程模型
总结
作者:LZ链接:https://juejin.cn/post/6979458051556245535来源:掘金
OK,本文到此结束,希望对大家有所帮助。
