建站公司哪个好,南京手机网站设计,佛山免费网站建站模板,网站建设准备工作总结蚂蚁实习一面面经
希望可以帮助到大家
tcp建立连接为什么要三次握手#xff1f;
三次握手的过程
注意#xff1a;三次握手的最主要目的是保证连接是双工的#xff0c;可靠更多的是通过重传机制来保证的
所谓三次握手#xff0c;即建立TCP连接#xff0c;需要客户端和…蚂蚁实习一面面经
希望可以帮助到大家
tcp建立连接为什么要三次握手
三次握手的过程
注意三次握手的最主要目的是保证连接是双工的可靠更多的是通过重传机制来保证的
所谓三次握手即建立TCP连接需要客户端和服务端总共发送至少三个包确认连接的建立。流程如下 为什么要三次挥手
第一次握手 Client什么都不能确认 Server确认:自己接收正常 对方发送正常 第二次握手 Client确认自己发送/接收正常对方发送/接收正常 Server确认自己接收正常 对方发送正常 第三次握手 Client确认自己发送/接收正常 对方发送/接收正常 Server确认自己发送/接收正常对方发送/接收正常 如果两次握手会怎么样
有这样一种情况当A发送一个消息给B但是由于网络原因消息被阻塞在了某个节点然后阻塞的时间超出设定的时间A会认为这个消息丢失了然后重新发送消息。 当A和B通信完成后这个被A认为失效的消息到达了B
对于B而言以为这是一个新的请求链接消息就向A发送确认
对于A而言它认为没有给B再次发送消息因为上次的通话已经结束所有A不会理睬B的这个确认但是B则会一直等待A的消息 这就导致了B的时间被浪费对于服务器而言CPU等资源是一种浪费这样是不可行的这就是为什么不能两次握手的原因了。 tcp断开连接为什么四次挥手
挥手过程 第一次挥手 Clien发送一个FIN用来关闭Client到Server的数据传送Client进入FIN_WAIT_1状态。 第二次挥手 Server收到FIN后发送一个ACK给Client,Server进入CLOSE_WAIT状态。 第三次挥手 Server发送一个FIN用来关闭Server到Client的数据传送Server进入LAST_ACK状态。 第四次挥手 Client收到FIN后Client进入TIME_WAIT状态发送ACK给ServerServer进入CLOSED状态完成四次握手。 为什么要四次挥手
四次挥手的本质原因是tcp是全双公的通信是双向的 A到B是一个通道B到A又是另一个通道。
A端确认没有数据发送后发出结束报文此时B端返回确认后B端也不会接收A端数据。
但是此时B端可能还有数据没有传输完A端还是可以接收数据。
只有当B端数据发送完之后才能发出结束报文并且确认A端接收到的时候两边才会真正的断开连接双方的读写分开。 四次挥手释放连接时等待2MSL的意义
MSL是TCP报文的最大生命周期因为TIME_WAIT持续在2MSL就可以保证在两个传输方向上的尚未接收到或者迟到的报文段已经消失否则服务器立即重启可能会收到来自上一个进程迟到的数据但是这种数据很可能是错误的同时也是在理论上保证最后一个报文可靠到达假设最后一个ACK丢失那么服务器会再重发一个FIN这是虽然客户端的进程不在了但是TCP连接还在仍然可以重发LAST_ACK。
kafaka如何保证消息可达
消息传递语义 1首先当 Producer 向 Broker 发送数据后会进行 commit如果commit成功由于 Replica 副本机制的存在则意味着消息不会丢失但是 Producer 发送数据给 Broker 后遇到网络问题而造成通信中断那么 Producer 就无法准确判断该消息是否已经被提交commit这就可能造成 at least once 语义。
2在 Kafka 0.11.0.0 之前 如果 Producer 没有收到消息 commit 的响应结果它只能重新发送消息确保消息已经被正确的传输到 Broker重新发送的时候会将消息再次写入日志中而在 0.11.0.0 版本之后 Producer 支持幂等传递选项保证重新发送不会导致消息在日志出现重复。为了实现这个, Broker 为 Producer 分配了一个ID并通过每条消息的序列号进行去重。也支持了类似事务语义来保证将消息发送到多个 Topic 分区中保证所有消息要么都写入成功要么都失败这个主要用在 Topic 之间的
exactly once 语义。
3从 Consumer 角度来剖析, 我们知道 Offset 是由 Consumer 自己来维护的, 如果 Consumer 收到消息后更新 Offset 这时
Consumer 异常 crash 掉 那么新的 Consumer 接管后再次重启消费就会造成 at most once 语义消息会丢但不重复。
4) 如果 Consumer 消费消息完成后, 再更新 Offset如果这时 Consumer crash 掉那么新的 Consumer 接管后重新用这个
Offset 拉取消息 这时就会造成 at least once 语义消息不丢但被多次重复处理。
具体策略
消息持久化Kafka使用持久化存储方式将所有的消息写入磁盘并且使用复制机制来保证消息的可靠性即使在某些节点出现故障的情况下也能够保证消息的可靠性。分区机制Kafka将每个主题分为多个分区每个分区都有自己的副本。当消息发送到某个分区时它将被写入该分区的所有副本中。如果一个副本无法接收消息则Kafka会将消息写入其他可用的副本中。Leader选举每个分区都有一个leader副本和多个follower副本。当leader副本出现故障时follower副本会参与到leader选举过程中Kafka会自动选举一个新的leader副本来保证消息的可达性。确认机制生产者可以选择同步发送或异步发送消息。同步发送将等待所有副本都收到消息之后才会返回确认而异步发送则会立即返回确认。这个机制可以确保消息已被正确写入所有副本。偏移量Kafka使用偏移量来追踪消费者消费的位置。当消费者读取完一个消息后它会将偏移量提交回Kafka以便下一次读取时能够从正确的位置开始读取。这样可以保证消息不会被重复消费也不会漏掉任何消息。
线程的状态
新建状态New当通过调用Thread类的构造方法创建一个线程对象时该线程将处于新建状态。在这种状态下线程已经被创建但尚未开始执行。就绪状态Runnable当调用线程对象的start()方法后线程将进入就绪状态。在这种状态下线程已经准备好执行但尚未获得CPU时间片。运行状态Running当线程获得CPU时间片并开始执行时它将进入运行状态。阻塞状态Blocked线程在执行过程中可能需要等待某些事件的发生如I/O操作的完成或锁的释放等。在这种情况下线程将被阻塞并进入阻塞状态。Java中的阻塞状态分为三种
等待阻塞Wait线程通过调用wait()方法等待某个条件的满足。同步阻塞Blocked on synchronization线程等待获取某个对象的锁。其他阻塞Blocked on Others线程等待一些其他事件的发生如Thread.sleep()方法调用或等待I/O操作的完成等。
暂停状态SuspendedJava中的线程暂停状态不是线程的正式状态而是一种用于调试的状态。当线程被挂起后它将暂停执行但仍处于运行状态。终止状态Terminated当线程执行完毕或出现未处理的异常时它将进入终止状态。线程在终止状态下无法被重新启动。 数据库了解哪些索引
主键索引Primary Key Index是一种唯一索引它强制要求每个记录都必须有一个唯一的标识通常是一个自增长的整数值。唯一索引Unique Index保证某个列中的所有值都是唯一的可以用于加速查找和避免重复数据。普通索引Normal Index是最基本的索引类型可以提高查询的速度。聚集索引Clustered Index在数据库表中只能有一个聚集索引它决定了数据在磁盘上的物理存储顺序即表数据的存储顺序和索引顺序是一致的。非聚集索引Non-Clustered Index在数据库表中可以有多个非聚集索引它不会改变数据在磁盘上的物理存储顺序而是单独维护一个索引表来记录数据的位置信息。全文索引Full Text Index用于加速全文搜索它可以在文本数据中查找包含特定关键词的记录。空间索引Spatial Index用于加速地理信息系统GIS等应用程序中的空间查询。复合索引Composite Index基于多个列的组合来创建的索引可以提高复合查询的性能。 索引多了有什么坏处
空间占用索引需要占用磁盘空间如果数据量很大索引也会变得很大占用大量的磁盘空间。写入操作变慢每次写入操作都需要更新索引这会导致写入操作变慢。维护成本高索引需要维护例如重建、重新组织等操作这些操作都需要时间和资源。查询变慢尽管索引可以提高查询效率但是如果索引不合理或者不正确查询效率反而会变慢。例如如果创建了过多的索引查询时需要扫描的索引也会增加导致查询变慢。数据不一致如果索引不正确或者不同步会导致查询结果不一致。 实现一个静态单例模式
静态单例模式是指单例对象在程序运行时就已经创建好而不是在使用时动态创建。下面是一个实现静态单例模式的示例静态单例模式是指单例对象在程序运行时就已经创建好而不是在使用时动态创建。下面是一个实现静态单例模式的示例
public class MySingleton {// 静态成员变量存储单例对象private static MySingleton instance new MySingleton();// 私有构造方法防止其他类创建该类的实例private MySingleton() {// 初始化操作}// 静态方法获取单例对象public static MySingleton getInstance() {return instance;}
} 在高并发的环境下如何设置mysql
MySQL主从复制实现读写分离
数据存储Master将数据改变记录到二进制日志(binary log)中也就是配置文件log-bin指定的文件这些记录叫做二进制日志事件(binary log events)数据传输Slave 通过 I/O 线程读取 Master 中的 binary log events 并写入到它的中继日志(relay log)数据重放Slave 重做中继日志中的事件把中继日志中的事件信息一条一条的在本地执行一次完成数据在本地的存储从而实现将改变反映到它自己的数据。
分区
把一张表的数据分成多个区块在逻辑上看最终只是一张表但底层是由多个物理区块组成的。
MySQL的物理数据存储在表空间文件.ibdata1和.ibd中这里讲的分区的意思是指将同一表中不同行的记录分配到不同的物理文件中几个分区就有几个.idb文件。
分表
分表分为水平分表和垂直分表。
水平分表
水平分表和分区很像或者说分区就是水平分表的数据库实现版本它们分的都是行记录就像用一把刀水平的将一个表切成多张表一样。 垂直分表
水平分表分的是行记录而垂直分表分的是列字段它就像用一把刀垂直的将一个表切成多张表一样。
垂直分表是基于列字段进行的。一般是表中的字段较多或者有数据较大长度较长比如textblobvarchar(1000)以上的字段的字段时我们将不常用的或者数据量大的字段拆分到“扩展表”上。这样避免查询时数据量太大造成的“跨页”问题。 分表和分区的区别
分区只是一张表中的数据和索引的存储位置发生改变分表则是将一张表分成多张表是真实的有多套表的配套文件分区没法突破数据库层面不论怎么分区这些分区都要在一个数据库下。而分表可以将子表分配在同一个库中也可以分配在不同库中突破数据库性能的限制。分区只能替代水平分表的功能无法取代垂直分表的功能。
分库的类型
分库同样分为水平分库和垂直分库。
水平分库
水平分库和水平分表相似并且关系紧密水平分库就是将单个库中的表作水平分表然后将子表分别置于不同的子库当中独立部署。因为库中内容的主要载体是表所以水平分库和水平分表基本上如影随形。例如用户表我们可以使用注册时间的范围来分表将2020年注册的用户表usrtb2020部署在usrdata20中2021年注册的用户表usrtb2021部署在usrdata21中。 垂直分库
同样的垂直分库和垂直分表也十分类似不过垂直分表拆分的是字段而垂直分库拆分的是表。垂直分库是将一个库下的表作不同维度的分类然后将其分配给不同子库的策略。例如我们可以将用户相关的表都放置在usrdata这个库中将订单相关的表都放置在odrdata中以此类推。垂直分库的分类维度有很多可以按照业务模块划分用户/订单...按照技术模块分日志类库/图片类库...或者空间时间等等。 如何保证线程的有序性
join方法
用join方法来保证线程顺序其实就是让main这个主线程等待子线程结束然后主线程再执行接下来的其他线程任务
ExecutorService方式
这种方式的原理其实就是将线程用排队的方式扔进一个线程池里让所有的任务以单线程的模式按照FIFO先进先出、LIFO后进先出、优先级等特定顺序执行但是这种方式也是存在缺点的就是当一个线程被阻塞时其它的线程都会受到影响被阻塞不过依然都会按照自身调度来执行只是会存在阻塞延迟。 volatile的使用
在Java中volatile是一种关键字用于告诉编译器和JVM该变量的值可能被其他线程修改因此每次访问该变量时都需要从内存中读取最新的值而不是使用缓存的值。这可以确保多线程环境下该变量的值的一致性。
//使用了volatile
public class VolatileDemo {public static volatile boolean stop false;//任务是否停止volatile变量public static void main(String[] args) throws Exception {Thread thread1 new Thread(() - {while (!stop) { //stopfalse不满足停止条件继续执行//do someting}System.out.println(stoptrue满足停止条件。 停止时间 System.currentTimeMillis());});thread1.start();Thread.sleep(100);//保证主线程修改stoptrue在子线程启动后执行。stop true; //trueSystem.out.println(主线程设置停止标识 stoptrue。 设置时间 System.currentTimeMillis());}
}threadlocal的作用以及可能产生的问题
作用
通常情况下我们创建的变量是可以被任何一个线程访问并修改的。如果实现每个线程都有自己的专属变量该如何设置ThreadLocal类主要就是解决让每个线程绑定自己的专属变量。
ThreadLocal主要用来存储当前上下文的变量信息他可以保障存储进去的信息只能被当前的线程读取到并且线程之间不会受到影响。ThreadLocal为变量在每个线程都创建了一个副本那么每个线程可以访问自己的内部的副本变量。
可能产生的问题
1.ThreadLocal内存泄漏是指ThreadLocal对象中持有的变量副本没有被及时清理而导致的内存泄漏问题。当一个线程结束时如果ThreadLocal对象中持有的变量副本没有被清理这些变量副本会一直存在于内存中无法被回收从而导致内存泄漏。
解决方案
ThreadLocalMap实现中已经考虑了这种情况在调用set()、get()、remove()方法时会处理掉key为null的记录。使用ThreadLocal时最好手动调用remove()方法。 2.上下文切换开销每个线程都有自己的变量副本当线程切换时需要保存和恢复这些变量这可能会带来一些上下文切换的开销。