• 后端面经学习自测(二)


    1、Http1.1和2.0的区别大概是什么?

    HTTP1.1

    1、无状态、明文传输,不安全

    2、长连接,超时服务端主动断开

    3、管道传输,减小响应时间

    问题:没有解决响应的队头阻塞

    HTTP2.0

    1、头部压缩:多个请求,头部相似,会消除重复部分

    2、二进制格式:头部信息和数据信息都采用二进制格式,头部帧、数据帧,增加数据传输效率

    3、并发传输:使用Stream ID来区分不同的HTTP请求,通过多路复用一条tcp连接,实现了http并发传输的效果,性能比http1.1高了很多

    4、服务器主动推送资源

    问题:一旦发生丢包,就会阻塞住所有的 HTTP 请求

    HTTP3.0

    基于 UDP 的 QUIC 协议

    • 无队头阻塞:当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响
    • 更快的连接建立:QUIC 协议握手只需要1RTT,目的是为确认双方的连接 ID
    • 连接迁移

    基于TCP的HTTP协议:四元组(源 IP、源端口、目的 IP、目的端口)

    QUIC 协议:通过连接 ID 来标记通信的两个端点

    HTTP & HTTPS

    HTTP

    1、超文本传输协议,明文传输,不安全

    2、连接简单,TCP三次握手后即可进行传输

    3、端口:80

    HTTPS

    1、引入SSL/TLS安全协议(传输层和网络层之间),加密传输

    2、不仅需要TCP三次握手,还需要SSL/TLS握手过程

    3、端口:443

    4、需要向CA申请数字证书

    2、HTTP,用户后续的操作,服务端如何知道属于同一个用户

    HTTP是无状态协议,可通过cookie、session保存用户状态,cookie是在客户端存储,session是在服务端存储

    cookie & session & token

    cookie:服务端收到HTTP请求,在**浏览器(客户端)**的响应头中添加cookie信息(保存sessionId或token),用于保存用户登录信息

    客户端每发一次新请求,浏览器都会将之前保存的cookie信息通过cookie请求头再发给服务器,此时浏览器发出之后,就有可能被拦截,不安全

    session:在服务端记录用户的登录信息

    1. 客户端发送http请求,提交用户信息
    2. 服务端保存生成session,并存储在内存
    3. 服务端将sessionId返回给客户端
    4. 客户端将sessionId存储在cookie中
    5. 客户端后续所有请求都需要携带cookie
    6. 服务端通过cookie中的sessionId校验用户信息

    **token:**由服务端生成,并发给客户端

    img

    token:客户端发起登录请求,服务端验证通过后,生成全局唯一Token,绑定用户信息(value),并将Token作为key存入Redis中

    多端登录:(secretId:客户端的唯一标识)

    • token
    • token+secretId
    • token+secretId+userId

    使用Token获取用户信息

    1、根据request获取userId

    2、根据userId获取用户信息

    // 根据token获取用户信息
    @GetMapping("getMemberInfo")
    public R getMemberInfo(HttpServletRequest request) {
        // 调用jwt工具类方法,根据request对象,获取头信息,返回用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        // 查询数据库,根据用户id,获取用户信息
        UcenterMember member = memberService.getById(memberId);
        return R.ok().data("userInfo", member);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    手机验证码登录流程

    1. 客户端输入手机号
    2. 客户端向服务端发送请求,获取手机验证码
    3. 服务端向手机发送验证码
    4. 服务端生成Token,并发送给客户端(验证码+手机号+Token绑定)
    5. 客户端携带【验证码+Token】请求服务端
    6. 服务端校验【验证码+Token】,校验通过后记录用户登录的session信息
    7. 服务端返回登录成功,客户端进行页面跳转

    可使用Redis设置验证码5分钟有效时间

    @RestController
    @CrossOrigin
    @RequestMapping("/edumsm/msm")
    public class MsmController {
        @Autowired
        private MsmService msmService;
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        // 发送短信
        @GetMapping("send/{phone}")
        public R sendMsm(@PathVariable String phone) {
            // 1. 先从redis中获取验证码,如果能获取直接返回
            String code = redisTemplate.opsForValue().get(phone);
            if (! StringUtils.isEmpty(code)) {
                return R.ok();
            }
    
            // 2. 如果获取不到,再进行阿里云发送
            // 生成随机的值,传递给阿里云发送
            code = RandomUtil.getFourBitRandom();
            Map<String, Object> param = new HashMap<>();
            param.put("code", code);
            // 调用service发送短信的方法
            boolean isSend = msmService.send(param, phone);
            if (isSend) {
                // 发送成功,把发送成功验证码放到redis里,并设置有效时间
                redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
                return R.ok();
            } else {
                return R.error().message("短信发送失败");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    SSO单点登录

    只需要登录一次,即可访问互相信任的系统

    三种常见方式
    1、session广播机制实现(基本不用了)

    session复制

    2、使用cookie+redis 实现

    1. 在项目中任何一个模块进行登录,登录后将数据放到两个地方

    redis:在key:生成唯一随机值(ip、id等),在value:用户数据

    cookie:把redis里面生成的key值放到cookie中

    1. 访问项目中其他模块,发送请求时带着cookie进行发送,获取cookie值后,把获取到的cookie值放入redis进行查询,根据key查询,如果能查到数据,就是登录

    3、使用token实现

    令牌

    token:按照一定的规则生成字符串,字符串可以包含用户信息

    1.在项目某个模块进行登录,登录之后,按照规则生成字符串,将用户信息包含在字符串里,最后返回字符串

    • 把字符串通过cookie返回
    • 把字符串通过地址栏返回

    2.再访问项目其他模块,每次访问在地址栏带着生成的字符串,在访问模块里获取地址栏字符串,根据字符串获取用户信息。如果可以获取到,就是登录

    session默认过期时间:30min

    session、redis、token都可以设置过期时间

    JWT令牌:按照规定好的规则,使用JWT可以直接生成字符串,包含用户信息(JWT头+用户信息+签名哈希)

    3、如果服务端是一个集群机器?

    对于集群,如果 session 保存在其中一台机器上,就会涉及到数据不一致的问题,可以使用MySQL或Redis来存储用户信息

    常用:基于redis实现共享session登录

    4、hashmap是线程安全的吗

    不安全

    1. JDK1.7 及之前版本,在多线程环境下,HashMap 扩容时会造成死循环和数据丢失的问题
    2. JDK 1.8 后,在 HashMap 中,多个键值对可能会被分配到同一个桶(bucket),并以链表或红黑树的形式存储。多个线程对 HashMap 的 put 操作会导致线程不安全,具体来说会有数据覆盖的风险

    补充HashMap和ConcurrentHashMap相关知识

    5、Java有没有提供线程安全的结构

    ConcurrentHashMap

    6、如果用for循环对ArrayList进行元素遍历删除,是否安全?这个不考虑单线程还是多线程

    不建议在使用for循环对ArrayList进行元素遍历删除

    1. 删除元素后,ArrayList中元素的索引发生变化,可能会导致某些元素被跳过或重复遍历
    2. 在遍历时修改ArrayList的大小,可能会导致ConcurrentModificationException异常

    如果必须使用for循环进行元素遍历删除操作,可以使用迭代器来实现

    7、网站对外提供HTTP协议的接口,但是不期望底层异常堆栈信息向客户端暴露,可以如何做

    怎么实现?有没有其他方式?

    整个项目使用一个统一的异常处理方式

    1. 使用**@ControllerAdvice** 注解:可以定义一个类,在该类中使用**@ExceptionHandler**注解来处理控制器中抛出的异常
    @ControllerAdvice
    @ResponseBody
    public class GlobalExceptionHandler {
        @ExceptionHandler(BaseException.class)
        public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
          //......
        }
        @ExceptionHandler(value = ResourceNotFoundException.class)
        public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
          //......
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    @ControllerAdvice:aop思想的一种实现,你告诉我需要拦截规则,我帮你把他们拦下来,具体你想做更细致的拦截筛选和拦截之后的处理,你自己通过@ExceptionHandler实现

    1. 实现ErrorController接口:可以自定义一个错误控制器来处理应用程序中出现的所有HTTP错误
    2. 使用@ResponseStatus注解:可以在自定义异常类中使用@ResponseStatus注解来指定HTTP错误代码和消息
    3. 使用Servlet的ErrorPage配置:可以在web.xml或通过注解配置中指定一个或多个错误页面来处理应用程序中出现的所有HTTP错误
    4. 使用Spring Boot的ErrorAttributes:可以使用Spring Boot的ErrorAttributes接口来自定义错误响应的内容和格式。

    8、想把服务端所有的异常全部拦截掉,在 try catch 的时候应该 catch 什么类型的异常?

    Exception

    9、error可以被拦截吗

    不可以,error是不能处理的错误

    10、OOM属于什么类的异常

    不可检测异常,运行时异常

    img

    11、OOM在异常堆栈里是否会暴露程序的一些结构,比如在某一段代码因为什么情况导致OOM

    1. 堆内存溢出:死循环、递归层数太多、内存泄露
    2. 栈内存溢出:单线程调用方法次数太多,递归层数太多
    3. 元空间不足
    4. GC效率太低
    5. 数组大小越界

    img

    12、Java的双亲委派机制了解吗

    类加载时,先判断当前类是否被加载过,已加载直接返回,否则才尝试加载

    加载首先会把请求委派给父类加载器的loadClass()处理,即传送到顶层的启动类加载器 BootstrapClassLoader 。父类加载器无法处理时才自己处理。父类加载器为null,使用启动类加载器作为父类加载器

    13、想破除双亲委派机制,一般以什么方式

    自定义加载器,需要继承 ClassLoader,重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载

    14、如果一个线程执行里面赋值比较耗时,不知道耗时多久,还想打印赋值的正确结果,有没有其他方式

    不知道执行多久就用join,主线程等待子线程结束之后再执行,这个时候不需要sleep,直接 join 的话,返回的就一定是子线程执行完毕的结果了

    15、String a = new String(“ABC”);内存结构?

    String a在堆中,“ABC”在字符串常量池中

    补充String相关知识

    16、Spring了解多少?

    17、Spring的事务标签有没有用过?什么场景会生效

    18、mysql现在有一个联合索引a,b,c,有一个where条件是b=XX查询,索引会失效吗

    不会,最左匹配原则

    索引失效:

    1、使用左、左右模糊查询 like %xx,like %xx%

    2、对索引列做计算、函数、类型转换操作

    3、联合索引未遵循最左匹配原则

    4、WHERE子句中,ON前是索引列,ON后不是索引列

    19、描述一下最左匹配原则

    假设有一个(a, b, c)联合索引,它的存储顺序是先按 a 排序,在 a 相同的情况再按 b 排序,在 b 相同的情况再按 c 排序

    • 联合索引从最左边的列开始匹配,如果查询条件没有使用到某个列,那么该列右边的所有列都无法走索引
    • 如果查询条件使用了某个列,且该列的值包含范围查询,那么范围查询的字段可以走索引,范围查询后面的字段无法走索引

    20、where条件等于b符合最左原则吗?等于a符合吗

    等于b不符合,等于a符合

    21、查询a LIKE一个String前缀,能走索引吗

    不能,因为索引的 B+树结构是按照索引列的值从小到大排序的,而左模糊匹配的查询条件中,通配符 “%” 出现在了左侧,导致无法按照索引列的值有序查找

  • 相关阅读:
    内网穿透实现Windows远程桌面访问Ubuntu,简单高效的远程桌面解决方案
    python学习笔记(9)—— 虚拟环境和包
    #微信小程序创建(获取onenet平台数据)
    C/C++面经嵌入式面经软件开发面经<28/30>-- 单片机相关(一)
    Unity布料系统_Cloth组件(包含动态调用相关)
    js实现转义、反转义
    Mysql高级(进阶)SQL语句
    通过 Docker Compose 本地启动 hadoop 集群
    RabbitMQ常用命令(一)
    通过docker启动Jenkins容器报错
  • 原文地址:https://blog.csdn.net/mys_mys/article/details/133587882