当前位置: 首页 > news >正文

Ceph EC pg backfill run

pg的backfill请求也是发送到osd的work queue中与业务IO一起竞争。

PGRecovery::run backfill

 57 void PGRecovery::run( 58  OSD *osd, 59  OSDShard *sdata, 60  PGRef& pg, 61  ThreadPool::TPHandle &handle) 62 { 63  osd->do_recovery(pg.get(), epoch_queued, reserved_pushes, handle); 64  pg->unlock();                                   65 } 

OSD::do_recovery

  1. Call pg->start_recovery_ops
  2. 释放占用资源,并进行pg的下一轮恢复请求调度。
10018 void OSD::do_recovery( 
10019  PG *pg, epoch_t queued, uint64_t reserved_pushes, 
10020  ThreadPool::TPHandle &handle) 
10021 { 
... 
10031  float recovery_sleep = get_osd_recovery_sleep();   
... 
10070   dout(10) << "do_recovery starting " << reserved_pushes << " " << *pg << dendl; 
... 
10075   bool do_unfound = pg->start_recovery_ops(reserved_pushes, handle, &started); 
10076   dout(10) << "do_recovery started " << started << "/" << reserved_pushes 
10077       << " on " << *pg << dendl; 
... 
10089  service.release_reserved_pushes(reserved_pushes);            
10090 } 

PrimaryLogPG::start_recovery_ops

  1. 入参的max即osd_recovery_max_single_start,决定了pg得到执行一次recovery op后能同时恢复多少个object。
  2. 执行recovery请求必须是primary pg
  3. 检查是否设置了nobackfill。
  4. 检查是否人为设置了norebalance。
  5. 检查是否完成资源预约。
  6. Call recover_backfill。
12452 bool PrimaryLogPG::start_recovery_ops( 
12453  uint64_t max, 
12454  ThreadPool::TPHandle &handle, 
12455  uint64_t *ops_started) 
12456 { 
... 
12461  ceph_assert(is_primary());                       
... 
12465  ceph_assert(recovery_queued);                
12466  recovery_queued = false;  // recovery op已经得到,pg的下一轮恢复需要重新入队检查 
... 
12502  if (recovering.empty() &&                      
12503    state_test(PG_STATE_BACKFILLING) && 
12504    !backfill_targets.empty() && started < max && 
12505    missing.num_missing() == 0 && 
12506    waiting_on_backfill.empty()) { 
12507   if (get_osdmap()->test_flag(CEPH_OSDMAP_NOBACKFILL)) {         
12508    dout(10) << "deferring backfill due to NOBACKFILL" << dendl; 
12509    deferred_backfill = true; 
12510   } else if (get_osdmap()->test_flag(CEPH_OSDMAP_NOREBALANCE) &&     
12511        !is_degraded()) { 
12512    dout(10) << "deferring backfill due to NOREBALANCE" << dendl; 
12513    deferred_backfill = true; 
12514   } else if (!backfill_reserved) {                    
12515    dout(10) << "deferring backfill due to !backfill_reserved" << dendl; 
12516    if (!backfill_reserving) { 
12517     dout(10) << "queueing RequestBackfill" << dendl; 
12518     backfill_reserving = true; 
12519     queue_peering_event( 
12520      PGPeeringEventRef( 
12521       std::make_shared<PGPeeringEvent>( 
12522        get_osdmap_epoch(), 
12523        get_osdmap_epoch(), 
12524        RequestBackfill()))); 
12525    } 
12526    deferred_backfill = true; 
12527   } else { 
12528    started += recover_backfill(max - started, handle, &work_in_progress);   
12530  } 
12531 
12532  dout(10) << " started " << started << dendl; 

PrimaryLogPG::recover_backfill

  1. pg第一次执行backfill,需要把peer_backfill_info、backfill_info、backfills_in_flight重置。
  2. 更新primary的begin obj为last_backfill_started。
  3. 检查backfill info(记录恢复进度)是否要更新恢复对象的范围,一次最多scan 512个obj。
  4. 遍历backfill targets,更新所有peer_backfill_info的begin对象。

Backfill targets是要回填的目标pg shard,例如:

[1,28,120,278,90,17,210,322,184,186]/[1,121,192,278,106,17,60,322,31,53]为一个pg当前的up set和acting set。由于扩容导致up set与acting set不一致,需要backfill,backfill target为:

[28(1),90(4),120(2),184(8),186(9),210(6)]

  1. 如果backfill_info为空,表示没有要backfill的obj。
  2. 获取所有backfill targets中排序最小的obj作为check obj。

有了check obj之后,backfill target的pg shard都要向check obj看齐:

  • check obj < primary的begin obj,说明拥有该check obj的peer需要删除该check obj
  • check obj >= primary的begin obj,定义了need_ver_targs, missing_targs, keep_ver_targs, skip_targs 4个容器。
  • 如果peer begin obj的版本与primary begin obj的版本不一致,拥有该check obj的peer需要加入need_ver_targs进行修复。
  • Peer begin obj ver == begin ver的peer不需要修复,加入keep_ver_targs。
  • 缺少begin obj的peer加入missing_targs进行修。
  • peer的last backfill >= begin obj,该peer可以跳过该obj的恢复,加入skip_targs。
  1. 到prep_backfill_object_push说明已经确定需要恢复obj的peer,生成RecoveryOp,并记录当前正在恢复的obj到backfills_in_flight容器。
  2. ops++之后仍然小于max(osd_recovery_max_single_start),则pg可以继续执行下一个obj的恢复,这里可以作为提速。
  3. pgbackend->run_recovery_op 将recoveryOp封装成message。
13049 uint64_t PrimaryLogPG::recover_backfill( 
13050  uint64_t max, 
13051  ThreadPool::TPHandle &handle, bool *work_started) 
13052 { 
... 
13061  if (new_backfill) {   // pg第一次执行backfill,需要把peer_backfill_info、backfill_info、backfills_in_flight重置 
13062   // on_activate() was called prior to getting here 
13063   ceph_assert(last_backfill_started == earliest_backfill()); 
13064   new_backfill = false; 
13065 
13066   // initialize BackfillIntervals 
13067   for (set<pg_shard_t>::iterator i = backfill_targets.begin(); 
13068     i != backfill_targets.end(); 
13069     ++i) { 
13070    peer_backfill_info[*i].reset(peer_info[*i].last_backfill); 
13071   } 
13072   backfill_info.reset(last_backfill_started); 
13073 
13074   backfills_in_flight.clear(); 
13075   pending_backfill_updates.clear(); 
13076  } 
13077 
... 
13089  // update our local interval to cope with recent changes 
13090  backfill_info.begin = last_backfill_started;   // 更新begin对象为last_backfill_started 
13091  update_range(&backfill_info, handle);        
13092 
13093  unsigned ops = 0; 
13094  vector<boost::tuple<hobject_t, eversion_t, pg_shard_t> > to_remove;       // 放置要删除的obj 
13095  set<hobject_t> add_to_stat; 
13096 
13097  for (set<pg_shard_t>::iterator i = backfill_targets.begin();          // 遍历backfill targets,更新peer_backfill_info的begin对象 
13098    i != backfill_targets.end(); 
13099    ++i) { 
13100   peer_backfill_info[*i].trim_to(                   
13101    std::max(peer_info[*i].last_backfill, last_backfill_started)); 
13102  } 
13103  backfill_info.trim_to(last_backfill_started); 
13104 
13105  PGBackend::RecoveryHandle *h = pgbackend->open_recovery_op();           
13106  while (ops < max) { 
... 
13116   dout(20) << "  my backfill interval " << backfill_info << dendl; 
... 
13148   if (backfill_info.empty() && all_peer_done()) {                
13149    dout(10) << " reached end for both local and all peers" << dendl; 
13150    break; 
13151   } 
13152 
13153   // Get object within set of peers to operate on and 
13154   // the set of targets for which that object applies. 
13155   hobject_t check = earliest_peer_backfill();           
13156 
13157   if (check < backfill_info.begin) {                       
... 
13189   } else { 
13190    eversion_t& obj_v = backfill_info.objects.begin()->second; 
13191 
13192    vector<pg_shard_t> need_ver_targs, missing_targs, keep_ver_targs, skip_targs;                                             
13193    for (set<pg_shard_t>::iterator i = backfill_targets.begin(); 
13194      i != backfill_targets.end(); 
13195      ++i) { 
13196     pg_shard_t bt = *i; 
13197     BackfillInterval& pbi = peer_backfill_info[bt]; 
13198     // Find all check peers that have the wrong version 
13199     if (check == backfill_info.begin && check == pbi.begin) { 
13200      if (pbi.objects.begin()->second != obj_v) { 
13201       need_ver_targs.push_back(bt); 
13202      } else { 
13203       keep_ver_targs.push_back(bt); 
13204      } 
13205     } else { 
13206      pg_info_t& pinfo = peer_info[bt]; 
13207 
13208      // Only include peers that we've caught up to their backfill line 
13209      // otherwise, they only appear to be missing this object 
13210      // because their pbi.begin > backfill_info.begin. 
13211      if (backfill_info.begin > pinfo.last_backfill) 
13212       missing_targs.push_back(bt); 
13213      else 
13214       skip_targs.push_back(bt); 
13215     } 
13216    } 
... 
13225    if (!need_ver_targs.empty() || !missing_targs.empty()) {        
13227     ceph_assert(obc); 
13228     if (obc->get_recovery_read()) { 
13229      if (!need_ver_targs.empty()) { 
13230       dout(20) << " BACKFILL replacing " << check 
13231          << " with ver " << obj_v 
13232          << " to peers " << need_ver_targs << dendl; 
13233      } 
13234      if (!missing_targs.empty()) {                       
13235       dout(20) << " BACKFILL pushing " << backfill_info.begin 
13236         << " with ver " << obj_v 
13237         << " to peers " << missing_targs << dendl; 
13238      } 
13239      vector<pg_shard_t> all_push = need_ver_targs; 
13240      all_push.insert(all_push.end(), missing_targs.begin(), missing_targs.end());  // 记录所有要推送的target shard 
13241 
13242      handle.reset_tp_timeout();    // 防止线程心跳超时 
13243      int r = prep_backfill_object_push(backfill_info.begin, obj_v, obc, all_push, h);     
... 
13249      ops++;                                  
... 
13276  } 
... 
13315  pgbackend->run_recovery_op(h, get_recovery_op_priority()); 

ECBackend::run_recovery_op

  1. 遍历RecoveryOp
  2. Call continue_recovery_op生成RecoveryMessages
  3. dispatch_recovery_messages完成ECSubRead请求发送
 715 void ECBackend::run_recovery_op( 716  RecoveryHandle *_h, 717  int priority) 718 { 719  ECRecoveryHandle *h = static_cast<ECRecoveryHandle*>(_h); 720  RecoveryMessages m; 721  for (list<RecoveryOp>::iterator i = h->ops.begin(); 722    i != h->ops.end(); 723    ++i) { 724   dout(10) << __func__ << ": starting " << *i << dendl; 725   ceph_assert(!recovery_ops.count(i->hoid)); 726   RecoveryOp &op = recovery_ops.insert(make_pair(i->hoid, *i)).first->second; 727   continue_recovery_op(op, &m); 728  } 729 730  dispatch_recovery_messages(m, priority); 731  send_recovery_deletes(priority, h->deletes); 732  delete _h; 733 } 

ECBackend::continue_recovery_op

恢复过程表示为一个状态机:

  • IDLE:IDLE为recovery op的初始状态。读取将被启动,状态转为READING
  • READING:正在等待一次读取操作,一旦完成,转到WRITING状态。
  • WRITING:等待push完成,一旦完成,转到COMPLETE状态。
  1. get_min_avail_to_read_shards找到存在缺失object的pg shard,因为需要在这些pg shard上读取object来push到新的pg shard上。
  2. 读取请求封装到RecoveryMessages
 567 void ECBackend::continue_recovery_op( 568  RecoveryOp &op, 569  RecoveryMessages *m) 570 { 571  dout(10) << __func__ << ": continuing " << op << dendl; 572  while (1) { 573   switch (op.state) { 574   case RecoveryOp::IDLE: { 575    // start read 576    op.state = RecoveryOp::READING; 577    ceph_assert(!op.recovery_progress.data_complete); 578    set<int> want(op.missing_on_shards.begin(), op.missing_on_shards.end()); 579    uint64_t from = op.recovery_progress.data_recovered_to; 580    uint64_t amount = get_recovery_chunk_size(); 581 582    if (op.recovery_progress.first && op.obc) { 583     /* We've got the attrs and the hinfo, might as well use them */ 584     op.hinfo = get_hash_info(op.hoid); 585     ceph_assert(op.hinfo); 586     op.xattrs = op.obc->attr_cache; 587     encode(*(op.hinfo), op.xattrs[ECUtil::get_hinfo_key()]); 588    } 589 590    map<pg_shard_t, vector<pair<int, int>>> to_read;                    591    int r = get_min_avail_to_read_shards(                         592     op.hoid, want, true, false, &to_read); 593    if (r != 0) { 594     // we must have lost a recovery source 595     ceph_assert(!op.recovery_progress.first); 596     dout(10) << __func__ << ": canceling recovery op for obj " << op.hoid 597         << dendl; 598     get_parent()->cancel_pull(op.hoid); 599     recovery_ops.erase(op.hoid); 600     return; 601    } 602    m->read( 603     this, 604     op.hoid, 605     op.recovery_progress.data_recovered_to, 606     amount, 607     std::move(want), 608     to_read, 609     op.recovery_progress.first && !op.obc); 610    op.extent_requested = make_pair( 611     from, 612     amount); 613    dout(10) << __func__ << ": IDLE return " << op << dendl; 614    return; 615   } 

后面就是等待读取请求回复,收到所有的回复之后,发起push。收到所有的pushreply之后说明该obj完成backfill。以此重复,完成pg所有需要backfill的obj。

相关文章:

Ceph EC pg backfill run

pg的backfill请求也是发送到osd的work queue中与业务IO一起竞争。 PGRecovery::run backfill 57 void PGRecovery::run( 58 OSD *osd, 59 OSDShard *sdata, 60 PGRef& pg, 61 ThreadPool::TPHandle &handle) 62 { 63 osd->do_recovery(pg.get(), epoch_queued…...

腾讯云服务器地域怎么选?广州上海北京?

腾讯云服务器地域有什么区别&#xff1f;怎么选择比较好&#xff1f;地域选择就近原则&#xff0c;距离地域越近网络延迟越低&#xff0c;速度越快。关于地域的选择还有很多因素&#xff0c;地域节点选择还要考虑到网络延迟速度方面、内网连接、是否需要备案、不同地域价格因素…...

Apple Configurator iphone ipad 设备管控 描述文件使用方法

一、准备 App Store 下载安装 Apple Configurator 二、Apple Configurator 注册组织&#xff0c; -----------这个组织可以是个人&#xff0c;或者其它组织导出-------再导入进来&#xff1a; 三、描述文件配置&#xff1a;“” 根据管控需求进行配置 “” 四、使用 Ap…...

Linux 管道(pipe)用法

在 Linux 中&#xff0c;管道&#xff08;pipe&#xff09;是一种特殊的机制&#xff0c;用于连接一个进程的标准输出到另一个进程的标准输入。通过使用管道&#xff0c;可以将一个命令的输出直接传递给另一个命令进行处理&#xff0c;实现了进程之间的通信和数据传输。 管道的…...

元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型

今天在写ts文件的过程中&#xff0c;我遍历了一个对象&#xff0c;然后取值的时候发现爆红,如下图&#x1f447; 经过我一通排查&#xff08;原因我对ts也不是很熟练&#xff09;&#xff0c;了解到大致意思是说key的值类型不是string类型&#xff0c;在javascript中是默认给你…...

34、springboot切换内嵌Web服务器(Tomcat服务器)与 生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS

知识点1&#xff1a;springboot切换内嵌Web服务器&#xff08;Tomcat服务器&#xff09; 知识点2&#xff1a;生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS ★ Spring Boot默认的Web服务器&#xff08;Tomcat&#xff09; ▲ 基于Servlet的应用&#xff08;使用Spring MV…...

3种CSS实现背景图片全屏铺满自适应的方式

01 margin:0px; background: url(images/bg.png) no-repeat; background-size:100% 100%; background-attachment:fixed; url(images/beijing.png)——图片路径的位置&#xff1b; no-repeat—— 图片不重复&#xff1b; center 0px——center是距离页面左边的定位&#xf…...

M1 Pro 利用docker 搭建pytho2的开发环境,以vscode连接开发为例

使用 M1 Pro &#xff08;不支持python2的安装&#xff09;开发&#xff0c;需要使用 Python 2.7 的环境&#xff0c;在使用 pyenv 安装 Python 2 时遇到了各种奇怪的问题。最终&#xff0c;我决定使用 Docker 搭建开发环境&#xff0c;并使用 VS Code 连接到本地容器。以下是详…...

MySQL概述,架构原理

一.MySQL简介 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典的MySQL AB公司开发&#xff0c;后被oracle公司收购&#xff0c;MySQL是当下最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&#xff0c;MySQL是最好的RDBMS&#xff08;Relational Database Man…...

Three.js实现模型,模型材质可拖拽效果 DragControls

Three.js提供了一个拖拽的API DragControls 用于实现模型材质拖拽效果 DragControls&#xff1a;是一个用于在Three.js中实现拖拽控制的辅助类。它简化了在Three.js中实现拖拽物体的过程。 DragControls的构造函数接受三个参数&#xff1a; objects&#xff1a;一个包含需要…...

机器学习笔记之优化算法(二十)牛顿法与正则化

机器学习笔记之优化算法——再回首:牛顿法与正则化 引言回顾&#xff1a;经典牛顿法及其弊端牛顿法&#xff1a;算法步骤迭代过程中可能出现的问题正则化 Hessian Matrix \text{Hessian Matrix} Hessian Matrix与相应问题 引言 本节我们介绍经典牛顿法在训练神经网络过程中的迭…...

【Go 基础篇】深入探索:Go语言中的切片遍历与注意事项

嗨&#xff0c;Go语言学习者&#xff01;在我们的编程旅程中&#xff0c;切片&#xff08;Slice&#xff09;是一个极其重要的工具。它可以帮助我们处理各种类型的数据&#xff0c;从而让我们的代码更加灵活和高效。本文将围绕Go语言中切片的遍历方法以及在遍历时需要注意的事项…...

一些经典的SQL语句

查sql中as的用法搜索到的一些经典的sql语句 convert(2008-11-20 18:03:50) In:等值连接&#xff0c;用来查找多表相同字段的记录 Not In:非等值连接&#xff0c;用来查找不存在的记录 Inner join:内连接&#xff0c;主要用来查找都符合条件的记录 Left join:左连接&#xff…...

〔018〕Stable Diffusion 之 批量替换人脸 篇

✨ 目录 &#x1f388; 下载插件&#x1f388; 插件基础使用&#x1f388; 基础使用效果&#x1f388; 批量处理图片&#x1f388; 多人脸部替换 &#x1f388; 下载插件 如果重绘图片的时候&#xff0c;你只想更换人物面部的话&#xff0c;可以参考这篇文章扩展地址&#xff…...

Unity字符串性能问题

前言 分享一些通过书籍和网络学到的知识 每次动态创建一个string&#xff0c;C#都会在堆内存分配一个内存用来分配字符串&#xff0c;因为C#没有对字符串的缓存机制&#xff0c;会导致每次连接、切割、组合的时候都会申请新的内存&#xff0c;并且抛弃原来的内存&#xff0c;等…...

深入浅出SSD:固态存储核心技术、原理与实战(文末赠书)

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python、C/C 主页链接&#xff1a;阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 内容简介 作者简介 使用Python做一个计算器 本期赠书 近年来国家大力支持半导体行业&#xff0…...

关于layui+php,三级联动-编辑回显的问题。

注 忍不住吐槽一波。都什么年代了。现在都前后端分离&#xff0c;但是公司老项目非得用tplayui。。 代码如下 layui.use([form], function () {var form layui.form;//php代码渲染页面的时候&#xff0c;将一级分类id和二级分类id带过来&#xff0c;存到页面input框中&#x…...

lua的函数

1.一个示例实现列表的元素的求和 [root]# more funcAdd.lua function add(a)local sum 0for i 1,#a dosum sum a[i]endreturn sum enda {1,2,3,4,5,6}local sum add(a)print(sum)...

pytorch/tensorflow 直接给张量中的某个位置的值赋值,操作不可导。

问题&#xff1a;给一个tensor A中[i,j]&#xff0c;赋值p。直接操作A[i,j]p可能会导致值覆盖&#xff0c;操作不可导。 解决方案&#xff1a;通过引入一个额外的mask实现。 mask[i,j] 0 mask tf.convert_to_tensor(mask, dtypetf.float32) A (A * mask) (p * (1-mask))p…...

如何使用CSS实现一个平滑滚动到页面顶部的效果(回到顶部按钮)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 平滑滚动到页面顶部的效果&#xff08;回到顶部按钮&#xff09;⭐ 创建HTML结构⭐ 编写CSS样式⭐ 编写JavaScript函数⭐ 添加滚动事件监听器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...