中介中心性算法原理与源码解析(Between Centrality)

news2024/11/18 22:29:29

前言

中介中心性(Between Centrality),或者叫介数中心性,是基于最短路径对关系图谱中节点的中心性进行测量的典型图论算法。和其它的图论中心性算法一样,中介中心性用来衡量社会关系网络中,个人、企业或者其它的实体在整个网络中的重要程度。例如,在一个洗钱犯罪团伙的交易关系网络中,中介中心性高的账号,有可能是隶属于“庄主”的高风险账号,因为大量的资金都是通过这些账号进行归集或者拆分出去的。又例如,在交通运输领域的运用上,中介中心性不仅可以用于预测交通堵塞情况,也可以用于对城市道路运输效率进行分析。同时,又因为中介中心性算法的时间复杂度很高,所以在实际的工程化落地过程中也充满了挑战。

参考资料

[1] Freeman, Linton (1977). “A set of measures of centrality based on betweenness”. Sociometry. 40 (1): 35–41. https://www.jstor.org/stable/3033543?origin=crossref
[2] Brandes, Ulrik (2001). “A faster algorithm for betweenness centrality” . Journal of Mathematical Sociology. 25 (2): 163–177. https://pdodds.w3.uvm.edu/research/papers/others/2001/brandes2001a.pdf
[3] Ulrik Brandes and Christian Pich."Centrality Estimation in Large Networks. " International Journal of Bifurcation and Chaos 17(7):2303-2318, 2007. https://dx.doi.org/10.1142/S0218127407018403
[4] Betweenness centrality. https://en.wikipedia.org/wiki/Betweenness_centrality#CITEREFFreeman1977

中介中心性的定义

中介中心性的定义最早是Freeman提出的,一个节点 v v v的中介中心性由下面的公式给出:

C B ( v ) = ∑ s ≠ v ≠ t σ s t ( v ) σ s t ( 1 ) C_B(v)=\sum\limits_{s \neq v \neq t} \frac{\sigma_{st}(v)}{\sigma_{st}}\qquad\qquad (1) CB(v)=s=v=tσstσst(v)(1)

其中, σ s t \sigma_{st} σst指的是从节点 s s s到节点 t t t的所有最短路径的总数,而 σ s t ( v ) \sigma_{st}(v) σst(v)则是代码这些从节点 s s s t t t的最短路径中,经过节点 v v v的那部分最短路径的数量。需要注意的是,节点 v v v不能是最短路径的起点或者终点,也就是 s ≠ v ≠ t s \neq v \neq t s=v=t,因为这里的要求是“经过”。总结一句话就是,一个节点的中介中心性就是,其它任意两个节点之间的所有最短路径经过该节点的次数。中介中心性测量了某个节点在多大程度上能成为其它节点之间的“中介”,以此来衡量该节点在整个网络中的重要程度。

从上述对中介中心性的定义来看,是需要计算全图节点两两之间的最短路径数,其计算复杂度达到了 Θ ( ∣ V 3 ∣ ) \Theta(|V^3|) Θ(V3), 这同时也是一个典型的单源最短路径计算问题(single-source shortest-paths)。

一种用于中介中心性计算的快速算法

2021年,Brandes在他的论文《A Faster Algorithm for Betweenness Centrality》中提出了中介中心性算法的一种实现,将原本 Θ ( ∣ V 3 ∣ ) \Theta(|V^3|) Θ(V3)的时间复杂度降为 Θ ( ∣ V ∣ ∣ E ∣ ) \Theta(|V||E|) Θ(V∣∣E),其中 ∣ E ∣ |E| E为全图边的总数量。Brandes因为其良好的性能,在许多实际应用中都得到了广泛的使用。

接下来,我们来看看Brandes算法如何实现。

首先, δ s t ( v ) = σ s t ( v ) σ s t \delta_{st}(v)= \frac{\sigma_{st}(v)}{\sigma_{st}} δst(v)=σstσst(v)表示节点 s s s和节点 t t t之间的所有最短路径中,经过节点 v v v的最短路径的比例。所以,显而易见,节点 v v v的中介中心性可以表示为:

C B ( v ) = ∑ s ≠ v ≠ t ∈ V δ s t ( v ) C_B(v)=\sum\limits_{s \neq v \neq t \in V}\delta_{st}(v) CB(v)=s=v=tVδst(v)

接着,我们需要再引入三个基础定义,
定义1:
d G ( s , t ) d_G(s,t) dG(s,t) 表示从节点 s s s t t t的最短路径的距离。有且只有 d G ( s , t ) = d G ( s , v ) + d G ( v , t ) d_G(s,t)=d_G(s,v)+d_G(v,t) dG(s,t)=dG(s,v)+dG(v,t)成立时,我们才认为有一个节点 v v v在节点 s s s t t t之间的最短路径上。

定义2:
节点 v v v在以 s s s为源点的最短路径上时,节点 v v v的前序节点。这些前序节点的集合定义如下:
P s ( v ) = { u ∈ V : { u , v } ∈ E , d G ( s , v ) = d G ( s , u ) + w ( u , v ) } P_s(v)=\{u \in V :\{u,v\} \in E, d_G(s,v)=d_G(s,u)+w(u,v)\} Ps(v)={uV:{u,v}E,dG(s,v)=dG(s,u)+w(u,v)}

定义3:
δ s ∗ ( v ) = ∑ t ∈ V δ s t ( v ) \delta_{s*}(v)= \sum\limits_{t \in V}\delta_{st}(v) δs(v)=tVδst(v)
这里表示的是以 s s s为源点的所有节点对之间的最短路径经过节点 v v v的总比例。这是一个递归公式,它也是后续Brandes算法实现最短路径数快速累加的关键。

有了以上的基础概念后,我们假设当节点 s s s到任意节点 t t t之间的最短路径都有且只有一条,那么 δ s t ( v ) \delta_{st}(v) δst(v)的结果不是1,就是0。故我们可以得到以下公式:
δ s ∗ ( v ) = ∑ w : v ∈ P s ( w ) ( 1 + δ s ∗ ( w ) ) ( 2 ) \delta_{s*}(v)= \sum\limits_{w:v\in P_s(w)}(1 + \delta_{s*}(w))\qquad\qquad (2) δs(v)=w:vPs(w)(1+δs(w))(2)

在这里插入图片描述接下来,将公式(2)推广到一般形式,也就是节点 s s s到节点 t t t之间存在多条最短路径,
且有 σ s v σ s w ∗ σ s t ( w ) \frac{\sigma_{sv}}{\sigma_{sw}} * \sigma_{st}(w) σswσsvσst(w) 表示从节点 s s s到任意 t ≠ w t \neq w t=w的节点的所有最短路径中,包含了节点 w w w, 节点 v v v以及边 { v , w } \{v,w\} {v,w}的最短路径数量,其中 w w w满足 v ∈ P s ( w ) v\in P_s(w) vPs(w)

那么可以得到以下公式:

δ s ∗ ( v ) = ∑ w : v ∈ P s ( w ) σ s v σ s w ( 1 + δ s ∗ ( w ) ) ( 3 ) \delta_{s*}(v)= \sum\limits_{w:v\in P_s(w)} \frac{\sigma_{sv}}{\sigma_{sw}}(1 + \delta_{s*}(w))\qquad\qquad (3) δs(v)=w:vPs(w)σswσsv(1+δs(w))(3)

因为公式(3)的证明较为繁琐,所以这里就不再说明,感兴趣的同学可以自己去看原论文。

所以,Brandes算法实际上就是先使用BFS对每个节点做最短路径的计算,计算每个节点到其它节点的最短路径数量,然后同时记录了每个节点的前驱节点;接下来就是通过公式(3)在BFS树上递推计算得到每个节点的中介中心性。

其它基于Brandes算法的中介中心性算法实现

正如之前提到的,Brandes算法因为其优异的性能,其也被大量的开源框架所运用。例如经典的开源Python图计算框架NetworkX,其内部的中介中心性算法也是基于Brandes算法实现。虽然Brandes算法将计算复杂度下降到了 Θ ( ∣ V ∣ ∣ E ∣ ) \Theta(|V||E|) Θ(V∣∣E),但在对上亿的图数据进行中介中心性计算时,因为单源最短路径计算的效率问题,使得其在工程化落地时同样还是充满了挑战。
因此,NetworkX框架依据《Centrality Estimation in Large Networks》这篇论文中所做的实验,以及论文中提到的Hoeffding不等式的理论依据,提出了基于节点度数,节点和节点之间最大最短路径等多种采样方式,来达到近似计算中介中心性的目的。另外,在基于Spark的开源SparklingGraph图计算框架中,则是参考了Edmonds的《A Space-Efficient Parallel Algorithm for Computing Betweenness Centrality in Distributed Memory》这一篇论文,引入successor set来加快对最短路径累加计算的效率。

中介中心性算法源码解读

接下来,我们将从一个开源框架 SparklingGraph去学习如何用代码实现中介中心性的计算。因为我们平时在处理工业界的图数据时,都是上亿的节点和边,所以这也是为什么去选择一个分布式的图计算框架来讲解。

GitHub : https://github.com/sparkling-graph/sparkling-graph

因为该框架是基于Spark Graphx去实现的图计算方法,所以Louvain算法的开发语言是Scala。这里建议大家具备一些Spark和Scala的基础知识,不然下面的源码解读比较难以理解。

Spark Graphx的官网: https://spark.apache.org/docs/latest/graphx-programming-guide.html

另外,下面的讲解中,提到的代码行数对应的是源码文件中实际的代码行数。

首先,我们进入中介中心度计算的主类,EdmondsBC.scala中的computeBC方法就是中介中心度的核心。该方法主要做了三件事:
Step1. 遍历全图所有节点,并以一个节点作为Source节点,然后使用BFS算法计算以Source节点为开始节点到其它节点的最短路径数量 σ s ∗ {\sigma_{s*}} σs,相当于是SSSP的计算。
Step2. 根据步骤1计算的最短路径数以及上文的公式(3),从最短路径深度最深的节点开始反推迭代计算每个节点的中介中心性。
Step3. 更新图上所有节点的中介中心性,接着以另一个新的节点作为新的Source节点,重复步骤1和步骤2,并继续更新累加节点的中介中心性。

  def computeBC = {

    val verticesIds = simpleGraph.vertices.map({ case (vertexId, _) => vertexId }).cache
    val verticesIterator = verticesIds.toLocalIterator
    var betweennessVector: VertexRDD[Double] = simpleGraph.vertices.mapValues(_ => .0).cache()

    verticesIterator.foreach(processedVertex => {
      val bfsSP = edmondsBFSProcessor.computeSingleSelectedSourceBFS(simpleGraph, processedVertex)
      val computedGraph = bcAggregator.aggregate(bfsSP, processedVertex)

      val partialBetweennessVector = computedGraph.vertices.mapValues(_.bc)

      val previousBetweennessVector = betweennessVector
      betweennessVector = updateBC(betweennessVector, partialBetweennessVector)

//      betweennessVector.checkpoint()
      betweennessVector.count
      previousBetweennessVector.unpersist(false)

      bfsSP.unpersist(false)
      computedGraph.unpersistVertices(false)
      computedGraph.edges.unpersist(false)
    })

    verticesIds.unpersist(false)
    finalize(betweennessVector)
  }

step1

我们可以看到下面的这一行代码就是在执行step1计算单源最短路径的任务。

val bfsSP = edmondsBFSProcessor.computeSingleSelectedSourceBFS(simpleGraph, processedVertex)

在下面的代码块中,我们可以发现这里使用的是GraphX框架的pregel消息传播机制来从Source节点向周围相同深度的节点迭代地发送消息。

  def computeSingleSelectedSourceBFS(graph: Graph[VD, ED], source: VertexId): Graph[VD, ED] = {
    val initGraph = graph.mapVertices((vId, attr) => vPredicate.getInitialData(vId, attr)(source)).cache

    val result = initGraph.ops.pregel[MD](processor.initialMessage)(
      vPredicate.applyMessages,
      processor.sendMessage,
      processor.mergeMessages
    )

    initGraph.unpersist(false)
    result
  }

通过下面的sendMessage和mergeMessage两个代码段的解读,我们可以知道在传递消息时,记录了并更新了当前节点最短路径的前序节点,当前节点的最短路径距离 d G ( s , v ) d_G(s,v) dG(s,v), 以及当前节点的最短路径数量 σ s v \sigma_{sv} σsv

  override def sendMessage(triplet: EdgeTriplet[EdmondsVertex, ED]): Iterator[(VertexId, EdmondsMessage)] = {

    def msgIterator(currentVertexId: VertexId) = {
      val othAttr = triplet.otherVertexAttr(currentVertexId)
      val thisAttr = triplet.vertexAttr(currentVertexId)
      if (othAttr.explored) Iterator.empty else Iterator((triplet.otherVertexId(currentVertexId), EdmondsMessage(List(currentVertexId), thisAttr.sigma, thisAttr.depth + 1)))
    }

    def hasParent(source: VertexId) = triplet.vertexAttr(source).explored

    val srcMsg = if (hasParent(triplet.srcId)) msgIterator(triplet.srcId) else Iterator.empty
    val dstMsg = if (hasParent(triplet.dstId)) msgIterator(triplet.dstId) else Iterator.empty
    srcMsg ++ dstMsg
  }
 def merge(other: EdmondsMessage): EdmondsMessage = {
    require(depth == other.depth)
    EdmondsMessage(preds ++ other.preds, sigma + other.sigma, depth)
  }

step2

接下来我们回到EdmondsBC.scala中的computeBC方法,下面的代码就是用我们之前step1计算出来的SSSP结果执行step2,更新每个节点的中介中心性。

val computedGraph = bcAggregator.aggregate(bfsSP, processedVertex)

我们再进入到EdmondsBCAggregator.scala中的aggregate方法体中,如下面的代码片段所示,其实内容很简单。首先,使用aggregate方法获得最长的最短路径距离maxDepth, 然后根据maxDepth找到对应的相同最短路径距离节点(depth == maxDepth),因为这些节点没有任何的后续节点,也就是 δ s ∗ ( w ) \delta_{s*}(w) δs(w)为0。接着就可以根据公式(3)以及拥有最大最短路径距离的节点的前序节点,反推和计算每个节点的中介中心性。整个反推计算过程就是这段“for (i <- 1 until maxDepth reverse) ” 代码所体现的,从后往前找到最短路径距离依次减少的节点进行累加计算。

  def aggregate(graph: Graph[EdmondsVertex, ED], source: VertexId) = {
    val maxDepth = graph.vertices.aggregate(0)({ case (depth, (vId, vData)) => Math.max(vData.depth, depth) }, Math.max)

    var g = graph
    var oldGraph: Option[Graph[EdmondsVertex, ED]] = None

    var messages = aggregateMessages(g, maxDepth).cache
    messages.count

    for (i <- 1 until maxDepth reverse) {
      oldGraph = Some(g)

      g = applyMessages(g, messages).cache
      val oldMessages = messages
      messages = aggregateMessages(g, i).cache
      messages.count

      oldMessages.unpersist(false)
      oldGraph.foreach(_.unpersistVertices(false))
      oldGraph.foreach(_.edges.unpersist(false))
    }

    messages.unpersist(false)
    g
  }

接着我们进入到累加计算的方法aggregateMessages中,整个逻辑其实也就是找到符合当前迭代的最短路径距离的节点,然后根据公式(3)对该节点进行中介中心性的计算(delta),并通过graphx的消息传递机制将delta的值传递给前序节点进行更新。

  private def aggregateMessages(graph: Graph[EdmondsVertex, ED], depth: Int) = graph.aggregateMessages[Double](
    edgeContext => {
      val sender = createAndSendMessage(edgeContext.toEdgeTriplet, depth) _
      sender(edgeContext.srcId, edgeContext.sendToDst)
      sender(edgeContext.dstId, edgeContext.sendToSrc)
    }, _ + _
  )

  private def createAndSendMessage(triplet: EdgeTriplet[EdmondsVertex, ED], depth: Int)(source: VertexId, f: (Double) => Unit) = {
    val attr = triplet.vertexAttr(source)
    if (attr.depth == depth) sendMessage(produceMessage(triplet)(source), f)
  }

  private def produceMessage(triplet: EdgeTriplet[EdmondsVertex, ED])(source: VertexId) = {
    val attr = triplet.vertexAttr(source)
    val otherAttr = triplet.otherVertexAttr(source)
    val delta = (otherAttr.sigma.toDouble / attr.sigma.toDouble) * (1.0 + attr.delta)
    if (attr.preds.contains(triplet.otherVertexId(source))) Some(delta) else None
  }

  private def sendMessage(message: Option[Double], f: (Double) => Unit) = message.foreach(f)

  private def applyMessages(graph: Graph[EdmondsVertex, ED], messages: VertexRDD[Double]) =
    graph.ops.joinVertices(messages)((vertexId, attr, delta) => {
      EdmondsVertex(attr.preds, attr.sigma, attr.depth, delta, delta)
    })

step3

步骤3实际上就是EdmondsBC.scala中的computeBC方法里,循环计算所有节点的单源最短路径,然后累加更新节点中介中心性的逻辑。具体更新代码在updateBC方法里,这里比较简单就不展开讲了。

至此,中介中心性算法的原理和源码已经全部介绍完了,我们可以看到Spark的GraphX本质还是MapReduce的计算框架,无论在进行单源最短路径的计算,还是对于中介中心性的反推累加计算,都需要大量的对全图节点进行Map的操作,性能瓶颈也很大,所以还是建议涉及到SSSP的图算法,能不用GraphX框架就尽量不要用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/545139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

stm32wb15cc蓝牙芯片学习

由于项目选型需要&#xff0c;初次接触stm32的蓝牙芯片&#xff0c;需要总体做一些学习。也记些笔记&#xff0c;防止遗忘。 一、主要的ST的蓝牙芯片 简单介绍一下主要ST的蓝牙芯片 1.1. STM32WB系列 这个系列的芯片是一个双核的MCU&#xff0c;相当于一个普通的STM32 MCU和…

全面分析低代码平台:各大热门产品详细对比

低代码平台彻底改变了企业构建和部署定制应用程序的方式。它们提供了一种用最少的代码&#xff0c;更快、更高效地开发软件的方法。使得公司在加快创新的同时节省了时间和资源。对于一些想进行数字化转型&#xff0c;选择低代码平台入门的中小企业来讲&#xff0c;应该选择哪个…

NTP时间服务器同步时钟系统安装汇总分享

在现代科技发展的背景下&#xff0c;各种设备的时间同步变得越来越重要。同步时钟管理系统的应用可以让多个设备在时间上保持一致&#xff0c;提高工作效率和安全性&#xff0c;为各个行业的发展提供了重要的支持。 一、同步时钟系统介绍 同步时钟管理系统的应用范围非常广泛&…

私有化部署的即时通讯软件:消息、文件安全加密,全面可控

如今&#xff0c;数字化转型进入纵深阶段&#xff0c;在企业数字化转型过程中&#xff0c;数据规模激增&#xff0c;结构更为复杂&#xff0c;数据零散化和安全性问题日益显著&#xff0c;使得众多企业在数据资产管理上面临不小的挑战。企业为提高内部沟通效率&#xff0c;通常…

C++源码分析完美转发

C源码分析完美转发 完美转发作用&#xff1a; 可以保持实参数据在函数中的左值或者右值类型。 不使用完美转发的后果 #include<iostream> using namespace std;// 容器里面元素的类型 class A { public:A() {}// 带左值引用参数的赋值函数A& operator(const A&…

24 KVM管理虚拟机-配置VNC-TLS登录

文章目录 24 KVM管理虚拟机-配置VNC-TLS登录24.1 概述24.2 操作步骤 24 KVM管理虚拟机-配置VNC-TLS登录 24.1 概述 VNC服务端和客户端默认采用明文方式进行数据传输&#xff0c;因此通信内容可能被第三方截获。为了提升安全性&#xff0c;openEuler支持VNC服务端配置TLS模式进…

在光伏行业的自动化生产中,EAP起到了什么作用?

随着可再生能源的快速发展和环保意识的增强&#xff0c;光伏行业作为一种清洁能源产业正迅速崛起。光伏生产过程的自动化已成为行业的追求和趋势。在光伏行业的自动化生产中&#xff0c;EAP&#xff08;设备自动化程序&#xff09;系统发挥着关键的作用&#xff0c;为生产线的运…

drawio@绘制带有latex公式的图表@示意图@流程图@白板模式whiteboard

文章目录 drawio绘制带有latex公式的图表示意图流程图白板模式whiteboard使用drawio小结 公式编辑Use mathematical typesetting in diagramsUse mathematical typesetting in diagramsTroubleshooting关于文本框元素公式渲染问题&#x1f388;Maths is not rendered 模式切换d…

【K哥爬虫普法】你很会写爬虫吗?10秒抢票、10秒入狱,了解一下?

我国目前并未出台专门针对网络爬虫技术的法律规范&#xff0c;但在司法实践中&#xff0c;相关判决已屡见不鲜&#xff0c;K 哥特设了“K哥爬虫普法”专栏&#xff0c;本栏目通过对真实案例的分析&#xff0c;旨在提高广大爬虫工程师的法律意识&#xff0c;知晓如何合法合规利用…

【TES745D】基于复旦微的FMQL45T900 全国产化ARM 核心模块(100%国产化)方案设计中文资料

板卡概述 TES745D 是一款基于上海复旦微电子FMQL45T900 的全国产化ARM 核心板。该核心板将复旦微的FMQL45T900&#xff08;与XILINX 的XC7Z045-2FFG900I 兼容&#xff09;的最小系统集成在了一个87*117mm 的 核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩…

主成分分析(PCA)直观理解与数学推导

近期在完成信息论的作业&#xff0c;发现网上的资料大多是直观解释&#xff0c;对其中的数学原理介绍甚少&#xff0c;并且只介绍了向量降维&#xff0c;而没有介绍向量重构的问题&#xff08;重构指的是&#xff1a;根据降维后的低维向量来恢复原始向量&#xff09;&#xff0…

Yolov5轻量化:MobileNetV3,轻量级骨架首选

1.轻量化网络简介 轻量化网络是指在保持模型性能的前提下,尽可能减小模型参数量和计算量的神经网络。这种网络通常被用于在移动设备等资源受限的场景中部署,以提高模型的实时性和运行效率。 轻量化网络的设计思路可以包括以下几个方面: 去除冗余层和参数:通过剪枝、蒸馏等技…

基于RT-Thread的lwip网卡优化笔记

基于RT-Thread的lwip网卡优化笔记 一、RT-Thread的lwip框架二、网卡驱动三、网卡吞吐速率测试四、网卡吞吐速率优化4.1 TCP参数优化4.2 lwip参数优化4.3 内存拷贝优化4.3.1 rt_memcpy优化4.3.2 使用uboot下的memcpy.S 4.4 网卡收发优化4.3.1 lwip发送优化4.4.2 网卡发送优化 一…

MyBatis查询各种类型数据该如何处理才能得到数据

文章目录 1、前言2、查询一个实体类对象字段名和属性名无法映射处理方式一&#xff1a;起别名方式二&#xff1a;使用全局配置文件配置映射规则方式三&#xff1a;自定义resultmap 3、查询一个list集合4、查询单个数据5、查询一条数据为map集合6、 查询多条数据为map集合方式一…

课程分享:华清远见联合NXP推出i.MX8M Plus开发与实践课程,超干超实用!

​课程名称&#xff1a; i.MX8M Plus开发与实践课程 课程介绍&#xff1a; i.MX8M Plus应用处理器是NXP推出的一款致力于推动机器学习&#xff08;ML&#xff09;&#xff0c;机器视觉&#xff0c;多媒体与工业边缘物联网应用的工业人工智能芯片。拥有4个ARM Cortex-A53核心…

远程桌面连接黑屏怎么解决?方法大全

远程桌面连接是一种非常有用的技术&#xff0c;它可以让用户从任何位置远程访问到其它计算机。然而&#xff0c;当你尝试连接到一个计算机时&#xff0c;你有可能会遇到远程桌面连接黑屏的问题。这个问题很常见&#xff0c;但是它可能会给你带来很多麻烦。在本文中&#xff0c;…

ssRender Plugin 基础

ssRender Plugin 基础 一.什么是Plugin ​ 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。其只能运行在程序规定的系统平台下&#xff08;可能同时支持多个平台&#xff09;&#xff0c;而不能脱离指定的平台单独…

Windows安装配置Tomcat服务器教程 -- 外网远程访问

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 转载自cpolar文章&#xff1a;外网访问本地Tomcat服务器【cpolar内网穿透】…

【SpringBoot】五:Web服务---SpringMVC---控制器

文章目录 1 控制器介绍2 控制器工作流程3 控制器中的方法4 匹配请求路径到控制器方法5 RequestMapping6 控制器方法参数类型与可用返回值类型7 接收请求参数8 验证参数8.1 Bean Validation8.2 分组校验8.3 ValidationAutoConfiguration 1 控制器介绍 &#xff08;1&#xff09…

【Linux Network】数据链路层

目录 认识以太网 以太网帧格式 认识MAC地址 对比理解MAC地址和IP地址 认识MTU MTU对IP协议的影响 MTU对UDP协议的影响 MTU对于TCP协议的影响 MSS和MTU的关系&#xff1a; 查看硬件地址和MTU ARP协议 ARP协议的作用 ARP协议的工作流程 ARP数据报的格式 DNS(Domain Name System) …