0


秋招第二周面试经验

文章目录

前言

秋招开始接到面试的第二周,真的很感谢在这个寒冬这些公司愿意给我机会,当然自己面试感觉也确实是越来越好了,首先我觉得我们需要注意答题的思路:

先听清楚想好再回答,先整体再局部,注意自己回答的这个思路,可以水平拓展,但不要扯太远了,尤其是针对八股文的那种,还是建议尽量把深度往下挖

,感觉最近的面试很舒服。有些公司面试就像是在技术交流,很喜欢这种技术氛围。
再就是一些HR面,要尽量地展现自己的综合能力,也别怕说错话,把自己的观点尽量简短表述清楚,注意条理,我强烈建议大家可以考虑这样说话:

第一,第二....

这样自己表达的时候潜意识里面也会有更多的逻辑在里面。注意你是来推销自己的,是你觉得你能行,可以胜任这个岗位,我们都是一块宝玉,公司得到了是它的幸运,然后我们也很感谢得到一个锻炼和发展的平台并会继续努力下去,为公司创造更多的收益。然后本文有一些问题没有完全解答,因为博主有些文章已经写了相关内容,这里就不再过多赘述了,这里发上周面试相关知识点链接:上周面试经验,觉得不错的可以收藏一下,这种环境下接到面试确实不太容易,另外可以

关注博主一波

,后续也会经常更新一些技术博文。
想进秋招群一起学习交流的友友可以评论或私聊博主,大家一起加油。

第一家一面

聊一下你上次实习的收获在哪些方面,参与实际的开发了吗?

这个阶段最主要的不是关注最新的技术栈,而是要知道企业级项目的流程,具备企业级开发的能力。

img

你对SpringCloud组件的认知是怎样的?

SpringCloud:是一套目前完整的微服务框架,它是一系列框架的有序集合。它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简易易懂、易部署和易维护的分布式系统开发工具包。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署

其中一些具体组件

  1. Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台 Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现、配置和管理。Nacos主要提供以下功能:服务发现和服务健康监测、动态配置服务(维护的时候)。在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段如果微服务架构中没有使用统一配置中心时,所存在的问题:1. 配置文件分散在各个项目里,不方便维护2. 配置内容安全与权限3. 更新配置后,项目需要重启Nacos配置中心:系统配置的集中管理(编辑、存储、分发)、动态更新不重启、回滚配置(变更管理、历史版本管理、变更审计)等所有与配置相关的活动
  2. Gateway负载均衡、熔断降级、统一鉴权、请求过滤、路径重写、限流保护。面向服务的路由,如果写的是IP地址的话相当于地址写死了,无法做到负载均衡,应该把网关工程注册到Nacos注册中心,通过服务名去访问不同微服务。网关限流有这么一些算法:1. 令牌桶算法:所有请求在处理之前都需要拿到一个可用的令牌才会被处理,根据限流大小,设置按照一定的速率往桶里添加令牌;桶设置最大的放置令牌限制,当桶满时,新添加的令牌就会被丢弃或者拒绝;请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流。guaua和redis客户端就有其实现2. 漏桶算法:水(请求)从上方倒入水桶内,从水桶下方流出(被处理);来不及流出的水存在水桶中(缓冲),以固定速率流出;水桶满后水溢出(丢弃);这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃3. 两种算法的区别:两者主要区别在于“漏桶算法”能够强行限制数据的传输速率,而"令牌桶算法"在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在"令牌桶算法"中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,所以它适合于具有突发特性的流量
  3. Sentinel:是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException) 。流量控制有以下几个角度:1. 资源的调用关系,例如资源的调用链路,资源和资源之间的关系2. 运行指标,例如 QPS、线程数等3. 控制的效果,例如直接限流(快速失败)、冷启动(Warm Up)、匀速排队(排队等待)等Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果4. Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果
  4. OpenFeign:微服务之间的相互调用,相较于Feign它支持SpringMVC的注解,如@RequestMapping等等。OpenFeign的FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡并调用其他服务
  5. Zipkin:链路追踪: 能展示完整调用链的耗时。还能展示各个部分调用的耗时

你有了解过RBAC吗

RBAC(基于角色的权限控制 role base access control)是一种设计模式,是用来设计和管理权限相关数据的一种模型。这种表结构大致如下在这里插入图片描述

SpringAOP和拦截器过滤器的区别?

AOP(Aspect-Oriented Programming:⾯向切⾯编程)能够将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的子类来作为代理。

过滤器拦截器都体现了AOP的编程思想

  1. 过滤器的配置比较简单,直接实现Filter接口即可,也可以通过@WebFilter注解实现对特定URLl拦截,看到Filter接口中定义了三个方法:1. init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用2. doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter3. **destroy()**: 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
  2. 拦截器(Interceptor):拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。 将自定义好的拦截器处理类进行注册,并通过**addPathPatternsexcludePathPatterns**等属性设置需要拦截或需要排除的 URL。首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor 接口中也定义了三个方法。1. preHandle() :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行2. **postHandle()**:只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 有意思的是:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行3. **afterCompletion()**:只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行
    差别FilterInterceptor实现原理基于函数回调反射()使用范围实现的是
    javax.servlet.Filter
    
    接口依赖Tomcat,只能在Web程序中是一个Spring组件,可在Web、Application、Swing触发时机请求进入容器后,Servlet之前进行预处理;请求结束是在Servlet处理之后进入Servlet后,进入Controller之前;Controller渲染对应视图之后请求结束拦截的请求范围几乎可以对所有进入容器的请求起作用只会对
    Controller
    
    中请求或访问
    static
    
    目录下的资源请求起作用注入Bean情况
    拦截器
    
    加载的时间点在
    springcontext
    
    之前,而
    Bean
    
    又是由
    spring
    
    进行管理控制执行顺序用
    @Order
    
    注解控制执行顺序,值越小级别越高越先执行默认的执行顺序,就是它的注册顺序,也可以通过
    Order
    
    手动设置控制
    详细阅读: http://t.csdn.cn/Rlay1

你项目中对token怎么处理的?

基于token的身份验证是无状态的,服务器不需要记录哪些用户已经登录或者哪些JWTs已经处理。每个发送到服务器的请求都会带上一个token, 服务器利用这个token检查确认请求的真实性。 这里可以把token理解成一张演唱会的门票。服务器(演唱会主办方)每次只需要检查你这张门票的有效性,不需要知道你这张门票是在哪里买的,从谁买的,什么时候买的等等。不同等级的门票可以坐的位 置不同,同样的,权限不同的用户可以进行的操作也不同

JWT: JSON Web Token是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。 一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名

  1. 头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个 JSON对象
  2. 载荷就是存放有效信息的地方
  3. 签名这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过 header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

MySQL常用的聚合函数

count、avg、max、min、sum

hash和B树的区别

第二家

先简单介绍一下你实习的这个项目

功能:流程管理、流程辅助管理、制度管理、报表管理、代维管理(也就是通知具体人员故障在哪,以及后续处理流程)、铁塔、机楼巡检等功能。这个项目在**这边是属于比较核心的系统,进行着海量的数据信息处理与消息转发,与内部各系统之间协调合作,形成一个完整的从客户到开发人员再到用户的一个完整生态链,和这个项目对接的公司还有AA, BB, CC等等。然后呢我具体负责了一些投诉督办的重要信息管理(AA红名单,BB红名单、CC决策库以及业务场景库等信息管理的功能实现)、重要地点处理、DD相关信息(包括人和类型、项目、结果等)管理、涉及6231、432764等业务的处理,中间涉及大量数据的导入和导出功能,级联数据的传输与显示。另外自己在项目经理提需求之后根据公司的字典库和代码和一些操作规范设计了新的数据库表并进行相应功能的实现。

用户有哪些:普通用户、VIP、VVIP。首先看到的菜单是不一样的,普通用户相当于使用XX系统的一些用户,只有一些基本的读和少数的写权限,VIP相当于是XX的普通工作人员,可以进行一般的读写增加删除、可以发起工单让系统自动审批、VVIP相当于我们开发人员或者项目经理可以进行流程的中转和异常单号的具体的审核。系统很庞大,相关系统模块也很多。

用户是怎样区分的

通过登录时候输入账号密码,采用token、网关写拦截器进行拦截。首先呢是借助RBAC权限控制这种设计模式,基本是需要角色表,用户表,菜单表,用户角色和角色菜单的中间表等,然后项目中是由一个专门的security模块来处理这些,是关于哪些用户属于哪些角色,然后哪些角色又具有哪些菜单,这都是多对多的关系,并且在具体的用户控制层中进行相关验证,可以锁定用户解锁用户,看用户名是否存在。通过用户的名称来判断用户。

使用RestTemplate发送请求到授权服务器去申请令牌,获取基本的请求头,请求参数,如果是微服务之间调用的时候是通过OpenFeign,通过自定义一个UserDetailService实现这条同名接口,其中Spring security要求权限名称ROLE_ADMIN ROLE_MANAGER等有前缀"ROLE_",然后再通过security核心包中userdetails.User传入用户名称、密码和权限等。在Security配置中通过继承WebSecurityConfigurerAdapter,将所有的请求都加入HttpSecurity,开放所有/oauth/开头的所有请求

然后在一个专门的Auth模块里面,开启授权服务器配置,在这里配置令牌访问端点的权限,从数据库里的oauth_client_detail表中获得客户端信息,配置客户端信息,读取其中信息,使用Jwt存储令牌,采用非对称加密算法,私钥颁发令牌、配置令牌的产生、存储以及tokenService的基本信息。资源服务器,配置使用公钥校验令牌,配置一些访问地址的权限

另外在这个GateWay里面实现全局过滤器,其中如果访问登录等就放行,获得令牌并校验令牌,然后对解析的用户的角色判断,不同角色访问相应角色的微服务

工作流程是怎么样的

开会——>项目经理说需求——>技术组长——>分具体的任务——>需求分析到具体实现(根据集团的公共字典表和设计规范,设计具体的数据库表格,自己完成后端接口,前端继续的实现)

设计过哪些数据库的表

重要地点表,重要用户信息,涉及业务的一些像家客、集客红黑名单,机楼巡检人员信息表,机楼巡检配置表

能具体说一下是些什么配置吗

关于铁塔维修、机楼巡检、根据系统的一个字典表抽离出某一些关于业务的具体人员信息名单,重要地市等信息,投诉督办等信息配置等等,另外就是一些减少业务代码的配置表,通过进行表字段的设计将系统代码中很多复杂的判断检测进行抽离解耦、使得之后具体只需要去查询这个配置表得到数据,同时在前端提供这个配置表的展示、方便一些非技术人员,像项目经理等的观察和使用,同时也减少很多代码量

项目中最大的收获在哪几个方面

Mybatis、MybatisPlus、Hibernate区别

你们公司有一些什么设计开发规范

首先数据库三大范式:每一列字段不可再分、每一行数据只能与其中一行有关,即主键(唯一依赖)、数据不能有传递关系,直接关联而不是间接关联

建立索引要看情况 。注意最左匹配原则、不要select *、索引列不要计算或使用函数、字段类型要相同、like左边包含%、列不要对比、注意or关键字的使用、not in、order by这些

集团的字典库、字段类型以及长度、每个字段要有相应的comment注释、

客户端系统的部署和打包的过程

项目中怎么具体分工

你们小组分的哪一部分

用户中心——账户信息、你问我答、评价内容、用户收藏、签到、积分里程、用户手机绑定、常用旅客、实名认证、听障认证、账户注销、登陆注册、用户个人中心和设置页面、用户头像和昵称

接口间有交互怎么处理

SpringCloud你接触到比较多的是哪些组件

你们项目中权限认证和网关怎么相结合

认证中心单独一个模块,进行令牌的颁发,初始化资源角色的对应关系,用于网关的鉴权;以及判断登录模式比如看是密码模式还是验证码模式,如果是验证码模式还需要额外加密一次密码,便于后续的校验,

首先说说网关微服务本身的配置,在gateway中配置security中oauth2的资源服务器中设置jwt的uri,并且将这个网关也注册到Nacos进行统一的注册与配置管理,这里面有一些关于路径path的重写断言规则,进行负载均衡的uri以及一些过滤请求,配置了很多白名单路径,像那些以auth和登录相关的,以及前台门户系统。还有redis的一些配置与端口号。同时呢在这个工程下写一个**IgnoreUrlsRemoveJwtFilter(忽视url移除jwt的)**实现WebFilter接口的过滤器,以及一个实现了GlobalFilter和Order接口的AuthGlobalFilter授权全局过滤器。另外在网关里写资源服务的配置类,读取jwt载荷中的权限信息,自定义处理jwt请求头过期或者签名错误的结果;移除白名单中的请求头,白名单直接放行,其他的请求就走鉴权管理器。鉴权管理器(AuthorizationManager)实现ReactiveAuthorizationManager接口,对应跨域的预检请求直接放行。另外还可以根据token在redis里面进行查询,看是否是登出过未过期的token

简单讲讲ES

倒排索引你了解吗

谈谈你对Spring、SpringMVC和SpringBoot的理解

Spring生态中最重要的概念是什么呢

谈谈SQL调优

索引为什么会失效

团队开发遇到了些什么问题,你是怎么去解决的

职业规划

毕设是怎么样的安排

第一家架构师面(二面)

实习项目中负责哪些内容

你除了Hibernate还用过其他的ORM框架吗

你是怎样去做这个用户的设计的

首先我们小组是通过了多次的会议进行需求分析,最开始是每个人针对整个系统中可能涉及的模块进行构思,比如说站在客户的角度,或者商家的角度,就分别可以分为用户中心和商家中心,另外还有一些订单管理、平台活动服务等等。然后就是具体到我们小组负责的用户中心就有大概这么一些功能——账户信息、你问我答、评价内容、用户收藏、签到、积分里程、用户手机绑定、常用旅客、实名认证、听障认证、账户注销、登陆注册、用户个人中心和设置页面、用户头像和昵称。我呢就负责其中的用户签到积分里程以及钱包这部分模块,涉及到这么一些数据库表:用户信息表(用户基本信息和各种与业务相关的字段)、用户登录信息表(专门的登录信息,像微信的openid,用户手机号和登录密码等)、用户积分表(积分数和积分种类、获得方式、过期时间)、用户过期积分表(里程数等等)、用户积分流水表(存了用户信息id、订单信息id、折扣优惠券id、用户里程等)、签到表(连续签到天数)、钱包表(金额、余额、可提现金额)、用户账单流水表(账单id、零钱数、零钱类型等等、现金)

从前端页面分析用户行为:有一个签到页面,点击签到进行数据的验证与增加,签到这里有判断,是否连续签到,以及签到天数,来决定积分增加的多少,同时签到之后积分明细表中需要增加相应的记录。一方面积分可以通过下单进行获取,在用户在专门的一个积分商城消费下单的时候可以通过积分抵扣金额的方式进行积分扣除,这里就需要与订单模块相互调用,所以我提供了对外的一个积分消费接口,下单成功之后进行积分扣除并且积分明细表中也生成一条记录。这里调用订单模块所以我就需要和其他组的同学进行沟通,让他知道具体调用我们哪个接口,以及具体的code、msg和data里返回的信息

零钱模块呢主要就是负责零钱的查询,支持分月查询,然后显示最近一年的积分数据,程序里面有对那个时间的判断,然后也可以查看账单明细,然后接口测试工具我们用的是国产的ApiPost,然后用的Gitee码云

Nacos如何检测服务是否可用

首先Nacos它是一个阿里的开源产品,是针对微服务架构重的服务发现、配置管理、服务治理的综合型解决方案。(用来实现配置中心和服务注册中心)。四大功能:服务发现和健康监测、动态配置服务、动态DNS服务、服务及其元数据管理[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ui7LSJst-1608255477075)(images/image-20201218093642936.png)]在这里插入图片描述

Nacos中Name Server通过Virtual IP或者DNS的方式实现Nacos高可用集群的服务路由

注册中心原理:服务实例在启动时注册到服务注册表,并在关闭时注销;服务消费者查询服务注册表,获得可用实例,服务注册中心需要调用服务实例的健康检查API来验证它是否能够处理请求在这里插入图片描述

心跳机制在这里插入图片描述 从上述代码看,所谓心跳机制就是客户端通过schedule定时向服务端发送一个数据包 ,然后启动一个线程不断检测服务端的回应,如果在设定时间内没有收到服务端的回应,则认为服务器出现了故障。Nacos服务端会根据客户端的心跳包不断更新服务的状态

注册原理:Nacos提供了SDK和Open API两种形式来实现服务注册,这两种形式本质都一样,底层都是基于HTTP协议完成请求的,所以注册服务就是发送一个HTTP请求

另外如果单节点的Nacos挂了,不会导致整个系统不可用,而是会先使用本地缓存的数据,等Nacos恢复后再重新拉取最新数据,Nacos源码种通过putService()方法将服务缓存到内存,service.init()建立心跳机制,通过consistencyService.listen实现数据一致性监听

总结:Nacos客户端通过Open API的形式发送服务注册请求,Nacos服务端收到请求后,做以下三件事:构建一个Service对象保存到ConcurrentHashMap集合中,使用定时任务对当前服务下的所有实例建立心跳检测机制,基于数据一致性协议服务数据进行同步 源自:Nacos原理详解

你能说一下JMM内存模型吗

首先这个得从计算机存储结构开始说起,计算机存储结构,从本地磁盘到主存到CPU缓存,也就是从硬盘到内存,到CPU。一般对应的程序的操作就是从数据库查数据到内存然后到CPU进行计算在这里插入图片描述

JMM的产生是因为JVM规范中试图定义一种Java内存模型来 来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。JMM本身是一种 抽象的概念 并不真实存在它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是多线程)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,关键是围绕多线程的 原子性、可见性和有序性展开的

实现内存和主线程之间的抽象关系,屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问结果

JMM规范下,三大特性

  1. 原子性:一个操作是不可打断的,即多线程环境下,操作不能被其他线程干扰
  2. 可见性:指当一个线程修改了一个共享变量的值,其他线程是否能够立即知道该变更,JMM规定了所有的变量都存储在主内存
  3. 有序性:对于一个线程的执行代码而言,我们总是习惯性认为代码的执行总是从上到下,有序执行。但为了提升性能编译器和处理器通常会对指令序列进行重排序。Java规范规定JVM线程内部维持顺序化语义,即只要程序的最终结果与它顺序化执行的结果相等,那么指令的执行顺序可以和代码顺序不同,此过程叫指令的重排序

JMM规范下,多线程对变量的读写过程

读取过程:由于JMM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到的线程自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量拷贝副本,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图在这里插入图片描述

JMM定义了线程和主内存之间的抽象关系

  1. 线程之间的共享变量存储在主内存中(从硬件的角度来说就是内存条)
  2. 每个线程都有一个私有的本地工作内存,本地工作内存中存储了该线程用来读/写共享变量的副本(从硬件的角度来岁就是CPU的缓存,比如寄存器,L1,L2,L3缓存等)

小总结:

  1. 我们定义所有共享变量都存储在主内存中,每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
  2. 线程对共享变量的所有操作都必须先在线程自己的工作内存中进行写后写回主内存,不能直接从主内存中读写(不能越级)
  3. 不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量的值传递需要通过主内存来进行(同级之间不能相互访问)

你能聊聊Redis分布式锁是怎么实现的吗

首先我讲讲为什么需要分布式锁:首先jdk是给我们提供了一些原子操作的API,可以让我们在一个JVM之中实现资源加锁,保证某一个具体的时刻只有一个线程在请求资源,可是现在随着软件的发展和演变,以及业务发展,可能需要做集群,或者微服务架构,然后这就相当于有多个Java程序或者说多个JVM,然后它们之间也没有可见性,很明显对同一数据的处理是会有问题的,在这种分布式场景下原来JDK给我们提供的锁(比如ReentrantLock或Synchronized)的一些操作就不能满足。我在这个项目里也可以解决超卖问题,也就是说,多人同时抢购同一商品的时候,多人同时判断是否有库存,如果只剩一个,则都会判断有库存,此时会导致超卖现象产生,也就是一个商品下了多个订单的现象。

使用分布式锁,我们可用保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,这种就是一个跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。

分布式锁应该具备的条件:在分布式系统环境下,一个方法在同一时间只能被一个及其的一个线程执行;高可用、高性能的获取锁与释放锁;具备可重入特性;具备锁失效机制,防止死锁;具备非阻塞锁特性,即没有获取到锁将直接返回锁失败

然后分布式锁常用的解决方案:需要这个锁独立于每一个服务之外,而不是在服务里面

  1. 基于数据库表:核心思想是在数据库中创建一个表,表中包含方法名等字段,并在方法名上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。但是存在着锁强依赖数据库的可用性,一旦锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获取到锁,而且这把锁只能是非阻塞的,因为insert操作一失败就会报错。没有获得锁的线程的就不会进入排队队列,要想再次获得锁就要再次触发获得锁的操作。锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁,因为数据中数据已经存在了1. 对应可解决措施:单点可以搞两个数据库,数据之间双向同步,一旦挂掉快速切换到备库上;没有失效时间的问题可以自己做一个定时任务,每隔一段时间把数据库中的超时数据清理一遍;非阻塞的可以搞一个while循环,直到insert成功再返回;非重入问题就再数据库中加一个字段,记录当前获得锁的及其的主机和线程信息,下次再获取锁的时候先查数据库,如果当前及其的主机信息和线程信息在数据库查得到的话,直接把锁分配给他就行2. 数据库的第二种方式:查询语句后面加 for update,数据库会在查询过程种给数据库增加排它锁(InnoDB引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给method_name添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上)。当某条记录被加上排它锁之后,其他线程无法再在该行记录上增加排它锁。 我们可以认为获得排它锁的线程即可获得分布式锁,当获取到锁之后,可以执⾏⽅法的业务逻辑,执⾏完⽅法之后,再通过以下⽅法解锁在这里插入图片描述
  2. Zookeeper分布式锁:> zk通过临时节点,解决了死锁的问题,一旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会自动删除掉,其他客户端自动获取锁,临时顺序节点解决惊群效应> > > 1. 创建⼀个⽬录mylock;> 2. 线程A想获取锁就在mylock⽬录下创建临时顺序节点> 3. 获取mylock⽬录下所有的⼦节点,然后获取⽐⾃⼰⼩的兄弟节点,如果不存在,则说明当前线程顺序号最⼩,获得锁> 4. 线程B获取所有节点,判断⾃⼰不是最⼩节点,设置监听⽐⾃⼰⼩的节点> 5. 线程A处理完,删除⾃⼰的节点,线程B监听到变更事件,判断⾃⼰是不是最⼩的节点,如果是则获得锁来看下Zookeeper能不能解决前⾯提到的问题。1. 锁⽆法释放?使⽤Zookeeper可以有效的解决锁⽆法释放的问题,因为在创建锁的时候,客户端会 在ZK中创建⼀个临时节点,⼀旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会⾃动删除掉。其他客户端就可以再次获得锁。2. ⾮阻塞锁?使⽤Zookeeper可以实现阻塞的锁,客户端可以通过在ZK中创建顺序节点,并且在节点上绑定监听器,⼀旦节点有变化,Zookeeper会通知客户端,客户端可以检查⾃⼰创建的节点是不 是当前所有节点中序号最⼩的,如果是,那么⾃⼰就获取到锁,便可以执⾏业务逻辑了3. 不可重⼊?使⽤Zookeeper也可以有效的解决不可重⼊的问题,客户端在创建节点的时候,把当前客户端的主机信息和线程信息直接写⼊到节点中,下次想要获取锁的时候和当前最⼩的节点中的数据⽐对⼀下就可以了。如果和⾃⼰的信息⼀样,那么⾃⼰直接获取到锁,如果不⼀样就再创建⼀个临时的顺序节点,参与排队。4. 单点问题?使⽤Zookeeper可以有效的解决单点问题,ZK是集群部署的,只要集群中有半数以上的机器存活,就可以对外提供服务> 使⽤Zookeeper实现分布式锁的优点 有效的解决单点问题,不可重⼊问题,⾮阻塞问题以及锁⽆法释放的问题。实现起来较为简单。> > 使⽤Zookeeper实现分布式锁的缺点 性能上不如使⽤缓存实现分布式锁。 需要对ZK的原理有所了解,⽐较复杂
  3. Redis分布式锁:SETNX,单线程处理网络请求,不需要考虑并发安全性;所有服务节点设置相同的key,返回为0,则锁获取失败(Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行)1. 实现思想:获取锁的时候,使用SETNX加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断;获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁;释放锁的时候,通过UUID判断是不是该锁,如果是,就执行Delete进行锁释放> SETNX EXPIRE delete> 问题:> > > 1. 早期版本没有超时参数,需要单独设置,存在死锁问题(中途宕机)> > 2. 后期版本提供加锁与设置时间的原子操作,但是存在任务超时,锁自动释放,导致并发问题,加锁与释放锁不是同一线程问题> > 删除锁:判断线程唯一标志,再删除可重入性及锁续期没有实现,通过redisson解决(类似AQS的实现,看门狗监听机制)redlock:意思的机制都只操作单节点、即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况(redis同步设置可能数据丢失)。redlock从多个节点申请锁,当一半以上节点获取成功、锁才算获取成功,redission有相应的实现

Redis分布式锁的具体实现过程:引入redis和redission依赖并配置redis在这里插入图片描述
下单结束前释放锁在这里插入图片描述

你怎样从接收需求到上线

你怎么跟前端协作的

职业规划

上海某家电话面

MyBatis和MyBatis-plus的区别

如果Mybatis Plus是扳手,那Mybatis Generator就是生产扳手的工厂。

  1. MyBatis:一种操作数据库的框架,提供一种Mapper类,支持让你用Java代码进行增删改查的数据库操作,省去了每次都要手写SQL语句的麻烦。但是,有一个前提,你得首先在XML中写好SQL语句,也是很麻烦的,需要手动解析实体关系映射转换为MyBatis内部对象注入容器
  2. MP的存在就是为了稍稍弥补MyBatis的不足,在MyBatis的基础上进行扩展,只做增强不做改变,简化开发、提高效率。在我们使用MyBatis时会发现,每当要写一个业务逻辑的时候都要在DAO层写一个方法,再对应一个SQL,即使是简单的条件查询、即使仅仅改变了一个条件都要在DAO层新增一个方法,针对这个问题,MP就提供了一个很好的解决方案,比如代码生成器,通过继承它自己的IService和实现类的方式可以直接用一些通用的CRUD,强大的条件构造器也支持Lambda表达式,可以让我们避免许多重复性的工作。另外还支持一些公共字段自动填充自动解析实体关系映射转换为MyBatis内部对象注入容器

你知道年轻代,老年代和元空间吗

JDK8 HotSpot JVM 将移永久区,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace)。Metaspace容量:默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。新参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小,如果没有指定这个参数,元空间会在运行时根据需要动态调整。另外,对于僵死的类及类加载器的垃圾回收将在元数据使用达到"MaxMetaspaceSize",参数的设定值时进行。适时地监控和调整元空间对于减小垃圾回收频率和减少延迟是很有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄露或是大小设置不合适。

Java8中metaspace总结如下:

  1. PermGen空间的状况:这部分内存空间将全部移除。JVM的参数:PermSize和MaxPermSize会被忽略并给出警告(如果在启用时设置了这两个参数)
  2. Metaspace内存分配模型:大部分元数据都在本地内存中分配,用于描述类元数据的"klasses"已经被移除
  3. MetaSpace容量:默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。新参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小。如果没有指定这个参数,元空间会在运行时根据需要动态调整。
  4. Metaspace垃圾回收:对于僵死的类及类加载器的垃圾回收将在元数据使用到"MaxMetaspaceSize"参数的设定值时进行。适时地监控和调整元空间对于减小垃圾回收频率和延时是很有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄露或是大小设置不合适
  5. Java堆内存的影响:一些杂项数据已经移到Java堆空间中。升级到JDK8之后,会发现Java堆空间有所增长
  6. MetaSpace监控:元空间的使用情况可以从HotSpot1.8的详细GC日志输出中得到

你能说说一个类从加载到销毁的过程吗

讲讲你对微服务的理解

从单体架构到微服务架构的演进

  1. 单体架构:原来我写过一个快递驿站小项目,整个系统的架构非常简单,使用Spring+SpringMVC+MyBatis构建一个基础工程、MySQL数据库作为持久化存储,在这个工程中创建不同的Service实现项目中不同的业务场景,如线路、线路分类、用户等。最后把项目构建成一个war包部署在Tomcat容器上即可使用,这是之前经常采用的架构。通常来说,如果一个war包或者jar包里面包含一个应用的所有功能,则我们称这种架构为单体架构。很多传统互联⽹公司或者创业型公司早期基本都会采⽤这样的架构,因为这样的架构⾜够简单,能够快速开发和上线。⽽且对于项⽬初期⽤户量不⼤的情况,这样的架构⾜以⽀撑业务的正常运⾏
  2. 集群和垂直架化: 以商城系统为例。随着业务的发展,产品被越来越多的⼈使⽤,那么对于整个技术架构来说,可能会⾯临以下挑战:> 1. ⽤户量越来越⼤,⽹络访问量不断增⼤,导致后端服务器的负载越来越⾼> 2. ⽤户量⼤了,产品需要满⾜不同⽤户的需求来留住⽤户,使得业务场景越来越多并且越来越复杂> 3. 当服务器的负载越来越⾼的时候,如果不进⾏任何处理,⽤户在⽹站上操作的响应会越来越慢,甚⾄出现⽆法访问的情况,对于⾮常注重⽤户体验的互联⽹产品来说,这是⽆法容忍的> 4. 业务的场景越多越复杂,意味着war包中的代码量会持续上升,并且各个业务代码之间的耦合度也 会越来越⾼,后期的代码维护和版本发布涉及的测试和上线,也会很困难。举个最简单的例⼦,当 你需要在库存模块⾥⾯添加⼀个⽅法时,带来的影响是需要把整个系统重新测试和部署,⽽当⼀个 war包有⼏GB的⼤⼩时,部署的过程也是相当痛苦的因此,我们可用从两个方面进行优化:1. 通过横向增加服务器,把单台机器变成多台及其的集群2. 按照业务的垂直领域进行拆分,减少业务的耦合度,以及降低单个war包带来的伸缩性困难问题。比如把商城拆为:用户子系统、库存子系统、商品子系统,每个子系统由不同的业务团队负责维护并且独立部署。同时,我们针对Tomcat服务器进行了集群部署,也就是把多台Tomcat服务器通过网络连接进行连接组合,形成一个整体对外提供服务。这样做的好处就是能够在不改变应用本身的下,通过增加服务器来进行水平扩容从而提升整个系统的吞吐量。另外数据库也可以进行垂直分库,分为用户库和库存库和商品库等等。总体来说,数据库层面的拆分思想和业务系统的拆分思想是一样的,都是采用分而治之的思想在这里插入图片描述
  3. SOA:面向服务的架构,核心目标是把一些通用的,会被多个上层服务调用的共享业务提取成独立的基础服务。这些被提取出来的共享目标是把一些通用的、会被多个上层服务调用的共享业务抽取成独立的基础服务。这些被提取出来的共享服务相对来说比较独立,并且可以宠用。所以在SOA中,服务是最核心的抽象手段,业务被划分为一些粗粒度的业务服务和业务流程。比如电商系统中的用户服务、库存服务、商品服务等多个共享服务。在SOA中,会采用ESB(企业服务总线)来作为系统和服务之间的通信桥梁,ESB本身还提供服务地址的管理,不同系统之间的协议转化和数据格式转化等。调用端不需要关心目标服务的位置,从而使得服务之间的交互是动态的,这样做的好处是实现了服务的调用者和服务的提供者之间的高度耦合。可以解决:信息孤岛、共享业务的重用在这里插入图片描述
  4. 微服务架构:微服务关注的是解耦,虽然解耦和可重用性从特定的角度来看是一样的,但本质上是有区别的,解耦是降低业务之间的耦合度,而重用性关注的是服务的复用。微服务会更多地关注在DevOps的持续交付上,因为服务粒度细化之后使得开发运维变得更加重要,因此微服务与容器化技术的结合更加紧密。如下图所示,将每个具体的业务服务构成可独立运行的微服务,每个微服务只关注某个特定的功能,服务之间采用轻量级通信机制REST API进行通信。对于微服务的拆分粒度是没有统一的标准的,更多的时候是需要在粒度和团队之间找平衡,微服务的粒度越小,服务独立性带来的好处就越多,但是管理大量的微服务也会越复杂。 ⽐如我们⽹上购物,⾸先应该去购物⽹站搜索商品,这个搜索功能就可以开发成⼀个微服务。我们也可 以看到相关商品的推荐,这些推荐项也可以是⼀个微服务。后⾯⽐如加⼊购物⻋、下订单、⽀付等功能 都可以开发成⼀个⼀个的独⽴运⾏的微服务。 在这里插入图片描述

嘴巴写基本的SQL

我看你熟悉多线程,你知道线程的含义吗

⼀个进程中可以有多个线程,多个线程共享进程的堆和⽅法区 (JDK1.8 之后的 元空间)资源,但是每个线程有⾃⼰的程序计数器、虚拟机栈 和 本地⽅法栈。 线程 是

进程

划分成的更⼩的运⾏单位。线程和进程最⼤的不同在于基本上各进程是独⽴ 的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互影响。线程执⾏开销⼩,但不利于资源的管理和保护;⽽进程正相反

kafka的基本架构

消息队列的作用异步处理,应用解耦,流量削锋和消息通讯


本文转载自: https://blog.csdn.net/qq_57469718/article/details/127043941
版权归原作者 慢慢敲吧 所有, 如有侵权,请联系我们删除。

“秋招第二周面试经验”的评论:

还没有评论