1.BoltDB源码解析(七)Put和Delete操作
2.Mac搭建grafana二次开发环境操作教程(图文详解)
3.html简单网页代码?
4.沉浸式go-cache源码阅读!
BoltDB源码解析(七)Put和Delete操作
Put和Delete的实现
上一篇文章我们了解了BoltDB的Get API的实现。现在,我们来探讨Put和Delete API的实现:
Put API的主要功能是将一对键值对插入到Bucket中,如果键已经存在,则更新对应的libubox源码分析值。首先,进行一些限制条件的检查,例如Put操作是否由写事务发起的,因为Put只能由写事务调用。此外,还需要检查键和值的大小是否符合限制条件。需要注意的是,Put操作和Get操作一样,这里也使用了Cursor来定位键应该放置的位置。
在实际的Put操作中,会调用Cursor的一个不显眼的方法:
这个方法实际上非常有用,它从当前Bucket的B-tree的根节点开始,一直到Cursor定位到的leaf page,为每个page创建一个对应的node结构。当然,如果一个page已经有对应的node,就直接使用它。
为什么要这么做呢?这是因为事务篇中提到的修改操作具有“传染性”,修改B-tree的leaf节点会导致从root到leaf的所有page都需要修改,而BoltDB的修改操作都是在page对应的node里进行的,不是直接在page上修改,因此需要为这些page建立node结构。具体建立node结构的是Bucket的node方法:
Bucket的node方法有两处需要注意,一个是新建的node会被追加到parent node的children中,记录下这些修改的node之间的关系,这个children在node持久化时会有用(node.spill方法)。另一个是node的数据是如何从page中读取的,这是由node的read方法完成的。
node建立好之后,就在要修改的拓荒神器源码leaf对应的node上调用put方法:
node的put方法相对简单,它是在inodes数组上查找对应的位置,如果exact为true,表示找到了相同的key,直接更新value;如果exact为false,相当于找到了应该插入的位置,然后在对应的inode上记录数据。我们来看一下inodes数组的定义:
inodes数组是node实际存储数据的地方,由多个inode组成,每个不同的key对应一个不同的inode,inode之间是按key排序的。对于leaf节点来说,inode里使用key和value;对于branch节点来说,inode里使用key和pgid,pgid代表一个child page的id。value和pgid不会同时使用。
put方法结束后,当前的Put操作也就结束了。也就是说,Put操作所做的仅仅是把新增或修改的数据放入到它所在的page对应的node内存中。
顺便提一下Delete操作,它和Put操作非常类似,在建立起node结构之后,在对应的node的inodes数组中删除找到的key相等的inode就完成了,这里不再展开。
那么,什么时候会把这些node里的数据持久化到DB文件里呢?是在整个写事务commit的时候。
事务的Commit实现
下面是事务commit的代码简化,保留了重要部分:
Commit的整体流程比较长,下面一点一点进行说明。
tx.root.rebalance(),这个root是root Bucket,rebalance是对root Bucket下所有子Bucket的所有node进行rebalance。这是什么意思?注意node的初始数据虽然来自一个page,但在经历了一些Delete操作后,有些node里面的源码之家网址数据可能过少,这时会先把这个node和它的左兄弟或右兄弟node合并(node的rebalance方法),合并后node数会减少,但不存在node里数据过少的情况。这个操作对应于B-tree的merge操作,只不过这些node都是Go的内存结构,合并起来非常简单。当然,合并后把这些node spill到page的操作,需要的page总数也会减少。
tx.root.spill(),这个方法是把root Bucket下所有子Bucket的所有node的内容都写入这个事务分配的dirty page里。注意这些dirty page是这个事务临时分配在内存里的,结构和DB文件的page完全一样,但还不是mmap映射的DB的page。
刚开始看到spill这个方法时,感觉它代价有些高,感觉像是把整个B-tree都走了一遍。后来仔细看才发现不是这么回事。这个spill只对有node结构的节点进行处理,那些没修改过的page没有对应的node,根本不会处理。
注意在经过多次Put操作后,node里存放的数据可能出现一个page写不下的情况,比如insert了几千个key value。spill会先把这样的node split成多个大小合适的node(node的split方法),然后把这些node分别写入不同的page中。这个操作对应于B-tree的split操作。和rebalance方法类似的道理,因为这些node都是Go的内存结构,split起来非常容易。
if tx.meta.pgid > opgid,这个判断是看当前事务需要的page数是否大于事务执行前DB文件有的page数,如果大于,说明DB文件放不下了,就调用db.grow增大文件,get指标源码以容纳新增的page。
紧接着是freelist的持久化操作,因为写事务可能使用了freelist里的一些page,同时也可能释放了一些page到freelist里,所以freelist很可能发生了变化,需要持久化。
tx.write(),这个方法就是把所有的临时分配的dirty page都写入DB文件对应的page里。
tx.writeMeta(),这个方法是把这个tx里的meta写到meta0或者meta1里面(写事务会交替写这两个meta page,这也是个常用技术,叫ping-pong buffer)。它的代码值得看一下:
首先把meta写到临时分配的buf里,然后用文件IO写到DB文件里,最后调用fdatasync,把OS文件的buffer cache持久化到磁盘上。至此,写事务的所有数据都已经落盘完毕。后面新开启的事务会因为这个meta的txid是最大的,而选择使用这个最新的meta page。而这个meta page包括最新的root bucket,最新的freelist,最新的pgid,这些总体构成了一个DB的最新版本,保证新开启的事务读到最新版本的数据。
看tx.write()和tx.writeMeta()的实现可以发现,写入数据用的是db.ops.writeAt,而这个方法默认值就是File.WriteAt方法,所以实际写入文件用的是文件IO,而不是直接写mmap内存。而BoltDB使用mmap一开始就把mmap映射的内存标记为只读的,压根不允许直接写mmap内存。为什么要这么做呢?
猜测可能是为了安全。前面讲到Get操作为了性能是zero copy的,发现Get返回来的dogs网站源码value是mmap上数据的指针,如果mmap设置为可读写的,应用程序代码五花八门,可能会通过指针一不小心修改了mmap上的数据,这样的修改因为走的不是API是无法保证事务的。把mmap设置为只读的消除了这种可能性。反过来说,如果mmap设置为可读写的,Get就不能返回mmap上的指针了,为了安全一定要copy一份数据出来才行,降低了Get的性能。
这里还有个很自然而且很重要的问题是,如果事务commit失败了呢,BoltDB如何保证事务的原子性(ACID的A),确保这个写事务的所有操作,不论是落盘的,还是没落盘的,都不会生效?
原子性要求,不管是commit走到哪一步,哪怕是已经把修改的数据,甚至包括修改的freelist已经落盘,只要最终事务commit失败,都不能对正确性产生任何影响。这里的正确性是指,数据库的状态(有实际的key value数据,freelist, pgid等共同构成)必须是在这个写事务运行之前的状态,数据不能被破坏,这个写事务也不能留下可被后续事务读到的任何更新。
要做到原子性貌似挺难的,因为事务的commit里包括很多步骤,这些步骤都不是原子性的。不过重要的一点是,不论commit运行到哪一步,因为tx.writeMeta是最后一步,只有这一步运行成功commit才算成功,如果说commit失败了,那么tx.writeMeta一定是没运行,或者运行了半截,这个meta page没写完整,机器断电了。总之,这些情况下我们不会得到一个合法的新的meta page(这种情况下meta的validate方法会失败,因为meta的checksum不对)。这时候ping-pong buffer的meta page就起重要的作用了,因为交替写meta page的原因,即使这个写事务新的meta page没写成功,这个写事务运行前版本的meta page还在,而这个meta page包括这个写事务运行前的DB版本所有的状态(kv数据,freelist,pgid等)。这个meta page会被后续事务使用,就像那个失败的写事务从来没有运行过一样。而那个写事务留下的kv数据的page,freelist的page,即使是持久化了,也因为没有写成新的meta,没有机会被用到。
还有个自然的疑问,即使这个失败的写事务写的page因为没有合法的meta无法被引用,不会影响正确性,但无法被引用是不是也意味着这些page无法被回收,浪费了磁盘空间?
答案是也不会。在原来版本的meta里的free list和pgid的共同作用下,这些page会被视为free的,还可以使用,不会出现无法回收这些page的情况。
还有个疑问,既然BoltDB交替写meta0和meta1,是不是连续两个事务commit正好在写meta时失败,数据库就废了?
仔细研究发现,还是没事!因为写事务的txid也是meta的一部分,一个写事务失败,导致txid不会增长,下一次写事务的txid还是一样,meta的交替写是因为txid的变化引起的,既然没变化,就不交替了。所以下一个写事务即使写meta还失败了,也还是写的上一个写事务写的那个meta,不会把两个meta都写坏。
总结一下,ping-pong buffer的meta page真是设计得精巧,是BoltDB达到原子性的关键!
Mac搭建grafana二次开发环境操作教程(图文详解)
Grafana是开源的图表数据展示系统,可以配置很多的dashboard,还可以下载别人已经配置好的dashboard。这篇博客介绍下如何在mac下搭建grafana的二次开发环境。
一、安装grafana首先,我们可以通过brew把grafana安装到系统里,来看一看这个开源系统的功能。
如果你的brew是直接可用的,就在terminal中直接运行下面的命令,否则,需要先brewupdate一下。
brewinstallgrafana装好以后,通过如下命令可以启动grafana
brewservicesstartgrafana然后打开localhost:,看到下面的图,就可以了。
初始账号密码是admin/admin。
通过下面的命令可以关闭和重启grafana
brewservicesstopgrafanabrewservicesrestartgrafana二、搭建grafana的开发环境grafana依赖node和go,需要先安装好node和go。
1、安装node我这里的grafana是7.3,需要版本的node
安装node有两种方式
如果之前没有装过,直接brewinstallnode就可以了,这样安装的node默认是最新版本,如果需要指定版本,可以在node后通过@来指定安装某个版本。
通过版本管理神器n来升级
npmin-g//安装node版本管理神器n-V//安装后查看n的版本nlatest//使用或安装最新的官方发布:nstable//使用或安装稳定的正式版本:nlts//使用或安装最新的LTS正式版本:安装好node后顺便升级下npm
npminpm@latest-g2、安装golang依然通过brew安装go
brewinstallgo//安装golang然后查看go的环境变量配置
goenv上面的命令默认得到如下信息
GOMODULE=""GOARCH="amd"GOBIN=""GOCACHE="/Users/XXXXX/Library/Caches/go-build"GOENV="/Users/XXXXX/Library/ApplicationSupport/go/env"GOEXE=""GOFLAGS=""GOHOSTARCH="amd"GOHOSTOS="darwin"GOINSECURE=""GOMODCACHE="/Users/XXXXX/go/pkg/mod"GONOPROXY=""GONOSUMDB=""GOOS="darwin"GOPATH="/Users/XXXXX/go"GOPRIVATE=""GOPROXY=""GOROOT="/usr/local/go"GOSUMDB="sum.golang.org"GOTMPDIR=""GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd"GCCGO="gccgo"AR="ar"CC="clang"CXX="clang++"CGO_ENABLED="1"GOMOD=""CGO_CFLAGS="-g-O2"CGO_CPPFLAGS=""CGO_CXXFLAGS="-g-O2"CGO_FFLAGS="-g-O2"CGO_LDFLAGS="-g-O2"PKG_CONFIG="pkg-config"GOGCCFLAGS="-fPIC-m-pthread-fno-caret-diagnostics-Qunused-arguments-fmessage-length=0-fdebug-prefix-map=/var/folders/3m/xp_vx4jj3lh8chqq5tq_dwgn/T/go-build=/tmp/go-build-gno-record-gcc-switches-fno-common"注意GOPATH地址,在"/Users/XXXXX/go"下,其中XXXXX是我的用户名。但是在XXXXX下是没有go这个文件夹,需要我们自己建立。
假如我们想写一个go的helloworld,就需要把这个程序放到go目录下。
假如想要修改GOPATH也是可以的。用如下命令:
vim~/.bash_profile添加如下代码:exportGOPATH="XXXXXXXXXX"然后快捷键shift+zz关闭文件
这时候,在terminal中输入echo$GOPATH,就可以看到刚刚设置的GOPATH了。
到这一步,go就算安装到我们的电脑里了。
3、下载grafana的源代码由于grafana是开源的,因此代码托管在github上,地址:makerun
到这里,grafana的二次开发环境就搭建好了。
作者:晴天同学html简单网页代码?
HTML做一个打开网页代码
html的head区域中加上scriptlanguage="javascript"location.replace("")/script即可html
html代码如下:
head
title页面跳转/title
scriptlanguage="javascript"location.replace("")/script
/head
body
/body
/html
扩展资料:
网页HTML代码大全
文件类型HTML/HTML(放在档案的开头与结尾)
文件主题TITLE/TITLE(必须放在「文头」区块内)
文头HEAD/HEAD(描述性资料,像是「主题」)
文体BODY/BODY(文件本体)
(由浏览器控制的显示风格)
标题H?/H?(从1到6,有六层选择)
标题的对齐H?ALIGN=LEFT|CENTER|RIGHT/H?
区分DIV/DIV
区分的对齐DIVALIGN=LEFT|RIGHT|CENTER|JUSTIFY/DIV
引文区块BLOCKQUOTE/BLOCKQUOTE(通常会内缩)
强调EM/EM(通常会以斜体显示)
特别强调STRONG/STRONG(通常会以加粗显示)
引文CITE/CITE(通常会以斜体显示)
码CODE/CODE(显示原始码之用)
样本SAMP/SAMP
键盘输入KBD/KBD
变数VAR/VAR
定义DFN/DFN(有些浏览器不提供)
地址ADDRESS/ADDRESS
大字BIG/BIG
小字SMALL/SMALL
与外观相关的标签(作者自订的表现方式)
加粗B/B
斜体I/I
底线U/U(尚有些浏览器不提供)
删除线S/S(尚有些浏览器不提供)
下标SUB/SUB
上标SUP/SUP
打字机体TT/TT(用单空格字型显示)
预定格式PRE/PRE(保留文件中空格的大小)
预定格式的宽度PREWIDTH=?/PRE(以字元计算)
向中看齐CENTER/CENTER(文字与都可以)
网页设计常用HTML代码
网页设计常用HTML代码大全
HTML是用来描述网页的一种语言。下面我为大家分享HTML代码,希望对大家学习html代码有帮助!
忽视右键
bodyoncontextmenu="returnfalse"
或
bodystyle="overflow-y:hidden"
1.如何几秒后转到别的页面?
METAHTTP-EQUIV="Refresh"CONTENT="时间;URL=地址"
2.点击关闭窗口
ahref="javascript:top.window.close();"点击关闭窗口/a!
3.请问如何去掉主页右面的滚动条?
bodyscroll="no"
bodystyle="overflow-y:hidden"
4.请问如何做到让一个网页自动关闭.
html
head
OBJECTid=closestype="application/x-oleobject"classid="clsid:adba6-d8ff-cf--aab7a"
paramname="Command"value="Close"
/object
/head
bodyonload="window.setTimeout(‘‘‘‘closes.Click()‘‘‘‘,)"
这个窗口会在秒过后自动关闭,而且不会出现提示./body
如何在不刷新页面的情况下刷新css?
style
button{ color:#;}
/style
buttononclick=document.styleSheets[0].rules[0].style.color=‘‘‘‘red‘‘‘‘点击按钮直接修改style标签里button选择符使按钮改为红色/button
请问如何让网页自动刷新?
在head部记入METAHTTP-EQUIV="Refresh"content=""其中为秒后自动刷新,你可以更改为任意值。
5.如何让页面自动刷新?
方法一,用refresh
HTML代码片段如下:
head
metahttp-equiv="refresh"content="5"
/head
5表示刷新时间
[Ctrl+A全部选择提示:你可先修改部分代码,再按运行]
方法二,使用setTimeout控制
imgsrc=/logo.gif
script
functionrl(){
document.location.reload()
}
setTimeout(rl,)
/script
6.如何让超链接没有下划线
在源代码中的HEAD…/HEAD之间输入如下代码:
styletype="text/css"!--
a{ text-decoration:none}
--/style
7.请问如何去掉IE的上下滚动条?
bodystyle=‘‘‘‘overflow:scroll;overflow-y:hidden‘‘‘‘
/body
8.怎样才能把RealPlayer文件在网页做一个试听连接?
embedheight=src=js.rmtype=audio/x-pn-realaudio-pluginwidth=autostart="false"controls="PlayButton"
9.如何用html实现浏览器上后退按钮的功能?
ahref="javascript:history.go(-1)"点击后退/a
或者
scripthistory.back()/script
.请问怎么在网页中改变鼠标的箭头形状?
HTML代码片段如下:
body
ahref="#"style="cursor:auto;"auto/abr
ahref="#"style="cursor:crosshair"crosshair/abr
ahref="#"style="cursor:default"default/abr
ahref="#"style="cursor:hand"hand/abr
ahref="#"style="cursor:move"move/abr
ahref="#"style="cursor:e-resize"e-resize/abr
ahref="#"style="cursor:ne-resize"ne-resize/abr
ahref="#"style="cursor:nw-resize"nw-resize/abr
ahref="#"style="cursor:n-resize"n-resize/abr
ahref="#"style="cursor:se-resize"se-resize/abr
ahref="#"style="cursor:sw-resize"sw-resize/abr
ahref="#"style="cursor:s-resize"s-resize/abr
ahref="#"style="cursor:w-resize"w-resize/abr
ahref="#"style="cursor:text"text/abr
ahref="#"style="cursor:wait"wait/abr
ahref="#"style="cursor:help"help/abr
/body
.怎样不使用页面的缓存?即每一次打开页面时不是调用缓存中的东西
METAHTTP-EQUIV="Pragma"CONTENT="no-cache"
.页面打开时自动弹出一个窗口的代码怎么写?
HTML代码片段如下:
html
head
titleUntitledDocument/title
metahttp-equiv="Content-Type"content="text/html;charset=gb"
scriptlanguage="Bstyle="color:black;background-color:#A0FFFF"javascript/B"
!--
functionMM_openBrWindow(theURL,winName,features){ //v2.0
window.open(theURL,winName,features);
}
//--
/script
/head
bodybgcolor="#FFFFFF"text="#"onLoad="MM_openBrWindow(‘‘‘‘‘‘‘‘,‘‘‘‘,‘‘‘‘width=,height=‘‘‘‘)"
/body
/html
.如何让我的页面出现一个会讲话的小人?Merlin
HTML代码片段如下:
HTML
HEAD
TITLE默林/TITLE
METAhttp-equiv=Content-Typecontent="text/html;charset=gb"
/HEAD
BODY
pOBJECTid=simsclassid=CLSID:DFDB-5C6E-D1-9EC1-CFDF
/OBJECT
SCRIPT
varMerlinID;
varMerlinACS;
sims.Connected=true;
MerlinLoaded=LoadLocalAgent(MerlinID,MerlinACS);
Merlin=sims.Characters.Character(MerlinID);
Merlin.Show();
Merlin.Play("Surprised");
Merlin.Speak("大家好");
Merlin.Play("GestureLeft");
Merlin.Think("我是默林!");
Merlin.Play("Pleased");
Merlin.Think("可爱吗?");
Merlin.Play("GestureDown");
Merlin.Speak("哈哈!");
Merlin.Hide();
functionLoadLocalAgent(CharID,CharACS){
LoadReq=sims.Characters.Load(CharID,CharACS);
return(true);
}
/SCRIPT
/p
p/p
p看此效果必须装有office!!!/p
/BODY
/HTML
.在页面中如何加入不是满铺的背景,拉动页面时背景图不动
HTML代码片段如下:
htmlhead
STYLE
body{ background-image:url(logo.gif);
background-repeat:no-repeat;background-position:center}
/STYLE
/head
bodybgproperties="fixed"
/body
/html
[Ctrl+A全部选择提示:你可先修改部分代码,再按运行]
background-repeat:no-repeat;是让背景图不占满整个页面
bodybgproperties="fixed"是拉动scroll时背景图不动
.文本输入框什么属性能实现不可输入?
HTML代码片段如下:
inputtype="text"name="textfield"disabled
或者
inputtype="text"name="textfield"readonly
.如何禁止自己的页面在别人的框架里打开?
把以下代码加至你的head区
script
if(window.top!=self){
window.top.location=self.location
}
/script
.如何实现首页全屏幕显示?
HTML代码片段如下:
html
bodyscriptlanguage="Bstyle="color:black;background-color:#A0FFFF"javascript/B"
varcoolw=
varcoolh=
varcoolhuang=window.open("","coolhuang","width="+coolw+",height="+coolh+",
fullscreen=1,toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=0,resizable=0")
window.close()
/script/body/html
.如何监听一个窗口被关闭了?
HTML代码片段如下:
bodyonunload="alert(‘‘‘‘你关闭了这个窗口‘‘‘‘)"
.如何禁止Ctrl+N?
HTML代码片段如下:
bodyonkeydown=return(!(event.keyCode==event.ctrlKey))
如何把页面加入用户的收藏夹?
HTML代码片段如下:
ahref="Bstyle="color:black;background-color:#A0FFFF"javascript/B:window.external.AddFavorite(‘‘‘‘‘‘‘‘,‘‘‘‘无忧脚本‘‘‘‘)"收藏无忧脚本/a
如何在我的'页面中加入背景音乐?
IE:bgsoundsrc="*.mid"loop=infinite
NS:embedsrc="*.mid"autostart=truehidden=trueloop=true
*.mid你的背景音乐的midi格式文件
关于页面转换效果
metahttp-equiv="page-enter"content="revealTrans(Duration=4,Transition=)"
或
metahttp-equiv="page-exit"content="revealTrans(Duration=4,Transition=)"
说明:Transition=是随机效果,另可以选0-任一数字固定某个效果
如何设定打开页面的大小
HTML代码片段如下:
bodyonload="top.resizeTo(,);"!--(width,height)--
怎样双击滚屏,单击停止?
HTML代码片段如下:
html
head
title新网页1/title
/head
body
scriptlanguage"Bstyle="color:black;background-color:#A0FFFF"javascript/B"
varcurrentpos,timer;
functioninitialize()
{
timer=setInterval("scrollwindow()",);
}
functionsc(){
clearInterval(timer);
}
functionscrollwindow()
{
currentpos=document.body.scrollTop;
window.scroll(0,++currentpos);
if(currentpos!=document.body.scrollTop)
sc();
}
document.onmousedown=sc
document.ondblclick=initialize
/script
pa/ppa/ppa/ppaa/ppaa/ppaa/p
paa/ppaa/ppaa/ppaa/ppaa/ppaa/p
paa/ppaa/ppaa/ppaa/ppaa/ppaa/p
paa/ppaa/ppaa/ppaa/ppa/p
/body
/html
如何让body中的文字不被选中?
HTML代码片段如下:
bodyonselectstart="returnfalse"aaa/body
如何让弹出的窗口不能关闭?
在新开的窗口中加入如下代码
bodyonunload=open(location.href)
/body
如何让浏览器在保存页面时保存失败?
HTML代码片段如下:
NOSCRIPT
Bstyle="color:black;background-color:#ffff"IFRAME/BSRC="*.html"
/Bstyle="color:black;background-color:#ffff"IFRAME/B
/NOSCRIPT
表单中如何用按钮实现reset?
html
head
script
functionaaa(){
document.forms[0].reset()
}
/script
/head
body
form
textarearows="2"name="S1"cols=""/textarea
inputtype="submit"values="提交"name="B1"
imagesrc="logo.gif"onclick=aaa()
/form
/body/html
进入网页时弹出的信息对话框
bodyonLoad="window.alert(‘‘‘‘欢迎光临本站‘‘‘‘)"
关闭窗口后弹出对话框
bodyonUnload="window.alert(‘‘‘‘谢谢你的光临!欢迎下次再来!‘‘‘‘)"
告别提示
bodyonUnload=alert("再见,感谢你的访问!")
右键菜单的制作
OBJECTid=menutype="application/x-oleobject"classid="clsid:adba6-d8ff-cf--aab7a"
PARAMname="Command"value="RelatedTopics,menu"
PARAMname="Item1"value="动易;"
PARAMname="Item2"value="搜狐;"
PARAMname="Item3"value="新浪;"
PARAMname="Item4"value="网易;"
PARAMname="Item5"value="互动学院;"
/OBJECTscriptif(document.all)document.body.onmousedown=newFunction("if(event.button==2)menu.Click();")/script
下拉菜单
objectid=HHCtrltype="application/x-oleobject"classid="clsid:adba6-d8ff-cf--aab7a"PARAMname="Command"value="RelatedTopics,Menu"PARAMname="Item1"value="aspease;"PARAMname="Item2"value="byhu;"PARAMname="Item3"value="lzz;"/objectahref=javascript:HHCtrl.Click()title="下拉菜单"下拉菜单/a
html简单网页代码怎么写?1、在电脑桌面空白处单击右键,新建一个记事本并打开
2、在新建文件中输入如下代码。html语言都是以htmlheadtitlebody等标签开始,以/html/head/title/body标签作为结束。
3、接下来要在title/title标签中间输入标题(如个人网页)在body/body中输入“自己做的第一个网页,厉害吧!”完成后点击保存
沉浸式go-cache源码阅读!
大家好,我是豆小匠,这期将带领大家探索go-cache的内部实现,深入理解本地缓存机制,并分享一些阅读源码的实用技巧。
首先,我们从源码入手,Goland中仅需关注cache.go和sharded.go两个文件,总共行代码,是不错的学习资源。通过README.md,可以了解到包的使用方法。
创建缓存实例时,我们注意到它依赖于清理间隔,而非实时过期删除。这引出了一个问题:如何在逻辑上处理过期缓存?我们开始在cache.go中寻找答案。
首先,我们关注Cache结构体,它定义了整个缓存的框架。接下来,重点阅读New函数,这里使用了runtime.SetFinalizer来确保即使对象被设置为nil,清理协程的GC回收也受到影响。
通过源码解析,我们明白,如果清理协程与Cache对象关联,即使对象不再活跃,GC仍无法立即回收。再深入Get方法,你会发现,缓存失效并非通过key是否存在,而是通过item中的过期时间判断,定时清理主要为了释放存储空间。
最后,我们对常用的方法进行挑选,梳理cache类的成员变量和功能,通过创建图示的方式,来帮助我们更好地理解和记忆。值得注意的是,onEvicted是删除key的回调函数,而sharded.go是未公开的分片缓存实验代码。