前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Spark源码系列(一)spark-submit提交作业过程

Spark源码系列(一)spark-submit提交作业过程

作者头像
岑玉海
发布于 2018-02-28 09:08:48
发布于 2018-02-28 09:08:48
2K05
代码可运行
举报
文章被收录于专栏:岑玉海岑玉海
运行总次数:5
代码可运行

前言

折腾了很久,终于开始学习Spark的源码了,第一篇我打算讲一下Spark作业的提交过程。

这个是Spark的App运行图,它通过一个Driver来和集群通信,集群负责作业的分配。今天我要讲的是如何创建这个Driver Program的过程。

作业提交方法以及参数

我们先看一下用Spark Submit提交的方法吧,下面是从官方上面摘抄的内容。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Run on a Spark standalone cluster
./bin/spark-submit \
  --class org.apache.spark.examples.SparkPi \
  --master spark://207.184.161.138:7077 \
  --executor-memory 20G \
  --total-executor-cores 100 \
  /path/to/examples.jar \
  1000

这个是提交到standalone集群的方式,打开spark-submit这文件,我们会发现它最后是调用了org.apache.spark.deploy.SparkSubmit这个类。

我们直接进去看就行了,main函数就几行代码,太节省了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def main(args: Array[String]) {
    val appArgs = new SparkSubmitArguments(args)
    val (childArgs, classpath, sysProps, mainClass) = createLaunchEnv(appArgs)
    launch(childArgs, classpath, sysProps, mainClass, appArgs.verbose)
}

我们主要看看createLaunchEnv方法就可以了,launch是反射调用mainClass,精华全在createLaunchEnv里面了。

在里面我发现一些有用的信息,可能在官方文档上面都没有的,发出来大家瞅瞅。前面不带--的可以在spark-defaults.conf里面设置,带--的直接在提交的时候指定,具体含义大家一看就懂。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
val options = List[OptionAssigner](
      OptionAssigner(args.master, ALL_CLUSTER_MGRS, false, sysProp = "spark.master"),
      OptionAssigner(args.name, ALL_CLUSTER_MGRS, false, sysProp = "spark.app.name"),
      OptionAssigner(args.name, YARN, true, clOption = "--name", sysProp = "spark.app.name"),
      OptionAssigner(args.driverExtraClassPath, STANDALONE | YARN, true,
        sysProp = "spark.driver.extraClassPath"),
      OptionAssigner(args.driverExtraJavaOptions, STANDALONE | YARN, true,
        sysProp = "spark.driver.extraJavaOptions"),
      OptionAssigner(args.driverExtraLibraryPath, STANDALONE | YARN, true,
        sysProp = "spark.driver.extraLibraryPath"),
      OptionAssigner(args.driverMemory, YARN, true, clOption = "--driver-memory"),
      OptionAssigner(args.driverMemory, STANDALONE, true, clOption = "--memory"),
      OptionAssigner(args.driverCores, STANDALONE, true, clOption = "--cores"),
      OptionAssigner(args.queue, YARN, true, clOption = "--queue"),
      OptionAssigner(args.queue, YARN, false, sysProp = "spark.yarn.queue"),
      OptionAssigner(args.numExecutors, YARN, true, clOption = "--num-executors"),
      OptionAssigner(args.numExecutors, YARN, false, sysProp = "spark.executor.instances"),
      OptionAssigner(args.executorMemory, YARN, true, clOption = "--executor-memory"),
      OptionAssigner(args.executorMemory, STANDALONE | MESOS | YARN, false,
        sysProp = "spark.executor.memory"),
      OptionAssigner(args.executorCores, YARN, true, clOption = "--executor-cores"),
      OptionAssigner(args.executorCores, YARN, false, sysProp = "spark.executor.cores"),
      OptionAssigner(args.totalExecutorCores, STANDALONE | MESOS, false,
        sysProp = "spark.cores.max"),
      OptionAssigner(args.files, YARN, false, sysProp = "spark.yarn.dist.files"),
      OptionAssigner(args.files, YARN, true, clOption = "--files"),
      OptionAssigner(args.files, LOCAL | STANDALONE | MESOS, false, sysProp = "spark.files"),
      OptionAssigner(args.files, LOCAL | STANDALONE | MESOS, true, sysProp = "spark.files"),
      OptionAssigner(args.archives, YARN, false, sysProp = "spark.yarn.dist.archives"),
      OptionAssigner(args.archives, YARN, true, clOption = "--archives"),
      OptionAssigner(args.jars, YARN, true, clOption = "--addJars"),
      OptionAssigner(args.jars, ALL_CLUSTER_MGRS, false, sysProp = "spark.jars")
 )

Driver程序的部署模式有两种,client和cluster,默认是client。client的话默认就是直接在本地运行了Driver程序了,cluster模式还会兜一圈把作业发到集群上面去运行。

指定部署模式需要用参数--deploy-mode来指定,或者在环境变量当中添加DEPLOY_MODE变量来指定。

下面讲的是cluster的部署方式,兜一圈的这种情况。

yarn模式的话mainClass是org.apache.spark.deploy.yarn.Client,standalone的mainClass是org.apache.spark.deploy.Client。

这次我们讲org.apache.spark.deploy.Client,yarn的话单独找一章出来单独讲,目前超哥还是推荐使用standalone的方式部署spark,具体原因不详,据说是因为资源调度方面的问题。

说个快捷键吧,Ctrl+Shift+N,然后输入Client就能找到这个类,这是IDEA的快捷键,相当好使。

我们直接找到它的main函数,发现了它居然使用了Akka框架,我百度了一下,被它震惊了。

Akka

在main函数里面,主要代码就这么三行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//创建一个ActorSystem
val (actorSystem, _) = AkkaUtils.createActorSystem("driverClient",Utils.localHostName(),0,
  conf, new SecurityManager(conf))
//执行ClientActor的preStart方法和receive方法
actorSystem.actorOf(Props(classOf[ClientActor], driverArgs, conf))
//等待运行结束
actorSystem.awaitTermination()

看了这里真的有点儿懵啊,这是啥玩意儿,不懂的朋友们,请点击这里Akka。下面是它官方放出来的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//定义一个case class用来传递参数
case class Greeting(who: String)
//定义Actor,比较重要的一个方法是receive方法,用来接收信息的
class GreetingActor extends Actor with ActorLogging {
   def receive = {
       case Greeting(who) ⇒ log.info("Hello " + who)
   }
}
//创建一个ActorSystem
val system = ActorSystem("MySystem")
//给ActorSystem设置Actor
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
//向greeter发送信息,用Greeting来传递
greeter ! Greeting("Charlie Parker")

简直是无比强大啊,就这么几行代码就搞定了,接下来看你会更加震惊的。

我们回到Client类当中,找到ClientActor,它有两个方法,是之前说的preStart和receive方法,preStart方法用于连接master提交作业请求,receive方法用于接收从master返回的反馈信息。

我们先看preStart方法吧。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override def preStart() = {
    // 这里需要把master的地址转换成akka的地址,然后通过这个akka地址获得指定的actor
    // 它的格式是"akka.tcp://%s@%s:%s/user/%s".format(systemName, host, port, actorName)
    masterActor = context.actorSelection(Master.toAkkaUrl(driverArgs.master))
    // 把自身设置成远程生命周期的事件
    context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])

    driverArgs.cmd match {
      case "launch" =>
        // 此处省略100个字
        val mainClass = "org.apache.spark.deploy.worker.DriverWrapper"
        // 此处省略100个字
        // 向master发送提交Driver的请求,把driverDescription传过去,RequestSubmitDriver前面说过了,是个case class
        masterActor ! RequestSubmitDriver(driverDescription)

      case "kill" =>
        val driverId = driverArgs.driverId
        val killFuture = masterActor ! RequestKillDriver(driverId)
    }
}

从上面的代码看得出来,它需要设置master的连接地址,最后提交了一个RequestSubmitDriver的信息。在receive方法里面,就是等待接受回应了,有两个Response分别对应着这里的launch和kill。

线索貌似到这里就断了,那下一步在哪里了呢?当然是在Master里面啦,怎么知道的,猜的,哈哈。

Master也是继承了Actor,在它的main函数里面找到了以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
val (actorSystem, boundPort) = AkkaUtils.createActorSystem(systemName, host, port, conf = conf, 
  securityManager = securityMgr)
val actor = actorSystem.actorOf(Props(classOf[Master], host, boundPort, webUiPort, securityMgr), actorName)
val timeout = AkkaUtils.askTimeout(conf)
val respFuture = actor.ask(RequestWebUIPort)(timeout)
val resp = Await.result(respFuture, timeout).asInstanceOf[WebUIPortResponse]

和前面的actor基本一致,多了actor.ask这句话,查了一下官网的文档,这句话的意思的发送消息,并且接受一个Future作为response,和前面的actor ! message的区别就是它还接受返回值。

具体的Akka的用法,大家还是参照官网吧,Akka确实如它官网所言的那样子,是一个简单、强大、并行的分布式框架。

小结:

Akka的使用确实简单,短短的几行代码即刻完成一个通信功能,比Socket简单很多。但是它也逃不脱我们常说的那些东西,请求、接收请求、传递的消息、注册的地址和端口这些概念。

调度schedule

我们接下来查找Master的receive方法吧,Master是作为接收方的,而不是主动请求,这点和hadoop是一致的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    case RequestSubmitDriver(description) => {
        val driver = createDriver(description)
        persistenceEngine.addDriver(driver)
        waitingDrivers += driver
        drivers.add(driver)
        // 调度
        schedule()
         // 告诉client,提交成功了,把driver.id告诉它
        sender ! SubmitDriverResponse(true, Some(driver.id), s"Driver successfully submitted as ${driver.id}")
      }

这里我们主要看schedule方法就可以了,它是执行调度的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private def schedule() {
    if (state != RecoveryState.ALIVE) { return }

    // 首先调度Driver程序,从workers里面随机抽一些出来
    val shuffledWorkers = Random.shuffle(workers) 
    for (worker <- shuffledWorkers if worker.state == WorkerState.ALIVE) {
      for (driver <- waitingDrivers) {
        // 判断内存和cpu够不够,够的就执行了哈
        if (worker.memoryFree >= driver.desc.mem && worker.coresFree >= driver.desc.cores) {
          launchDriver(worker, driver)
          waitingDrivers -= driver
        }
      }
    }

    // 这里是按照先进先出的,spreadOutApps是由spark.deploy.spreadOut参数来决定的,默认是true
    if (spreadOutApps) {
      // 遍历一下app
      for (app <- waitingApps if app.coresLeft > 0) {
        // canUse里面判断了worker的内存是否够用,并且该worker是否已经包含了该app的Executor
        val usableWorkers = workers.toArray.filter(_.state == WorkerState.ALIVE)
          .filter(canUse(app, _)).sortBy(_.coresFree).reverse
        val numUsable = usableWorkers.length
        val assigned = new Array[Int](numUsable) 
        // 记录每个节点的核心数
        var toAssign = math.min(app.coresLeft, usableWorkers.map(_.coresFree).sum)
        var pos = 0
        // 遍历直到分配结束
        while (toAssign > 0) {
          // 从0开始遍历可用的work,如果可用的cpu减去已经分配的>0,就可以分配给它
          if (usableWorkers(pos).coresFree - assigned(pos) > 0) {
            toAssign -= 1
            // 这个位置的work的可分配的cpu数+1
            assigned(pos) += 1
          }
          pos = (pos + 1) % numUsable
        }
        // 给刚才标记的worker分配任务
        for (pos <- 0 until numUsable) {
          if (assigned(pos) > 0) {
            val exec = app.addExecutor(usableWorkers(pos), assigned(pos))
            launchExecutor(usableWorkers(pos), exec)
            app.state = ApplicationState.RUNNING
          }
        }
      }
    } else {
      // 这种方式和上面的方式的区别是,这种方式尽可能用少量的节点来完成这个任务
      for (worker <- workers if worker.coresFree > 0 && worker.state == WorkerState.ALIVE) {
        for (app <- waitingApps if app.coresLeft > 0) {
          // 判断条件是worker的内存比app需要的内存多
          if (canUse(app, worker)) {
            val coresToUse = math.min(worker.coresFree, app.coresLeft)
            if (coresToUse > 0) {
              val exec = app.addExecutor(worker, coresToUse)
              launchExecutor(worker, exec)
              app.state = ApplicationState.RUNNING
            }
          }
        }
      }
    }
  }

它的调度器是这样的,先调度Driver程序,然后再调度App,调度App的方式是从各个worker的里面和App进行匹配,看需要分配多少个cpu。

那我们接下来看两个方法launchDriver和launchExecutor即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  def launchDriver(worker: WorkerInfo, driver: DriverInfo) {
    logInfo("Launching driver " + driver.id + " on worker " + worker.id)
    worker.addDriver(driver)
    driver.worker = Some(worker)
    worker.actor ! LaunchDriver(driver.id, driver.desc)
    driver.state = DriverState.RUNNING
  }

给worker发送了一个LaunchDriver的消息,下面在看launchExecutor的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  def launchExecutor(worker: WorkerInfo, exec: ExecutorInfo) {
    logInfo("Launching executor " + exec.fullId + " on worker " + worker.id)
    worker.addExecutor(exec)
    worker.actor ! LaunchExecutor(masterUrl,
      exec.application.id, exec.id, exec.application.desc, exec.cores, exec.memory)
    exec.application.driver ! ExecutorAdded(
      exec.id, worker.id, worker.hostPort, exec.cores, exec.memory)
  }

它要做的事情多一点,除了给worker发送LaunchExecutor指令外,还需要给driver发送ExecutorAdded的消息,说你的任务已经有人干了。

在继续Worker讲之前,我们先看看它是怎么注册进来的,每个Worker启动之后,会自动去请求Master去注册自己,具体我们可以看receive的方法里面的RegisterWorker这一段,它需要上报自己的内存、Cpu、地址、端口等信息,注册成功之后返回RegisteredWorker信息给它,说已经注册成功了。

Worker执行

同样的,我们到Worker里面在receive方法找LaunchDriver和LaunchExecutor就可以找到我们要的东西。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
case LaunchDriver(driverId, driverDesc) => {
      logInfo(s"Asked to launch driver $driverId")
      val driver = new DriverRunner(driverId, workDir, sparkHome, driverDesc, self, akkaUrl)
      drivers(driverId) = driver
      driver.start()

      coresUsed += driverDesc.cores
      memoryUsed += driverDesc.mem
}

看一下start方法吧,start方法里面,其实是new Thread().start(),run方法里面是通过传过来的DriverDescription构造的一个命令,丢给ProcessBuilder去执行命令,结束之后调用。

worker !DriverStateChanged通知worker,worker再通过master ! DriverStateChanged通知master,释放掉worker的cpu和内存。

同理,LaunchExecutor执行完毕了,通过worker ! ExecutorStateChanged通知worker,然后worker通过master ! ExecutorStateChanged通知master,释放掉worker的cpu和内存。

下面我们再梳理一下这个过程,只包括Driver注册,Driver运行之后的过程在之后的文章再说,比较复杂。

1、Client通过获得Url地址获得ActorSelection(master的actor引用),然后通过ActorSelection给Master发送注册Driver请求(RequestSubmitDriver)

2、Master接收到请求之后就开始调度了,从workers列表里面找出可以用的Worker

3、通过Worker的actor引用ActorRef给可用的Worker发送启动Driver请求(LaunchDriver)

4、调度完毕之后,给Client回复注册成功消息(SubmitDriverResponse)

5、Worker接收到LaunchDriver请求之后,通过传过来的DriverDescription的信息构造出命令来,通过ProcessBuilder执行

6、ProcessBuilder执行完命令之后,通过DriverStateChanged通过Worker

7、Worker最后把DriverStateChanged汇报给Master

后记:听超哥说,org.apache.spark.deploy.Client这个类快要被删除了,不知道cluster的这种模式是不是也被放弃了,官方给出来的例子推荐的是client模式->直接运行程序。难怪在作业调度的时候,看到别的actor叫driverActor。

不过这篇文章还有存在的意义, Akka和调度这块,和我现在正在写的第三篇以及第四篇关系很密切。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2014-06-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
ensp中ospf多区域管理
OSPF 多区域的主要作用是缩小链路状态数据库和路由表的规模,减少路由更新的频率,提高网络的可扩展性,实现路由过滤和路由汇总,从而提高网络的性能、稳定性、安全性和可管理性。
神秘泣男子
2024/04/02
2210
ensp中ospf多区域管理
基于华为ENSP的OSPF状态机、工作过程、配置保姆级别详解(2)
盛透侧视攻城狮
2025/01/20
2070
基于华为ENSP的OSPF状态机、工作过程、配置保姆级别详解(2)
ensp学习第三弹ospf
RIP 是一种基于距离向量的路由选择协议,它使用跳数(Hop Count)作为度量值来衡量到达目的地址的距离。直接相连的路由器跳数为 1。跳数最多为 15,超过则表示不可达。RIP 每隔30秒和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。应该是属于选择最短路径
用户8447427
2022/08/18
3790
ensp学习第三弹ospf
路由器的基础配置全解析:静态&动态路由 + 华为 ENSP 命令大全
路由器 主要用于 数据包的转发,它会根据 路由表(Routing Table) 确定数据的最优路径,并将其发送到目的地。
神的孩子都在歌唱
2025/03/14
2070
路由器的基础配置全解析:静态&动态路由 + 华为 ENSP 命令大全
OSPF技术连载5:OSPF 基本配置,含思科、华为、Junifer三厂商配置
OSPF(Open Shortest Path First)是一种开放式最短路径优先的路由协议,用于在IP网络中进行动态路由选择。瑞哥会从华为、思科、junifer三个厂商出发,H3C命令请参考华为的命令,锐捷请参考思科的命令。
网络技术联盟站
2023/07/22
4430
OSPF技术连载5:OSPF 基本配置,含思科、华为、Junifer三厂商配置
OSPF太难了,这份OSPF综合实验请每位网络工程师查收,周末弯道超车!
今天给大家带来的是OSPF综合实验,OSPF的理论一旦掌握好,实验其实就非常简单了,那么下面大家就打开Ensp模拟器开启实验之旅吧!
网络技术联盟站
2023/03/01
9340
OSPF太难了,这份OSPF综合实验请每位网络工程师查收,周末弯道超车!
华为ensp中BFD和OSPF联动(原理及配置命令)
BFD在两台设备之间建立会话,并在会话上周期性地发送BFD报文。每个BFD报文都有一个唯一的检测器ID,用于区分不同的BFD会话。如果一方在约定的检测时间内没有收到对端的BFD报文,则认为这条链路发生了故障。
神秘泣男子
2024/06/03
5660
华为ensp中BFD和OSPF联动(原理及配置命令)
华为交换机OSPF对接思科交换机EIGRP,牛逼配置!
思科交换机OSPF分别与华为交换机OSPF以及思科交换机EIGRP进行路由交互,间接实现华为交换机OSPF对接思科交换机EIGRP的功能。
网络技术联盟站
2023/03/01
8610
华为交换机OSPF对接思科交换机EIGRP,牛逼配置!
ENSP中静态路由和默认路由的配置命令
默认路由的作用是将无法匹配路由表中其他路由表项的数据包转发到指定下一跳路由器。在实际网络中,默认路由通常用于简化路由配置,通常在网络边缘的路由器上配置
神秘泣男子
2024/04/20
9150
ENSP中静态路由和默认路由的配置命令
ensp路由重分发
路由重分发(Route Redistribution)是指路由器将从一种路由协议学习到的路由信息,通过另一种路由协议通告出去的功能。路由重分发的作用是将不同路由协议的路由信息进行互通, 创建冗余路由路径,以及负载平衡流量。
神秘泣男子
2024/04/24
1720
ensp路由重分发
弄它!!!Ospf--动态路由--链路状态路由协议!全面解析OSPF协议!
开放式最短路径优先OSPF(Open Shortest Path First)是IETF组织开发的一个基于链路状态的内部网关协议(Interior Gateway Protocol)。 目前针对IPv4协议使用的是OSPF Version 2(RFC2328);针对IPv6协议使用OSPF Version 3(RFC2740)。如无特殊说明,本文中所指的OSPF均为OSPF Version 2。
不吃小白菜
2020/09/03
4.7K0
弄它!!!Ospf--动态路由--链路状态路由协议!全面解析OSPF协议!
OSPF基础实验及DR/BDR选举
今天给大家带来OSPF的基础实验及DR/BDR选举,邻居和邻接建立的文章我还在优化,下期给大家发布
Ponnie
2021/02/24
6460
OSPF+RIP路由配置【原理+实训+路径开销问题】——2022.5.13
OSPF路由协议是一种典型的链路状态的路由协议,一般用于同一个路由域内。在这里,路由域是指一个自治系统(AS),它是指一组通过统一的路由政策或路由协议互相交换路由信息的网络,在这个AS中所有的OSPF路由器都维护一个相同的描述这个AS结构的数据库,这个数据库中存放的是路由域中相应链路的状态信息,OSPF路由器正是通过这个数据库计算出其OSPF路由表的 作为一种链路状态的路由协议,OSPF将链路状态组播数据LSA(Link State Advertisement)传递给在某一区域内的所有路由器,(距离矢量路由协议的路由器是将部分或者全部路由表传递给与其相邻的路由器)
MIKE笔记
2023/03/22
5620
OSPF+RIP路由配置【原理+实训+路径开销问题】——2022.5.13
OSPF据说是网络技术中最难啃的技术之一,不存在的!华为、思科、瞻博网络三厂商实验绝了!
OSPF(Open Shortest Path First开放式最短路径优先)是IETF组织开发的一个基于链路状态的内部网关协议(Interior Gateway Protocol)。目前针对IPv4协议使用的是OSPF Version 2(RFC2328)。
网络技术联盟站
2023/03/01
7490
OSPF据说是网络技术中最难啃的技术之一,不存在的!华为、思科、瞻博网络三厂商实验绝了!
华为ensp中BGP(边界网关协议)基础原理及配置命令
BGP(边界网关协议)是一种路由协议,用于在互联网中的不同自治系统(AS)之间交换路由信息。它是一种路径矢量协议,用于决定最佳的路由路径,并具有很高的可扩展性和灵活性,因此在互联网核心路由器之间广泛应用。
神秘泣男子
2024/06/03
7290
华为ensp中BGP(边界网关协议)基础原理及配置命令
OSPF基础
OSPF(OPen Shortest Path First)开放最短路径优先,由IETF开发的基于链路状态的自治系统内部路由协议(IGP)采用Dijkstra的最短路径优先算法来计算和选择路由。 该协议关注网络中链路或者接口的状态、带宽、利用率、延时等。使用SPF算法计算和选择路由,OSPF 将协议包直接封装在 IP 包中,协议号 89。并且OSPF以组播形式发送协议报文,减少链路带宽资源浪费。
BreezeCloud
2023/03/24
7900
OSPF基础
OSPF技术连载1:OSPF基础知识,7000字总结!
OSPF(开放最短路径优先)是一种用于在IP网络中选择路由的内部网关协议(IGP)。它是一个开放标准协议,由RFC 2328定义,广泛应用于大型企业网络和互联网。
网络技术联盟站
2023/07/22
1K0
OSPF技术连载1:OSPF基础知识,7000字总结!
华为路由器配置笔记
路由器(Router),是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号,路由器工作在网络层,用来跨网段通信,路由器具有判断网络地址和选择IP路径的功能,它能在多网络互联环境中,建立灵活的连接,可用完全不同的数据分组和介质访问方法连接各种子网,路由器只接受源站或其他路由器的信息,属网络层的一种互联设备,因此路由器是互联网中必不可少的网络设备之一.
微软技术分享
2022/12/28
1.4K0
华为路由器配置笔记
网络边界安全ACL基本配置
企业网络中的设备进行通信时,需要保障数据传输的安全可靠和网络的性能稳定,网络安全很重要。
神的孩子都在歌唱
2025/03/05
1170
网络边界安全ACL基本配置
OSPF技术连载4:OSPF和BFD联动,含思科、华为、Junifer三厂商配置
在现代网络架构中,可靠性和快速故障检测与恢复是至关重要的。在此背景下,将OSPF(Open Shortest Path First)与BFD(Bidirectional Forwarding Detection)联动起来,成为提高网络性能和可靠性的有效策略。本文将详细介绍OSPF和BFD的基本原理,并探讨它们联动的好处和实施步骤。
网络技术联盟站
2023/07/22
7840
OSPF技术连载4:OSPF和BFD联动,含思科、华为、Junifer三厂商配置
推荐阅读
相关推荐
ensp中ospf多区域管理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文