沃尔玛供应链22
springboot 自动装配
springboot实现自动装配,主要能力来自@EnableAutoConfiguration注解,这个注解通过import注解引入了一个ImportSelector类,通过这个类可以扫描各个start包中的spring.factories文件,然后主要获取enableAutoConfiguration这个key对应的value,加载这些类并解析成beanDefinition。
问题1:那importSelector类是谁实例化的,在什么时机去扫描的
springBoot在启动的时候会初始化applicationContext,这个时候会内置添加几个beanDefinition,其中就包括ConfigurationClassPostProcessor,他是用来处理@Configuration、@import等注解的,他是实现beanDefinitonRegistryPostProcessor的,所以会在refresh里的invokerBeanFactoryPostProcessor方法中实例化然后扫描到importSelector实例并执行selectImports方法
问题2:自动装配中如何排除不需要的配置
在enableAutoConfiguration注解中使用exclude属性
dubbo超时组件
背景是为了缩短下游响应时长,进行实验,利用dubbo的filter机制,通过设置rpcContext上下文中的timeOut实现的,上下文是线程安全的,因此能实现针对同一个下游的不同流量设置不同超时时间且业务无感知,同时使用dubbo的动态配置可以精细控制到方法级是否用spi加载这个filter
nacos配置中心是如何做到实时感知的,如何知道哪些key发生了变化
客户端会和服务端建立一个长轮询,服务端收到请求后会挂起,默认30s进行响应,如果期间对比配置的md5发生了变化,则提前返回响应,客户端再根据返回的key去服务端拉取最新的值进行更新,然后通过spring的事件发布进行refresh操作。
netty常用handler,粘包拆包,如何解码的
netty常用handler有解压缩、编解码、心跳、日志等,粘包拆包主要通过解码器来完成,netty内置了一些解码器,包括定长消息、固定分隔符、消息头指定长度等,先用解码器把二进制流缓存到bytebuf,然后再可以实现messageToMessage把消息转化为java对象
链路追踪怎么做的
全链路监控及日志追踪用的是java agent,通过字节码增强对系统边界和日志MDC做了拦截,在threadLocal中初始化了traceId,然后在调用http或者dubbo前携带了traceId,同时也对线程池的excute方法做了拦截,把traceId相关信息复制到子线程中,从而实现异步调用追踪了
实操中由于业务系统复杂,除了全链路追踪外,也加了自身强相关业务的显示传参reqId,通过threadLocal+拦截器实现的,可以更灵活的满足问题追踪的诉求
docker file怎么写的
主要包含from镜像,run安装依赖及执行的命令,expose声明端口,copy复制文件,volume创建挂载,ARG声明参数等等
https://www.runoob.com/docker/docker-dockerfile.html
redis线程模型
redis6.0之前都是基于reactor线程模型来实现的,通过io多路复用去监听客户端连接、读写事件,只不过这些事包括操作内存都是一个线程完成的。
6.0之后为了进一步提升性能,在io读写上做了优化,通过新增一个io线程池来专门处理io读写,主线程阻塞等待结果,但是操作内存还是由一个线程完成的,整体来看还是可以认为redis的getset操作时单线程的。
redis怎么解决2个操作是事务
沙雕问题,redis本身事务不能保证原子性,也不能在某一步异常时回滚
热点表新增字段超时怎么处理
稳妥的做法是新建表,然后再新表新增字段,再把旧数据导过去
mysql8.0也可以考虑利用online ddl机制实现直接新增字段,对业务影响较小,几乎不阻塞dml操作
灰度部署怎么切量
先通过ng配置upstream,reload摘除节点流量,节点工程部署,通过定时任务检测工程启动成功情况,可以调指定接口检测,启动成功后再将流量切回
分布式事务,二段提交怎么实现的
二段提交时有一个事务协调者,当发起事务时由协调者询问各个事务参与者是否准备好,必须同步等待结果后再执行提交操作,否则回滚。整个期间资源都是锁住的,性能比较差。
目前开源比较火的是阿里的seata。主要用到其中2种,一个TCC事务补偿方式,强侵入,还有一种就是AT模式,类似二阶段提交,但是是各自参与者提交事务,通过undolog日志回滚。
注意tcc种的空回滚和悬挂以及幂等性问题,本质上都可以利用状态机解决,每个阶段都定义一个状态,只能在指定状态下才能操作
nginx如何实现高可用
可以在ng上挂一个keepalive,比如部署2个ng,分别挂上keepalive,然后设置好虚拟ip,当ng挂掉了keepalive可以感知到然后实现ip漂移。
此外也可以通过dns配置多个ip,相当于水平扩展ng,实现高可用
如何得到二叉树的高度
import java.util.Deque;
import java.util.LinkedList;
public class TreeHigh {
public static void main(String[] args) {
TreeNode node = new TreeNode(1);
TreeNode node11 = new TreeNode(2);
TreeNode node12 = new TreeNode(3);
TreeNode node21 = new TreeNode(4);
TreeNode node22 = new TreeNode(5);
TreeNode node31 = new TreeNode(6);
node.left = node11;
node.right = node12;
node11.left = node21;
node11.right = node22;
node22.left = node31;
int high = getHigh(node);
System.out.println(high);
}
public static int getHigh(TreeNode node) {
if (node == null) {
return 0;
}
int high = 0;
Deque<TreeNode> stack = new LinkedList<>();
stack.push(node);
while (!stack.isEmpty()) {
int size = stack.size();
for (int i = 0; i < size; i++) {
TreeNode n = stack.pollLast();
if (n.left != null) {
stack.push(n.left);
}
if (n.right != null) {
stack.push(n.right);
}
}
high++;
}
return high;
}
public static class TreeNode {
public TreeNode left;
public TreeNode right;
public int value;
public TreeNode(int value) {
this.value = value;
}
}
}
2个数组,如何得到2个数组所有成员的中位数
public class ArraysMiddle {
public static void main(String[] args) {
int[] a = new int[]{2, 3, 7, 1, 3};
int[] b = new int[]{1, 32, 6, 5, 8};
//预期1,1,2,3,3,5,6,7,8,32取中位数,偶数取3+5/2,奇数个取中间数
//目标是取中位数,可以不用完全排序,可以考虑使用冒泡
System.out.println(getMuddle(a,b));
}
private static int getMuddle(int[] a,int[] b ){
int al = a.length;
int bl = b.length;
int l = al + bl;
int m = (l+1) / 2;
//只排序一半<m+1
for (int i = 0; i < m+1; i++) {
for (int j = 1; j < l-i ; j++) {
int temp;
if (j < al) {
if (a[j-1] > a[j]) {
temp = a[j-1];
a[j - 1] = a[j];
a[j] = temp;
}
}else if(j == al){
if (a[j-1] >b[j-al]) {
temp = a[j-1];
a[j - 1] = b[j-al];
b[j-al]= temp;
}
}else{
if (b[j-al-1] >b[j-al]) {
temp = b[j-al-1];
b[j-al-1] = b[j-al];
b[j-al]= temp;
}
}
}
}
//已排序一半
if (l % 2 == 1) {
if (m > al){
return b[m-al-1];
}else{
return a[m-1];
}
}else{
if (m < al){
return (a[m - 1] + a[m]) / 2;
}else if (m == al){
return (a[m - 1] + b[0]) / 2;
} else{
return (b[m - al-1] + b[m-al]) / 2;
}
}
}
}