记一次杭州施强集团面试题
本文最后更新于218 天前,其中的信息可能已经过时,如有错误请发送邮件到[email protected]

还有好几个实在想不起来了 /(ㄒoㄒ)/~~

openfeign 的执行原理

  1. 首先通过@EnableFeignClients注解后会导入FeignClientsRegistrar这个类,类中会通过一个方法registerFeignClients扫描所有的定义了@FeignClients接口获取到@FeignClients中定义的属性,并且生成代理类。
  2. 在接口方法上,Feign 会通过SpringMvcContract类中的processAnnotationOnClass方法解析如 @RequestMapping@PathVariable@RequestParam 等注解,以构建实际的 HTTP 请求。
  3. 发送HTTP请求,如果什么都没配置的话默认使用Apache HttpClient,也可以配置成OKhttp。
  4. 响应处理 :接收到响应后,Feign 会解析返回的数据,并根据方法的返回类型进行反序列化。如果请求失败,Feign 会根据配置的错误解码器处理异常情况。

你们openfeign用的长链接还是短链接

openfeign默认使用的短链接。即当请求完毕后就关闭此次链接,下次请求再开启。

这里也说一下长短链接的优缺点

使用长连接和短连接各有其优缺点,适用的场景也有所不同。

短连接

优点

  1. 简单性:每个请求后自动关闭连接,减少了连接管理的复杂性。
  2. 资源释放:每次请求后,系统会释放资源,减少潜在的资源泄露风险。
  3. 防止闲置:避免了长时间闲置连接带来的资源浪费。

缺点

  1. 性能损失:频繁的建立和关闭连接会增加延迟,降低性能。
  2. 开销:每个请求都需要进行 TCP 握手,增加网络开销。

场景示例

  • 低频请求:例如一个用户信息查询接口,用户并不频繁访问,使用短连接可以减少资源占用。

长连接

优点

  1. 提高性能:连接可以重用,减少了 TCP 握手的频率,降低延迟。
  2. 适合高频请求:在高频交互场景中,重用连接可以显著提高系统吞吐量。

缺点

  1. 资源占用:连接长时间保持可能导致资源泄露,尤其是未被正确关闭的情况下。
  2. 管理复杂性:需要对连接的生命周期进行管理,增加了实现的复杂性。

场景示例

  • 实时通信:如聊天应用或实时数据推送,频繁的请求和响应需要使用长连接以保持低延迟和高效的交互。

在我们系统中使用长链接还是短链接具体还要根据业务场景来决定,如果我们服务之间的信息传递非常频繁,使用长链接可以有效降低建立连接的开销,提高性能。

如果交互并不频繁可以选用短链接,能够自动管理连接状态,也能保证安全性。

如果一个其他系统想要我们系统认证才能启动,请设计

这个问题当时问的时候,说了一大堆什么双向认证或者什么Token啦、JWT啦,面后想了想面试官根本不是想问这个,不然也不会突然从Spirng的问答中跳出问这个,而且题目也说了被认证系统连启动都不行。我在看Spring官网的时候,看到了事件这一章才明白这个题的答案。

可以通过实现BootstrapRegistryInitializer接口,在实现类中进行认证,如果认证无法通过则项目无法启动,这个题的核心目的还是Spring的核心内容——事件监听

@Configuration
public class CustomBootstrapInitializer implements BootstrapRegistryInitializer {

    @Override
    public void initialize(BootstrapRegistry bootstrapRegistry) {
        // 实现你的认证逻辑,可以是从配置文件中读取密钥或者其他方式
        boolean isAuthenticated = performAuthentication();
        if (!isAuthenticated) {
            throw new RuntimeException("Authentication failed!");
        }
    }

    private boolean performAuthentication() {
        // 你的认证代码
        return true; // 或 false 取决于认证结果
    }
}

关于BootstrapRegistryInitializer这个类代表感知引导初始化监听器,这个监听器会在bootstrapContext加载后触发,系统中环境、上下文等都是在这之后加载的。

如果每个字段都加上索引会怎么样?

在数据库中为每个字段都加上索引可能会带来一些严重的弊端,尽管索引可以提高某些查询的性能,但过多的索引也会导致负面影响。以下是详细的弊端分析:

写操作性能下降

索引的存在意味着每当有数据插入、更新或删除时,数据库不仅需要修改数据本身,还需要相应地维护所有的索引。

  • 插入操作:每次插入新数据时,数据库不仅要把数据写入表中,还要在相关索引中插入相应的条目。如果每个字段都有索引,插入操作就会变得非常缓慢。
  • 更新操作:如果更新涉及到被索引的字段,数据库需要重新调整索引中的条目,维护索引的有序性。这会增加更新操作的复杂性和时间开销。
  • 删除操作:删除操作同样需要维护索引中的记录,也会降低性能。

存储空间的消耗

每个索引都占用额外的存储空间。为每个字段都创建索引会显著增加数据库的存储需求,特别是在大表中,这种存储空间的开销会非常显著。索引越多,占用的磁盘空间越大。

  • 索引文件大小:数据库中的每个索引通常会生成独立的文件或结构来存储,这些文件会占用磁盘资源,导致存储成本增加。
  • 内存开销:数据库在查询时需要将索引加载到内存中以加速检索操作。大量的索引会占用更多的内存资源,从而降低数据库的整体性能。

查询优化器的困惑

现代数据库系统使用查询优化器来确定最优的查询执行计划。然而,如果表中存在过多的索引,优化器可能会花费更多的时间来分析和选择适合的索引,甚至可能做出错误的选择。

  • 过度选择:优化器需要为每个查询确定最优的索引路径。过多的索引可能会导致优化器在选择最佳索引时遇到困难,从而选择不适合的索引路径,导致查询性能下降。
  • 索引冲突:多个索引可能会彼此竞争,特别是在多列查询时,数据库可能无法明确选择最佳的组合索引,导致不必要的全表扫描或非最优查询路径。

维护与管理复杂性

索引需要定期进行维护,比如重建或重组,尤其是当索引碎片化严重时(插入、删除操作频繁导致的碎片)。当为每个字段都创建索引时,管理这些索引将变得非常复杂和耗时。

  • 定期维护:大量的索引需要频繁的维护操作,这会占用更多的资源并增加管理负担。
  • 性能监控:管理员需要定期检查哪些索引是有效的,哪些是冗余的或者未被使用的。索引的过度增加会使得这个监控过程更加繁琐。

冗余索引的可能性

如果为每个字段都加上索引,许多索引可能在实际使用中是冗余的。例如,假设表中有复合索引(多列索引),单独为每列再创建索引是没有必要的,因为复合索引已经可以满足多个字段的查询需求。

  • 重复性:一些复合查询可能已经被现有的组合索引覆盖,再为单独的列加索引会变得冗余,既占用存储空间又影响性能。
  • 无用索引:某些字段的索引可能在实际查询中几乎从未使用,这样的索引不仅浪费空间,还拖慢写操作的性能。

优化并不是线性递增的

并不是每个字段的索引都会带来显著的性能提升。某些字段的索引在查询中可能很少使用,甚至有些字段的数据特性(例如高重复性或低选择性)使得索引的效果微乎其微。

  • 低选择性字段的索引:如果字段的取值范围非常小(如性别字段,只有“男”和“女”两种可能值),为这些字段加索引可能不会带来显著的性能提升,反而可能增加额外的负担。

锁竞争增加

当数据库执行并发操作时,过多的索引会增加锁的竞争。在写入数据时,数据库需要锁住相关的索引条目,确保数据一致性。索引越多,锁竞争的可能性越大,尤其在高并发环境下,这会导致整体性能的下降。

一个文件每一行都是一个整形的数字,从这个文件中找出最大的 10 数字,怎么做

可以使用最小堆来做。

  1. 初始化最小堆 :首先,你创建一个最小堆,并初始化为空。这个堆将用于存储当前找到的最大的10个数字。
  2. 逐个读取文件中的数字 :你逐个读取文件中的数字,并将每个数字与最小堆的根节点(即当前堆中最小的数字)进行比较。
  3. 维护堆的大小 :如果当前数字大于堆的根节点,则将根节点替换为当前数字,并重新调整堆以保持最小堆的性质。如果当前数字小于或等于根节点,则忽略该数字。
  4. 重复直到文件结束 :重复上述过程,直到你读取完文件中的所有数字。此时,最小堆中将包含文件中最大的10个数字。

具体步骤

  1. 初始化最小堆 :创建一个大小为10的最小堆。
  2. 逐个读取数字
    • 如果堆未满(即堆中的元素数量小于10),直接将当前数字插入堆中,并调整堆。
    • 如果堆已满,将当前数字与堆的根节点进行比较:
      • 如果当前数字大于根节点,则替换根节点,并调整堆。
      • 如果当前数字小于或等于根节点,则忽略该数字。
  3. 调整堆 :每次替换根节点后,通过下沉操作重新调整堆,确保堆的性质(即每个父节点都小于其子节点)得以保持。
  4. 完成读取 :当文件中的所有数字都被读取并处理后,最小堆中将包含文件中最大的10个数字。
import java.util.Arrays;

public class MinHeap {
    private int[] heap;
    private int size;
    private int capacity;

    public MinHeap(int capacity) {
        this.capacity = capacity;
        this.heap = new int[capacity];
        this.size = 0;
    }

    private int parent(int index) {
        return (index - 1) / 2;
    }

    private int leftChild(int index) {
        return 2 * index + 1;
    }

    private int rightChild(int index) {
        return 2 * index + 2;
    }

    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

    public void insert(int value) {
        if (size == capacity) {
            if (value > heap[0]) {
                heap[0] = value;
                heapifyDown(0);
            }
        } else {
            heap[size] = value;
            size++;
            heapifyUp(size - 1);
        }
    }

    private void heapifyUp(int index) {
        while (index > 0 && heap[index] < heap[parent(index)]) {
            swap(index, parent(index));
            index = parent(index);
        }
    }

    private void heapifyDown(int index) {
        int smallest = index;
        int left = leftChild(index);
        int right = rightChild(index);

        if (left < size && heap[left] < heap[smallest]) {
            smallest = left;
        }

        if (right < size && heap[right] < heap[smallest]) {
            smallest = right;
        }

        if (smallest != index) {
            swap(index, smallest);
            heapifyDown(smallest);
        }
    }

    public int[] getTopK() {
        int[] result = Arrays.copyOf(heap, size);
        Arrays.sort(result);
        return result;
    }
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TopKNumbers {
    public static void main(String[] args) {
        String filePath = "numbers.txt"; // 文件路径
        int k = 10; // 找出最大的10个数字

        MinHeap minHeap = new MinHeap(k);

        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                int number = Integer.parseInt(line);
                minHeap.insert(number);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        int[] topK = minHeap.getTopK();
        System.out.println("最大的 " + k + " 个数字是:");
        for (int num : topK) {
            System.out.println(num);
        }
    }
}
欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇