极客

疑难杂症之apollo value的使用

Aoollo config可以通过spring 注解@value注入,但是在使用中发现,如果在不同namespace下配置重复key的config,在注入value时会有问题,注入的value可能不是自己想要的配置 原因是apollo sdk在向spring 配置中添加该配置时,只是用到了key,而未使用namepsace,故当存在不同的namespace下相同key时,无法区分想要哪个配置,且只会保留一个,故会出现在业务使用配置中,通过@Value获取错误的情况,如果使用apollo的注解,指定namespace即不会存在该问题

  • 凯文
1 min read

20220707-记录两个线上问题

线上限流问题 问题 定向发券时,部分用户发券失败,原因是service-1调用service-2时被ingateway限流(http 596 status),service-1执行fallback逻辑,抛出异常;活动发券用户1602+394+224*1左右,故批量时qps在 700左右,但是限流配置的是1k,故不应被限制 原因 inrouter的限流是单节点限流,比如配置限流策略是1000,存在17个inrouter节点,即每个节点的限流qps为58,当该节点qps超过58时,即会被限流响应598,上述问题时总qps为722,故单节点平均为 42,当某一节点倾斜量超过16时,超过的请求即会被限流,共限流29个请求 解决方案 暂时解决方案

  • 凯文
2 min read
golang

go随机数

我们在通过go获取随机数时,会发现每次获取的均一致,如下面的程序,无论执行多少次,打印的结果均是81、87、47、59 for i := 0; i < 40; i++ { println(rand.Intn(100)) } 81 87 47 59 这是因为在默认情况下,go的rand会使用相同的源来产生一个确定的伪随机数序列,即产生一个不变的数列。 由于源代码已经发布到 Go 的官方标准库中,因此任何运行此程序的计算机都会得到相同的结果。 但是,由于 Go

  • 凯文
1 min read
java

20220720-记录一次生产导出问题

问题 线上导出订单时,节点未重启,但是调接口不通,原因是调用三方接口超时错误,查看gc、cpu&内存监控,发现 cpu idle 40% mem.used 3730m,容器是4g 堆大小为3g gc time 大幅增加 故问题原因是由于频繁gc导致 stw,故无法在timeout时间内处理请求 导出订单数量3w+ 临时方案 修改网关接口路由配置,导出接口路由至单独的小流量集群 模拟环境 由于sim环境数据较少,故通过模拟的方式导出,即调整代码逻辑,一直导出第一页订单,

  • 凯文
10 min read
mysql

mysql通过MapperScan实现多数据源

MapperScan org.mybatis.spring.annotation.MapperScan 在SpringBoot中集成MyBatis,可以在mapper接口上添加@Mapper注解,将mapper注入到Spring,但是如果每一给mapper都添加@mapper注解会很麻烦,这时可以使用@MapperScan注解来扫描包。 @MapperScan注解只会扫描包中的接口,不会扫描类,所以可以在包中写Provider类。 属性 basePackages @MapperScan("com.demo.mapper"):扫描指定包中的接口 @MapperScan("com.demo..mapper"):一个代表任意字符串,

  • 凯文
2 min read
java

sentinel介绍及原理

sentinel作用 Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 sentinel整体设计的很精巧,只需要一个sentinel-core便可以运行,它提供了诸如服务降级、黑白名单校验、QPS、线程数、系统负载、CPU负载、流控等功能,可谓是功能非常的强大。 sentinel使用 SphU.entry 手动执行限流逻辑 sentinel使用SphU或者SphO标示一个被保护的资源,比如: Entry entry = SphU.entry("HelloWorld", EntryType.

  • 凯文
11 min read

Reactor网络模型

主-从Reactor网络模型 Main reactor thread 负责绑定监听端口 绑定端口后通过单线程acceptor建立tcp连接 建立tcp连接后通过epoll注策读事件 sub-reactor dispatch 一个四核 CPU服务器,我们可以设置 sub-reactor 为 4 负责监听tcp连接读写事件 当acceptor建立连接后,交由sub-reactor中的一个处理 收到tcp读事件后,交给工作线程处理任务 与单reactor线程相比(即读写事件监听线程和acceptor线程为同一线程),单reactor 线程既分发连接建立,又分发已建立连接的 I/O,有点忙不过来,在实战中的表现可能就是客户端连接成功率偏低。 threadpool 任务任务线程池 由于处理任务比较耗时,故将任务处理线程与tcp

  • 凯文
1 min read

JVM之JIT

JIT概述 即时编译是一项用来提升应用程序运行效率的技术。通常而言,代码会先被 Java 虚拟机解释执行,之后反复执行的热点代码则会被即时编译成为机器码,直接运行在底层硬件之上。 从 Java 8 开始,Java 虚拟机默认采用分层编译的方式。它将执行分为五个层次,分为为 0 层解释执行,1 层执行没有 profiling 的 C1 代码,2 层执行部分 profiling 的 C1 代码,3 层执行全部 profiling

  • 凯文
7 min read

jvm之线程池

java线程池实现继承关系 Executor==>ExecutorService-->AbstractExecutorService==>ThreadPoolExecutor Executor-接口 execute,执行某个Runnable实现类 ExecutorService-接口,较Executor提供了更多的功能 submit,执行Callable实现类,返回值通过Future返回 AbstractExecutorService-线程池抽象类 实现了submit方法 在执行submit方法前会将Callable转为FutureTask类,然后调用execute方法执行 FutureTask是Runnable的实现类 线程池基本都继承该抽象类 线程池实现类 ThreadPoolExecutor 实现了execute方法,在该方法中判断核心线程数 如果小于核心线程数,则通过addWorker新增核心线程执行该command 如果小于核心线程数,则将command添加至任务队列中 如果添加队列失败,则继续通过addWorker新增非核心线程 如果新增线程失败,

  • 凯文
3 min read

java线程池之ForkJoinPool

作用 计算机中一个任务一般是由一个线程来处理的,如果此时出现了一个非常耗时的大任务,比如对一个大的ArrayList每个元素进行+1操作,如果是普通的ThreadPoolExecutor就会出现线程池中只有一个线程正在处理这个大任务而其他线程却空闲着,这会导致CPU负载不均衡,空闲的处理器无法帮助工作。ForkJoinPool就是用来解决这种问题的,将一个大任务拆分成多个小任务后,使用fork可以将小任务分发给其他线程同时处理,使用join可以将多个线程处理的结果进行汇总;这实际上就是分治思想的并行版本 Java8中的parallelStream API就是基于ForkJoinPool实现的 ForkJoinPool的线程使用的是 Thread子类ForkJoinWorkerThread 实例 我们在提交任务时,一般不会直接继承ForkJoinTask,只要继承它的子类即可,框架提供了两种子类: RecursiveAction:用于没有返回结果的任务(类似Runnable) RecursiveTask:用于有返回结果的任务(类似Callable) public class ForJoinPollExecutor { public static

  • 凯文
2 min read

sleep、wait及park介绍

Thread.sleep(time) 该方法必须传入指定的时间,线程将进入休眠状态,通过jstack输出线程快照的话此时该线程的状态应该是TIMED_WAITING,表示休眠一段时间。 该方法会抛出InterruptedException异常,这是受检查异常,调用者必须处理。 通过sleep方法进入休眠的线程不会释放持有的锁,因此,在持有锁的时候调用该方法需要谨慎。 Object.wait() 方法 java的每个对象都隐式的继承了Object类。因此每个类都有自己的wait()方法。我们通过object.wait()方法也可以让线程进入休眠。wait()有3个重载方法: public final void wait() throws InterruptedException; public final

  • 凯文
8 min read
java

生产问题之CaffeineCache使用中的坑

问题描述 服务缓存中使用CaffeineCache时,通过CaffeineCache在数据更新并且超过refreshAfterWrite时间后首次获取缓存时,偶发性的获取不到最新的缓存 实现 public class CaffeineCacheTest { public static void main(String[] args) throws Exception { CaffeineCacheTest test = new CaffeineCacheTest(); Cache cache = test.caffeineCacheManager().getCache("test"); CacheObject cacheObject = (CacheObject) cache.get(

  • 凯文
3 min read
linux

linux上下文切换

上下文切换 CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。 上下文切换的类型 中断上下文切换 线程上下文切换 用户态内核态上下文切换 进程上下文切换 用户态内核态上下文切换 进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。 从用户态到内核态的转变,需要通过系统调用来完成。比如,当我们查看文件内容时,就需要多次系统调用来完成:首先调用 open() 打开文件,

  • 凯文
5 min read
redis

分布式锁之redisson

使用redisson的原因 比zk性能要好 比使用redis.setnx的优势 使用redis.setnx时,如果系统挂掉,会一直锁住 redisson将ttl和set 通过lua脚本原子性批量执行 实现 Lock lock = redisson.getLock("test"); lock.lock(); 源码 RLock锁API public interface RLock { //----------------------Lock接口方法----------------------- /** * 加锁 锁的有效期默认30秒 */ void lock(); /** * tryLock()方法是有返回值的,它表示用来尝试获取锁,

  • 凯文
5 min read