下面是我的自我介绍,各位看官大佬们如果发现有什么问题,也请帮我指正,万分感谢。
各位面试官好,我的名字叫狗剩儿,做java开发工作已经七年了,先后在四家公司做过java开发的岗位,在第二家公司的时候后期当过一段时间的项目组长,当时我们组一共四个人包括我两个后端,两个前端,最近的一家公司主要做java开发,根据项目组要求后期也做一些前端和移动端的开发。下面我来介绍一下我最近的项目。
这个项目是一个MOM项目,技术框架后端是springboot3+springcloudalibaba 全家桶,前端使用的vue3,移动端使用的时候uniapp,我主要负责后端下面说一下我们项目中用到了哪些技术,网关是使用的gateway 主要包括动态路由的配置存储类监听类来读取和监听nacos配置中心中写入的路由信息的数据来实现动态路由、JWT +gateway实现认证鉴权、因为gateway内置了ribbon和hystrix,所以我们在gateway中做负载均衡和限流熔断,在网关层处理完请求之后通过负载均衡会转发到业务层, 业务层我们的技术开发框架是springboot+springcloud,采用mybatisPuls做的持久化的处理,使用redis做的缓存,底层使用的orcle的数据库,业务层这里我们的注册中心和配置中心是使用的nacos,服务之间的接口的调用我们使用的是feign,服务之间数据的实时传递我们使用的rebbitmq,使用seata做的分布式事务,CICD方案使用的就是gitlab + kubespere(整合了jenkins),业务层面我主要负责的是pms(设备维修维护管理)的开发,我主要负责的维护管理,维修管理和备件库存管理。
面试问题总结:
1.说一下Hystrix和Sentinel区别?
Hystrix和Sentinel都是服务熔断器,用于提高分布式系统的弹性。它们的主要区别在于实现方式、适用场景和资源模型设计。
Hystrix基于命令模式设计,将外部资源的调用封装在命令对象中,通过线程池或信号量来实现隔离。它提供了丰富的配置选项,如线程池大小、超时时间等,以实现对系统资源的有力控制。Hystrix更适用于需要高并发、快速响应的场景,因为它可以快速隔离和恢复故障。
Sentinel则基于流量控制和熔断降级的思想,它通过定义资源规则和应用策略来实现对系统资源的控制。Sentinel更适用于需要流量控制和熔断降级的场景,它可以根据系统负载和响应时间来实现自动熔断和降级操作。
2.hystrix有哪些机制?
断路器: 当服务访问量过大,并且超出预先配置的访问阈值时,hystrix会直接开启断路器。同时会拒绝后续的所有请求,在一段时间之后断路器会进入半打开状态,此时会允许一部分的请求通过并进行调用尝试,如果尝试依旧存在问题,则继续保持打开状态,如果尝试没问题,则会进入关闭状态。
资源隔离: 在hystrix中采用了舱壁隔离模式对资源进行隔离,使多个资源彼此之间不会产生影响。资源隔离又分为线程池隔离和信号量隔离;
线程池隔离: 在hystrix中每一个资源创建一个线程池,这若干个线程池彼此之间相互隔离,即便某一个服务资源出现问题,导致线程池资源耗尽,也不会影响到其他线程池的正常工作,所以该模式适合耗时较长的借楼调用场景。同时为了进一步提醒处理性能,线程池之中的所有线程并不参与实际的服务调用。这那这个呢的服务调用由转发线程处理。这样就保证容器有足够的线程来处理新的请求。使用线程次隔离模式虽然会有一定的线程调度开销,但是在并发访问量大的情况下可以给予延迟队列产线调度处理(例如:等待超时,异步调用),带来较好的并发处理性能。
信号量隔离: 改用访问统计技术的形式来进行服务调度,如果当前访问的用户量小于系统规定的最大值,则可以进行调度,这种隔离机制只是一个简单地计数器操作,所以没有任何的调度开销,属于轻量化的事项,所以无法进行有效的队列管理。用户的请求线程与转发线程为同一个线程,所以适合于接口响应速度快的应用场景。
降级回退: 在某些服务节点出现故障后一番可以正常的返回处理结果。
降级回退的几种情况:
1.断路器打开;
2.线程池/信号量资源不足;
3.服务调用超时;
4.服务调用异常;
降级回退的方式:
1.快速失败:出现故障直接抛异常;
2.无声失败:服务降级时返回一些特定的数据,如null、空集合或者其他类似的响应。
3.静态值:由开发者预先配置的数据内容。
4.复合数据:有开发者定义的复合对象内容。
5.网络缓存:如果调用服务失败,可以通过缓存服务中查询旧数据版本。
6.主次策略:将失败回退分为主要故障与次要故障两种处理模式。
请求结果缓存: hystrix实现了一个内部缓存机制,可以将请求结果进行缓存,对弈相同的请求则会直接走缓存而不用请求后台服务。
请求合并: 可以实现将一段时间内的请求合并,然后只对后端服务发送一个请求。
3.有个订单系统,和另外一个系统,订单系统提供了两个接口,接口1返回ID给你,接口2通过id获取订单信息,说一下你怎么去设计去接收订单信息,需要考虑到哪些问题?
这是一个典型的异步订单处理系统设计问题,涉及到解耦、可靠性、幂等性、超时重试、状态管理等多个关键点。
持久化 + 可恢复性
拿到 orderId,比如存个 order_sync_task 表,字段简单点:order_id, status(比如 PENDING, SUCCESS, FAILED), retry_count, next_retry_time, create_time。
异步轮询,别阻塞主线程
可以用 @Scheduled 定时任务,比如每 10 秒扫一次状态是 PENDING 的任务。
或者更高级点,用 RabbitMQ / Kafka,把 orderId 扔进一个 delay queue(延迟队列),第一次延迟 5s,拿不到再发个延迟 10s 的……这样避免频繁轮询。轮询的时候调 orderFeignClient.getOrderById(orderId),拿到数据就处理。
幂等性消费
所以处理前先 select 一下,或者直接在 order_info 表上加个 唯一索引:unique key uk_order_id (order_id),这样重复插入直接抛 DuplicateKeyException,catch 一下当它没发生就行。
重试策略得讲究,搞个指数退避(exponential backoff)
第1次:延迟 5s
第2次:延迟 10s
第3次:延迟 20s
……最多重试 5~10 次,再不行就标 FAILED,发个 alarm 告警,让运维看看是不是对方系统出问题了。
可以用 Spring Retry 搞个 @Retryable,或者自己写个简单的计数逻辑。
异常处理要全面
FeignException、TimeoutException?记录日志,重试。
返回 404?可能是订单还在生成,也重试。
返回 200 但 data 是 null?也当未就绪处理。
明确报错比如 INVALID_ORDER?那就直接标失败。
最终一致性 + 容灾兜底
即使前面做得再好,也可能因为网络抖动、消息丢失漏掉几个单子。
所以每天凌晨跑个 Scheduled 任务,调对方的批量接口或者对账接口,把最近几小时的订单拉一遍,对比一下这边有没有 missing,有的话补上。
4.springboot的自动装配原理?
SpringBoot的自动装配主要依赖于Spring框架的条件配置和Java的配置类功能。
- 启动类注解:SpringBoot应用的启动类上通常会有一个@SpringBootApplication注解,这是一个复合注解,它包括了@EnableAutoConfiguration,正是这个注解开启了自动装配的功能。
- 自动配置类:在SpringBoot的jar包中,包含了许多以META-INF/spring.factories文件指定的自动配置类。这些类上通常会有@Configuration注解,表明它们是用来定义Bean的配置类。
- 条件化配置:这些自动配置类使用了诸如@ConditionalOnClass、@ConditionalOnBean、@ConditionalOnMissingBean等条件注解,来决定是否创建和配置某个Bean。这些条件注解根据类路径中是否存在某个类、是否已经定义了某个Bean等条件来决定配置是否生效。
- 依赖注入:当自动配置类中的条件满足时,SpringBoot会自动创建和配置相应的Bean,并通过依赖注入的方式将它们注入到其他需要它们的Bean中。
持续更新中…