SpringBoot微服务的发布与部署(3种方式)

news2024/11/18 11:43:05

基于 SpringBoot 的微服务开发完成之后,现在到了把它们发布并部署到相应的环境去运行的时候了。

SpringBoot 框架只提供了一套基于可执行 jar 包(executable jar)格式的标准发布形式,但并没有对部署做过多的界定,而且为了简化可执行 jar 包的生成,SpringBoot 提供了相应的 Maven 项目插件:

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-maven-plugin</artifactId>
  6. </plugin> <!-- 其他插件定义 -->
  7. </plugins>
  8. </build>

然后只要我们运行 mvn package,当前 SpringBoot 项目就会被打包成一个包含了其所有项目依赖以及该项目本身的可执行 jar 包,通过 scp 或者 rsync 等方式将这个可执行 jar 包部署到目标环境的服务器之后,就可以通过 java-jar your-project.jar 启动 SpringBoot 应用了。

整个流程看起来很简单,也很符合大部分开发人员的认知,但是,相对于一套较为严谨的软件交付流程来说,以上流程则难免过于粗糙了。

软件的发布和部署可以有多种不同的形式,这更多由软件项目的属性决定,比如:

  • 这个项目使用的是什么语言?
  • 这个项目属于类库项目还是可独立运行的项目?
  • 这个项目是面向什么平台和环境的项目?

此外,我们希望使用什么样的形式进行软件的交付,这里则涉及生态管理以及技术选型的喜好等因素,所以,为了降低讲解的复杂度,我们还是先将发布和部署分开来说吧。

首先,大家应该都知道,发布并不等于部署,这是两个阶段的事情,如图 1 所示。


图 1 发布与部署示意图

 

发布一般是将项目以指定的格式打包成某种可直接交付的形式,然后放置到预先指定的交付地点。

比如对于 Java 类库(Java Library)来说,我们一般将其打包成 jar 包,然后 mvn deploy 到公司内部的 Maven 仓库中(Maven Repository),像 Nexus Repository Manager 或者 JFrog Artifactory 以及 Apache Archiva。

而对于可独立运行的程序,比如 SpringBoot 微服务或者一般的 Java Standalone 程序,我们既可以将它们打包成 RPM、DEB 等面向特定目标系统的发布形式,也可以将它们制作成一个个的 docker images,然后将制作完成的发布成品存储到相应的仓库中(Repository)去。

部署一般紧接着发布完成之后进行,它的主要职能就是将已经发布好的成品从仓库中拿出来,然后分发到目标环境的指定资源池(比如物理机结点,虚拟机结点,docker 宿主机等),并最终启动服务。

软件成品分发的手段和工具可以有很多种,从最常见的 scp、rsync,到 Chef、Puppet,进而再到最新的 saltstack、ansible 等,一般根据团队对这些工具的把控力度和喜好进行选型。

下面我们就几种典型的发布和部署形式跟大家一起探索相应的实践。

spring-boot-starter 的发布与部署方式

spring-boot-starter(s)属于 Java 类库性质的组件,只被其他可独立运行的程序依赖使用,自身不可独立运行,对于这种性质的软件实体,我们一般将其发布到公司内部的软件仓库或者以开源形式发布到 Maven 的中央仓库(Maven Central Repository)。

下面我们就以 spring-boot-starter-metrics 为例,向大家展示如何将类似 spring-boot-starter-metrics 这样的 Java 类库发布到自己公司内部搭建的 Nexus 服务器上。

首先,你要有一套已经搭建完成并运行的 Nexus 服务器,然后我们需要对 spring-boot-starter-metrics 的 pom.xml 附加一点儿发布相关的内容:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.keevol</groupId>
  7. <artifactId>spring-boot-starter-metrics</artifactId>
  8. <version>0.0.1-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10. <name>spring-boot-starter-metrics</name>
  11. <description>auto configuration module for dropwizard metrics</description>
  12. <parent>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-parent</artifactId>
  15. <version>1.3.0.RELEASE</version>
  16. <relativePath /> <!-- lookup parent from repository -->
  17. </parent>
  18. <distributionManagement>
  19. <repository>
  20. <id>deployment</id>
  21. <name>internal repository for releases</name>
  22. <url>http://{内部nexus服务器地址}/nexus/content/repositories/ releases/</url>
  23. </repository>
  24. <snapshotRepository>
  25. <id>deployment</id>
  26. <name>internal repository for snapshots</name>
  27. <url>http://{内部nexus服务器地址}/nexus/content/repositories/ snapshots/</url>
  28. </snapshotRepository>
  29. </distributionManagement>
  30. <properties>
  31. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  32. <java.version>1.8</java.version>
  33. <metrics.version>3.1.2</metrics.version>
  34. </properties> <!--其他配置 -->
  35. <dependencies>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter</artifactId>
  39. </dependency>
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-starter-aop</artifactId>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-actuator</artifactId>
  47. </dependency>
  48. <dependency>
  49. <groupId>io.dropwizard.metrics</groupId>
  50. <artifactId>metrics-core</artifactId>
  51. <version>${metrics.version}</version>
  52. </dependency>
  53. <dependency>
  54. <groupId>io.dropwizard.metrics</groupId>
  55. <artifactId>metrics-annotation</artifactId>
  56. <version>${metrics.version}</version>
  57. </dependency>
  58. <dependency>
  59. <groupId>org.aspectj</groupId>
  60. <artifactId>aspectjrt</artifactId>
  61. <version>1.8.7</version>
  62. </dependency>
  63. </dependencies>
  64. </project>

主要关注我们添加的 distributionManagement 相关内容,用于将我们的当前项目与内部的 Nexus 服务器进行关联,这样,就可以将当前项目不同阶段的成品(比如 SNAPSHOT 版本或者 RELEASE 版本)发布到特定的仓库路径下。

但是,只是在项目的 pom.xml 中添加 distributionManagement 相关内容还不够,对发布服务器的安全管控等敏感信息不能与 pom.xml 一同公开,所以,还需要在 ~/.m2/settings.xml 配置文件中添加 Nexus 服务器访问和认证信息:

  1. <server>
  2. <id>deployment</id>
  3. <username>deployment</username>
  4. <password>********</password>
  5. </server>

因为我们前面 distributionManagement 定义的 repository 和 snapshotRepository 的 id 都是 deployment,所以,这里的 server 的 id 也是匹配性地指定为 deployment,至于 username 和 password,则完全是我们内部的 nexus 服务器对应的安全认证用的用户名和密码啦。

将内部 Nexus 服务器的认证信息放到 maven 的 settings.xml 中并非什么好的实践,纯粹是为了便利性而牺牲安全性,二者之间需要根据情况做出权衡,如果对安全性要求比较高的公司或者组织,最好将这些认证信息移除,并只在管控的范围内使用。

比如将这些认证信息回收到发布和部署平台这一可控的小范围环境中,而所有开发人员使用的 settings.xml 属于“消毒”后无安全认证等敏感信息的版本。

当 pom.xml 中的 distributionManagement 以及 settings.xml 中对应的 server 设定都准备好之后,我们就可以直接 mvn deploy 将 spring-boot-starter-metrics 或者类似的 Java Library 项目发布到内部 Nexus 仓库了。

对于 Java 类库类型的项目来说,并无明确的部署过程,如果说有,也是存在于可独立运行项目的开发过程中,比如使用 lib 目录或者结合 Ant“部署”为项目的依赖,或者直接享受 maven、gradle、sbt 等编译工具提供的“透明”的依赖部署过程。

基于RPM的发布与部署方式

部署的目标服务器从硬件到系统软件,一般情况下都应该是尽量相同,这与软件的标准化目的相同,一个是可以减少应对不同类型实体的复杂度,另一个就是标准化硬件和软件之后,就可以通过工具批量化以“边际成本递减”近乎为 0 的做法来提升效率,减少成本。

所以,对于大部分互联网公司来说,在硬件标准化的基础上,还会使操作系统尽量统一,比如大多数都是使用稳定性和可靠性经过长期验证过的 Red Hat CentOS 系统,而 CentOS 本身经过长期的沉淀也有一套自己的系统管理工具,比如像 YUM 或者 RPM 这样的系统包依赖管理器(Debian/Ubuntu 等 Linux 发行版也有对应的 deb 形式的包管理器)。

像 RPM 这样的包管理器对系统软件包的依赖和配置提供了很好的支持,如果我们的微服务等可独立执行实体要部署到像 CentOS 这样的目标环境中,使用 RPM 完成微服务的发布和部署,对于运维人员来说几乎就是无缝衔接的。

而且,对于 SpringBoot 微服务来说,单单一个可执行的 jar 包实际上是远远无法达到发布和部署要求的,如果只是发布一个可执行的 jar 包,那就意味着在部署阶段,运维要做更多的事情来弥补某些缺失,比如:

  • 启动参数是否调整?
  • 配置文件是否修改?
  • 安装部署结构如何规范?
  • 资源的对接和映射要不要做?

但是,如果我们能够将整个软件交付体系标准化和规范化,然后通过 RPM 这样的发布形式将这些标注和规范固化到发布包中,那么,整个部署过程就可以简化为一条命令,这其实也是使用 RPM 这种系统原生包管理工具完成交付部署的好处:自动化的 orcherstration,减少不必要的人工干预中间环节。

使用 RPM 发布 SpringBoot 微服务,我们简单将这一个过程划分为几步,如图 2 所示。


图 2 使用 RPM 交付的 SpringBoot 微服务发布流程图

 

首先,我们需要有一个特定的编译和项目构建环境,可以不管这个编译和项目构建环境是搭建在本地(比如你的开发机上),还是搭建在特定的一台服务器上,但这个编译和项目构建环境需要安装 rpmbuild,用来构建 rpm 包。

其次,我们不推荐 RPM 编译和构建的过程使用某些定义在项目编译脚本中的插件来完成,这样会让一些通用的逻辑散落在所有需要发布的项目中而不好治理。所以,我们建议使用一个外部化的独立的编译服务器完成整个 SpringBoot 微服务的 RPM 发布和部署。

SpringBoot 微服务的 rpmbuild 脚本构建过程主要分几个主要步骤(如图 2 中 1 所标注):

1)调用标准的 mvn package 完成可执行 jar 包的打包。

2)根据软件交付规范,构建标准发布格式的 rpm 包,如下所示:

  • 使用 bin 目录存放根据脚本模板以及环境变量生成的启停脚本。
  • 使用 config 目录(SpringBoot 默认文件系统中的配置目录名)或者 conf 目录存放特定的配置文件。
  • 使用 docs 目录存放文档。
  • 使用 agents 目录存放某些 javaagent。
  • ……

3)在标准发布格式的基础上,生成从标准发布格式到具体目标环境的映射。

比如原来应用的日志是默认打印到当前项目部署目录,而根据要求,我们希望打印到 /var/logs/{projectId}/ 或者其他服务器磁盘容量分配更大的分区,这个时候,可以在 rpmbuild 过程中指定安装类似的安装规则。

下面是一个简化的 SpringBoot 微服务的 rpmbuild 脚本定义:

  1. Summary: metrics autoconfigure module for spring boot
  2. applicationName: spring-boot-starter-metricsVersion: {version}Release: 1Copyright: ...Group: Applications/ProductivitySource: ...URL: ...Distribution: Vendor: KEEp eVOLution, Inc.Packager: Darren <afoo@keevol.com>%description%prep%buildgit clone ....
  3. # 检出代码到本地cd {project folder}mvn package ...%install%clean%files/{install_location}/{projectId}/bin/start.sh/{install_location}
  4. /{projectId}/bin/stop.sh/{install_location}/{projectId}/agents/jolokia-jvm-1.3.1-agent.jar...%attr(755, user, group) /{install_location}/{projectId}/agents/jolokia-jvm-1.3.1-agent.jar...%doc%changelog

然后需要调用 rpmbuild 的 spec 定义完成最终 rpm 包的构建:

  1. rpmbuild -bb {projectId}.spec#

然后 scp or sftp 生成的 rpm 包到指定的 rpm 仓库 我们可以像上面那样直接执行 rpmbuild 命令完成最终的 rpm 包构建,也可以将这些逻辑纳入编译构建脚本并部署到像 Jenkins 这样现成的持续集成服务器上,总之,执行完成后,打包好的 rpm 就发布到目标环境对应的 rpm 仓库了。

rpm 包发布到 rpm 仓库之后,就可以执行部署,比如通过 Salt 或者 ansible 在目标环境执行 rpm 或者 yum 命令,但具体的部署行为可能因为不同开发者的习惯和理念而有所不同。

有的开发者喜欢将不同目标环境的配置都一股脑地打包到发布包中,然后通过配置文件的命名和启动程序时单独指定一个环境变量来决定如何启用哪一个配置文件,对于这种做法,只需要打一个 rpm 包,同时也只需要搭建一个内部的 rpm 仓库,部署的时候,则需要运维人员根据具体的操作环境传递相应的环境变量来启停程序。

有的开发者则认为,一个软件实体发布的时候就应该是针对目标环境“装配”完备的,rpm 包中的各项配置都是针对特定目标环境配置好的,只要将 rpm 包部署到目标环境,就可以直接启动,启停完全无差别操作,唯一的差别是,rpm 包分别是根据目标环境发布和部署到不同的 rpm 仓库的,如图 3 所示。


图 3 “根据不同交付目标环境,设置不同RPM仓库作为交付地点”示意图

以上两种策略并无优劣之分,但却有各自适合的场景:

1)在团队小,以人为本的时候,前者更适合,原因在于,整个软件交付链路更多是通过开发人员来协调和完成的。所以,开发、测试、运维一把抓,即使是不同环境的配置文件,也都是为了开发人员方便,直接放到了项目目录下一起管理和修改。

当到了线上,开发人员同时担当运维人员的角色,启停程序的可控性也很高,所以,可以在熟知自身程序的前提下,很好地完成整个链路的工作。

2)随着团队规模的扩张,职能更加明确,交付链路要承担的关注点也更多的时候,为了保证整个软件的交付质量,需要引入规范化的流程来关联和约束整个链路上各个环节和团队之间的工作。

这个时候,开发人员的职责范围将缩小到明确的范围,测试团队、安全团队、应用运维团队等也将加入并根据流程各司其职,每个团队之间的工作需要横向关联的同时,又需要垂直隔离。

这个时候,我们就需要从交付的源头一直到发布和部署,根据环境进行隔离,每个人即使只关注自己负责的事情,也可以让整个软件交付链路很好地工作,这考虑的是规范和流程对整体粒度上的把控和支撑。

对于小团队来说,微服务并不是什么太好的选择,高内聚的应用开发和部署单元,对整个交付链路的要求没那么高,也不需要更多自动化和平台化层面的投入和支持。

而一旦你选择了微服务的软件交付策略,数量庞大的微服务治理将耗费更多资源在支撑整个交付链路的自动化和平台化建设层面。否则,如此数量上的差异化的实体管理,单纯还靠人工拼苦劳是“捞不着好果子吃的”。

所以,我们要标准化和规范化微服务的开发、交付、部署以及运维,从而收敛整条链路的治理复杂度,以近乎无差别的方式,完成各个环节上的工作。这个时候,规范、流程、平台是核心,对人的要求则适当降低。

说了这么多,其实就一点,如果团队要转向微服务的交付策略,那么,标准化、单一化的微服务发布和部署行为是大家努力的方向,虽然我们在说基于 RPM 这种特定的微服务发布和部署形式,但并不意味着我们只应该关注这一点或者这单一环节,只有系统的从整体的微服务交付链路和体系层面考虑,才能够在各个单一环节落地的时候选择合适的方案。

基于 RPM 的发布与部署方式就说这么多,希望对大家有所启发。

基于 Docker 的发布与部署方式

随着资源虚拟化技术的持续精进,一种基于容器(container)的方式开始风行,Docker 就属于当下这个风口上最耀眼的明星,而且,很多微服务相关的文章也是言必提 Docker,好像没有 Docker 的微服务就不是正宗的微服务了,所以,我们自然也要适当提及一下如何结合 Docker 发布和部署我们的 SpringBoot 微服务。

我们知道,RPM 包的构建是使用系统的 rpmbuild 工具完成的,该工具需要一个构建描述文件,即 .spec 文件,rpmbuild 工具读取 .spec 构建描述文件之后,根据构建描述文件生成一个 rpm 包,然后我们就可以把 rpm 包发布到相应的 RPM 仓库(RPM Repository)。

使用 Docker 其实也是类似的过程,如图 4 所示。


图 4 基于Docker的SpringBoot微服务发布流程图

 

我们需要提供一个 Dockerfile 用于描述 Docker 发布成品的构建过程,这类似于 rpmbuild 需要的 .spec 文件。

在编写好要使用的 Dockerfile 之后,我们使用 docker build 命令读取 Dockerfile 开始构建一个 Docker 的 image,docker build 完成 rpmbuild 类似的功能,Docker 的 image 则类似于 rpm 包,即 Docker 的软件发布成品。

有了 docker image 之后,我们就可以将其发布到一个 Docker 的 image registry,这里的 image registry 就类似于 RPM 仓库(RPM Repository),而将 docker image 发布到 image registry 的过程,可以通过 docker push 完成。

所以,假设要以 Docker 的形式发布我们的汇率查询 SpringBoot 微服务,首先需要编写一个对应的 Dockerfile 来构建相应的 docker image:

  1. FROM java:8MAINTAINER AFOO <afoo@afoo.me>LABEL
  2. groupId=...LABEL artifactId=...LABEL
  3. version=...LABEL ...USER deployerEXPOSE 8080ENV
  4. {key}={value}...VOLUME ...RUN mkdir /{group}/{projectId}/configRUN mkdir /{group}/{projectId}/agentRUN mkdir /{group}/{projectId}/docsRUN mkdir /{group}/{projectId}/libCOPY target/docker-springboot-chapter4-0.0.1-SNAPSHOT.jar /{group}/{projectId}/lib/docker-springboot-chapter4-0.0.1-SNAPSHOT.jarCOPY conf/application.properties /{group}/{projectId}/config/application.propertiesCOPY agent/jolokia-jvm-1.3.3-agent.jar /{group}/{projectId}/jolokia-jvm-1.3.3-agent.jar...ENTRYPOINT ["java", "...", "-jar", "lib/docker-springboot-chapter4-0.0.1-SNAPSHOT.jar"]

之后,我们就可以使用这个 dockerfile 来进行构建并发布了:

  1. $ docker build . -t "{groupId}/{artifactId}:{version}

$ docker push 基于 Docker 的微服务部署与 RPM 类似,都是从发布仓库中拉取发布的成品,并在目标环境安装部署,一般情况下我们也同样是使用 Salt 或者 Ansible 之类的工具执行如下类似的命令完成基于 Docker 的微服务的部署:

  1. $ ansible {cluster} -m shell -a "cd /microservices/{groupId}/{artifactId}; docker pull"

为了简化基于 Docker 的发布和部署流程,实际上,以上演示的只是单人单微服务项目的方法,在讲究集团军作战的微服务场景下,我们希望的是能够快速、批量且标准化的形式完成数量巨大的微服务发布和部署。

这就要求不能只盯着单一项目内部去思考如何实现发布和部署,而应该将视线从单一项目内部抽取出来,以更高的视角来审视如何快速地完成批量微服务的发布和部署。

笔者建议的一个思路是适当地弱化 Docker 属性,将发布和部署逻辑外部化到发布脚本中。

外部化后的发布脚本将集中协调 Docker 基础设施,要发布的微服务上下文信息以及其他中间步骤,将微服务项目与 Docker 挂钩的唯一纽带也仅仅是一个模板化、标注化后的 Dockerfile,整个过程如图 5 所示。


图 5 外部化的基于Docker的SpringBoot微服务发布脚步逻辑流程图

 

如此一来,对于所有希望以 Docker 形式发布的标准化的微服务来说,一套发布脚本即可完成所有微服务的发布和部署,而不需要每一个微服务自己去编写 Dockerfile 甚至发布脚本。

上面的 Docker 实践并非 Docker 社区建议的最佳实践方式,这里更多只是为了简化说明和对比,Docker 背后是一套更为庞大完备的体系,比如 Docker 容器的注册和发现,容器的编排及调度等功能和系统,限于篇幅和内容定位,这里不再赘述。

总的来说,其实基于 Docker 的微服务发布和部署与其他形式从本质上来说没有太大差别,唯一的差别只是各自方案特定的实现不同而已。

大部分人选择和认同 Docker 方案,更多是从系统资源利用率以及 Docker 对整个软件交付链路的支撑体系比较完备这些角度考虑的,而微服务自身的很多特点以及需求(比如隔离、轻量),恰好与 Docker 能提供的相互吻合,或许这就是二者经常被“相提并论”的原因。

不过,对于 Java 应用和微服务来说,Docker 能给予的好处可能没有想象的那么多。

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

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

相关文章

2022年Python面试题汇总【面试官爱问】

2022年Python面试题汇总【常问】1、请你讲讲python获取输入的方式&#xff0c;以及python如何打开文件2、Python数据处理的常用函数3、请你说说python传参传引用4、请你说说python和java的区别5、Python你常用的包有哪些&#xff1f;6、简单说明如何选择正确的Python版本。7、简…

Qt动态库

QT带界面的动态库 创建动态库 一、新建一个C的动态库的项目 选择C的动态库的项目&#xff0c;进行下一步 修改项目的名字和项目的保存的路径。 选着编译的方式&#xff0c;不需要改&#xff0c;进行下一步。 选着动态库&#xff0c;编译成动态库&#xff0c;进行下一步。 项目…

[附源码]JAVA毕业设计社区生活超市管理系统(系统+LW)

[附源码]JAVA毕业设计社区生活超市管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

[附源码]Python计算机毕业设计SSM计算机学院课程设计管理系统(程序+LW)

[附源码]Python计算机毕业设计SSM计算机学院课程设计管理系统&#xff08;程序LW) 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。…

基于安卓的课程管理系统app设计

目 录 第1章 绪论 1 1.1 什么是电子课程管理 1 1.2 系统开发的背景 1 1.3 本文主要工作 1 1.4 系统设计目标分析 2 第2章 基本技术方案 3 2.1 Android概述 3 2.2 Android系统的四大组件 3 2.3 Android中的相关技术介绍及分析 5 2.3.1 Android系统架构研究 5 2.3.2 Android架构分…

LeetCode 167. 两数之和 II - 输入有序数组

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 167. 两数之和 II - 输入有序数组&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称…

2.zookeeper

1.工作机制 从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受观察者的注册&#xff0c;一旦这些数据的状态发生变化&#xff0c;Zookeeper就将负责通知已经在Zookeeper上注册…

p2.第一章 基础入门 -- 冯诺依曼体系和计算机基础 (二)

1.2 核心基础 1.2.1 计算机基础知识 艾伦麦席森图灵&#xff08;Alan Mathison Turing&#xff0c;1912年6月23日&#xff0d;1954年6月7日&#xff09;&#xff0c;英国数学家、逻辑学家&#xff0c;被称为计算机科学之父&#xff0c;人工智能之父。 图灵提出的著名的图灵机模…

铺瓷砖问题

1、题目 用 121 \times 212 的瓷砖&#xff0c;把 N2N \times 2N2 的区域填满&#xff0c;返回铺瓷砖的方法数。 2、思路 记录 F(n) 表示空的 n∗2n * 2n∗2 区域的铺瓷砖方法数。 如果第一块瓷砖 A 竖着放&#xff0c;则问题就变成了 F(n−1)F(n-1)F(n−1) 即空的 (n−1)∗…

C++习题3

求余运算符% 位运算&#xff08;<< >> & | ! ^ 左右移位&#xff0c;与&#xff0c;或&#xff0c;非 &#xff0c;异或&#xff09; 这些符号的运算对象都是整型 unsigned short x0xffff cout<<x; cout输出的是整数&#xff0c;所以要将16进制…

基于jsp+ssm的员工人事工资管理系统-计算机毕业设计

项目介绍 本系统是基于JSP的人事管理系统&#xff0c;使用java来实现动态管理以及数据库管理系统采用mysql等共同来完成。管理员可以通过人事管理系统进行工资管理、用户管理、员工考勤管理、部门管理、权限管理等。经理用户则可通过人事管理系统对进行工资管理、考勤管理、部…

快速排序(看完就会)

目录 什么是快速排序 快速排序的步骤&#xff1a; 以上&#xff1a; 图片步骤简绘&#xff1a; 代码实现&#xff1a; 什么是快速排序 快速排序是由C.A.R.Hoare在1960年提出。它的基本思想是&#xff1a;通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部…

easypoi导出excel,列要求是数字导出是文本的问题~并分析源码

&#x1f4c3;目录跳转&#x1f4da;简介&#xff1a;实体类分析源码效果&#x1f4da;简介&#xff1a; 由于使用了easypoi导出的excel列需要是数字的但是导出的默认是文本类型&#xff0c;这样选择列就无法进行统计求和&#xff0c;由于没有认真看注解导致我跑去分析源码。所…

ssm校园兼职系统|求职招聘系统计算机专业毕业论文java毕业设计开题报告

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 《ssm校园兼职系统》该项目采用技术&#xff1a;jsp springmvcspringmybatis cssjs等相关技术&#xff0c;项目含有源码、文档、配套开发软件、软…

《歌在飞》后蒙古包美女又一力作,《一路欢歌到北京》超然来袭

纵观中国华语乐坛&#xff0c;内蒙古音乐独树一帜&#xff0c;而要说到内蒙古的音乐人&#xff0c;就不得不提及苏勒亚其其格。说起著名音乐人苏勒亚其其格&#xff0c;她是一个地道的蒙古族人&#xff0c;如今是内蒙古乌兰牧骑独唱演员。 苏勒亚其其格是草原飞出的金凤凰&…

c++状态机的使用

什么是状态机 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型。英文名字叫State Machine &#xff0c;不是指一台实际机器&#xff0c;一般就是指一张状态转换图。全称是有限状态自动机&#xff0c;自动两个字包含重要含义。给定一个状态…

Pandas中使用Merge、Join 、Concat合并数据效率对比

在 Pandas 中有很多种方法可以进行dataframe(数据框)的合并。 本文将研究这些不同的方法&#xff0c;以及如何将它们执行速度的对比。 合并DF Pandas 使用 .merge() 方法来执行合并。 import pandas as pd # a dictionary to convert to a dataframe data1 {identificati…

配件厂商Hyper推出支持苹果Find My背包,背包防丢越来越智能

配件厂商 Hyper 今天推出了一款支持 Find My 的双肩包 HyperPack Pro&#xff0c;这样发生遗失或者失窃事件之后能够通过苹果的这项追踪技术快速锁定背包的位置&#xff0c;帮你快速找回。 背包有一个太阳镜口袋和易于拿取的前袋&#xff0c;此外背包上还有一个隐藏式口袋&am…

Github Student Developer Pack申请流程【不在学校的认证方法】

一、GitHub Student Developer Pack申请流程 首先你需要注册一个Github账号 进入GitHub Student Developer Pack申请页面&#xff0c;点击sign up for student developer pack 接着会出现如下图所示的界面&#xff0c;因为是学生&#xff0c;所以点击左下角的Get student ben…

Python打包神器,打包速度快生成文件小防反编译

分享一款打包速度快、生成文件小、更加安全的Python打包神器&#xff0c;本内容来源于网络。 一. pyinstaller和Nuitka使用感受 1.1 使用需求 这次也是由于项目需要&#xff0c;要将python的代码转成exe的程序&#xff0c;在找了许久后&#xff0c;发现了2个都能对python项目…