侧边栏壁纸
  • 累计撰写 79 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

OceansWu

水龙吟
2022-09-27 / 0 评论 / 0 点赞 / 396 阅读 / 9,185 字
温馨提示:
本文最后更新于 2022-09-27,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

https://segmentfault.com/a/1190000020258483
https://juejin.cn/post/6890769045608480776
https://juejin.cn/post/6844903945060483079
https://segmentfault.com/a/1190000040420111
https://juejin.cn/post/7112826654291918855
steam:
oceanszd

Whz@!12zd
&developPWD1212

docker run -p 3306:3306 --name mysql
-e MYSQL_ROOT_PASSWORD=root
-d mysql:5.7

Jemet压测的时候发现
3、

分布式锁解决并发问题

  1. 项目采用SpringBoot整合Dubbo进行微服务构建。
  2. 采用Nacos实现服务注册与发现以及配置中心。
  3. 采用GateWay网关技术实现动态路由、鉴权、过滤
    4.采用Spring Task迁移文件至Fastdfs分布式文件系统
    5.通过Redis+AOP实现注解自动缓存
    6.采用RabbitMq实现业务中的消息通知,削峰、解耦
    7.ELK日志系统
    8.分布式锁处理多机器的资源竞争

异步编排
1. 采用帆软生成模板及指纹合成

组织活动:电子知识竞赛(刚开学 找到大家的共同点 游戏 游戏知识竞赛)
文艺汇演:服装的租赁(班费出一点 拉的赞助出一点 剩下的自费 劝说大家给班费 因为是代表班级出战)、

老年人诈骗
保健品
中奖
温情 装亲人
养老金交社保

ArrayList和LinkedList
ArrayList查找快,增删慢,底层数组,index查找
LinkedList查找慢,增删快,底层链表,开销大,存数据之外还有引用

集合排序底层:
TimeSort:由归并排序(merge sort)和插入排序(insertion sort)组成,
当数组小于一定值时是二分插入排序(binarySort)
归并排序就是一直分,然后排序,最后合并

HashMap内部实现node[]数组

HashMap实现put:
计算key的hash值,看table[i]计算到的位置是否为空,为空直接插入,不为空去添加到链表后面。存在相同key时覆盖value即可

HashMap计算index:
先获取哈希值(哈希算法也叫散列,可以把任意长度值key'通过此算法变换成固定长度的key地址)hashcode,
将hashcode高18位二进制和低18位二进制进行异或运算,得到的结果再和数组长度-1进行与运算

HashMap扩容因子0.75,初始容量16,16*0.75=12,就是在12的时候会扩容至32,且扩容后会重新分配。

如何解决hash冲突
hash冲突就是同一数组下标,所以用链表解决。将新来的数据放在之前的数据的尾节点。而且节点过8之后就会
红黑树(左节点小于根节点,右节点大于根节点)化,提升查找效率。而节点小于6会链化。之所以不在8链化,
是为了防止左右横跳

HashMap的容量是2的倍数
算法需要(n-1)的值尽可能是1,即10000,减一之后是01111,这样计算的值相对离散,避免hash冲突

TreeMap实现sortmap接口,按照key升序排序

为什么用迭代器Iterator
遍历快、安全不暴露内部数据、不过可以用remove

concurrentHashMap线程安全的Hash Map

Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,可以确保集合做参数时不被改变

为什么hashMap适合String、Integer这样的类做key
因为他们都是final修饰的类,保证了key的不可更改性,不会存在获取的hash值不同的情况
自身重写了hashcode()、equals()方法,遵循hashMap的内部规范

d
良好的编码习惯 注释 命名 测试demo

面试官你好,我叫吴昊智,今年26,有三年java开发经验,期间有过小组项目管理经验,就职过两家公司,一家主营房地产交易相关业务,一家主营电商业务。
平时会看博客与技术文档,喜欢研究新技术。个人性格外向开朗,有良好的沟通能力,有良好的编码习惯,对电商、短视频、直播比较熟悉。目前已属于离职阶段。

InnoDB
show profile 查看sql指行情况
explain sql 解析sql
慢查询日志
like前不要用%
索引使用:
全值匹配我最爱
最佳左前缀法则
计算、函数导致索引失效
范围条件右边的列索引失效
is not null无法使用索引,is null可使用索引
关联查询优化
建立索引的时候,应该在从表{被驱动表}上建立!

排序、分组优化
无过滤 不索引!
先排序再分组

多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

多线程
同步锁 Sync lock
异步锁 分布式锁 redis
lock有condition 创建多把钥匙 await 和 signal 注意finally要unlock 可重入锁 读写锁

  • jdk代理 :通过反射生成代理类,代理类实现接口,所以只能代理接口定义的方法
  • CGlib代理:操作字节码生成子类,继承原有的类,所以final修饰的类不能代理,final修饰的方法不能代理
  • AOP:springboot默认了用CGlib(配置spring.aop.proxy-target-class=true)

循环依赖 @Lazy 设计之初避免彼此依赖

Java特性:
面向对象(封装,继承,多态)
平台无关性(JVM运行.class文件)
语言(泛型,Lambda)
类库(集合,并发,网络,IO/NIO)
JRE(Java运行环境,JVM,类库)
JDK(Java开发工具,包括JRE,javac,诊断工具)

Java是解析运行吗?
不正确!
1,Java源代码经过Javac编译成.class文件
2,.class文件经JVM解析或编译运行。
(1)解析:.class文件经过JVM内嵌的解析器解析执行。
(2)编译:存在JIT编译器(Just In Time Compile 即时编译器)把经常运行的代码作为"热点代码"编译与本地平台相关的机器码,并进行各种层次的优化。
(3)AOT编译器: Java 9提供的直接将所有代码编译成机器码执行。

面向对象:
封装:类 把属性和方法封装
继承:继承父类的属性与方法
多态:List a = new ArrayList(); 父类引用指向子对象
抽象:把类的共同特征抽象出来

八大基本数据类型:
字符类:char
布尔类:boolean
数值类:整数型:byte short int long 浮点型:float double

Integer的默认值是null,int的默认值是0

==和equals()区别
==, 如果是基础数值类型比较的值相等,引用数值的话就是地址值,指向的是否为同一内存
equals,没有重写比较的是对象的引用,重写了比较的是对象的属性值

String被final修饰,不可继承与重写方法

StringBuffer:线程安全(synchronized )修饰,效率慢
StringBUilder:线程不安全,效率快

Object方法:equals clone hashcode toString wait notify

集合:
ArrayList:动态数组实现,get set 快
LinkedList:链表实现,remove insert 快

集群
读写分离
主从复制(基于读写分离)
分库分表 垂直分表(不常用的字段 大数据的字段分开) 垂直分库(根据业务分库) 水平分表(比如说根据id分开,或者根据时间分开) 水平分库(把表放到不同库)

nginx
反向代理:proxy_pass
负载均衡:upstream

redis持久化:
RDB:redis database, rdb持久化通过快照实现的,达到特定条件,redis将内存中的所有数据以二进制的方式生成一份副本存储在硬盘上。
触发条件:
主动触发:
使用save、bgsave命令。 save会让redis进入阻塞状态,直到快照执行完毕。 bgsave是开一个子线程去执行快照,创建子线程的时候会阻塞,创建完之后正常响应请求,
之后子线程创建快照,替换之前的快照。
被动触发:
save m n 在m秒内有n个键发生改变,redis会触发bgsave操作
fullshall,清空redis的时候也会被动触发bgsave
shutdown命令有一个参数指定是否持久化 save/nosave
主从复制,master会执行bgsave,将rdb文件发送给slave

AOF:append only file,rdb相当于冷备(定时、条件备份),aof相当于热备。当redis突然宕机,内存中部分数据没有写入磁盘,造成部分数据丢失。aof就是解决这一问题,
aof将redis的所有写操作命令存入aof文件中。但是当写入一个键时,后边又删除,就造成了命令冗余,这时候用文件重写。重写也分手动和主动,手动:bgrewriteaof,
主动是配置文件auto-aof-rewrite-min-size,达到指定大小的时候主动重写,auto-aof-rewrite-percentage,指定比率,设置100,是上次重写的2倍才开始重写

rdb与aof:
    rdb性能更好,适用于恢复大数据
    aof秒级内的数据恢复,数据更完整

所以混合持久化:子线程将前半段的rdb文件写入到aof中,简单来说就是前半段是rdb格式的数据,后半段是aof
    aof-use-rdb-preamble=yes

自我介绍
面试官你好,我叫吴昊智,今年26,从事java开发三年,经历了2家公司,过去一直担任Java主力开发,期间有小组项目管理经验,技术栈主要以Java为主,
平时会看博客和技术文档,喜欢研究一些新技术,目前处于离职阶段。

在之前的工作中参与过5个项目的开发,主要任务是负责调研分析需求,代码维护开发,分配子任务工作,平时也会积极跟踪一些线上存在的Bug。
最近一份参与的项目是觅呼购电商,我在本次项目中担任Java研发,主要负责商品详情和订单模块的功能,跟随项目发布了多次迭代,后期也参与过秒杀功能的研发。

这个电商系统,由SpringBoot+SpringCloud框架开发,用到了SpringCloud中gateway、nacos、sentinel、task、Feign、zipkin、Sleuth、组件,注册中心、配置文件采用的nacos,
gateway做网关同时负载均衡、认证授权、限流,feign做服务调用,Sleuth做链路追踪,查询出错服务,sentinel做熔断降级限流,elk做日志采集,rabbitmq做消息管理异步、削峰、解耦
数据库采用mysql5.7同时使用mycat分库分表,缓存采用redis等。目前支持微信、支付宝支付。

springboot的启动原理

互联网 南昌的一些银行办理一些房产抵押的一些
信息交换平台 和政府各部门交换数据 获取分类数据
资金监管 银行资金监管 贷款 办理房产的转移 抵押

@EnableDiscoveryClient
@EnableFeignClients(basePackages= {"com.atguigu.gmall"})

@FeignClient(value = "service-cart",fallback = CartDegradeFeignClient.class)
openfeign的日志
NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、RUL、响应状态码及执行时间
HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据

网关 路由 断言 过滤

predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.atguigu.com
#- Method=GET
#- Query=username, \d+ #要有参数名称并且是正整数才能路由

routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service 根据微服务名 动态路由

CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
AP

sentinel
流控:
1秒只能查询一次,超过就快速失败,报默认错误 qps 单机阈值1 流控模式选直接 流控效果选快速失败
流控模式关联 B惹事把A关了 即设置成B一秒只能访问一次,超过把A关闭
流控模式直接 流控效果预热 单机阈值设为10 预热时长5秒 (即5秒内只能一秒10/3=3次请求 预热嘛,用于秒杀开始)
流控模式直接 流控效果排队等待 超时时间20000

熔断降级:
平均响应时间(秒级) rt
异常数(秒级)
异常比例(分钟级)

热点key限流:
配置指定参数的指定值的限流阈值
指定url的限流
自定义限流兜底逻辑方法,@SentinelResource(value = "customerBlockHandler",blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")

@SentinelResource主管配置出错,运行出错该走异常走异常

sentinel重启规则消失,所以sentinel整合nacos,持久化规则。
sentinel的配置下边加个datasource,指定nacos的dataid、groupid、地址

熔断 防止服务雪崩 返回虚拟mock值 因为服务与服务之间调用太多

限流 防止攻击 高并发 一秒一次请求

{
// 声明对象
Map<String, Object> result = new HashMap<>();
// Supplier T get();
// 使用异步编排!
CompletableFuture skuInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
// 获取到的数据是skuInfo + skuImageList
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
// map 中 key 对应的谁? Thymeleaf 获取数据的时候 $

result.put("skuInfo",skuInfo);
// 返回数据
return skuInfo;
},threadPoolExecutor);

    //  Consumer  void accept(T t);
    CompletableFuture<Void> categoryViewCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync((skuInfo -> {
        //  获取分类数据
        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        result.put("categoryView", categoryView);
    }),threadPoolExecutor);

    CompletableFuture<Void> spuSaleAttrCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync((skuInfo -> {
        //  获取销售属性+销售属性值
        List<SpuSaleAttr> spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());
        result.put("spuSaleAttrList", spuSaleAttrListCheckBySku);
    }),threadPoolExecutor);

    CompletableFuture<Void> mapCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync((skuInfo -> {

        //  查询销售属性值Id 与skuId 组合的map
        Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());
        //  将这个map 转换为页面需要的Json 对象
        String valueJson = JSON.toJSONString(skuValueIdsMap);
        result.put("valuesSkuJson", valueJson);
    }),threadPoolExecutor);

    CompletableFuture<Void> priceCompletableFuture = CompletableFuture.runAsync(() -> {
        //  获取价格
        BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
        //  保存数据
        result.put("price", skuPrice);
    },threadPoolExecutor);

    //  调用热度排名方法
    CompletableFuture<Void> hotScoreCompletableFuture = CompletableFuture.runAsync(() -> {
        listFeignClient.incrHotScore(skuId);
    },threadPoolExecutor);

    //  使用多任务进行组合
    CompletableFuture.allOf(
            skuInfoCompletableFuture,
            categoryViewCompletableFuture,
            spuSaleAttrCompletableFuture,
            mapCompletableFuture,
            priceCompletableFuture,
            hotScoreCompletableFuture
            ).join();

    return result;
}

死信
消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。
如何设置TTL:
我们创建一个队列queue.temp,在Arguments 中添加x-message-ttl 为5000 (单位是毫秒),那所在压在这个队列的消息在5秒后会消失。

2、商品详情模块
2.1 业务参考话术:
商品详情页,就是以购物者的角度展现一个sku的详情信息。这个页面不同于传统的页面,使用者并不是管理员,需要对信息进行查删改查,取而代之的是点击购买、放入购物车、切换颜色等等。另外一个特点就是该页面的高访问量,虽然只是一个查询操作,
但是由于频繁的访问所以我们必须对其性能进行最大程度的优化。所以我们页面数据放入Redis缓存来提高性能,为了防止缓存击穿,我们采用了分布式锁。由于商品详情展示的数据需要要调用多个查询接口,为了提高响应速度,
我们采用线程异步编排的方式进行接口调用。
2.2 模块相关问题:
Redis是这块的重点之一,有关reids的高频问题,详见高频面试题,这里只说项目相关redis常见问题
1)Redis在你们项目中是怎么用的?
商品详情中的数据放入缓存,价格是实时获取的;
单点登录系统中也用到了redis。因为我们是微服务系统,把用户信息存到redis中便于多系统之间获取共用数据;
我们项目中同时也将购物车的信息设计存储在redis中,用户未登录采用UUID作为Key,value是购物车对象;用户登录之后将商品添加到购物车后存储到redis中,key是用户id,value是购物车对象;
因为针对评论这块,我们需要一个商品对应多个用户评论,并且按照时间顺序显示评论,为了提高查询效率,因此我们选择了redis的list类型将商品评论放在缓存中;
在统计模块中,我们有个功能是做商品销售的排行榜,因此选择redis的zset结构来实现;
2)缓存击穿解决(分布式锁)
缓存击穿是指对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:如果这个key在大量请求同时进来之前正好失效,那么所有对这个key的数据查询都落到db,
我们称为缓存击穿。
使用分布式锁,采用redis的KEY过期时间实现

订单有效期是多久,怎么取消订单?
订单有效期为24小时,我们采用rabbitmq的延时队列,在生成订单时,发送延时消息,设置消息24小时后触发,然后监听到这个消息后,进行订单的取消。
2)怎么防止订单重复提交?
在进入结算页面是我们生产了一个流水号,然后保存到结算页面的隐藏元素中一份,Redis中存一份,每次用户提交订单时都检查reids中流水号与页面提交的是否相符,如果相等可以提交,当订单保存以后把后台的流水号删除掉。
那么第二次用户用同一个页面提交的话流水号就会匹配失败,无法重复保存订单。

订单超卖问题怎么解决的?(库存只剩1件商品,多个用户同时下单)
我们采用的是下订单不减库存,只验证库存,在支付成功后再去扣减库存,如果库存扣减失败,通知后台进行补货,如果这个商品不能补货,人工客户介入,和买家进行沟通,给予退款或相应补偿。
如果想实现不超卖,就得在下单时进行库存锁定,(可以使用数据库锁方式)然后减库存操作。

0

评论区