一、kafka数据分区和消费者的关系,kafka的数据offset读取流程,kafka内部如何保证顺序,结合外部组件如何保证消费者的顺序1、kafka数据分区和消费者的关系:1个partition只能被同组的一个consumer消费,同组的consumer则起到均衡效果2、kafka的数据offset读取流程a.连接ZK集群,从ZK中拿到对应topic的partition信息和partition的Leader的相关信息b.连接到对应Leader对应的brokerc.consumer将⾃己保存的offset发送给Leaderd.Leader根据offset等信息定位到segment(索引文件和⽇志⽂文件)e.根据索引文件中的内容,定位到日志文件中该偏移量对应的开始位置读 取相应⻓度的数据并返回给consumer3、kafka内部如何保证顺序:kafka只能保证partition内是有序的,但是partition间的有序是没办法的。爱奇艺的搜索架构,是从业务上把需要有序的打到同⼀个partition。二、cms垃圾回收机制
1、概念:CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器器,
2、使用场景:GC过程短暂停,适合对时延要求较高的服务,用户线程不允许⻓时间的停顿。
3、缺点:
a.服务长时间运⾏,造成严重的内存碎片化。
b.算法实现比较复杂(如果也算缺点的话)。
4、实现机制:根据GC的触发机制分为:
a.周期性Old GC(被动):2s执行一次;
b.主动Old GC:触发条件:
i. YGC过程发⽣生Promotion Failed,进⽽对老年代进行回收
ii.比如执行了System.gc(),前提是没有参数ExplicitGCInvokesConcurrent
iii. 其它情况
三、springcloud各个组件功能,内部细节,与dubbo区别,dubbo架构,dubbo负载策略略
1、springcloud各个组件功能:
a. Ribbon,客户端负载均衡,特性有区域亲和、重试机制。
b. Hystrix,客户端容错保护,特性有服务降级、服务熔断、请求缓存、请求合并、依赖隔离。
c. Feign,声明式服务调用,本质上就是Ribbon+Hystrix
d. Stream,消息驱动,有Sink、Source、Processor三种通道,特性有订阅发布、消费组、消息分区。
e. Bus,消息总线,配合Config仓库修改的一种Stream实现,
f. Sleuth,分布式服务追踪,需要搞清楚TraceID和SpanID以及抽样,如何与ELK整合。
g. Eureka,服务注册中心,特性有失效剔除、服务保护。
h. Dashboard,Hystrix仪表盘,监控集群模式和单点模式,其中集群模式需要收集器Turbine配合。
i. Zuul,API服务网关,功能有路由分发和过滤。
j. Config,分布式配置中⼼,支持本地仓库、SVN、Git、Jar包内配置等模式,
2、dubbo负载策略:
Random LoadBalance
a.随机,按权重设置随机概率。
b.在一个截面上碰撞的概率高,但是调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
a.轮询,按公约后的权重设置轮询比率。
b.存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
a.最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
b.让慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差越大。
ConsistentHashLoadBalance
a.一致性Hash,相同参数的请求总是发到同一提供者。
b.当某一台提供者挂时,原本发生该提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动。
c.缺省只对第一个参数Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments" value="0.1"/>
d.缺省用160份虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320"/>
四、mapreduce原理
1、简介:mapreduce源⾃自google的一篇⽂章,将海量数据处理的过程拆分为map和reduce。mapreduce 成为了最早的分布式计算框架,这样即使不懂的分布式计算框架的内部运行机制的用户,也可以利用分布式的计算框架实现分布式的计算,并在hadoop上面运行。
2、设计思想:
hadoop ⽂件系统 ,提供了一个分布式的文件系统,但是hadoop文件系统读写的操作都涉及到大量的网络的操作,并不能很好的完成实时性比较强的任务。但是hadoop可以给上面的应用提供⼀个很好的支持。比如hadoop文件系统上⾯可以运行mapreduce。mapreduce是⼀个计算的框架,mapreduce是一个分布式的计算框架,这样mapreduce利用分布式的⽂件系统,将不同的机器上完成不同的计算,然后就计算结果返回。这样很好的利用了分布式的文件系统。数据分布式的存储,然后计算的时候,分布式的计算,然后将结果返回。这样的好处就是不会涉及到⼤量的⽹络传输数据。
3、优点:mapreduce的计算框架的优点是,极强的扩展能力,可以在数千台机器器上并发的执行。其次,有很好的容错性,另外,就是向上的接口简洁。⽤户只需要写map和reduce函数,即可完成大规模数据的并⾏处理。
4、缺点:mapreduce并不适合对实时性要求比较⾼的场景,比如交互式查询或者是流式计算。另外,也不适合迭代类的计算(⽐如机器学习类的应⽤)。
五、nio,bio,sellector/epoll,aio,netty自带编解码器,netty优势,java内存模型
Netty高性能:
1、NIO异步非阻塞通信
2、“零拷贝”
3、内存池ByteBuf
4、Netty提供了多种内存管理策略,通过在启动辅助类中配置相关参数,可以实现差异化的定制。
5、⾼效的Reactor线程模型:Reactor单线程(多线程、主从)模型,指的是所有的IO操作都在同⼀一个NIO线程上面完成
6、为了尽可能提升性能,Netty采⽤了串⾏⽆锁化设计,在IO线程内部进行串⾏操作,避免多线程竞争导致的性能下降。表⾯上看,串⾏化设计似乎CPU利用率不⾼,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。
7、高效的并发编程:Netty的⾼效并发编程主要体现在如下⼏点:
a.volatile的⼤量、正确使用;
b. CAS和原⼦类的广泛使用;
c. 线程安全容器的使用;
d. 通过读写锁提升并发性能。
e.高效的序列化框架:
f.灵活的TCP参数配置能力:合理设置TCP参数在某些场景下对于性能的提升可以起到显著的效果,例如SO_RCVBUF和SO_SNDBUF。如果设置不当,对性能的影响是非常⼤的。
六.akka模型
1、概念:Akka是一个构建在JVM上,基于Actor模型的的并发框架,为构建伸缩性强,有弹性的响应式并发应用提⾼更好的平台。
2、Actor模型:Akka的核心就是Actor,所以不得不说Actor,Actor模型我通俗的举个例⼦,假定现实中的两个人,他们只知道对⽅的地址,他们想要交流,给对⽅传递信息,但是又没有手机,电话,⽹络之类的其他途径,所以他们之间只能用信件传递消息,很像现实中的的邮政系统,你要寄一封信,只需根据地址把信投寄到相应的信箱中,具体它是如何帮你处理送达的,你就不需要了解了,你也有可能收到收信人的回复,这相当于消息反馈。上述例⼦中的信件就相当于Actor中的消息,Actor与Actor之间只能通过消息通信。
七、java arraylist,linkedlist区分及实现原理,hashmap和concurrenthashmap区分及实现原理,concurrenthashmap 1.7和1.8区分,实现细节linkedhashmap排序原理,应⽤如何保证数据幂等
1、java arraylist,linkedlist区分及实现原理:
a. ArrayList是实现了基于动态数组的数据结构,⽽LinkedList是基于链表的数据构;
b. 对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;
c. 对于添加和删除操作add和remove,⼀般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。
2、concurrenthashmap 1.7和1.8区分:
去除 Segment + HashEntry + Unsafe的实现,改为 Synchronized + CAS + Node + Unsafe的实现。其实 Node 和 HashEntry 的内容一样,但是HashEntry是⼀个内部类。用 Synchronized + CAS 代替 Segment ,这样锁的粒度更⼩了,并且不是每次都要加锁了,CAS尝试失败了在加锁。put()⽅法中 初始化数组大小时,1.8不用加锁,因为⽤了个 sizeCtl变量,将这个变量置为-1,就表明table正在初始化。
3、linkedhashmap排序原理:
public linkedHashMap(){//调用HashMap的构造方法,其实就是初始化Entry[] tablesuper();//这里是指是否基于访问排序,默认为falseaccessOrder=false;}
a.LinkedHashMap存储数据是有序的,⽽且分为两种:插⼊顺序和访问顺序。默认为插入顺序。
b.LinkedHashMap有自⼰的静态内部类Entry,它继承了HashMap.Entry,定义如下:
/***LinkedHashMap entry*/private static class Entry<K,V>extends HashMap.Entry<K,V>{//These fieds comprise the doubly linked list used for iteration.Entry<K,V>befor,after;Entry(int hash,K key,V value,HashMap.Entry<K,V>next){ super(hash,key,value,next);}}
c.所以LinkedHashMap构造函数,主要就是调⽤HashMap构造函数初始化了⼀个Entry[] table,然后调⽤⾃身的init初始化了⼀个只有头结点的双向链表。
八、web.xml listener,filter,servlet加载顺序。
web.xml listener,filter,servlet加载顺序:context-param -> listener -> filter -> servlet
九、无穷数就top K问题,提供多个方案
1、最简单且最容易想到的算法是对数组进行排序(快速排序),然后取最大或最⼩的K个元素。总的时间复杂度为
O(N*logN)+O(K)=O(N*logN)。该算法存在以下问题:
a. 快速排序的平均复杂度为O(N*logN),但最坏时间复杂度为O(n2),不不能始终保证较好的复杂度
b. 只需要前k大或k小的数,,实际对其余不需要的数也进行了排序,浪费了大量排序时间
总结:通常不会采取该⽅案。
2、虽然我们不会采用快速排序的算法来实现TOP-K问题,但我们可以利用快速排序的思想,在数组中随机找⼀一个元素key,将数组分成两部分Sa和Sb,其中Sa的元素>=key,Sb的元素<key,然后分析两种情况:
a. 若Sa中元素的个数大于或等于k,则在Sa中查找最大的k个数
b. 若Sa中元素的个数小于k,其个数为len,则在Sb中查找k-len个数字
如此递归下去,不断把问题分解为更小的问题,直到求出结果。
3、寻找N个数中的第K大的数,可以将问题转化寻找N个数中第K大的问题。对于一个给定的数p, 可以在O(N)的时间复杂度内找出
所有不小于P的数。
根据分析,可以使⽤二分查找的算法思想来寻找N个数中第K大的数。假设N个数中最⼤的数为Vmax,最⼩的数为Vmin, 那么N个数
中第K大的数⼀定在区间[Vmin,Vmax]之间。然后在这个区间使用二分查找算法。
总结:该算法实际应⽤效果不佳,尤其是不同的数据类型需要确定max - min > delta,因此时间复杂度跟数据分布有关。整个算法的时间复杂度为O(N * log(Vmax-Vmin)/delta),在数据分布平均的情况下,时间复杂度为O(N * logN)。
4、上⾯⼏种解法都会对数据访问多次,那么就有⼀个问题,当数组中元素个数⾮常⼤时,如:100亿,这时候数据不能全部加载到内存,就要求我们尽可能少的遍历所有数据。针对这种情况,下面我们介绍⼀种针对海量数据的解决方案。
在学习堆排序的过程中,我们知道了堆这种数据结构。为了查找Top k大的数,我们可以使用大根堆来存储最⼤的K个元素。大根堆的堆顶元素就是最大K个数中最小的⼀个。每次考虑下一个数x时,如果x比堆顶元素小,则不需要改变原来的堆。如果想x比堆顶元素大,那么用x替换堆顶元素, 同时,在替换之后,x可能破坏最⼩堆的结构,需要调整堆来维持堆的性质。
总结:该算法只需要扫描所有的数据一次,且不会占⽤太多内存空间(只需要容纳K个元素的空间),尤其适合处理海量数据的场景。算法的时间复杂度为O(N * logk),这实际上相当于执行了部分堆排序。
5、TOP-K问题是一个经典的问题,这个问题是存在线性算法的,只不过算法的使用范围有⼀定的限制。如果所有N个数都是正整数,且他们的取值范围并不大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个。实际就是利⽤计数排序的思想。假设所有整数都在(0,maxN)区间,利⽤用一个数组count[maxN]来记录每个整数出现的次数。count[i]表示整数i在N个数中出现的次数。只需要扫描⼀遍就可以得到count数组,然后寻找第K大的元素。
这是一个典型的以空间换取时间的做法。当数组中取值范围⽐较大时,是极其浪费空间的。如[3,1...9999],为了求出最大的K个元素,需要额外申请一个长度为10000的数组。
极端情况下,如果 N 个整数各不相同,我们甚⾄只需要⼀个 bit 来存储这个整数是否存在,这样可节省很大的内存空间。