LLLLLuuusa的个人博客
type: channel
link: https://www.llllluuusa.top/
description: Your description here.
items: 连接超时导致消息堆积
items: specify规范驱动
items: Vue面试
items: TypeScript
items: java面试
items: 示例文章
items: seata
items: securityOuth2
items: 多级缓存
items: 任务调度

连接超时导致消息堆积
type: item
date: 2025-11-18T04:45:51+08:00
description: 问题背景线上生产环境客户反馈,客户在我们的sass系统中点击"同步tiktokshop"时,同步的数据与tiktopshop数据不一致问题描述上午收到阿里云报警,线上MQ某台机器消息堆积严重,其余机器均为正常问题排查初步怀疑sql慢查询问题由于问题主要出现在Proxy同步系统(一个专用于同步的服务),在RDS MySQL的SQL洞察和会话管理模块进行针对性排查,确实有发现慢sql(处理时间0.9秒,高于标准0.5秒),但分析认为不至于产生消息堆积问题尝试在sql查询条件上增加更多索引条件,使sql查询速度降到(0.2s)修复业务逻辑查询过慢问题慢查询主要是因为同步是根据类别分批同步,每次同步都会进行一个总表的关联查询,当同步任务过多时,导致mysql的cpu过载,虽然在阿里云监控上未发现mysql的cpu有峰值,但为避免后续出现问题,这里也同步做修复对查询的总表业务新增一个缓存,键值为同步任务Id,且在开始分发同步任务之前,先查询总表,构建缓存,避免所有的同步任务同时间查询缓存没查到导致走mysql问题问题自测通过以上方法,在测试环境进行压力测试,发现mq依然出现消息堆积问题,进一步排查在同时提醒下,使用jstack分析java线程,发现某一线程一张处于wait状态,导致mq消息堆积 进一步跟踪发现,该进程尝试http请求外网接口,连接一直处于超时配置添加超时时间,解决该问题

specify规范驱动
type: item
date: 2025-11-18T04:45:51+08:00
description: Spec Kit 规范驱动工具Spec Kit 是 GitHub 提出的 规格驱动开发（Specification-driven Development） 工具包，用于通过“规格 → 计划 → 任务 → 实现”的流程，让软件开发更加结构化、可追踪、可协作。 git官网:https://github.com/github/spec-kit 第三方汉化:https://github.com/888888888881/spec-kit-chinese 安装使用uv命令进行安装 shell uv tool install specify-cli --from git+https://github.com/github/spec-kit.git安装成功后使用命令检查是否安装 shell specify使用在终端上使用命令初始化项目,构建规范文档,并按照构建过程选择自己的ide shell specify init . 在ai终端上依次使用 |命令|作用| |-|-| |specify constitution 原则 |给ai定义开发原则,如变量命名规范| |specify specify 用户场景 |为当前项目定义用户场景,避免ai过度设计| |specify plan 实施计划 |为ai定义实施计划| |specify task |生成任务| |specify implement |执行任务|评价 优点: 相较于市面上一些常规的规范驱动工具,如OpenSpec,SpecifyKit定义的规范更加详细,更加注重产品价值和用户场景 缺点: 由于SpecifyKit定义的规范驱动过于庞大,导致一次任务消耗过量的token,性价比不高,且由于目前ai模型上下文都有限制,这种工具很容易导致ai失忆,即使常规ide有上下文压缩,该工具也有保存记录,但避免ai发散的会好解决办法还是避免超出上下文 不适用与多人协作团队开发,每个人关注的业务和模块不同,不能共用同一specify,会导致每个人的specify定义的用户场景不同,从而导致产品混乱 摒弃了cursor等ide自带的优点,cursor最大的优点在于其可以随时回退到指定chat去修改需求而不影响整体代码,但是SpecifyKit问题在于需求在最开始的时候进行定义,即使后期可以修改需求,但是所有的任务计划都会因此改变,从而影响流程(比如当前已执行到阶段3,但是需求又修改了,那么任务计划就会因为需求改变而改变,从而导致流程中断) 使用如"specify plan 写一份总分总格式的日记",agent可能不会执行specify plan,而是直接生成日记

Vue面试
type: item
date: 2025-10-22T08:42:05+08:00
description: Vue基础v-if和v-for的执行顺序 vue2先执行v-if,再执行v-for vue3先执行v-for,再执行v-if v-show和v-if的区别 v-if再渲染的时候,根据条件判断是否渲染对应的dom,因此其渲染快,更新慢,适用于更新频次不高的场景 v-show在对应的dom上添加display:条件来判断是否渲染,因此其渲染慢,更新快,使用与更新频次较高的场景 vue3的ref和react的异同/vue如何实现数据绑定的vue通过ref/react来实现数据绑定,它们都是通过defineProps来实现的 + ref可以响应式更新任何对象或数值或字符串 + react只能绑定js对象vue的生命周期| Vue2 生命周期 | Vue3 对应生命周期（组合式 API） | 执行阶段说明 | | --------------- | ---------------------------- | ----------- | | — | setup | 所有生命周期函数执行前 | | beforeCreate | beforeCreate（建议使用 setup） | Vue 实例初始化之前 | | created | created（建议使用 setup） | Vue 实例初始化之后 | | beforeMount | beforeMount | 组件挂载到页面前 | | mounted | mounted | 组件挂载到页面后 | | beforeUpdate | beforeUpdate | 数据更新前 | | updated | updated | 数据更新后 | | beforeDestroy | beforeUnmount（Vue3 改名） | 组件卸载前 | | destroyed | unmounted（Vue3 改名） | 组件卸载后 |vue的父子组件通信方法 子组件通过props获取父组件参数 ```vue vue export default { props: { message: { type: String, required: true } } } + 父组件通过$emit获取子组件参数vue export default { methods: { sendData() { this.$emit('update-message', '子组件传来的数据') } } } vue import ChildComponent from './ChildComponent.vue' export default { components: { ChildComponent }, methods: { handleMessage(value) { console.log(value) } } } ```vue2和vue3的处理虚拟dom区别 vue2通过diff算法来计算哪些dom需要更新 由于两个虚拟dom只是更换了顺序,vue2会重新所有都便利更新一遍,浪费效率,因此vue3采用最长公共子序列+diff算法来避免重复计算问题 options API和Composition API的区别 options API：vue2的传统api使用方式,结构简单,易于理解,但是代码过长时不易阅读,需要组件化处理 Composition API：vue3的api使用方式，灵活使用,代码简单,但是学习成本高,不易记 vue如何做权限控制vue可以通过守护路由的方式实现权限控制,当页面在跳转前后可自定义函数,做权限判断跳转对应页面 ```js Vue.use(Router)const router = new Router({ routes: [ { path: '/login', component: Login } ] })// 全局前置守卫：判断是否登录 router.beforeEach((to, from, next) => { // to:来自哪个页面 // from:去哪个页面 // next():放行 next() })// 全局后置守卫：页面切换后执行 router.afterEach((to, from) => {})export default router ```vuex是什么vuex是vue的一个状态管理工具,可以实现vue的数据缓存 使用vuex需要配合localstore和computed来使用vuex由5个核心参数构成： + store:存储vue参数,本身只是临时存储,需要配合localstore来使用 + getter:获取store的参数 + mutations:设置store的参数 + actions:通常配合new Promise来异步执行mutations的方法,使用场景:通过aj…

TypeScript
type: item
date: 2025-10-22T08:36:49+08:00
description: 类型推断ts的对象是强类型,js的对象是弱类型 + 强类型:不符合规范的赋值会报错 + 弱类型：赋值的时候可以随时修改对象类型ts会根据输入的数据自动判断类型 typescript let a = 'aaa' a = 10 //会报错类型注解可以在赋值的时候规定类型 typescript let a:string = 'aaa' let b:string类型断言ts在执行复制操作时,会判断该操作是否可以执行,如果可能不会执行成功,则不允许对结果对象操作 ```typescript let arr = [1,2,3] let result = arr.find(item => {item > 2}) result * 5 //会报错,认为result对象可能为空,不允许操作resultresult = arr.find(item => {item > 2}) as number result * 5 //断言result必然返回值 ```基本类型和联合类型typescript // 基本类型：string、number、boolean、null、undefined let str:string let num:number let bo:boolean let nu:null //不常用,通常用联合替代 let un:undefined //不常用,通常用联合替代 // 联合类型：可以指定对象有哪些类型或者范围 let str2:string|undefined = undefined //指定对象可能是string或者undefind let num2:1|2|3 = 5 //指定对象范围只能是1|2|3,赋值5会报错数组ts的数组有3种写法 typescript //不推荐,类型可以随意赋值 let arr1 = [1,'a',true] //推荐 let arr2:number[] = [1,2,3] //另类写法 let arr3:Array<number> = [1,2,3]元组ts的元组是一种特殊集合类型,限定了集合的存储个数和类型 typescript let t1:[number,string,boolean] = [1,'a',3] //会报错,第三个类型要求是boolean let t2:[number1,number2] = [1] //会报错,少了第二种类型 let t3:[number1,number2?] = [1] //不会报错,使用？代表该值可选枚举ts提供一种枚举类型 typescript enum myEnum{ A, B, C } console.log(myEnum.A) console.log(myEnum[0])函数typescript //第一个参数必填,第二个参数有默认值,第三个参数选填,第四个参数选填 function Fn(a:number,b = true,c?:string,...rest:number[]):void{ return 1 //会报错,指定了返回值是void } Fn('1') //会报错,指定了传参是number Fn(20,true,'aa',1,2,3,4) //正确用法接口接口定义了一个对象的使用规范 ```typescript interface Obj{ name:string, age,number }let obj:Obj = { name:'a', age:10 } ```类型```typescript let a:string|number //每次定义对象都要写一次很长的类型,过于麻烦type MyType = string|number let b:MyType //定义一次type,之后直接使用 ```泛型ts支持泛型,定义方法的时候先不定义其类型,使用的时候再定义 ```typescript function Fn(a:T,b:T):T[]{ return [a,b] } Fn(1,'a') //会报错,当指定泛型后,所有的T都会变为指定类型 Fn(1,2) //正确用法```函数重载js不支持函数重载,但是ts支持```typescript function hello(): string { return "hello" }function hello(value: string | number): string { if (typeof value === 'string') { return hello,我的名字是${value} } else if (typeof value === 'number') { return hello,我的年龄是${value} }else { return 'hello' } }hel…

java面试
type: item
date: 2025-10-22T08:07:14+08:00
description: Java 基础Java 面向对象有哪些特征封装、继承、多态、(抽象)什么是浅拷贝什么是深拷贝11 浅拷贝指对内存地址的拷贝,如= 深拷贝指对对象属性进行拷贝,如 beanUtilhashcode、==和 equals 的区别hashcode 求值的散列表值,相同值的哈希值一定相等,不同值的哈希值可能相等 ==是比较内存地址 equals 是比较对象,默认不重写的情况下,和==无区别,重写 equals 一般是通过==比较对象里的值是否相等,且重写也要重写 hashcode,否则在将对象存入 hashmap 这样的表时,hashcode 会取内存地址来求哈希值,进而导致不同对象可能存同一 hash 索引位置hashcode 是求键的位置之和,求哈希码,==是比值,equals 是比对象equals 相等/不等,hashcode 和==一定相等/不等 ==相等/不等,hashcode 和 equals 不一定相等/不等 hashcode 相等/不等,equals 和==不一定相等/不等java 代理模式有哪些 静态代理:直接在类里写函数 动态代理:基础接口,实现接口的函数 CGLIB 动态代理:导入 cglib 依赖,创建代理类和工厂类,通过反射来代理 jdk 动态代理和 CGlib 动态代理的取别 只有有接口的类才能使用 jdk 动态代理,它会使用代理类来操作 CGLIB 是没有接口的类会使用这种方法代理,spring 会将类作为父类来生成对于的子类,由子类重写父类方法从而增强补充父类方法.但是如果类被 final 修饰则无法使用 CGLIB 动态代理 理论上,CGLIB 生成代理类慢,调用快,jdk 动态代理生成快,调用慢 异常处理有那种方式 throw:抛出异常,catch(Exception e){throw new Exception("XXX");} throws:声明异常,public int xx(int x, int y) throws xxException {…}； try,cath,finally:捕获异常:try..cath..finally 重写和重载的区别 重载:同一类中,相同的方法不同的参数 重写,子类修改父类的方法 String、StringBuffter、StringBuilder 的区别和使用场景 String 是一个字符串类,其创建的值被放入字符串池,是不可变的,对象的值改变是因为改变了指针 StringBuffter 和 StringBuilder 其创建的值都是可变的,其原理是创建长度固定的数组,向数组里插入字符,数组满了再创建更大的数组,再插入 StringBuffter 使用了 synchronized 保证线程安全,但效率低 一个类如何避免被继承使用 final 修饰的类不能被继承一个类的构造方法被 private 修饰,如何获取这个类的对象将这个类用 static 修饰通过反射暴力破解讲讲你对反射的理解/为什么使用反射反射是获取类对象的一种方法,这种方法提出了一种叫反射机制的概念,其符合开闭原则,即不改变源代码的情况下对原有功能进行拓展,可以通过反射机制而不改变源码来读取操作任意类.反射通常使用在框架的使用上,在 bean 初始化时,其通过反射获取 bean 对象并存储在集合中.在 RPC 里,用反射调用远程传递过来的类对象并进行操作讲讲你对 static 的理解static 可以用来修饰类、函数或变量,让其具有类基本的属性,在类的生命周期中,如果一个变量被 static 修饰,那么其会在初始化阶段被赋予默认值.且如果一个变量是类变量,那么该类下的成员函数和成员变量都可共享使用该类变量自定义异常如何使用继承 RuntimeException,使用 throw new 自定义异常();抛出即可RuntimeException 和 Exception 的区别RuntimeException 是 Exception 的子类,代表运行时出现的异常Exception 的子类包括: RuntimeException:运行时的异常 IOException:读写时的异常 布尔类型可以和整数类型比较吗使用==比较不可以,只有 c++可以 使用 equals 比较指针的话,可以调用完的方法如何被回收方法存放在栈区,调用完成后出栈回收父类有构造函数,子类如何继承若父类构造函数无参,子类直接继承即可,默认会添加上 super(); 若父类构造函数有参,子类继承时需要加入 super(参数);讲讲你对泛型的理解/为什么要使用泛型由于集合存储时不知道对象的类型,会统一以 Object 类型存储,这会导致一些类型安全问题.因此,jdk1.5 后引用了一种泛型的设计,可以指定集合的存储类型,但是其只在编译时起作…

示例文章
type: item
date: 2025-07-19T07:24:20+08:00
description: 示例文章这是一个示例文章，展示了如何使用 Typecho Markdown Sync Tool。功能特性 自动同步 Markdown 文件到 Typecho 支持图片自动上传到七牛云 智能检测文件变化 代码示例```python这是一个 Python 代码示例def hello_world(): print("Hello, World!") return True ```图片示例列表 项目 1 项目 2 项目 3 表格| 列1 | 列2 | 列3 | |-----|-----|-----| | 数据1 | 数据2 | 数据3 | | 数据4 | 数据5 | 数据6 |引用这是一个引用示例可以包含多行内容链接访问项目主页本文由 Typecho Markdown Sync Tool 自动生成

seata
type: item
date: 2025-07-18T21:37:08+08:00
description: 微服务事务理论分布式系统无法完成三个指标,即CAP定理 Consistency(一致性):各服务之间数据一致 Availability(可用性):访问服务必须得到响应 Partition Tolerance(分区容错性):各服务之间异步执行,互不影响 事务问题在微服务中,一个服务出现问题,其关联的服务仍然会继续运行,这就容易出现事务问题出现这种原因是分布式不可避免的CAP定理,即分布式服务中(P),一旦A服务宕机,B服务为保持可用性(A),不会也跟着宕机,那么数据(C)就不一致,产生事务问题解决方法 AP模式:各服务在运行过程中,一旦出错则其关联服务执行弥补措施,即同时保证可用性(A)和分布式(P) 例:创建订单和扣减余额正常运行,但是扣减库存出现问题,那么就删除订单并添加余额 CP模式:各服务在运行过程中,等待其关联服务运行完成后再一起提交,即同时保证一致性(C)和分布式(P) 例:创建订单和扣减余额运行完成后等待扣减库存,扣减库存出现问题,则不提交事务无论是AP还是CP,都需要一个中间商检测各服务的健康状况,从而判断执行什么操作,因此我们需要一个事务协调者,即Seata认识Seata介绍Seata是一个微服务/分布式的事务管理框架,其主要有3各部分组成: TC-事务协调者:维护全局事务状态,协调全局事务提交或回滚 TM-事务管理器:定义全局事务范围、定义全局事务、提交或回滚全局事务 RM-资源管理器:管理分支事务资源,与TC交谈以注册事务和报告事务状态,并驱动分支事务提交或回滚 部署TC服务1.下载TC服务首先我们要下载seata-server包，地址在http://seata.io/zh-cn/blog/download.html2.修改本地配置```properties registry { # tc服务的注册中心类，这里选择nacos，也可以是eureka、zookeeper等 type = "nacos"nacos { # seata tc 服务注册到 nacos的服务名称，可以自定义 application = "seata-tc-server" serverAddr = "127.0.0.1:8848" group = "DEFAULT_GROUP" namespace = "" cluster = "SH" username = "nacos" password = "nacos" } }config { # 读取tc服务端的配置文件的方式，这里是从nacos配置中心读取，这样如果tc是集群，可以共享配置 type = "nacos" # 配置nacos地址等信息 nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" dataId = "seataServer.properties" } } ```3.nacos添加配置```properties数据存储方式，db代表数据库store.mode=db store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user=root store.db.password=123 store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000事务、日志等配置server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTime…

securityOuth2
type: item
date: 2025-07-18T21:37:08+08:00
description: 无论是密码登录还是第三方微信登录,我们都需要向前端返回一个token,这个过程就叫认证当用户向服务器发起请求时,会对token是否有效进行验证

多级缓存
type: item
date: 2025-07-18T21:37:08+08:00
description: 多级缓存概述多级缓存是一种设计思路,充分利用请求的每个环境进行缓存,减少Tomcat的压力 一级缓存:浏览器客户端缓存,主要由cookie或LocalStroage 二级缓存:Nginx本地缓存 三级缓存:Redis缓存,主要由Nginx用Lua发起向Redis的询问 四级缓存:JVM缓存,进程缓存 五级缓存:本地数据库 JVM缓存JVM是一种基于本地进程的缓存,相较于Redis其使用本地缓存,没有网络开销(Redis可能使用其他服务器)速度更快,但是不能数据共享.常见的JVM缓存就是以静态变量的形式进行缓存,线程未结束,变量就不会被释放.Spring官方推荐使用Caffeine,其内部就是使用静态Map创建Caffeinejava //创建构造器 Cache<String,String> cache = Caffeine.newBuilder().build();创建构造器时可添加其他功能 initialCapacity(long):存储初始化大小 maximumSize(long):存储最大大小 expireAfterWrite(Duration.ofSeconds(long)):存储存long秒后释放 一般在Config里创建Caffeinejava @Configuration public class CaffeineConfig { @Bean public Cache<String,String> XXCache(){ Cache<String,String> cache = Caffeine.newBuilder() .initialCapacity(1) .maximumSize(1) .expireAfterWrite(Duration.ofSeconds(10)) .build(); return cache; } }使用Caffeinejava //存数据 cache.put("键","值");java //取数据,若无数据返回Null cache.getIfPresent("键"); //取数据,若无数据执行方法 cache.get("键",key->{ return ""; });Nginx使用RedisNginx配置修改使用前需要导入OpenResty```nginxuser nobody;worker_processes 1; error_log logs/error.log;events { worker_connections 1024; }http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #导入lua模块 lua_package_path "user/local/openrestry/lualib/?.lua;;"; #导入c模块 lua_package_cpath "user/local/openrestry/lualib/?.so;;"; #设置集群 upstream tomcat-cluster{ server tomcatIp1:端口; server tomcatIp2:端口; } server { #监听端口和域名 listen 8081; server_name localhost; #映射位置 location / { root html; index index.html index.htm; } location /item{ proxy_pass http://tomcat-cluster; } #设置某接口监听由lua脚本控制 location ~ /api/item/(\d+){ # 默认响应类型 default_type application/json; # 响应结果由lua/item.lua文件决定 content_by_lua_file lua/item.lua } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } ```导入common.lua```lua -- 导入redis local redis = require('resty.redis') -- 初始化redis local red = redis:new() red:set_timeouts(1000,1000,1000)-- 关闭redis连接的工具方法，其实是放入连接池 local function close_redis(red) local pool_max_idle_time = 10000 -- 连接的空闲时间，单位是毫秒 lo…

任务调度
type: item
date: 2025-07-18T21:37:08+08:00
description: 任务调度技术任务调度是特定业务场景下的定时任务处理，在分布式架构下，分布式调度框架的设计显得尤为重要。这里简要介绍了两种常用的分布式调度框架Quartz和xxl-job的特性、基本架构和参数配置，以加深了解。Spring Schedulespring自带一种基于注解的任务调度在application中添加@EnableScheduling注解```java @SpringBootApplication @EnableScheduling //开启定时任务功能 public class SpringbootTaskApplication {public static void main(String[] args) { SpringApplication.run(SpringbootTaskApplication.class, args); } }```在定时任务类上添加规则和方法,使用cron表达式```java @Slf4j @Component public class MyJob {//@Scheduled(cron = "0/${mytask.abc:15} * * * * ?") 在application.yml配置中添加参数可以通过配置方式指定规则 mytask.abc = 10 ; @Scheduled(cron = "0,15,30,45 * * * * ?") public void hello(){ log.info("say hello"); } } ```Quartz介绍这是一个作业调度的开源项目,一般可用和spring结合使用,在实际开发中可用用 Quartz做一个定时任务,比如每小时执行一次,或每个月上午10点执行一次,每个月下午5点执行一次安装```xml org.springframework.boot spring-boot-starter-quartz ```使用 编写任务信息 编写任务处理规则 配置调度中心(启动时开启任务调度) 首先,先编写job类```java public class Job1 implements Job { private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");private void before() { System.out.println("任务1：开始执行-" + dateFormat.format(new Date())); } @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { before(); System.out.println("任务1：业务逻辑。。。"); after(); } private void after() { System.out.println("任务1：执行结束"); System.out.println(); } ```再通过工具类配置规则```java @Component public class QuartzSchedulerManager { @Autowired private Scheduler scheduler;// 开始执行定时器 public void startJob() throws SchedulerException { startJob1(scheduler); startJob2(scheduler); scheduler.start(); } // 获取Job信息 public String getJobInfo(String name, String group) throws SchedulerException { TriggerKey triggerKey = new TriggerKey(name, group); CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); return String.format("time:%s,state:%s", cronTrigger.getCronExpression(), scheduler.getTriggerState(triggerKey).name()); } // 修改某个任务的执行时间 public boolean modifyJob(String name, String group, String time) throws SchedulerException { Date date = n…