【Java八股文】11-分布式及场景面试篇

【Java八股文】11-分布式及场景面试篇


消息队列

你的项目为什么要用消息队列?

  • 解耦系统:让系统的各部分之间不直接依赖,通过消息传递进行沟通,提升系统的灵活性和可维护性。

  • 异步处理:让一些耗时操作(比如发送邮件、扣库存等)可以异步执行,不影响用户体验。

  • 流量控制:当系统流量激增时,消息队列可以暂时存储消息,等系统有空闲时再处理,避免系统崩溃。

  • 提高可靠性:即使某些部分出问题,消息队列可以暂存信息,等恢复后再处理,减少数据丢失。

  • 负载均衡:多个消费者可以并行处理队列中的消息,平衡系统负载,提高处理效率。

  • 扩展性:当业务增长时,可以方便地增加消费者来处理更多消息,支持系统扩展。

你说说 Kafka 为什么是高性能的?

  • 顺序写入:它将消息按顺序写入磁盘,避免了随机写入带来的性能瓶颈。
  • 零拷贝:Kafka 使用零拷贝技术,直接从磁盘传输数据到网络,减少了内存复制,提高了效率。
  • 分区并行:数据分成多个分区,多个消费者可以并行读取,提升处理速度。
  • 批量处理:Kafka 支持批量消息写入和读取,减少了网络和磁盘操作的开销。

kafka的使用场景,是否有消息丢失的情况

  • 实时数据流处理:Kafka 常用于处理实时数据流,如日志收集、监控数据传输、点击流分析等。

  • 日志聚合:它可以收集来自不同系统的日志,统一传输到中心化的日志存储或分析系统。

  • 事件源架构:用于在微服务架构中传递事件,确保不同服务之间的通信。

  • 消息队列:作为高吞吐量、高可用性的消息队列,支持异步消息处理。

  • 数据管道:Kafka 作为流数据平台,通常用于将数据从一个系统传输到另一个系统,像数据库同步、ETL等。

RocketMQ、Kafka 和 RabbitMQ

  • Kafka :Kafka 是基于 分区 (Partition)和 副本 (Replication)设计的分布式消息队列。它使用一个 发布-订阅 模型,生产者将消息写入到主题(Topic)中的分区,消费者可以根据分区消费消息。只支持 发布-订阅 模式
    • 优点:超高吞吐量,适合大规模数据流和实时处理。
    • 适用场景:日志聚合、流处理、大数据分析。
    • 缺点:不支持复杂的路由机制,消息顺序只保证在分区内。
  • RocketMQ :RocketMQ 也采用分布式架构,支持 主题(Topic)队列(Queue) 。消息可以被多个消费者消费,也支持严格的 顺序消息事务消息定时消息 。支持 发布-订阅点对点 两种模式,允许同一个消息被多个消费者消费,适合广播消息。
    • 优点:支持事务消息、顺序消息、消息精确一次投递。
    • 适用场景:金融、电商等需要高可靠性、顺序消费和事务的场景。
    • 缺点:性能稍逊于 Kafka,配置较复杂。
  • RabbitMQ :RabbitMQ 使用 AMQP(Advanced Message Queuing Protocol) 协议,采用 交换机(Exchange)队列(Queue) 的模式,消息通过交换机路由到队列,消费者从队列中获取消息。支持 发布-订阅点对点工作队列 等多种消息模型,适用于各种灵活的消息传递场景。
    • 优点:支持多种协议,灵活的消息路由(如交换机),适合多种消费模式。
    • 适用场景:小到中规模应用,微服务架构,复杂路由需求。
    • 缺点:吞吐量和性能不如 Kafka 和 RocketMQ,延迟稍高。

RabbitMq怎么消息被消费

消息持久化:确保消息不丢失。

消息确认:确保消息被成功消费。

死信队列:处理消费失败的消息。

重试机制:为消费失败的消息提供重试功能。

并发和负载均衡:提高消息消费效率。

项目中MQ是怎么用的?

在数据同步中使用到了MQ。

  1. 首先有一个用于数据同步的交换机(topic)
  2. 交换机绑定多个队列,每个队列对应一个数据同步的队列
  3. 当服务信息修改后,Canal将修改的数据发送到MQ,指定用于数据同步队列的Routing key。
  4. 同步程序监听MQ,得到修改的数据库,请求Elasticsearch创建索引,修改索引,删除索引。

MQ消息可靠性怎么保证?

保证MQ消息的可靠性分两个方面:保证生产消息的可靠性、保证消费消息的可靠性。

  • 保证生产消息的可靠性

    RabbitMQ提供生产者确认机制保证生产消息的可靠性。

    首先发送消息的方法如果执行失败会进行重试,这里我们在发送消息的工具类中使用spring提供的@Retryable注解,实现发送失败重试机制,通过注解的backoff属性指定重试等待策略,通过Recover注解指定失败回调方法,失败重试后仍然失败的会走失败回调方法,在回调方法中将失败消息写入一个失效消息表由定时任务进行补偿(重新发送),如果系统无法补偿成功则由人工进行处理,单独开发人工处理失败消息的功能模块。

    另外MQ提供生产者确认机制,我们在发送消息时给每个消息指定一个唯一ID ,设置回调方法,如果发送成功MQ返回ack,如果失败会返回nack,我们在回调方法中解析是ack还是nack,如果发送失败可以记录到失败表由定时任务去异步重新发送。

    还有一种情况是如果消息发送到MQ的Broker成功了但是并没有到达队列,此时会调用ReturnCallback回调方法,在回调方法中我们可以收到失败的消息进行补偿处理。

  • 保证消费消息的可靠性

    RabbitMQ是通过消费者回执来确认消费者是否成功处理消息的:消费者获取消息后,应该向RabbitMQ发送ACK回执,表明自己已经处理完成消息,RabbitMQ收到ACK后删除消息。

    RabbitMQ提供三个确认模式:手动ack,自动ack、关闭ack

    本项目使用自动ack模式,当消费消息失败会重试,重试3次如果还失败会将消息投递到失败消息队列,由定时任务程序定时读取队列的消息。

MQ怎么避免重复消费?

  • 需要保证MQ消费消息的幂等性。

    使用数据库的唯一约束去控制。添加唯一索引保证添加数据的幂等性

  • 使用token机制

    发送消息时给消息指定一个唯一的ID,发送消息时将消息ID写入Redis

    消费时根据消息ID查询Redis判断是否已经消费,如果已经消费则不再消费。

如何保证Canal+MQ同步消息的顺序性?

Canal解析binlog日志信息按顺序发到MQ的队列中。现在是要保证消费端如何按顺序消费队列中的消息。

解决方法:

  • 多个jvm进程监听同一个队列保证只有消费者活跃,即只有一个消费者接收消息。
    • 队列需要增加x-single-active-consumer参数,值为true,表示否启用单一活动消费者模式。
    • 消费队列中的数据使用单线程。
    • 在监听队列的java代码中指定消费线程为1

Canal+MQ如何同步商品信息到ES中?

  1. 首先在Elasticsearch中创建商品的索引结构,包括商品的spu信息、sku信息,这样输入关键字可以根据商品的spu去搜索也可以根据商品的sku去搜索。
  2. 在MySQL中创建一张商品信息同步表,当修改商品信息时同时修改商品信息同步表,商品信息同步表的结构与Elasticsearch中的商品索引是一致的。
  3. Canal会读取商品信息同步表的binlog日志,解析日志的内容写入MQ。
  4. 同步程序监听MQ,收到商品的信息后写入Elasticsearch,这样就完成了同步商品信息到ES中。

系统设计

你项目是怎么存密码的?

原先使用一些很简单的加密算法,比如MD5、SHA-1。直接用 MD5 加密存储的,因为 MD5 是不可逆的,也就是不能从MD5加密后的结果转换回加密之前的结果,所以当时就想当然的认为这是安全的。听说 MD5 虽然不可逆,但是破解起来也是相对容易的,比如碰撞攻击和彩虹表攻击,都有可能将 MD5 解密。

  • 碰撞攻击是指通过找到两个不同的输入,得出相同的输出值(即哈希值)。对于 MD5 和 SHA-1,这种攻击相对容易实现。随着计算机性能的提高,攻击者可以生成大量的哈希值,寻找碰撞。
  • 彩虹表是一种预计算的哈希值字典,它能够通过存储大量常见密码的哈希值来加速破解过程。如果密码没有加盐,攻击者只需要在彩虹表中查找对应的哈希值,迅速得到原密码。

后来采用密码加盐

  • 加盐是指在用户的密码之前或之后加入一个随机生成的字符串(盐值,salt)。然后,再对包含盐值的密码进行哈希计算。由于每个密码都加上不同的盐值,即使两个用户的密码相同,它们的哈希结果也会不同。

或者采用多因素认证(MFA)或更权威的第三方认证。

手机验证码或者把登录交由更加权威的第三方认证。

如何设计一个分布式ID?

  • 如果对性能和顺序性有较高要求,且有分布式系统的需求,Snowflake算法是一个常用且有效的方案。

  • 如果只需要保证全局唯一性,并且ID长度不敏感,可以选择UUID

  • 如果希望数据库生成ID并且控制分布式锁的使用,可以选择数据库自增ID + 分布式锁

雪花算法id

雪花算法生成步骤

  • 获取当前时间戳:记录当前时间(精确到毫秒)。
  • 时间回拨处理:如果当前时间小于上次生成 ID 的时间,发生了回拨(时钟回退)。
  • 计算 ID 生成的各个部分
    • 时间戳 :计算当前时间与自定义起始时间(Epoch)之间的差值,得到时间戳。41位
    • 机器 ID+服务ID :由机器/节点的标识决定,通常是由配置文件或其他方式分配的。这个可以自由组合,只要能区分开就行。
    • 序列号 :如果同一毫秒内生成多个 ID,序列号会递增。当同一毫秒内生成超过最大数量时,算法会等待下一毫秒。12位
  • 拼接生成唯一 ID:将以上的各个部分拼接起来,形成一个 64 位的唯一 ID。

uuid是多少位知道吗?uuid不同的生成方法有哪些?

UUID(通用唯一识别码)通常是 128 位长,表示为 32 个十六进制数字,通常以五个部分形式呈现:8+4+4+4+12

其中每个 x 是一个十六进制数字(0-9, a-f)。UUID 的总长度是 128 位,即 16 字节。

uuid不同的生成方法:

  • 基于时间(Time-based UUID)
  • 基于名称(Name-based UUID):通过对某些输入数据(如域名)进行哈希计算来生成,确保相同的输入数据每次生成的 UUID 相同。
    • UUID 的 Version 3 使用 MD5 哈希算法
    • Version 5使用 SHA-1 哈希算法。
  • 随机生成(Random-based UUID):UUID 的 Version 4,即使用 122 位的随机数生成。

Java Hutool UUID 生成方法 :Hutool 默认采用 UUID Version 4(随机生成) 的方式来生成 UUID。

单点登录是怎么工作的?

单点登录(SSO,Single Sign-On)是一种认证机制,它允许用户在一次登录后就能访问多个相互关联的应用系统,无需在每个应用中重复登录。SSO的基本原理是将用户身份认证的过程集中管理,用户只需在一个地方登录,其他需要认证的系统通过某种机制知道用户已通过认证。

JWT是一个由三部分组成的字符串,分别为:头部 (Header)、负载 (Payload)和签名 (Signature)。它们之间通过 . 分隔。<Header>.<Payload>.<Signature>

  • 用户在登录界面提供用户名和密码,提交给认证服务器。认证服务器验证用户身份后,生成一个JWT,通常会将一些用户信息(如用户ID、角色、权限)存放在负载部分,同时设置过期时间等。认证服务器使用密钥对JWT进行签名,生成最终的JWT令牌并返回给用户。
  • 用户在访问需要身份验证的资源时,会在请求头中携带JWT令牌。通常会使用 Authorization 头部字段,格式为:Authorization: Bearer <jwt_token>
  • 服务器收到请求后,首先从请求中提取JWT令牌。服务器使用预先共享的密钥(或者公钥)验证JWT的签名。如果签名有效,表示JWT没有被篡改,服务器会从JWT中提取用户信息(如用户ID、角色等),并允许用户访问资源。

JWT优点:

  • 无状态:JWT令牌是自包含的,它在令牌中就携带了所有需要的信息(如用户身份),不需要查询数据库或维护会话,节省了服务器的存储。
  • 跨平台:JWT基于JSON格式,可以在不同平台(如Web、移动端、微服务等)之间传输,兼容性好。
  • 支持跨域认证 :由于JWT通常存储在浏览器的 localStoragesessionStorage 中,且JWT为无状态认证,不依赖于服务端的会话,可以在多个域间传递JWT。
  • 灵活性:JWT的负载部分可以存储用户的自定义信息(如角色、权限等),便于服务端进行授权控制。

JWT缺点:

  • 过期问题:JWT通常有一个有效期,过期后需要重新获取。对于长时间登录的用户,可能需要实现刷新令牌机制。
  • 安全性问题:如果私钥泄露,攻击者可以伪造JWT,因此需要妥善保护签名密钥。
  • 无法撤销:一旦JWT被发放,直到过期前都无法撤销,除非通过特定的机制(如黑名单)来标记无效JWT。

分布式中CAP理论

三个主要特性之间的权衡关系:一致性 (Consistency)、可用性 (Availability)和 分区容错性(Partition Tolerance)。

  • 一致性 (Consistency)
    • 所有节点在同一时刻看到的数据是一样的,也就是说,所有读操作返回的结果都是最新写入的数据。
    • 一致性要求:无论在任何节点进行数据读取,所读取到的数据都是完全一致的。
  • 可用性 (Availability)
    • 系统在每个请求到来时,都能够返回一个有效的响应(即使数据未更新),即使部分节点不可用。
    • 可用性要求:系统始终能响应客户端请求,保证服务不被中断。
  • 分区容错性 (Partition Tolerance)
    • 系统能够继续正常工作,即使网络分区或者节点间的通信出现问题(网络延迟、节点失联等)。
    • 分区容错性要求:系统能够应对在某些节点之间失联的情况下继续正常运行。

多数组合为CA、CP、AP

  • CA(一致性 + 可用性) :传统的单节点数据库或集群中没有网络分区的情况。例如,MySQL、PostgreSQL 通过主从复制可以保证一致性和可用性,但如果网络出现问题,它们会丢失一些请求。
  • CP(一致性 + 分区容错性)HBase、Zookeeper 等,它们会在网络分区时牺牲可用性,保证数据的一致性。某些请求会被延迟或丢失,但数据的正确性是被保证的。
  • AP(可用性 + 分区容错性) :Cassandra、MongoDB、Couchbase 等,优先保证系统始终能够响应请求,即使在网络分区发生时,数据也可能是最终一致的。这些系统通常会容忍数据不一致,直到网络恢复后进行数据同步。

某个分片上传失败,重试机制是如何设计的

  • 检测失败:如果上传失败,记录错误并判断是否可以重试。

  • 限制重试次数:设置最大重试次数,避免无限重试。

  • 指数退避:每次重试时,增加等待时间(如第一次1秒,第二次2秒)。

  • 错误分类:根据错误类型(暂时性或永久性)决定是否重试。

  • 幂等性:确保重复上传时数据一致,不会产生重复。

  • 进度记录:记录已上传的分片,失败时从最后成功的分片继续上传。

订单的状态机怎么实现的

我们封装了一个状态机组件,包括:状态机抽象类、状态接口、事件接口、动作接口、快照基础类。

实现一个订单状态机的步骤(其它状态实现类似):

  • 定义订单状态:用枚举类列出所有可能的订单状态(比如:创建、支付、发货、完成、取消)。

  • 定义状态变更事件:每次订单状态变更时,创建一个事件类(比如,订单支付事件)。

  • 定义订单快照:记录订单当前的状态和时间,以便回溯。

  • 定义变更动作:每个状态变更事件都对应一个动作(比如,支付事件触发订单状态变为已支付)。

  • 定义状态机:订单状态机类负责管理所有状态、事件和动作,并控制状态之间的转换。

为什么要用状态机

状态机能够提供更高的灵活性、可维护性和可扩展性。

  • 规则明确:状态机明确规定了每个状态之间的转换规则,而静态状态量通常需要大量的条件判断,容易出错。
  • 易于扩展:如果要添加新状态或规则,状态机更容易扩展,修改集中在状态机内部;而静态状态量可能需要修改多个地方,扩展不方便。
  • 代码简洁:状态机将不同状态下的处理逻辑分开,不会在多个地方重复判断,代码更清晰。
  • 减少错误:状态机通过集中管理状态转换,减少了漏掉某些逻辑或错误状态转换的风险。
  • 支持复杂逻辑:对于状态之间复杂的行为,状态机能够清晰地组织和管理,而静态状态量会导致逻辑混乱。

责任链模式用在哪里?怎么使用?在开发中注意什么?

责任链模式适用于那些处理逻辑可以被划分成多个独立处理器,并且这些处理器之间存在某种顺序关系组合成一个链,依次执行链中的处理器,最终拿到处理结果。

通常应用在如下场景:

  • 事件处理系统: 在事件处理系统中,一个事件可能经过多个处理者处理,每个处理者负责一部分的事件处理逻辑。责任链模式可以用于处理事件的分发和处理。

  • 请求传递与处理解耦: 当一个请求可能被多个处理者中的一个处理,而请求的发送者不需要知道是哪个处理者来处理时,责任链模式可以实现请求的发送者和接收者的解耦。

  • 多级审批流程: 典型的场景是多级审批流程,例如请假审批、报销审批等。不同级别的审批人员可以构成一个责任链,每个审批人员处理请求后,可以将请求传递给下一级审批人员,直到得到最终审批结果。

  • 日志记录与处理: 日志记录可以通过责任链模式来实现。不同级别的日志处理者可以负责记录不同级别的日志信息,形成一个级别递增的责任链。

使用责任链模式步骤:

  1. 定义责任接口或抽象类。
  2. 定义处理器类
  3. 将多个处理器组成一个链。
  4. 客户端通过链中的第一个处理器开始执行。

开发中注意以下几点:

  1. 确保每个处理者的处理逻辑清晰: 每个具体处理者应该专注于自己的处理逻辑,确保责任链的每个环在·节都清晰可理解。
  2. 避免形成无限循环: 在设计责任链时,要避免形成闭环,确保请求不会在责任链中无限循环。
  3. 动态修改责任链: 责任链模式允许在运行时动态修改处理者链,这样可以灵活地调整请求的处理顺序。
  4. 适时终止责任链: 某个处理者在处理请求时,如果确定无需继续传递请求,可以适时终止责任链的传递,提高效率。

责任链应用场景

  1. 距离优先规则:首先通过Elasticsearch计算服务人员与用户的距离,按距离升序排序。
  2. 最少接单数优先规则:如果距离相同,再根据接单数排序,接单数少的优先。
  3. 如果上述两个规则都相同,那么就可以随机选择。

场景提问

CPU 100% 问题怎么排查?

  1. 使用 tophtop 检查 CPU 使用情况:
    • top 命令可以显示 CPU 使用最多的进程,htoptop 的一个增强版本,具有更好的界面。指定单个进程使用top -p <pid>
    • 如果发现 java 进程占用了大量 CPU 资源,可以进一步进行分析。
  2. 硬盘 I/O:硬盘故障或 I/O 阻塞也可能导致 CPU 占用过高。
  3. 检查是否有异常的守护进程(如 cron、systemd 服务等)消耗了过多资源。
  4. 是否面临大量的流量(例如 DDoS 攻击),可能会导致 CPU 高负荷。

是java的问题

  1. 使用 jstackjmap 获取 Java 堆栈和内存信息。
  2. 使用 Arthas 进行更详细的分析。

给个十亿个qq号,设计一个数据结构来管理

  • 如果对存储和查询效率要求较高,可以选择 哈希表Trie 树
  • 如果内存非常有限且只需要存在性检测,可以使用 布隆过滤器
  • 如果数据集超过内存容量,考虑使用 数据库分布式存储

有个java的服务,现在发现其中有个服务延迟很高,要你排查的话怎么解决这个事情

  1. 确认延迟发生的具体服务 :确保是哪个具体的服务或接口发生了延迟。你可以通过日志、监控工具(如 Prometheus等)来确定。
  2. 检查服务端日志:检查该服务的日志文件,看是否有异常(如错误堆栈、警告信息、连接问题等)。
  3. 查看性能监控工具 :使用如 JVM监控(例如:JVisualVM、JProfiler 或通过 Prometheus 的 Java Agent)来查看服务的内存使用、GC 频率、线程使用情况等,是否有内存泄漏或过度的垃圾回收造成延迟。
  4. 网络层面排查 :使用 tcpdump 抓包,检查服务间的网络通信是否存在问题(如超时、丢包等)。如果服务之间通过HTTP调用,可以使用 Fiddler 检查API请求/响应的延迟。
  5. 数据库查询性能:如果服务与数据库进行交互,检查数据库查询性能,检查慢查询日志。
  6. 分析线程和锁:查看是否存在线程池饱和、线程阻塞或死锁的情况。可以使用线程 dump 工具(如 jstack)来查看线程状态,找出可能的瓶颈。
  7. 资源瓶颈分析 :查看服务是否存在 CPU 饱和的情况,使用 tophtop 工具查看资源使用情况。
  8. 负载测试 :使用工具(如 JMeterGatling)对该服务进行负载测试,观察在高并发时的表现,帮助定位是否是由于负载过高导致的延迟。

订单支付怎么做的

支付服务对接了第三方支付平台,包括:微信、支付宝、第三方聚合支付平台等。

  1. 用户进行支付会请求支付服务,支付服务请求第三方支付平台进行下单,对于小程序支付会返回一个标识,通过该标识前端调起支付窗口。

交互流程是:业务模块请求支付服务,支付服务请求第三方支付平台,业务模块需要传入业务订单号、支付金额等信息。

  1. 用户进行支付,支付成功后第三方支付平台将支付结果通知支付服务,支付服务通过MQ将支付结果通知给我们项目的业务模块。
  2. 业务模块收到支付结果,更新订单的支付状态。

数据库

MySQL 的架构是怎么样的?

客户端:发起数据库请求的地方,比如应用程序或命令行工具。

连接管理:管理客户端和服务器之间的连接。

查询解析:解析客户端发来的 SQL 请求,确保语法正确。

查询优化:选择最优的执行计划来提高查询效率。

执行引擎:实际执行查询操作,访问数据并进行处理。

存储引擎:管理数据存储和检索的模块。常见的存储引擎有 InnoDB(支持事务)和 MyISAM(不支持事务)。

缓冲池:缓存数据以减少磁盘读取,提高查询速度。

日志:记录操作日志,用于数据恢复、复制和性能调优。

复制:支持将数据从主数据库复制到多个从数据库,实现数据同步。

管理和监控工具:用于数据库的管理、监控和性能优化。

Redis 除了用作缓存还能干嘛?

  • 消息队列:支持异步任务处理,通过列表(List)和发布/订阅(Pub/Sub)机制实现消息传递。或者Zset做延迟队列。
  • 实时统计:可以用来统计点击量、访问量等,支持高效的计数器。
  • 排行榜:通过排序集合(Sorted Set)实现游戏得分或文章点赞等排名功能。
  • 分布式锁:保证多个进程间的资源访问互斥。
  • 去重:通过集合(Set)去重,防止重复数据操作。

ES倒排索引说一下?

倒排索引(Inverted Index)是搜索引擎中常用的一种数据结构,目的是为了高效地进行文本搜索。具体到 Elasticsearch(ES),倒排索引用于存储文档中各个词项(词语)及其出现位置,以便快速查找包含某个词的文档。

倒排索引的基本原理:

  • 词项(Term):文档中的每个单词或短语。

  • 文档(Document):可以理解为一个包含多个词项的内容单元(如一篇文章、一条记录等)。

倒排索引构建过程

  • 文档索引:每个文档被拆分成多个词项,这些词项会被映射到文档上。
  • 查询:当你执行查询时,ES 会通过倒排索引找到包含查询词项的文档,快速返回搜索结果。
  • 反向分析:ES 在搜索时,首先将查询的词进行分析和分词,然后用倒排索引查找匹配的文档。

云原生

Docker和传统虚拟机有什么区别?

  • 架构:
    • 虚拟机:虚拟机在物理服务器上运行一个完整的操作系统,每个虚拟机都有自己的操作系统(包括内核)。
    • Docker:Docker 是基于容器的,容器共享宿主操作系统的内核,但它们运行自己的用户空间。
  • 资源利用
    • 虚拟机:虚拟机需要分配一部分硬件资源(如 CPU、内存、硬盘等)给每个虚拟机操作系统
    • Docker:Docker 容器利用宿主机的操作系统内核,资源消耗相对较少。
  • 启动时间
    • 虚拟机:启动虚拟机需要加载完整的操作系统,通常需要几分钟时间。
    • Docker:Docker 容器利用宿主操作系统内核,启动速度非常快,通常只需要几秒钟。
  • 隔离性
    • 虚拟机:虚拟机提供更强的隔离性,因为每个虚拟机都有独立的操作系统和内核。
    • Docker:Docker 容器提供了相对较弱的隔离性,因为容器共享宿主机的内核,但容器之间通过命名空间和控制组进行隔离。
  • 灵活性和可移植性
    • 虚拟机:虚拟机的迁移和部署较为复杂,因为每个虚拟机都是一个完整的操作系统,迁移时需要确保目标主机的资源和配置匹配。
    • Docker:Docker 容器是独立于操作系统的,它们可以在任何支持 Docker 的平台上运行,极大地提高了应用的可移植性和灵活性。
  • 使用场景
    • 虚拟机:适用于需要完全隔离的环境,或者需要不同操作系统的场景。常用于数据中心、虚拟化环境。
    • Docker:适用于微服务架构、开发和测试环境。由于 Docker 容器更轻便,广泛应用于 DevOps、持续集成/持续部署(CI/CD)等场景。

Docker 和 k8s 之间是什么关系?

Docker 和 Kubernetes(K8s)是密切相关的,但它们的功能不同:

  • Docker:是一个容器化平台,用于打包、分发和运行应用。它提供了创建、管理和运行容器的工具。容器可以理解为轻量级的虚拟机,但它们共享宿主操作系统的内核。
  • Kubernetes(K8s):是一个容器编排平台,用于自动化容器的部署、扩展和管理。它可以帮助管理多个 Docker 容器,处理容器的生命周期、调度、负载均衡等任务。

Docker 是 K8s 使用的容器运行时(container runtime)。也就是说,K8s 管理和调度的是 Docker 容器的实例。K8s 可以启动、停止和扩展 Docker 容器,但它不提供容器创建的功能,那个是由 Docker 提供的。

Docker 用于创建和运行容器,K8s 用于管理多个 Docker 容器的集群。

Dockerfile和Dockercompose

  • Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。

    • FROM 指定基础镜像 From Ubuntu:22.04

    • ENV 指令用来设置环境变量。这些环境变量将在容器中运行时保持有效。

    • COPY:将本地文件复制到镜像中。

    • RUN:用于执行命令,通常用于安装软件包或者执行其他操作来构建镜像。

    • EXPOSE:声明容器需要开放的端口。

    • ENTRYPOINT 指令用于设置容器启动时默认执行的命令。

      dockerfile 复制代码
      # 指定基础镜像
      FROM java:8-alpine
      # # 配置环境变量,JDK的安装目录
      # ENV JAVA_DIR=/usr/local
      
      # # 拷贝jdk和java项目的包
      # COPY ./jdk8.tar.gz $JAVA_DIR/ 
      
      # # 安装JDK
      # RUN cd $JAVA_DIR \
      #  && tar -xf ./jdk8.tar.gz \
      #  && mv ./jdk1.8.0_144 ./java8
      
      # # 配置环境变量
      # ENV JAVA_HOME=$JAVA_DIR/java8
      # ENV PATH=$PATH:$JAVA_HOME/bin
      
      COPY ./docker-demo.jar /tmp/app.jar
      
      # 暴露端口
      EXPOSE 8090
      # 入口,java项目的启动命令
      ENTRYPOINT java -jar /tmp/app.jar
  • Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器!

    Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。

    properties 复制代码
    version: "3.2"
    
    services:
      nacos:
        image: nacos/nacos-server
        environment:
          MODE: standalone
        ports:
          - "8848:8848"
      mysql:
        image: mysql:5.7.25
        environment:
          MYSQL_ROOT_PASSWORD: 123sjbsjb
        volumes:
          - "$PWD/mysql/data:/var/lib/mysql"
          - "$PWD/mysql/conf:/etc/mysql/conf.d/"
      userservice:
        build: ./user-service
      orderservice:
        build: ./order-service
      gateway:
        build: ./gateway
        ports:
          - "10010:10010"

部署

java源码打包成jar包括什么

  • 编译后的 .class 文件。
  • META-INF/MANIFEST.MF 文件。
  • 任何项目中的资源文件(如 .properties.xml 等)。
  • 根据构建配置,可能还会包含外部依赖(如果使用了 Maven Shade 插件等)。\

git使用工作流程

工作流程步骤

  1. master 分支创建一个新的功能分支

    shell 复制代码
    git checkout -b feature-branch
  2. 在功能分支上进行开发和提交

    shell 复制代码
    git add .                # 添加更改
    git commit -m "feature: add new feature"  # 提交更改
  3. 保持功能分支与 master 分支同步在功能开发过程中,定期将 master分支的最新更改合并到功能分支,以防止冲突。

    shell 复制代码
    git checkout master      # 切换到主分支
    git pull origin master   # 拉取最新的 master
    git checkout feature-branch
    git merge master         # 将 master 的更新合并到 feature 分支
  4. 功能完成后将功能分支合并到 master 分支

    shell 复制代码
    git checkout master
    git merge feature-branch
  5. 推送到远程仓库并删除功能分支

    shell 复制代码
    git push origin master    # 推送更新到远程仓库
    git branch -d feature-branch  # 删除本地功能分支
    git push origin --delete feature-branch  # 删除远程功能分支

优点:每个功能开发都在独立的分支中进行,避免了不同功能的代码干扰。

缺点:需要频繁合并,管理多个分支可能稍显复杂。

git merge 、git rebase、 git fetch的区别

命令 作用 特点
git merge 将两个分支的更改合并 保留分支历史,产生合并提交
git rebase 将一个分支的提交重放到另一个分支上 重写历史,产生线性历史
git fetch 从远程仓库拉取最新的提交和元数据 不修改本地工作区,只更新远程跟踪分支
  • 使用 git merge 时,你的分支历史会保留原样,适合保留不同开发路径的痕迹。
  • 使用 git rebase 时,历史会变得更整洁,适合在合并前清理历史。
  • 使用 git fetch 时,只有从远程仓库拉取数据,并不会对本地分支产生直接影响。
相关推荐
茂桑4 分钟前
MVCC(多版本并发控制)
java·开发语言·数据库
customer0819 分钟前
【开源免费】基于SpringBoot+Vue.JS医疗报销系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
barcke28 分钟前
【深度解析】Java接入DeepSeek大模型:从零实现流式对话+多轮会话管理(完整项目实战) —— SpringBoot整合、API安全封装、性能优化全攻略
java·spring boot
zl97989940 分钟前
MybatisPlus-注解
java·spring·maven
杰九1 小时前
【环境配置】maven,mysql,node.js,vue的快速配置与上手
java·vue.js·spring boot·mysql·node.js·maven
wapicn991 小时前
‌挖数据平台对接DeepSeek推出一键云端部署功能:API接口驱动金融、汽车等行业智能化升级
java·人工智能·python·金融·汽车·php
技术蔡蔡1 小时前
Android字节码处理-函数耗时统计揭秘
算法·面试
一个假的前端男1 小时前
RabbitMQ 消息队列
分布式·rabbitmq
逸狼2 小时前
【JavaEE进阶】Spring DI
java·开发语言
yonuyeung2 小时前
代码随想录算法【Day54】
java·数据结构·算法