Advertisement

海康威视面试经历

阅读量:

文章目录

  • 请阐述Tomcat是如何实现Web服务器功能的?如果由你来设计一个类似系统会采用何种架构?
  • 请求消息通常包含哪些核心要素?请列举并简述其重要性。
  • 请求消息一般会携带哪些响应内容?请结合实际应用场景进行说明。
  • 请比较HTTP与HTTPS在传输过程中的安全机制有何不同?
  • 在现代密码学领域中你知道哪些基本的加密算法及其适用场景?
  • 使用RSA对10MB文件进行加密解密整个过程大概需要多长时间?影响因素有哪些?
  • Redis中的INCR命令主要用于执行什么操作?Redis内部是否采用了整型数据类型的存储机制?
  • Redis支持的主要数据结构有哪些?它们各自的特点是什么?
  • 如何通过Redis实现分布式环境下的竞争排他机制?
  • 在分布式系统中锁机制过期后如何保证数据一致性?
  • 如果Redis发生故障会导致系统出现不可预见的结果怎么办?
  • 是否存在一种原生命令可以直接解决上述问题而不依赖外部脚本?
  • 你对RedisSonn框架有什么深入的理解?它在性能优化方面有何独特之处?
  • 如何基于Redis构建一个支持延迟队列特性的系统?其核心原理是什么?
  • 分布式事务的概念与传统ACID事务有何异同点?
  • 常见的分布式事务框架有哪些?它们各自的特点是什么?
  • 在设计分布式事务系统时如何平衡一致性和可用性这两个核心原则?

第二期话题挑战赛 参与话题: 面试指南

1、tomcat是怎么实现的,如果让你设计会怎么设计

当我们开始开发Web项目时,并没有类似于main方法那样的入口点可供使用。因此我们的Web应用程序无法直接启动。此时Tomcat服务器应运而生。开发人员可将Web项目托管到Tomcat服务器上,并由其负责执行Web项目的操作。随之而来的问题是:因为Web项目与Tomcat服务器之间存在相互独立性,Tomcat如何获取这些资源的访问权限?

Tomcat必须通过main方法来运行程序。
Tomcat必须使用listener函数来监听指定端口。
Tomcat必须捕获来自该端口的所有客户端连接,并解析每个连接中的请求信息以获取调用的目标方法及其所需参数。
当系统接收到一个请求时,首先会确定其使用的HTTP动作和所需的资源路径;然后系统会动态地加载对应的动作处理程序所在的类文件,并创建相应的对象实例;接着系统会通过实例化的对象获取相关的属性值以及事件处理程序,并将这些信息传递给动作处理程序以便执行相应的业务逻辑操作。
最后系统会将计算出的结果发送回客户端界面,并以JSON或XML格式呈现。

startup.sh->catalina.sh里面包含bootstrap

先对所有Java文件进行扫描,并收集所有.class文件的路径信息;通过反射机制对这些类文件进行处理,并利用注解信息定位出该Servlet对象;同时同步获取其对应的注解值信息;然后使用静态hashmap结构进行存储(以该注解值作为键,将新生成对象作为映射值);最后在哈希表中查找对应键项的同时读取该Servlet配置文件.servlet.xml的信息。

通过dom操作读到8080端口,启动socke监听8080窗口。

当前端发起http请求时,在8080端口处使用socket协议读取客户端的数据,并将其进行解析处理以获得完整的响应信息(包括Servlet路径和参数)。每次处理完一个http请求后就会启动一个新的线程来接收并封装这些信息到HttpServletRequest对象中。每当处理完一个http 请求就会启动一个新的线程来接收并封装这些信息到HttpServletRequest对象中。每当处理完一个http 请求就会启动一个新的线程来接收并封装这些信息到HttpServletRequest对象中。每当处理完一个http 请求就会启动一个新的线程来接收并封装这些信息到HttpServletRequest对象中。每当处理完一个http 请求就会启动一个新的线程来接收并封装这些信息到HttpServletRequest对象中

我的总结如下:首先读取文件内容并解析其中的servlet注解信息,并将其保存到hashMap中。当前端发送HTTP请求时,系统会通过特定协议接收并解析请求数据(如HttpservletRequest)。接着从解析出的数据中识别出与当前servlet实例相对应的对象,并调用该实例中定义的doGet或doPost方法以执行相应的操作。

在这里插入图片描述

手动简单实现:

在这里插入图片描述

2、典型的请求消息包含的内容有哪些?

请求url,请求参数 ,请求方法,最重要。

复制代码
      GET  http://download.microtool.de:80/somedata.exe  
      Host: download.microtool.de 
       Accept :*/* 
      Pragma: no-cache 
      Cache-Control: no-cache 
      Referer:  http://download.microtool.de/  
      User-Agent:Mozilla/4.04[en](Win95;I;Nav) 
      Range:bytes=554554- 
    
    
    
      
      
      
      
      
      
      
      
      
    
    代码解读

3、 典型的响应消息内容有哪些?

复制代码
    	HTTP/1.0200OK 
      Date:Mon,31Dec200104:25:57GMT 
      Server:Apache/1.3.14(Unix) 
      Content-type:text/html 
      Last-modified:Tue,17Apr200106:46:28GMT 
      Etag:"a030f020ac7c01:1e9f" 
      Content-length:39725426 
      Content-range:bytes554554-40279979/40279980 
    
    
    
      
      
      
      
      
      
      
      
      
    
    代码解读

1xx:信息响应类型(标志接收到请求并持续进行后续操作)
2xx:处理成功的响应类别(表明动作被成功接收、理解并加以接纳)
3xx:重定向型响应类别(用于实现指定动作时需要持续进行后续处理)
4xx:客户端错误(客户的请求可能包含语义上的不完整或语法上的错误或者无法实现其意图)
5xx:服务端故障(服务器无法正确处理应当由其负责执行的请求数量或内容)

4、http 和https 区别

即是在HTTP的基础上增加了SSL层

实际操作中

在SSSL协议的工作流程中

值得注意的是

    1. 加密/解密操作决定了该方法相较于HTTP协议而言运行速度较慢。
      1. 另一个重要原因在于HTTPS协议避开了缓存机制。
        相关测试数据显示,在相同条件下采用HTTPS协议进行数据传输的效率仅为HTTP协议传输效率的十分之一。因此,在大多数应用场景中仅限于那些对安全要求极高的数据才会选择采用HTTPS技术进行传输。
        HTTP与HTTPS采用了完全不同的通信方式并使用不同的端口进行通信:**端口80用于Web应用通信(如Apache、IIS),而端口443则用于HTTPS通信(如SSLLed、Let’s Encrypt)。

5、加密算法了解哪些?

对称加密 :加密和解密使用的是同一个密钥,信息接收双方都需事先知道这个密钥(最常见的对称加密算法是DES、AES )。
非对称加密 :加密和解密用的是不同的密钥(最常用的非对称加密算法是RSA )。是一对密钥,公钥和私钥,公钥对外公开,私钥由自己保存。
参考1
参考2

6、RSA加密10M文件要多久

使用RSA对流进行加密并保存到文件中

缺点:加密1M的文件大概需要5秒,但是解密却需要4分钟

7、redis Incr命令是什么?Redis有没有整型类型?

INCR命令具有字符串操作功能。 Redis未提供特定的整数数据类型, 因此键 key存储的值在执行INCR命令时被解析为十进制64位有符号整型数值

Redis采用单一指令实现不可分割性,其原因在于Redis采用了单线程机制。

8、redis常用的数据类型有哪些?

常用的有:
String、List、Hash、Set、Sorted Set(zset)

其中zset,默认采用升序排列,默认排序为从低到高;只有当使用该函数(或通过该函数)时(或通过该函数),排序才会按照score值递减排列。

9、 如何使用redis进行分布式竞争

带参数的set命令,可以在设置的同时,也可以设置过期时间。

当 serverLock 这个 key 不存在时,在 absence of serverLock 这个 key 的情况下,则将 serverLock 设置为 1,并指定其有效期为 10 秒。 如需进一步了解,请参考 string 类型的 set 命令文档 [link](http://redisdoc.com/string/set.html)。

在这里插入图片描述

当服务A成功加锁时,在此期间Redis已经默认将该锁进行了释放。然而由于业务流程较为复杂导致在10秒内未完成操作最终使得另一方(即服务B)即可重新取得该资源。随后另一方(即服务B)即可投入新的业务流程而当前主动方(即服务A)则会因为运行至第十二秒而导致当前主动方(即服务A)将不得不进行资源的解密操作从而导致当前主动方(即服务A)将不得不进行资源的解密操作

10、redis锁过期如何处理?

那么锁过期这种问题该如何处理的?

尽管可以通过延长删除键的时间来应对此问题,但这并未从根本上解决问题。假设将时间设置为100秒,在大部分情况下,在1秒后就能释放锁资源。然而,在服务出现故障时,在这100秒内其他服务将无法获得锁资源——这是一个严重的后果。

为此可以采取以下措施:当锁快到过期时间时(即将到期),若服务未完成业务处理,则重新续用该锁一段时间。具体来说,在设置key使其以10秒后自动失效的情况下(即设置时间为10秒),启动一个守护线程,在第8秒时检查服务是否已全部完成处理;若未完成,则再次续用该key至其10秒后的失效时间。

当某个线程获得了锁时(即获得了锁定),它将启动一个守护线程(也就是备用进程),用于延长即将失效的锁的有效时间。

假设在运行过程中(即正在运行中),当某一线程(比如A)已运行了29秒但尚未完成其任务时(即未达到预期结束时间),此时系统将触发该守护进程(备用进程)并使其执行expiring操作(超时指令)。该守护进程将在第29秒开始工作,并每隔20秒重复一次这个操作(即每运行20秒就再次触发expiring)。

② 情况一:当线程A执行完任务,会显式关掉守护线程。

当服务器突然停电发生时,在同一个进程中运行的线程A和守护线程都会停止运行。被锁定为超时状态的锁,在没有其他线程续命的情况下,从而导致该锁自动释放。

在Redisson(Redis SDK客户端)中已经提供了实现该功能的机制 并将其称为"看门狗"

在这里插入图片描述

11、那么redis宕机了呢?

这个时候就得引入redis集群了。

针对涉及Redis集群的问题会产生新的问题,在主从集群场景下由于主从数据不具备强一致性这一前提下当主节点发生故障无法及时同步至从节点进行切换时新的主节点将缺少老主节点全部数据从而导致老锁状态仍然有效影响其他服务正常获取资源此时分布式锁机制便无法保证安全性

redis的作者也意识到这个问题,并因此发明了RedLock算法。实现流程如下:第一步,请客户端采集当前时间戳T1,并将其发送给服务器进行处理。服务器接收到请求后,在其本地系统中生成一个临时锁,并将该锁信息返回给客户端以供其验证使用。

复制代码
    2、客户端依次向5个master实例发起加锁命令,且每个请求都会设置超时时间(毫秒级,注意:不是锁的超时时间),如果某一个master实例由于网络等原因导致加锁失败,则立即想下一个master实例申请加锁。
    
    3、当客户端加锁成功的请求大于等于3个时,且再次获取当前时间戳T2,
    
    
      
      
      
    
    代码解读

当时间戳T2 - 时间戳T1 < 锁的过期时间。则客户端加锁成功,否则失败。

复制代码
    4、加锁成功,开始操作公共资源,进行后续业务操作
    
    5、加锁失败,向所有redis节点发送锁释放命令
    
    
      
      
      
    
    代码解读

当用户端程序在大多数Redis实例上发起加锁请求并获得成功响应后,并且其总耗时小于对应的过期时间限制,则判定为该操作达到了预期效果。

释放锁需要向全部节点发送锁释放命令。

12、有没有使用原生的命令就可以解决?

(1)执行setnx(lockkey, 当前时间 + 超时时间)操作;若调用结果为1,则认为成功完成锁获取;若结果为0,则表示未成功,并转而执行步骤(2)

(2)调用get(lockkey)方法以获取oldExpireTime值,并将其与当前系统时间进行比较。如果该oldExpireTime值小于当前系统时间,则认为该锁已过期并允许其他请求重新获取。

(3)首先计算出新的过期时间 newExpireTime=当前时间+锁超时时间;接着调用getset(lockkey, newExpireTime),其结果为 currentExpireTime

比较 currentExpireTime 和 oldExpireTime 是否一致。若两者一致,则表明 currentExpireTime 的设置已达成并获得了相应的锁定。若两者不一致,则表明该 lock 已被其他请求占用而无法实现当前 getset 设置的成功。从而导致该 request 无法完成操作。则需重新尝试以解决冲突并获得 lock。

比较 currentExpireTime 和 oldExpireTime 是否一致。若两者一致,则表明 currentExpireTime 的设置已达成并获得了相应的锁定。若两者不一致,则表明该 lock 已被其他 request 占据而无法实现当前 getset 设置的成功。从而导致该 request 无法完成操作并返回相应结果;则需重新尝试以解决冲突并获得 lock。

(5)在成功解锁后,在启动本线程的业务处理流程中进行操作。当业务处理完成并被正确标记为已结束时,在对自身耗时与系统设定的超时阈值进行对比后发现耗时未超过设定阈值的情况下,则应立即执行删除操作以释放该锁定状态;若耗时超过设定阈值,则无需再进行任何锁定相关操作。在此释放锁定的过程中,请确保正在运行的操作确实属于本线程所负责的任务范围,并且只有本线程拥有该锁定权限才能实施此操作。

复制代码
    public boolean lock(long acquireTimeout, TimeUnit timeUnit) throws InterruptedException {
    acquireTimeout = timeUnit.toMillis(acquireTimeout);
    long acquireTime = acquireTimeout + System.currentTimeMillis();
    //使用J.U.C的ReentrantLock
    threadLock.tryLock(acquireTimeout, timeUnit);
    try {
    	//循环尝试
        while (true) {
        	//调用tryLock
            boolean hasLock = tryLock();
            if (hasLock) {
                //获取锁成功
                return true;
            } else if (acquireTime < System.currentTimeMillis()) {
                break;
            }
            Thread.sleep(sleepTime);
        }
    } finally {
        if (threadLock.isHeldByCurrentThread()) {
            threadLock.unlock();
        }
    }
     
    return false;
    }
     
    public boolean tryLock() {
     
    long currentTime = System.currentTimeMillis();
    String expires = String.valueOf(timeout + currentTime);
    //设置互斥量
    if (redisHelper.setNx(mutex, expires) > 0) {
    	//获取锁,设置超时时间
        setLockStatus(expires);
        return true;
    } else {
        String currentLockTime = redisUtil.get(mutex);
        //检查锁是否超时
        if (Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) < currentTime) {
            //获取旧的锁时间并设置互斥量
            String oldLockTime = redisHelper.getSet(mutex, expires);
            //旧值与当前时间比较
            if (Objects.nonNull(oldLockTime) && Objects.equals(oldLockTime, currentLockTime)) {
            	//获取锁,设置超时时间
                setLockStatus(expires);
                return true;
            }
        }
     
        return false;
    }
    }
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

tryLock方法中,其主要逻辑如下:通过调用tryLock方法实现锁管理功能。该方法接受指定的超时时间和单位长度作为参数。在获得锁后的超时时间内,在此处等待并进行自动重试(自旋)的操作会阻塞直至自旋完成后由持有者解除锁定。

复制代码
    public boolean unlock() {
    //只有锁的持有线程才能解锁
    if (lockHolder == Thread.currentThread()) {
        //判断锁是否超时,没有超时才将互斥量删除
        if (lockExpiresTime > System.currentTimeMillis()) {
            redisHelper.del(mutex);
            logger.info("删除互斥量[{}]", mutex);
        }
        lockHolder = null;
        logger.info("释放[{}]锁成功", mutex);
     
        return true;
    } else {
        throw new IllegalMonitorStateException("没有获取到锁的线程无法执行解锁操作");
    }
    }
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

13、了解redisson吗?

Redisson - 是一个高级的分布式管理Redis客服端,能够帮助用户在分布式环境中方便地实现一些Java对象的功能。Redisson、Jedis、Lettuce 是三个不同的 Redis 客户端功能模块,在Reds数据库上的API侧重于 CRUD(增删改查)操作上存在差异:Jedis 和 Lettuce 更注重基本 CRUD 操作支持,而 Redisson 则针对分布式开发场景提供了特定功能。例如,在调用时可以通过传入 redis Lua脚本 进行交互操作。Redis底层使用 Lua脚本 基础上实现了组合命令的操作原子性保障功能。

在这里插入图片描述

14、如何使用redis 设计一个延时队列

在这里插入图片描述

在分布式架构下,
当资源耗尽时,
该节点将通过ZRemoval (ZR)操作删除该元素,
由于该操作具有原子性,
若成功则会明确地返回被删除的数量,
因此,在单次操作中删除一个元素时,
其返回值为1表示成功,
若失败则会明确地返回0。
基于这一特性设计相应的算法即可实现目标。

https://baijiahao.baidu.com/s?id=1676610160717887972&wfr=spider&for=pc

15、分布式事务

异步化已成为分布式系统设计中的常见手法,在各个领域均有广泛应用。基于消息队列的最终一致性可被视为一种异步事务机制,在业务应用中具有广泛的应用。在具体实现层面,基于消息补偿的一致性主要采用本地消息表和第三方可靠的消息队列等。

在这里插入图片描述

向数据表中添加一条记录后会带有状态标记;随后将该条信息发布到MQ队列中等待处理;由下游端从MQ队列中读取并解析这些信息;如果成功,则会立即再次发送该条信息至MQ队列以便后续处理;在此过程中,在MQ队列中的消息如果出现故障或重传失败的情况;另外如果连续失败次数超过设定阈值时,则启动人工干预机制进行处理;若问题仍未解决,则按照规定流程申请退款

16 分布式事务框架了解哪些

该平台发布了一篇详细文章介绍了Seata的技术特点及应用价值。
百度百家号的一篇文章深入解析了Seata的核心技术及其实际应用场景。

17、如何保证分布式事务幂等

当试图达成最终一致性时会不可避免地出现消息重复发送的情况。
在实际应用中,在分布式系统中绝对保证消息传递是不可能的。
准确地传递一条消息完全依赖于消息生成者能够从其使用方那里可靠地收到确认。
然而, ACK本身并不可靠,并非绝对安全。
当处理一条消息时,在网络出现问题或消费者发生故障的情况下,则可能导致ACK丢失。

在这里插入图片描述

为了确保信息传递的准确性,请采用什么措施?其核心在于建立一致性和不可重复执行的操作。**你需要确保消费者的某些操作具备一致性特征。**一致性意味着无论执行一次还是多次某个操作的结果都是一样的——不会因为执行次数的不同而产生不同的结果。例如,在购买商品后付款成功的情况下(支付扣款完成),如果返回结果时出现网络异常(导致款项已扣除),此时消费者再次点击支付按钮(重复尝试)不应该导致第二次扣款失败(防止重复扣除)的情况发生。

添加幂等性处理方法:

● 唯一标识是实现无冲突调用的关键机制。通过创建独一无二的标识符并将其集成到业务流程中,系统能够方便地追踪和管理各项操作,从而有效避免重复访问问题。
● 借助工作流引擎框架,实时监控生产者发送的消息,系统能够自动识别并剔除冗余数据,从而提高处理效率和性能。
● 为了确保消息处理的一致性,推荐采用哈希算法进行计算。将计算得到的哈希值作为操作依据,不仅能够快速判断信息属性的一致性,还能有效减少数据传输过程中的冗余信息。

参考:

https://juejin.cn/post/6931997953854799879

全部评论 (0)

还没有任何评论哟~