【2024秋招】2023-9-21 度小满信贷系统平台部三面

1 实习

1.1 你做了哪方面的工作

1.2 你负责的是项目中哪一部分

1.3 你这些小需求都是什么类型的呢,介绍一下

1.4 机房换线是你们做的吗

1.5 你具体负责你这个系统中的哪方面的内容呢,比如具体的一个服务

答:服务基本都建的差不多,所以我负责的主要还是在原有的基础上做一些增加操作

1.6 具体有哪些类型的接口

答:

1.7 别人是怎么调用你的呢?调用方是谁呢

答:使用客户端调用我们,我们使用springcloud feign进行rpc调用

1.8 从需求提出到上线经历过哪些步骤

答:运维方跟我商量->系统发布->接收任务->本地编写代码并且测试->测试环境测,然后使用它的人会进行测试->合并到线上环境分支->通过流水线的构建进行发布

1.9 有专门的测试人员测试还是自测

答:没有

1.10 有没有碰到过什么问题呢,怎么解决的呢

答:OOM

1.11 发生oom的文件是手动dump还是自动dump的呢

答:设置heapdumpOnOOM这个参数,还设置了一个dump path,指定dump日志

1.12 你怎么分析dump文件的呢

答:使用mat

1.13 你这个dump文件大概有多大呢,为什么呢

答:当时设置的堆内存是8GB吧,然后dump文件接近8GB

当Java应用发生OutOfMemoryError(OOM)并生成堆转储(heap dump)时,转储文件的大小取决于当时的堆使用情况。

  1. 最大情况: 如果你为JVM设置了8GB的堆大小(通过-Xmx参数),那么在最坏的情况下,即堆几乎完全被使用时,转储文件的大小接近8GB。

  2. 通常情况: 但在实际情况中,当OOM发生时,堆可能没有完全被使用,所以转储文件的大小可能小于8GB,但仍然接近此值。

  3. 压缩: 一些工具和JVM实现提供了在创建时压缩堆转储的功能,这可以显著减小转储文件的大小。例如,使用Oracle HotSpot JVM的-XX:+HeapDumpOnOutOfMemoryError参数生成的转储文件是未压缩的,但可以使用工具(如gzip)进行后续压缩。

  4. 分析工具: 请注意,不是所有的堆分析工具都能处理压缩的转储文件。如果你计划使用特定的工具来分析转储,你应该先检查其对压缩转储的支持情况。

  5. 考虑磁盘空间: 考虑到转储文件可能非常大,确保在配置JVM生成OOM转储时有足够的磁盘空间是很重要的。如果磁盘空间不足,转储过程可能失败。

总的来说,如果为JVM设置了8GB的堆,并期望生成OOM转储,你应该为转储文件预留接近8GB的磁盘空间(或更多,以考虑其他文件和操作系统需求)。

1.14 你这个对象大概占用了多大的空间呢,为什么呢

答:大概6个GB吧,因为老年代和年轻代的比例是2:8,所以老年代的空间是6.4G,但是年轻代中还有一些其他的对象,所以这个对象大概占用gGB左右

1.15 你们g1的年轻代和老年代的比例设置的是多少,是用默认的吗,能不能用默认的呢

答:

对于G1垃圾收集器,默认情况下,年轻代(Young Generation)和老年代(Old Generation)的比例是动态计算和调整的,以满足暂停时间目标(默认情况下是200ms)。

G1垃圾收集器并没有直接提供设置固定年轻代与老年代比例的参数。但它提供了以下参数来影响年轻代的大小:

  • -XX:G1NewSizePercent:这是年轻代最小值占整个堆大小的百分比,默认值为5%。
  • -XX:G1MaxNewSizePercent:这是年轻代最大值占整个堆大小的百分比,默认值为60%。

这意味着,按默认设置,年轻代的大小将会动态调整,在整个堆的5%到60%之间。

然而,G1会根据实际的工作负载和指定的目标暂停时间(通过-XX:MaxGCPauseMillis参数设置)来调整年轻代的实际大小。目标是使垃圾收集的暂停时间尽可能地接近指定的目标。

总的来说,虽然你不能直接设置一个固定的年轻代和老年代的比例,但可以通过上述参数间接地影响和控制年轻代的大小。如果你决定更改这些默认值,建议进行性能测试,以确保修改后的设置能满足你的需求。

1.16 通过mat分析dump文件之后,你是怎么找到那个大的对象呢?你怎么知道这个对象是你的某一个对象呢?

答:

Memory Analyzer Tool (MAT) 是一个非常强大的工具,用于分析Java堆转储文件,并识别内存泄漏和高内存消耗的原因。以下是使用MAT分析转储文件并找到大的或潜在泄露的对象的基本步骤:

  1. 概述报告 (Overview Report):

    • 打开转储文件后,MAT会显示一个概述报告,其中列出了大的对象、大的对象数组和其他可能的内存泄漏的线索。
  2. Histogram视图:(确定大对象)

    • 这是一个对象按类型排序的列表,它显示了每种对象的实例数和它们在堆中占用的总空间。
    • 通过检查这个列表,你可以很容易地看到哪些对象类型占用了最多的内存。
  3. 支配者树 (Dominator Tree):

    • 这个视图显示了哪些对象持有了最大部分的内存。这不仅仅是单个对象的大小,还包括由该对象引用的其他对象的大小。
    • 这有助于找到那些“持有”大量内存的对象。
  4. 路径到GC根:

    • 当你选中一个特定的对象时,可以查看它到垃圾收集根的路径。这有助于确定为什么一个对象没有被垃圾回收。
  5. 泄漏疑点报告 (Leak Suspects Report):

    • 如果MAT识别出可能的内存泄漏,它会生成一个泄漏疑点报告。这个报告可以提供关于潜在泄漏原因的有价值的线索。
  6. 识别自定义对象:

    • 在Histogram或Dominator Tree视图中,对象通常按它们的完整类名列出。通过类名,你可以识别这些对象是来自你的应用的哪一部分,或它们属于哪个特定的库或框架。
  7. 查看对象内容:(确定某一个大对象是自己写的)

    • 当你选中一个对象时,MAT允许你查看其字段和值。这可以帮助你进一步理解为什么某个对象在内存中占用那么多空间。
  8. 查询语言:

    • MAT提供了一个强大的查询语言(OQL),你可以使用它来执行复杂的查询和分析。

总之,分析堆转储文件并识别高内存消耗的原因需要经验和对应用的深入理解。使用MAT提供的工具和视图,结合你对应用的知识,通常可以有效地识别和解决内存相关问题。

1.17 所以你的解决办法是什么呢

答:串行化数据中心

1.18 那有没有考虑过一个数据中心也会导致OOM呢

答:我当时是做了冗余的,大概是考虑到快手的日活再增加一倍,流量增加一倍,然后数据中心的零件增加一倍,同步的数据增加一倍,那么单个数据中心零件可能只是由10w增加到20w这样的,这样内存也是够用的。而零件数量翻倍可能需要好几年才能实现,甚至实现不了

1.19 你的日活的数量根数据中心的数据量关系是什么

答:正相关

1.20 有没有一种情况日活不变,但是机器的使用量变大呢

答:有可能的,可能存在流量突增,比如快手的某一个热点新闻这里的

1.21 你这个项目中之前的日志是怎么记的呢

答:之前的日志做的很粗粒度

1.22 你给这个日志是做了什么优化呢?

答:我的任务是将日志进行分级,然后分发到kafka和ES中

1.23 这个是你mt让你做的,还是你自己想做的

答:mt布置的

1.24 你是怎么做的呢

答:我是将不同级别的日志放到不同的目录下,然后有专门的工具负责将这个数据同步到ES和kafka中

1.25 你在里面做了什么呢

答:主要是将日志进行分级吧,仅仅是改一下logback的配置文件

1.26 我想在每一行的日志里面打印当前机器的ip,怎么做

答:

为了在每条Logback日志中都加上机器的IP,你可以采用以下方法:

1. 获取IP地址

首先,你需要有一个方法来获取机器的IP地址,就像我之前提供的那样。可以创建一个工具类或者在你的主应用类中实现这个方法。

2. 将IP地址加入MDC(Mapped Diagnostic Context)

Logback提供了MDC机制,允许你将额外的上下文信息加入到每条日志消息中。首先,你需要在应用启动时将IP地址放入MDC中。

例如:

import org.slf4j.MDC;

public class AppInitializer {
    public static void initialize() {
        String ipAddress = getLocalIPAddress(); // 使用之前提供的方法
        MDC.put("ipAddress", ipAddress);
    }
}

确保在应用启动时调用AppInitializer.initialize()

3. 在Logback配置中使用MDC

接下来,你需要在logback.xml中配置Logback,以使其使用MDC中的IP地址。这可以通过添加%X转换词来实现。

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [IP: %X{ipAddress}] - %msg%npattern>
        encoder>
    appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    root>
configuration>

注意这里的[IP: %X{ipAddress}]部分,这会从MDC中获取IP地址并将其加入到每条日志消息中。

以上步骤应该使每一条日志都带有机器的IP地址。不过,一旦设置,确保你不要在应用运行期间修改MDC中的IP地址值,以免产生不一致的日志记录。

1.27 curd启动器是怎么做的

答:和mybatisPlus的差别

2 mysql

2.1 现在有一张学生成绩表sc,有sno,class,grade,我现在想要一个sql找出每一个班级分数最高的学生的成绩,班级以及学号?

答:

SELECT s.sno, s.class, s.grade
FROM student_scores s
JOIN (
    SELECT class, MAX(grade) as highest_grade
    FROM student_scores
    GROUP BY class
) as max_scores
ON s.class = max_scores.class AND s.grade = max_scores.highest_grade;

2.2 SELECT class, MAX(grade) as highest_grade FROM student_scores GROUP BY class order by highest_grade; 其中order by是对分组后的结果排序呢还是先排序再分组呢

答:先分组,然后再对分组后的结果进行排序

3 秒杀系统

3.1 你为什么会想做秒杀系统呢,而不是单独的订单系统呢

答:是因为能从这个项目中学到很东西

3.2 说说微服务拆分原则,以及你是怎么拆分的

答:

微服务拆分是一个复杂的过程,涉及将单一的、大型应用拆分为多个独立、松耦合的小型服务。正确地拆分微服务可以带来伸缩性、维护性和灵活性的优势,但如果拆分得不好,可能会导致更多的管理负担和复杂性。

以下是一些微服务拆分的基本原则:

  1. 单一职责原则 (Single Responsibility Principle):
    每个微服务应该有一个明确的、独特的功能或责任。这样,当某个功能需要更改或扩展时,只需要修改一个服务。

  2. 领域驱动设计 (Domain-Driven Design, DDD):
    根据业务领域和子领域(也称为边界上下文)将服务划分为独立的微服务。这有助于确保服务之间的职责明确,并减少它们之间的耦合。

  3. 常见模式的识别:
    例如,某些通用功能(如身份验证或日志记录)可能适合作为独立的服务,供其他服务调用。

  4. 避免紧密耦合:
    微服务之间应该松耦合。一个微服务的更改不应该需要更改其他微服务。这也意味着服务间的接口应该保持稳定,即使服务的内部实现发生变化。

  5. 数据库解耦:
    每个微服务应该有自己的数据库或数据存储,以避免多个服务直接访问同一数据库,这可能会导致紧密耦合。

  6. 考虑团队结构:
    Conway’s Law指出,组织结构会影响软件设计。根据团队的能力和结构来拆分微服务可以提高效率,因为每个团队可以完全拥有和管理一个或多个微服务。

  7. 考虑性能和数据一致性:
    某些高吞吐或低延迟的操作可能不适合拆分为微服务,因为网络调用可能会增加延迟。此外,某些业务流程可能需要事务性或强一致性,这可能会影响如何拆分服务。

  8. 迭代和增量拆分:
    开始时,不必急于将所有东西都拆分为微服务。可以从一个宏服务开始,然后根据需要和学到的经验逐渐拆分。

  9. 技术异构性:
    微服务允许使用不同的技术栈为不同的业务需求选择最合适的工具。但同时,这也可能带来管理和维护的复杂性。要在技术多样性和标准化之间找到平衡。

  10. 考虑网络和故障:
    微服务之间的通信是通过网络进行的,这意味着网络延迟和故障是必然的。设计微服务时,要考虑重试、超时和断路器等模式。

当考虑微服务拆分时,除了上述原则外,还需要考虑团队的经验、当前的技术堆栈、业务需求和长期的维护和管理成本。

3.3 秒杀主要体现在哪个地方呢,吞吐量高吗还是

答:对

3.4 你的极限吞吐量怎么得来的

答:是因为我发现进一步提高线程数的时候,它的tps在下降

3.5 有没有可能是发压的地方扛不住了呢

答:不会。

3.6 你在什么样机器上发出这1w个请求呢,为什么能抗住这1w线程或者更多的并发请求呢

答:本地,16GB的内存,8核心,首先我是在本地进行压测的,它不涉及到远程通信,此外它的秒杀接口返回的信息量很少,各个服务也是本地部署,也就是说本机进程之间的通信速度也很快,这样就是说开的这些线程并发数虽然很高,但是也会很快消失。

3.7 那你这里2600TPS没法提高的瓶颈大概是在什么地方呢?假设我想让他提到3000,4000,需要在哪些地方做优化呢

答:对redis进行水平扩展,redis可以分片,然后对每一个微服务都进行集群部署,进行负载均衡,也可以

3.8 刚刚你说对redis做水平扩展,是默认了redis已经是瓶颈了是吗

答:并不一定,得整个系统进行扩展,综合考虑

3.9 你觉得你写的这个系统在,是哪一个地方的资源或者哪一种资源不够,导致需要做水平扩展呢,比如IO,磁盘,网络,线程,内存和cpu

答:

(1)如果是指提升整个的这个微服务的后端系统的总体的tps能力,可以在发压方进行集群部署,然后进行压测,每一个线程随机访问某一个接口

(2)如果是为了提升秒杀接口的tps,我觉得还是订单集群做水平扩展比较好,相当于扩充了它的cpu处理能力吧,这样的话客户端调用秒杀接口的请求可以做负载均衡操作

4 rpc框架

4.1 你从哪儿了解到这个rpc框架

答:

4.2 你用到哪个协议,讲讲

答:用的手写的上层应用协议

4.3 为什么不使用http呢,而是要自定义呢

答:

(1)适配特定应用场景,更加适配,发挥极致性能

(2)想了解netty是如何做到自定义协议的,我自己的自定义能不能跑的通

(3)和http对比一下,深刻了解http的一些机制

4.4 实现序列化的方式有哪些

5 学习能力

5.1 你平常是怎么学习一项技术的

答:首先肯定是因为我现在正需要这一项技术去解决当前的业务问题,所以我会抽取一两周的时间集中攻克这个难题,然后我会看看有没有介绍demo或者视频,更倾向于跟着视频一步步复现,然后实现一些diy的功能,如果跑通了,就应用到自己的业务场景中,最后会再和其他的解决方案做一下对比,看看能不能优化

5.2 学习渠道

答:gpt4,看书,公众号,视频学习

5.3 快手的时候为什么不能转正呢

答:

5.4 有无其他offer,哪个部门

答:用友bip

5.5 后面想去哪些行业和部门

答:待遇为导向,没有特别偏好

5.6 对我们了解多吗

答:…

6 反问

6.1 后面有没有hr面呢

答:可能有,也可能没有

6.2 您管理的是哪个部门呢

答:管理的就是风控下的数据团队

6.3 你们做的大数据也是用java吗

答:上层系统是java,但是底层是hive,spark,python等吗

6.4 你们团队的hive,spark等框架也是自己写的是吗

答:对,很多东西我们自己都会研发一部分,跟开源的有一些区别

6.5 面试结果什么时候出来

答:等这一个批次结束,每一个有一个分数,然后进行排序

你可能感兴趣的:(面经,java后端,度小满)