最新要闻
- 环球播报:日本拉面店禁止边吃边看手机!店长给出的理由看醉了:有损厨师尊严
- 4个月了!AMD RX 7900 XTX旗舰卡终于降价:一度杀到6399元 降幅20%
- 世界快播:Nimo全球盛典圆满举行 提速布局全球化业务
- 当前观点:客车司机开车途中嗦粉吓坏乘客 双手离开方向盘:官方回应
- HTC发布Wildfire入门级4G手机:国产展锐处理器、3G内存
- 焦点速看:中国人自己的智能车身控制系统!比亚迪云辇实现0-1突破:填补国内技术空白
- 全球时讯:比亚迪云辇发布 王传福:传统豪华看车标、新能源豪华看技术
- 全球车身控制技术最高水平:比亚迪U9搭云辇X“跳舞”登台
- 伊宝馨2022年净利3220.96万同比增长161.43% 销售规模增加
- 观点:未势能源首发“木星”车载液氢储氢系统:一罐氢气续航超1000km
- 最新消息:你掉的不是头发 而是“钻石”!
- 世界速读:长辈送礼绝不踩坑!平仄普洱茶大促:高档礼盒装到手99元
- 就等小米13 Ultra发布了!米粉晒工资:称“资金已到位”
- 比亚迪云辇技术来了:四套系统 汽车会自己“看路”
- 领失业金的年轻人,重新思考工作的意义
- 看点:北上高铁出现大规模晚点 官方回应:故障抢修完毕 有序恢复通行
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
世界通讯!mit 6.824 lab1分析
6.824 lab1 笔记
1. 阅读论文
略
2. 官网rules & hints
2.1 rules
- map阶段每个worker应该把中间文件分成nReduce份,nReduce是reduce任务的数量
- worker完成reduce任务后生成文件名
mr-out-X
mr-out-X
文件每行应该是"%v %v"
格式,参考main/mrsequential.go
- worker处理完map任务,应该把生成的中间文件放到当前目录中,便于worker执行reduce任务时读取中间文件
- 当所有任务完成时,
Done()
函数应该返回true,使得coordinator
退出 - 所有任务完成时,worker应该退出,方法是:
- 当worker调用rpc向coordinator请求任务时,连接不上coordinator,此时可以认为coordinator已经退出因为所有任务已经完成了
- 当worker调用rpc向coordinator请求任务时,coordinator可以向其回复所有任务已经完成
2.2 hints
刚开始可以修改
mr/worker.go
"s ``Worker()向coordinator 发送一个RPC请求一个任务。然后修改coordinator回复一个文件名,代表空闲的map任务。然后worker根据文件名读取文件,调用
wc.so-Map函数,调用Map函数可参考
mrsequential.go`如果修改了
mr/
目录下任何文件,应该重新build MapReduce plugins,go build -buildmode=plugin ../mrapps/wc.go
(相关资料图)
worker处理完map任务后产生的中间文件命名格式
mr-X-Y
,x是map任务的编号,y是reduce任务编号。// 初始文件,通过命令行传入的,如// pg-being_ernest.txt pg-dorian_gray.txt pg-frankenstein.txt // len(files) = 3 nReduce = 4// 中间文件 x:map任务的编号 y:reduce任务编号// mr-0-0 mr-1-0 mr-2-0// mr-0-1 mr-1-1 mr-2-1// mr-0-2 mr-1-2 mr-2-2// mr-0-3 mr-1-3 mr-2-3
map任务存储数据到文件可以使用json格式,便于reduce任务读取
// map enc := json.NewEncoder(file) for _, kv := ... { err := enc.Encode(&kv) // reduce dec := json.NewDecoder(file) for { var kv KeyValue if err := dec.Decode(&kv); err != nil { break } kva = append(kva, kv) }
map阶段使用
ihash(key)
函数把key映射到哪个reduce任务,如某个worker取得了2号map任务,ihash("apple") = 1
,那么就应该把该key放到mr-2-1
文件中可以参考
mrsequential.go
代码:读取初始输入文件、排序key、存储reduce输出文件coordinator是rpc server,将会被并发访问,需要对共享变量加锁
若当前未有空闲的map任务可以分配,worker应该等待一段时间再请求任务,若worker频繁请求任务,coordinator就会频繁加锁、访问数据、释放锁,浪费资源和时间。如使用
time.Sleep()
,worker可以每隔一秒发送一次请求任务rpccoordinator无法辨别某个worker是否crash,有可能某个worker还在运行,但是运行极其慢(由于硬件损坏等原因),最好的办法是:coordinator监控某个任务,若该任务未在规定时间内由worker报告完成,那么coordinator可以把该任务重新分配给其他worker,该lab规定超时时间是10s
为了确保某个worker在写入文件时,不会有其他worker同时写入;又或者是某个worker写入文件时中途退出了,只写了部分数据,不能让这个没写完的文件让其他worker看到。可以使用临时文件
ioutil.TempFile
,当写入全部完成时,再使用原子重命名os.Rename
。Go RPC只能传struct中大写字母开头的变量
调用RPC
call()
函数时,reply struct应该为空,不然会报错reply := SomeType{} call(..., &reply)
3. 架构设计
3.1 RPC设计
在该lab中,我们需要两个RPC,一个是callTask RPC
向coordinator请求一个任务,一个是callTaskDone RPC
向coordinator报告某个任务的完成,以下皆在rpc.go
中定义
首先定义一个枚举变量,表示coordinator给worker分配的任务类型,也可用来表示coordinator当前的phase
type taskType intconst ( // map任务mapType taskType = iota // reduce任务reduceType // 当前没有空闲任务,请等待 waitting // 已经完成全部任务,可以退出了done)
定义拉取任务RPC的args和reply struct
CallTaskArgs
中不需要包含变量,只需要让coordinator知道该worker正在请求一个任务,coordinator会随机选择空闲任务进行分配填入CallTaskReply
中CallTaskReply
包含以下变量:FileName
:map阶段,worker需要知道具体的文件名才能解析该文件tp
:指示该任务的具体类型TaskID
:worker将该变量放入CallTaskDoneArgs
中,coordinator可以迅速定位Task[TaskID],并且在reduce阶段中,搭配nFiles
变量,worker读取mr-0-TaskID
、mr-1-TaskID
....mr-nFiles-1-TaskID
文件nFiles
:初始文件的数量,用于搭配TaskID
,在上面已介绍nReduce
:用于map阶段,ihash(key) % nReduce
type CallTaskArgs struct {}type CallTaskReply struct {FileName stringTaskID inttp taskTypenFiles intnReduce int}
定义报告任务完成RPC的args和reply struct
TaskID
变量作用在CallTaskReply: TaskID
中提及tp
的作用是用于coordinator判断该RPC是否是合法的,举例:worker-1成功请求到map-1任务,但是因为worker-1节点硬件问题处理缓慢而导致coordinator检测到该map-1任务超时,于是把map-1任务分配给了worker-2。等到某个时间点,已经完成所有map任务,coordinator进入到了reduce阶段,但此时worker-1节点才刚运行完map-1任务并报告给coordinator,coordinator检测到当前是reduce阶段,但收到报告完成的rpc是map类型,不会对其进行任何操作。type CallTaskDoneArgs struct {TaskID inttp taskType}type CallTaskDoneReply struct {}
3.2 Coordinator
3.2.1 结构体设计
type taskState intconst (spare taskState = iotaexecutingfinish)type task struct {fileName stringid intstate taskStatestart time.Time}
首先设计一个task struct,该结构体代表一个任务
filename
:在map阶段,用于coordinator告知worker要读取的初始文件id
: 该任务的id,传给worker,作用在RPC设计中提及state
:任务有三个状态:空闲、执行中、已完成。若空闲则可以分配给worker;若执行中,则监视该任务是否超时start
:任务刚开始执行的时间
type Coordinator struct {// Your definitions here.mu sync.Mutexstate taskTypetasks []*taskmapChan chan *taskreduceChan chan *tasknReduce intnFiles intfinished int}
接着设计主要Coordinator结构体
state
:当前系统的状态,map阶段(分配map任务)、reduce阶段(分配reduce任务)、全部完成done(可以结束系统运行)tasks
: *task的切片,维护了一组任务mapChan
、reduceChan
:用于分发map、reduce任务的channel。map阶段,若有空闲map任务,则放至channel中,当有worker请求任务时,则可取出来。reduce阶段同理finished
:当前已完成任务的数量。map阶段,若finished == nFiles
,则表示所有map任务完成,可以进入reduce阶段。reduce阶段同理,进入done
3.2.2 初始化
func MakeCoordinator(files []string, nReduce int) *Coordinator {c := Coordinator{}// Your code here.c.mapPhase(files, nReduce)go c.watch()c.server()return &c}func (c *Coordinator) mapPhase(files []string, nReduce int) {c.state = mapType //设置系统状态为map阶段c.nReduce = nReduce c.nFiles = len(files)c.tasks = make([]*task, c.nFiles)c.mapChan = make(chan *task, c.nFiles) // c.nFiles长度的map channelfor i := 0; i < c.nFiles; i++ {c.tasks[i] = &task{fileName: files[i], id: i}c.mapChan <- c.tasks[i] // 刚开始所有任务都是空闲状态,放入channel中}}
系统刚开始时即map阶段,mapPhase
初始化Coordinator结构体。然后启动c.watch()
协程,用于监视任务是否超时,放后面讲
3.2.3 分配任务
func (c *Coordinator) CallTask(args *CallTaskArgs, reply *CallTaskReply) error {c.mu.Lock()defer c.mu.Unlock()if c.state == done {reply.Tp = done} else if c.state == mapType {switch len(c.mapChan) > 0 {case true:task := <-c.mapChanc.setReply(task, reply)case false:reply.Tp = waitting}} else {switch len(c.reduceChan) > 0 {case true:task := <-c.reduceChanc.setReply(task, reply)case false:reply.Tp = waitting}}return nil}func (c *Coordinator) setReply(t *task, reply *CallTaskReply) {if t.state == finish {reply.Tp = waittingreturn}reply.Tp = c.statereply.TaskID = t.idreply.NReduce = c.nReducereply.NFiles = c.nFilesreply.FileName = t.fileNamet.state = executingt.start = time.Now()}
分配任务的主要函数,worker处会调用call("Coordinator.CallTask", &args, &reply)
。
- 若当前系统状态为done,则返回done,告知worker可以退出了
- 若当前系统状态为map阶段:如果当前有任务可以分配
len(c.mapChan) > 0
,则取出一个task,调用c.setReply(task, reply)
,将任务的相关信息填入reply中,并把task的当前状态设为执行中,开始时间设为time.Now()
。如果没有可分配的任务,则设reply.Tp = waitting
,让worker等待一会再请求任务 - 若当前系统状态为reduce阶段:同上
3.2.4 任务完成
func (c *Coordinator) CallTaskDone(args *CallTaskDoneArgs, reply *CallTaskDoneReply) error {c.mu.Lock()defer c.mu.Unlock()if c.state != args.Tp || c.state == done {return nil}if c.tasks[args.TaskID].state != finish {c.tasks[args.TaskID].state = finishc.finished++//fmt.Printf("task %v done\n", args.TaskID)if c.state == mapType && c.finished == c.nFiles {c.reducePhase()} else if c.state == reduceType && c.finished == c.nReduce {close(c.reduceChan)c.state = done}}return nil}func (c *Coordinator) reducePhase() {//fmt.Printf("reduce phase\n")close(c.mapChan)c.state = reduceTypec.tasks = make([]*task, c.nReduce)c.finished = 0c.reduceChan = make(chan *task, c.nReduce)for i := 0; i < c.nReduce; i++ {c.tasks[i] = &task{id: i}c.reduceChan <- c.tasks[i]}}
worker处会调用call("Coordinator.CallTaskDone", &args, &reply)
来报告某任务的完成
首先判断c.state != args.Tp
,即报告完成的任务类型和当前系统状态不匹配,可能发生在该情况:work-1请求了map-1任务,但是work-1运行太缓慢导致Coordinator监测到map-1任务超时,于是把map-1任务分配给了work-2。当所有map任务完成时,Coordinator进入了reduce阶段,这时work-1才报告map-1任务完成,与当前系统状态不匹配,故会直接返回
若该任务未完成,则将该任务标记未已完成,c.finished++
。
- 如果当前为map阶段并且所有map任务已完成
c.state == mapType && c.finished == c.nFiles
,则进入reduce阶段:- 关闭map channel
- 将系统状态设为reduce
- 重置c.tasks为一系列reduce任务
- 创建长度为c.nReduce的reduce channel
- 放入任务
- 如果当前为reduce阶段并且所有map任务已完成
c.state == reduceType && c.finished == c.nReduce
,则进入done阶段:- 关闭reduce channel
- 将系统状态设为done
3.2.5 监测超时任务goroutine
func (c *Coordinator) watch() {for {time.Sleep(time.Second)c.mu.Lock()if c.state == done {return}for _, task := range c.tasks {if task.state == executing && time.Since(task.start) > timeout {task.state = spareswitch c.state {case mapType:c.mapChan <- taskcase reduceType:c.reduceChan <- task}}}c.mu.Unlock()}}
如果当前系统状态为done了,可以退出协程了
循环任务列表,如果该任务状态是正在执行但是超时了time.Since(task.start) > timeout
(time.Since可以计算系统当前时间距离start过去了多少时间),将该任务状态重置为空闲状态,并且根据系统当前状态,把该任务重新放入对应的channel中
3.3 Worker
3.3.1 主流程
func Worker(mapf func(string, string) []KeyValue,reducef func(string, []string) string) {for {args := CallTaskArgs{}reply := CallTaskReply{}ok := call("Coordinator.CallTask", &args, &reply)//now := time.Now()if ok {switch reply.Tp {case mapType:executeMap(reply.FileName, reply.NReduce, reply.TaskID, mapf)case reduceType:executeReduce(reply.NFiles, reply.TaskID, reducef)case waitting:time.Sleep(time.Second * 2)continuecase done:os.Exit(0)}} else {time.Sleep(time.Second * 2)continue}//fmt.Printf("finish task: %v %v use %v\n", reply.TaskID, rs(reply.Tp), time.Since(now).Seconds())a := CallTaskDoneArgs{reply.TaskID, reply.Tp}r := CallTaskDoneReply{}call("Coordinator.CallTaskDone", &a, &r)time.Sleep(time.Second * 2)}}
首先向Coordinator发送请求任务rpc:
- map任务:执行
- reduce任务:执行
- waitting:当前Coordinator没有空闲任务,sleep一段时间再请求
- done:所有任务已完成,退出
任务执行完成后,报告任务完成
3.3.2 执行map任务
func executeMap(fileName string, nReduce, taskID int, mapf func(string, string) []KeyValue) {file, err := os.Open(fileName)if err != nil {log.Fatalf("cannot open %v", fileName)}content, err := ioutil.ReadAll(file)if err != nil {log.Fatalf("cannot read %v", fileName)}file.Close()kva := mapf(fileName, string(content)) // 上面的代码参考mrsequential.gofiles := []*os.File{}tmpFileNames := []string{}encoders := []*json.Encoder{}for i := 0; i < nReduce; i++ {tempFile, err := ioutil.TempFile("./", "")if err != nil {log.Fatalf("cannot open temp file")}files = append(files, tempFile)tmpFileNames = append(tmpFileNames, tempFile.Name())encoders = append(encoders, json.NewEncoder(tempFile))}for _, kv := range kva {n := ihash(kv.Key) % nReduceencoders[n].Encode(kv)}for i := 0; i < nReduce; i++ {files[i].Close()os.Rename(tmpFileNames[i], "./"+intermediateFileName(taskID, i))}}
在当前目录创建nReduce个临时文件ioutil.TempFile("./", "")
,使用该临时文件创建json.Encoder
(在hints第四条),使用ihash
函数把每个key映射到哪个文件,写入json格式,然后对每个临时文件重命名为mr-x-y
格式
生成中间文件名函数:
func intermediateFileName(x, y int) string {return fmt.Sprintf("mr-%v-%v", x, y)}
3.3.3 执行reduce
func executeReduce(nFiles, taskID int, reducef func(string, []string) string) {kvs := []KeyValue{}for i := 0; i < nFiles; i++ {filename := intermediateFileName(i, taskID) // 读取每个中间文件file, err := os.Open(filename)if err != nil {log.Fatalf("cannot open %v", filename)} // 参考hints第四条,从文件中取出json格式的每条数据decoder := json.NewDecoder(file)for {var kv KeyValue // 已读到文件末尾if err := decoder.Decode(&kv); err != nil {break}kvs = append(kvs, kv)}file.Close()} // 参考mrsequential.gooname := fmt.Sprintf("mr-out-%v", taskID)tempFile, _ := ioutil.TempFile("./", "")tempFileName := tempFile.Name()sort.Sort(ByKey(kvs))for i := 0; i < len(kvs); {j := i + 1for j < len(kvs) && kvs[j].Key == kvs[i].Key {j++}values := []string{}for k := i; k < j; k++ {values = append(values, kvs[k].Value)}output := reducef(kvs[i].Key, values)fmt.Fprintf(tempFile, "%v %v\n", kvs[i].Key, output)i = j}tempFile.Close()os.Rename(tempFileName, "./"+oname)}
关键词:
全球消息!解释一下布隆过滤器原理
世界通讯!mit 6.824 lab1分析
每日热点:AQS源码学习
环球播报:日本拉面店禁止边吃边看手机!店长给出的理由看醉了:有损厨师尊严
4个月了!AMD RX 7900 XTX旗舰卡终于降价:一度杀到6399元 降幅20%
世界快播:Nimo全球盛典圆满举行 提速布局全球化业务
最新:screenfetch报错 /usr/bin/screenfetch: 行 1851: -: 语法错误:需要操作数(错误记号是 "-") 的一种解决方法
天天精选!直线光栅化-Bresenham算法
当前观点:客车司机开车途中嗦粉吓坏乘客 双手离开方向盘:官方回应
HTC发布Wildfire入门级4G手机:国产展锐处理器、3G内存
焦点速看:中国人自己的智能车身控制系统!比亚迪云辇实现0-1突破:填补国内技术空白
全球时讯:比亚迪云辇发布 王传福:传统豪华看车标、新能源豪华看技术
全球车身控制技术最高水平:比亚迪U9搭云辇X“跳舞”登台
伊宝馨2022年净利3220.96万同比增长161.43% 销售规模增加
天天视讯!百度松果菁英班--oj赛(第一次)
观点:未势能源首发“木星”车载液氢储氢系统:一罐氢气续航超1000km
最新消息:你掉的不是头发 而是“钻石”!
世界速读:长辈送礼绝不踩坑!平仄普洱茶大促:高档礼盒装到手99元
就等小米13 Ultra发布了!米粉晒工资:称“资金已到位”
比亚迪云辇技术来了:四套系统 汽车会自己“看路”
焦点速读:Android开发_记事本(2)数据库
领失业金的年轻人,重新思考工作的意义
看点:北上高铁出现大规模晚点 官方回应:故障抢修完毕 有序恢复通行
环球快讯:马斯克离开OpenAI内幕:大权独揽想法被拒 10亿美元打水漂
形同灾难电影!四子王旗沙尘暴红警:天空变橙红色
全球时讯:好吃无负担:诺梵55%~100%黑巧巧克力9.9元大促
全球今头条!Windmill 搭建个人任务中心
环球微头条丨市建局协办活动 重塑龙城特色风情
环球快播:水平接近ChatGPT!昆仑万维国产大语言模型“天工”3.5来了
世界热门:《铁甲小宝》高清重制引热议 网友:这次好好看看小姐姐
苦等703天后 《生化危机8村庄》Denuvo加密移除:正版玩家终于不卡了
售价20多万续航仅160公里 日系车工业垃圾果然凉凉:今年只卖出15辆
环球新资讯:1TB手机干到2599元 手机降价原因找到了:闪存市场暴跌
欧菲光:公司已预约4月28日披露2022年度报告
天天最资讯丨借力函数计算 FC,HEROZ 打造专业级 AI 日本将棋服务
Excelize 发布 2.7.1 版本,Go 语言 Excel 文档基础库
环球今头条!WebAssembly 助力云原生:APISIX 如何借助 Wasm 插件实现扩展功能?
世界速看:记录-html-docs-js避坑指南
阿里正式加入ChatGPT战局,“通义千问”上线后表现如何?
全球消息!债市日报:4月10日
焦点关注:重磅!亚洲最大推力液体火箭发动机试车台调试完成
视焦点讯!堆料最足超大杯!小米13 Ultra确认可变光圈+全焦段四摄
全优大七座SUV!全新一代揽月正式上市 18.99万元起售
环球最新:2人观影全程用手机录制电影内容 目击者:中途还换了一个手机
米哈游《崩坏星穹铁道》姬子角色PV:成熟妩媚的大姐姐
今日快讯:“算力中国行”大型调研采访活动启动,将深入各地了解算力应用发展情况
天天日报丨刷题疑惑3
今日观点!TCP三次握手和四次挥手
【环球报资讯】收评:人工智能板块跳水 10只注册制主板新股大涨
播报:ChatGPT消除无聊的工作 诺奖得主:人类上四休三就靠AI
对标特斯拉Model Y 小鹏G6再放送 博主:兴奋起来吧、有电动尾翼
《名侦探柯南:黑铁的鱼影》新宣传图 成人版灰原哀太劲爆
精彩看点:第三次!沈腾主演《满江红》宣布密钥再次延期:上映至5月15日
亚马逊安抚员工:公司并没有在AI竞争中落下
天天微资讯!私募巨头阿波罗:美国信贷紧缩已开始
全球信息:【AIGC】ChatGPT 相关分享
互联网+医疗|如何满足各项安全合规要求
全球新资讯:一代“金牌配角” 中国香港知名演员吴耀汉去世:终年83岁
世界热消息:单核性能猛增68% 龙芯确认下代CPU性能达市场主流水平
【全球速看料】已做到笔记本电脑大小 国产量子计算机再进化:三大产品升级
环球信息:撞脸苹果!金立发布F1 Plus手机:刘海屏设计 国产8核CPU
《生化危机4:重制版》艾达王配音首次发声:亚裔女演员不止是花瓶
今日热文:广东2023年注会考试报名系统
世界即时:低至200元 / 月,火山引擎DataLeap帮你搭建企业级数据中台
天天短讯!奔驰EQG将采用革命性电池技术:续航、充电性能大增
全球看热讯:又是单踏板让车失控?重庆一特斯拉突然加速撞7车:官方确认车没问题驾驶员全责
mp3是什么格式的文件?怎么下载歌曲到MP3上?
怎么增大电脑虚拟内存页面文件大小?电脑虚拟内存页面怎么设置?
荣耀7i上市时间及价格是什么?荣耀7i配置和参数
英雄联盟s24赛季什么时候开始的?英雄联盟s24赛季结束时间
东部战区重磅发布:砺剑亮剑,驱虎打狐
天天短讯!Github使用教程
新资讯:在.NET7中使用MQTTnet简单实现MQTT通信
dnf成就格林赛罗斯战场怎么进?dnf成就格林赛罗斯战场任务怎么做?
焦点速读:击败《冰雪奇缘2》创新世界纪录!《超级马力欧兄弟》5天狂揽26亿票房
每日热文:自动驾驶是忽悠?蔚来李斌:目标是解放时间 减少事故
当前快讯:269元 米家快煮电饭煲4L预售:25分钟即熟
手工扯面+秘制辣油 西安饭庄油泼biangbiang面大促:6.66元/盒
用 Go 剑指 Offer 40. 最小的k个数 (Top K 问题)
快看点丨Web渗透测试流程
重点聚焦!SonarQube(CE)版集群环境下的高可用性实践
当前短讯!Java-泛型机制详解
最新快讯!Go常用库之GoRequests
十五五规划时期,未来十大新兴产业有哪些
世界视点!中兴Axon Pad来了:全球唯一5G双系统平板
医生误判将女孩恒牙当乳牙拔掉 科普:恒牙和乳牙区别
当前关注:日系车在中国真不香了!丰田、本田、日产、马自达全线暴跌
快资讯:iOS 16.4体验大翻车!苹果关闭降级通道:只许升新版
天天即时看!80/90后回忆杀!经典日漫《铁甲小宝》高清修复了:画质提升明显
【环球新要闻】全文索引:Apache Lucene(二)
Selenium做Web自动化踩过的坑
每日聚焦:通俗解释 JVM CAS 机制
世界观点:4月10日11时浙江嘉兴疫情最新消息 4月10日11时浙江嘉兴今日确诊人数
每日快播:米家空调巨省电首销:2匹大风量 2799元
环球热点!小米MIJIA智能音频眼镜发布!独特耳机眼镜二合一设计
世界热推荐:新手爸爸拿狗狗练拍嗝 网友:狗狗从来没享受过这么高的待遇
环球观察:周杰伦就不正当竞争起诉网易!4月17日开庭
日本非现金支付比例仅占36% 远落后于中国农村
重磅来了!
介绍一下sourcemap