使用SBT构建和发布基于SpringBoot的Scala应用

news2024/11/20 8:42:09

SBT 是 Scala 生态圈里的经典构建工具,虽然很多人觉得 SBT 很复杂,还戏称其为 SB Tool,但其全称确是 Simple Build Tool。

实际上,很多产品(包括像 SBT 这样的工具和技术产品)只有一个打动用户的特性就够了, Triggered Execution 这一特性支持,就可以在一个屏幕上写代码,而在另一个屏幕(或者窗口)实时地获得编译结果反馈,如图 1 所示,参与感十足。


图 1 结合双屏使用SBT的工作场景示意图

当然,SBT 毕竟是一套新的工具体系,与 Maven 或者其他构建工具在配置和使用上还是会有不小的差异,这就意味着,如果我们要使用 SBT 来构建基于 Scala 的 SpringBoot 微服务项目,就需要针对 SBT 的特点做很多定制工作。

探索基于SBT的SpringBoot应用开发模式

SpringBoot 团队围绕 Maven 提供了很多便利和支持,比如我们只要使用 Maven 的继承特性,将 spring-boot-starter-parent 作为当前项目的 parent,就可以直接开发 SpringBoot 微服务项目,代码如下所示:

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>1.3.2.RELEASE</version>
  5. </parent>

或者,如果当前要开发的 SpringBoot 微服务项目需要继承其他 parent 项目而不能使用 spring-boot-starter-parent,那么也可以使用 Maven 的依赖引入特性完成一系列 SpringBoot 相关依赖导入:

  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency> <!-- Import dependency management from Spring Boot -->
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-dependencies</artifactId>
  6. <version>1.3.2.RELEASE</version>
  7. <type>pom</type>
  8. <scope>import</scope>
  9. </dependency>
  10. </dependencies>
  11. </dependencyManagement>

当使用 SBT 时,这些围绕 Maven 提供的便利就不复存在了,我们需要搞清楚这些便利背后都发生了什么,才能最终决定如何使用 SBT 进行相应的配置并最终达到相同的目的。

因为 spring-boot-starter-parent 项目只有一个 pom.xml 定义,且其 parent 就是 spring-boot-dependencies,所以,我们先沿着 spring-boot-dependencies 开始寻找端倪。

但是我们发现,spring-boot-dependencies 也只是一个只有 pom.xml 定义的“干瘪”项目,其 pom.xml 中仅仅通过 dependencyManagement 和 pluginManagement 固定了 SpringBoot 项目的一系列依赖和版本号,并无实际有用信息,所以,我们需要回头再从 spring-boot-starter-parent 的 pom.xml 中寻找线索。

不幸的是,spring-boot-starter-parent 也没有提供什么有用的线索,它的 pom.xml 中也是定义了相应的 pluginManagement 来规范依赖和依赖的版本,并在编译期间进行相应资源和配置的过滤。

到了这个地步,我们就只能另寻他途了。不过,既然大部分的 SpringBoot 项目都会从依赖相应的 starter 项目开始,那么,我们可以分析几个 spring-boot-starter-XXX 模块来看看它们有什么共性。

假设我们关联查看了 spring-boot-starter-web、spring-boot-starter-jdbc、spring-boot-starter-actuator 等模块,就会发现,除了这些模块自身特定要提供的依赖,它们都同时依赖如下模块:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter</artifactId>
  4. </dependency>

而继续追踪下去到 spring-boot-starter 的 pom.xml 定义内部,最终才“柳暗花明又一村”:

  1. ...
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot</artifactId>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-autoconfigure</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-logging</artifactId>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework</groupId>
  17. <artifactId>spring-core</artifactId>
  18. <exclusions>
  19. <exclusion>
  20. <groupId>commons-logging</groupId>
  21. <artifactId>commons-logging</artifactId>
  22. </exclusion>
  23. </exclusions>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.yaml</groupId>
  27. <artifactId>snakeyaml</artifactId>
  28. <scope>runtime</scope>
  29. </dependency>
  30. </dependencies>
  31. ...

所以,一个 SpringBoot 项目得以成型,如下依赖基本是必备的:

  • org.springframework.boot:spring-boot
  • org.springframework.boot:spring-boot-autoconfigure
  • org.springframework.boot:spring-boot-starter-logging
  • org.springframework:spring-core
  • org.yaml:snakeyaml

作为 SpringBoot 项目,spring-boot 依赖自然不可少。

  • spring-boot-autoconfigure 依赖可以帮助我们进行自动配置,SpringBoot 项目的便利的核心就在于此。
  • spring-boot-starter-logging 则配置默认的日志依赖。
  • SpringBoot 项目就是一个 Spring 项目,所以 org.springframework:spring-core 也是必备依赖。
  • org.yaml:snakeyaml,是运行期依赖,主要提供针对 yml 格式配置文件的支持。

至此,我们基本已经了解了 SpringBoot 项目背后的故事全貌,接下来就要介绍 SBT 了。

构建在 SPRING INITIALIZR 之上的 http://start.spring.io 可以在每次创建新的 SpringBoot 项目的时候提供“脚手架”功能,创建 SBT 项目其实也有这样的“脚手架”工具,即 Typesafe Activator(https://www.typesafe.com/activator/download)。

我们使用 Typesafe Activator 来构建一个新的 SBT 项目,用于构建同样功能的一个汇率查询 Web API 微服务,Typesafe Activator 安装完成后执行如下命令:

  1. $ activator new currency-webapi-with-scala-sbt minimal-scala

其中,currency-webapi-with-scala-sbt 为我们要生成的 SBT 项目名,而 minimal-scala 为我们选择要使用的“脚手架”模板项目名。

初始生成的 SBT 项目的构建文件内容如下:

  1. name := """currency-webapi-with-scala-sbt"""
  2. version := "1.0"scalaVersion:= "2.11.7"
  3. // Change this to another test framework if you
  4. preferlibraryDependencies += "org.scalatest" %
  5. % "scalatest" % "2.2.4" %"test"
  6. // Uncomment to use Akka
  7. // libraryDependencies += "com.typesafe.akka" %
  8. % "akka-actor" % "2.3.11"

我们需要对其进行定制和丰富一些内容:

  1. organization := "com.keevol.springboot.chapter5"
  2. name := """currency-webapi-with-scala-sbt"""
  3. version := "1.0.0-SNAPSHOT"
  4. scalaVersion := "2.11.7"
  5. resolvers += "Local Maven Repository" at
  6. "file://"+Path.userHome.absolutePath+"/.m2/repository"
  7. libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" %
  8. "1.3.2.RELEASE"
  9. libraryDependencies += "com.keevol.springboot" % "currency-rates-service" % "1.0-SNAPSHOT"
  10. // Change this to another test framework if you
  11. preferlibraryDependencies += "org.scalatest" %
  12. %"scalatest" %
  13. "2.2.4" % "test"

因为我们已经了解了 SpringBoot 项目依赖的传递关系,所以,现在只要将 spring-boot-starter-web 作为核心依赖配置到 build.sbt 中,就可以信心满满地着手开发了(毕竟 spring-boot-starter-web 的依赖会一路上接力传递,必需的基础依赖一个都不会少)。

除此之外,currency-rates-service 属于业务依赖,也需要一并遵循 SBT 的配置语法加以配置。因为实例项目是使用本地开发,所以,我们配置了相应的 resolvers 指引 SBT 从本地的 Maven 仓库中加载业务依赖。

实际情况下,大家可以根据需要,将自己公司内部的 Maven 远程仓库或者其他 Maven 仓库也加入进来。

SBT 的项目源码结构与 Maven 的项目源码结构约定基本相同,所以,在 build.sbt 中所有配置完成后,就可以在 src/main/scala 目录下开始编写 scala 代码了:

  1. // CurrencyRateQueryController.scala源码文件
  2. package com.mengma.controllers
  3. import com.mengma.WebApiResponse
  4. import com.keevol.springboot.services.currency.rates.CurrencyPair
  5. import com.keevol.springboot.services.currency.rates.CurrencyRateService
  6. import com.keevol.springboot.services.currency.rates.ExchangeRate
  7. import org.springframework.beans.factory.annotation.Autowired
  8. import org.springframework.web.bind.annotation.{RequestMethod, RequestMapping,
  9. RestController}
  10. @RestController
  11. class CurrencyRateQueryController{
  12. @Autowired
  13. var currencyRateService: CurrencyRateService = _
  14. @RequestMapping(value = Array("/"), method = Array(RequestMethod.GET))
  15. def quote(symbol: String): WebApiResponse[ExchangeRate] = {
  16. val response: WebApiResponse[ExchangeRate] = new WebApiResponse [ExchangeRate]
  17. response.setCode(WebApiResponse.SUCCESS_CODE)
  18. response.setData(currencyRateService.quote(CurrencyPair.from (symbol)))
  19. response
  20. }
  21. }
  22. // CurrencyWebApiBootStrap.scala源码文件
  23. package com.mengma.springboot
  24. import com.keevol.springboot.services.currency.rates.CurrencyRateRepository
  25. import com.keevol.springboot.services.currency.rates.CurrencyRateService
  26. import com.keevol.springboot.services.currency.rates.CurrencyRateServiceImpl
  27. import org.springframework.boot.SpringApplication
  28. import org.springframework.boot.autoconfigure.SpringBootApplication
  29. import org.springframework.context.annotation.Bean
  30. @SpringBootApplication
  31. class CurrencyWebApiBootStrap{
  32. @Bean
  33. def currencyRateService:CurrencyRateService = {
  34. val service: CurrencyRateServiceImpl = new CurrencyRateServiceImpl
  35. service.setRateRepository(new CurrencyRateRepository) service
  36. }
  37. }
  38. object CurrencyWebApiBootStrap{
  39. def main(args: Array[String]) {
  40. SpringApplication.run(classOf[CurrencyWebApiBootStrap], args: _*)
  41. }
  42. }

开发完成后,运行 sbt run 或者直接通过 sbt”run-main com.keevol.springboot.chapter5.CurrencyWebApiBootStrap”即可启动微服务。

探索基于 SBT 的 SpringBoot 应用发布策略

针对我们基于 SBT 和 Scala 的 SpringBoot 微服务开发完成后,需要发布出去才能发挥其价值,要完成这种类型的项目发布,有几种策略可以考虑。

一种策略是使用 SpringBoot 推荐的可执行 jar 包发布形式,SpringBoot 团队提供的 spring-boot-maven-plugin 默认对这种可执行 jar 包有很好的支持。

但是,对于微服务来说,我们前面说了,不建议采用这种方式,除非零星个别的一些项目,不需要强大体系支撑,仅靠人工就可以满足或者忍受。

不过,如果因为某些因素必须使用 SpringBoot 建议的可执行 jar 包发布形式,那么,可以结合 spring-boot-maven-plugin 的源码和逻辑,并实现一个相应的 SBT 插件即可。

另一种策略是使用 SBT 生态圈中已经形成一定口碑或者说基本上已经是事实标准的方案,比如使用 sbt-native-packager(https://github.com/sbt/sbt-native-packager),通过 sbt-native-packager 这个 SBT 插件,我们可以将当前 SBT 项目发布为 ZIP、TAR、RPM、DEB 甚至 docker 等形式,相对于自己开发一个 SBT 插件完成可执行 jar 包形式的发布,显然直接使用 sbt-native-packager 是更明智的做法。

针对我们基于 SBT 和 Scala 的汇率查询 SpringBoot 微服务,要使用 sbt-native-packager 进行发布,我们只要对项目的 build.sbt 添加少许发布指引就可以了:

  1. import sbt._
  2. import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport._
  3. import com.typesafe.sbt.packager.MappingsHelper._lazy
  4. val root = project in file(".") enablePlugins(JavaAppPackaging)
  5. organization := "com.mengma.springboot"\
  6. name := """currency-webapi-with-scala-sbt"""
  7. version := "1.0.0-SNAPSHOT"
  8. scalaVersion := "2.11.7"
  9. resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
  10. libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" %
  11. "1.3.2.RELEASE"
  12. libraryDependencies += "com.keevol.springboot" % "currency-rates-service" % "1.0-SNAPSHOT"
  13. // Change this to another test framework if you prefer
  14. libraryDependencies += "org.scalatest" % %"scalatest" % "2.2.4" % "test"
  15. // 发布相关main
  16. Class in Compile := Some("com.mengma.springboot.CurrencyWebApiBootStrap") mappings
  17. in Universal += file("LICENSE") ->
  18. "LICENSE"mappings in Universal ++= directory("config")

我们首先通过 import 引入相应的依赖,然后声明当前项目以 Java 应用(JavaAppPackaging)的形式发布(不是 Scala 应用类型是因为编译完运行期都是 Java 字节码的形式),最后通过 mainClass 来指定启动类是哪一个,以及应该将哪些其他文件或者目录打入发布包之中。

在执行发布命令之前,我们还有最后一步要做,就是将 sbt-native-packager 添加为当前项目的依赖,在 SBT 项目结构下,这需要我们在当前项目根目录下新建 project/plugins.sbt 文件,然后添加如下内容:

  1. resolvers += "Typesafe repository" at
  2. "http://repo.typesafe.com/typesafe/releases/"resolvers +=
  3. Resolver.url("fix-sbt-plugin-releases",
  4. url("https://dl.bintray.com/sbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)addMavenResolverPluginaddSbtPlugin("com.typesafe.sbt"
  5. % "sbt-native-packager" % "1.0.0")

之后,build.sbt 才可以依赖到 sbt-native-packager 并且编译成功,现在,只要我们运行 sbt universal:packageBin 命令,就可以在 target/universal 目录下获得一个 zip 形式的发布安装包 currency-webapi-with-scala-sbt-1.0.0-SNAPSHOT.zip,安装包格式包含以下内容:

  • 自动生成启动脚本的 bin 目录。
  • 存放了项目所有依赖的 lib 目录。
  • 以及在 build.sbt 中通过 mapping 添加的一系列希望打包的文件和目录。

当然,zip 不是微服务发布的理想形式,但基于 RPM 和 DEB 等发布包需要依赖复杂的本地环境准备,故此,这里为了示例的简化,使用 zip 发布形式来说明如何使用 sbt-native-packager,以轻松愉快的心情完成基于 SBT 和 Scala 的 SpringBoot 项目发布。

关于如何准备 RPM、DEB 等发布包的编译环境,以及如何配置 sbt-native-packager 使之相应的将当前项目发布为 RPM、DEB 等形式,可以参考 sbt-native-packager 插件的网站文档(http://www.scala-sbt.org/sbt-native-packager/index.html)。

最后一种策略(或许还有)我认为是最适合微服务的终极发布策略,即外部化(Externalization)的发布策略。

无论是在 Maven 项目的 pom.xml 中定义和使用 spring-boot-maven-plugin,还是在 SBT 项目的构建配置中定义和使用 sbt-native-packager 插件,本质上都是一种将通用逻辑分散到一个个单独项目中的做法,如果只是小团队以及少量项目,这样做是没有太大问题的。

而一旦规模膨胀(对于微服务来说,数量、发布频度等指标规模膨胀很正常),加上人员流动、时机错配等一系列的变化因素,这种通用逻辑最终很可能会在这些一个个离散的项目中被“玩坏”,造成发布的成品千差万别,完全背离标准化的微服务这一理想轨道,进而带来整体微服务交付链路上的一系列不必要的负累。

所以,为了避免“该标准化的地方却使用个性化”所可能引发的一系列问题,我们应该将这些发布和部署逻辑从单一项目中剥离出来,即外部化掉(Externalize it),将这些逻辑以发布和部署平台的形式抽象和固化,从而将原来因为各种因素导致的不确定性最大化地转变成确定性。

发布和部署平台是外部化的最终成果,每个 SpringBoot 微服务项目,不管你使用的是什么语言开发,也不管你使用的什么构建工具,只要你想用并且能帮助你提高开发效率,在开发阶段尽管用即可。

一旦项目要发布,直接将发布请求对接发布和部署平台。对于 SpringBoot 项目的开发者来说,发布和部署平台是访问接口(Interface);而对于发布和部署平台的开发者来说,则是服务提供方。服务提供方可以灵活替换和升级:

如果你的 SpringBoot 项目是使用 Maven 作为构建工具,那么,发布和部署平台实现层会自动感知并构建发布。

如果你的 SpringBoot 项目是使用 SBT 作为构建工具,那么,发布和部署平台实现层也会自动感知并构建发布。

如果组织内部当前的微服务体系是基于 RPM 标准统一发布形式,那么,所有 SpringBoot 项目不管你使用什么构建工具,都将以 RPM 标准形式发布。

如果组织内部要实现 DevOps 体系的升级换代(比如 docker),那么,只要发布和部署平台实现层进行变更和升级即可,SpringBoot 项目的开发者无须感知,发布和部署平台起到了很好的抽象和防火墙效果。

所以,对于一个拥有成熟微服务体系的组织来说,将发布等通用逻辑外部化到发布和部署平台,应该是最合适,也是目前最终极的策略。

 

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

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

相关文章

面经攻略:详谈Redis常见数据类型

&#x1f449;本篇速览 早在最开始学Redis的时候&#xff0c;我们就学到了这九种数据类型&#xff1a;String、Hash、List、Set、Zset、BitMap、HyperLogLog、GEO、Stream&#xff0c;但其实在学的时候并不了解它的底层是怎么存储这些数据&#xff0c;而不同的数据类型又有哪些…

深兰科技|党政干部科技学习权威读本《元宇宙与社会治理新范式》

近日&#xff0c;由中共中央党校出版社出版&#xff0c;深兰科技创始人、深兰科技科学院创始院长陈海波撰写的《元宇宙与社会治理新范式》一书正式发售。 该书与陈海波主编的《与领导干部谈AI——人工智能推动第四次工业革命》、《与领导干部谈量子科技》一同入选了中共中央党校…

Web3中文|微软:黑客通过Telegram侵害加密公司

微软最近调查了一次攻击&#xff0c;在该攻击中&#xff0c;被追踪为 DEV-0139 的威胁参与者通过加入加密货币交易平台的Telegram群来寻找目标用户。 10月19日&#xff0c;威胁参与者冒充加密资产管理公司的代表邀请目标用户到另一个Telegram群&#xff0c;在那里威胁参与者要…

osgEarth示例分析——osgearth_eci

前言 osgearth_eci示例&#xff0c;展示了J2000的天体坐标系和ECEF地固系的转换&#xff0c;绘制坐标系&#xff0c;以及读取卫星参数绘制卫星的功能。绘制卫星轨迹&#xff0c;添加差值效果和未添加差值的效果。 关于卫星两行根数的数据文件下载路径&#xff1a;CelesTrak: …

0基础怎么转行软件测试?

前言 0基础转行软件测试难吗&#xff1f;怎么学才能找到工作&#xff1f;这应该是所有新人都会面临的问题&#xff0c;所以我结合自己的经历&#xff0c;做了一些总结和学习方法&#xff0c;希望能对大家有所帮助。 我按照薪资的不同大致划分成3个档位&#xff1a; 月薪5-9k&…

高压直流输电(HVDC)的最优潮流(OPF)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

ZIP压缩包的自动设置密码可以这样解除

WinRAR设置了自动添加密码&#xff0c;每次压缩完ZIP文件后&#xff0c;都会自带打开密码&#xff0c;打开压缩包里的文件都需要输入密码。如果后续不需要每个ZIP文件都带有密码&#xff0c;要如何去除这个设置呢&#xff1f; 首先&#xff0c;打开WinRAR压缩软件&#xff0c;点…

晶品特装在科创板上市:总市值约为48亿元,前三季度收入下滑12%

12月8日&#xff0c;北京晶品特装科技股份有限公司&#xff08;下称“晶品特装”&#xff0c;SH:688084&#xff09;在上海证券交易所科创板上市。本次上市&#xff0c;晶品特装的发行价格为60.98元/股&#xff0c;发行数量为1900万股&#xff0c;募资总额约为11.59亿元&#x…

IncepFormer:用于语义分割的高效inception transformer

前言 语义分割通常得益于全局上下文、精细定位信息、多尺度特征等。为了在这些方面改进基于Transformer的分割器&#xff0c;本文提出了一种简单而强大的语义分割架构——IncepFormer。IncepFormer介绍了一种新颖的金字塔结构Transformer编码器&#xff0c;它同时获取全局上下文…

中药中天然类固醇—艾美捷胆固醇肉豆蔻酸酯

艾美捷胆固醇肉豆蔻酸酯相关参数说明&#xff1a; CAS Registry No.: 1989-52-2 Formal Name: (3β)-cholest-5-en-3-ol 3-tetradecanoate Synonyms: Cholesterol Myristate, Cholesteryl Tetradecanoate, Myristic Acid cholesteryl ester, NSC 226867 MF: C41H72O2 FW: …

[附源码]Python计算机毕业设计Django预约挂号app

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

密码学-1-数字签名体制

密码学-1-数字签名体制密码学-2-RSA签名验签方案写在前面 1 数字签名 1.1 什么是数字签名 1.2 数字签名的作用 1.3 数字签名的特性 1.4 数字签名的算法 2 数字签名的原理 2.1 签名过程&#xff1a;创建数字签名 2.2 验证过程&#xff1a;验证数字签名 写在前面 …

Matlab|模拟电动汽车的充放电【充电顺序,波动发电,电池缓冲】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

使用redis Zset根据score和时间从多个维度进行排序(Zset榜单多维度排序)

文章目录1. 分段bit位实现排序2. 除数实现排序&#xff08;推荐&#xff09;3. 基于分段bit为实现的redis排序工具类一般我们都会用redis的Zset这个数据结构来做排行榜 问题引入&#xff1a;使用zSet进行排序的时候一直有一个痛点&#xff0c;就是只能根据score进行排序&#x…

牛客算法刷题-BM6 判断链表中是否有环

描述 判断给定的链表中是否有环。如果有环则返回true&#xff0c;否则返回false。 数据范围&#xff1a;链表长度 0≤\leq≤ n ≤\leq≤ 10000&#xff0c;链表中任意节点的值满足 |val| ≤\leq≤ 100000。 要求&#xff1a;空间复杂度 O(1)&#xff0c;时间复杂度 O(n)。 输…

思科模拟器 | 静态路由和默认路由的配置

静态路由与默认路由一、静态路由1、自定义IP地址2、基本配置与接线3、接口配置与指令描述4、静态路由配置【⭐】5、主机测试连接二、默认路由1、基本命令配置2、测试连接一、静态路由 1、自定义IP地址 以下是我自己分配的主机和个接口的IP地址、子网掩码以及默认网关&#xf…

SSL协议

目录 理论部分 实验部分 环境搭建 web1配置 配置负载均衡 web2配置 lb配置 实验目标&#xff1a;模拟颁发证书实现https访问&#xff0c;搭建负载均衡。 理论部分 1. SSL:安全套接字层 它是由Netscape公司于1994年创建&#xff0c;它旨在通过Web创建安全的Internet通信。 它是…

什么是股票委托接口?

什么是股票委托接口&#xff1f;相信大家对这些做股票量化交易接口系统都有一定是了解&#xff0c;其实股票委托接口是一些预先定义的接口&#xff0c;如函数与HTTP接口&#xff0c;以及api接口等这些&#xff0c;或指软件系统不同组成部分衔接的协议&#xff0c;用来提供应用程…

举个栗子~Tableau 技巧(246):将标签置于条形图的末端

用条形图呈现数据时&#xff0c;为增加直观性&#xff0c;通常会用标签显示条形对应的数值。Tableau 默认将标签呈现在条形最右侧外部&#xff0c;我们通过设置格式&#xff0c;可以将其挪动到条形里的中间或最左边。 有数据粉反馈&#xff1a;有没有办法&#xff0c;将数值标…

【C语言经典面试题】memcpy函数有没有更高效的拷贝实现方法?

【C语言经典面试题】memcpy函数有没有更高效的拷贝实现方法&#xff1f; 我相信大部分初中级C程序员在面试的过程中&#xff0c;可能都被问过关于memcpy函数的问题&#xff0c;甚至需要手撕memcpy。本文从另一个角度带你领悟一下memcpy的面试题&#xff0c;你可以看看是否能接得…