mysql 记录一次签到表分表的过程 建表 create table sign_{0,9}{ xxx } 定制策略 创建ModShardingStrategy 实现 AbstractShardingStrategy 修改mapper mapper 添加注解 @TableSharding(tableName = "activity_xxx", shardBy = "uid",strategy = "com.xxx.ModShardingStrategy") 添加拦截器 @Bean("
极客 疑难杂症之apollo value的使用 Aoollo config可以通过spring 注解@value注入,但是在使用中发现,如果在不同namespace下配置重复key的config,在注入value时会有问题,注入的value可能不是自己想要的配置 原因是apollo sdk在向spring 配置中添加该配置时,只是用到了key,而未使用namepsace,故当存在不同的namespace下相同key时,无法区分想要哪个配置,且只会保留一个,故会出现在业务使用配置中,通过@Value获取错误的情况,如果使用apollo的注解,指定namespace即不会存在该问题
linux curl 展示http status 正常使用curl访问 http 时,如果出现http status不为200时,无响应时,看不到http status,即无任何展示,如果需要查看http status 可通过下面的命令 curl -w '%{http_code}' https://baidu.com
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个请求 解决方案 暂时解决方案
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
java 20220720-记录一次生产导出问题 问题 线上导出订单时,节点未重启,但是调接口不通,原因是调用三方接口超时错误,查看gc、cpu&内存监控,发现 cpu idle 40% mem.used 3730m,容器是4g 堆大小为3g gc time 大幅增加 故问题原因是由于频繁gc导致 stw,故无法在timeout时间内处理请求 导出订单数量3w+ 临时方案 修改网关接口路由配置,导出接口路由至单独的小流量集群 模拟环境 由于sim环境数据较少,故通过模拟的方式导出,即调整代码逻辑,一直导出第一页订单,
http http之Range 介绍 http进行文件访问时,可通过header Range指定请求的位置,实现分段下载,断点续传的功能 使用 正常访问文件 curl --location --request GET 'https://pics3.baidu.com/feed/574e9258d109b3deaee28959b7a2cd8b800a4c4d.png?token=686e4f2c779c94c6c4af388ff06104d2' 访问部分字节 curl --location --request GET 'https://pics3.baidu.com/feed/574e9258d109b3deaee28959b7a2cd8b800a4c4d.png?
other 分布式id之雪花算法 为什么使用雪花算法 数据库自增id,在分表的情况下会导致重复 uuid不是自增的,对于 innodb会导致页分裂和碎片的问题 雪花算法是按时间戳递增的,分布式id生成算法 使用 雪花算法的实现非常多,各大厂都有自己的实现,本文以mybatisplus为例 添加依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-core</artifactId> <version&
git git clone时出现 info/refs not valid错误 git clone/pull时报错 info/refs not valid: is this a git repository 执行git命令时,添加用户及密码 用户名:kevin 密码:k@12345 仓库地址:http://1xx.1xx.1.1xx:80/kevin/project.git 命令格式 (注:@转成%40)
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"):一个代表任意字符串,
java sentinel介绍及原理 sentinel作用 Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 sentinel整体设计的很精巧,只需要一个sentinel-core便可以运行,它提供了诸如服务降级、黑白名单校验、QPS、线程数、系统负载、CPU负载、流控等功能,可谓是功能非常的强大。 sentinel使用 SphU.entry 手动执行限流逻辑 sentinel使用SphU或者SphO标示一个被保护的资源,比如: Entry entry = SphU.entry("HelloWorld", EntryType.
jvm之Spi 作用 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。 使用 开发接口模块 public interface PayService { String pay(); } 开发实现模块 public class AliPayService implements PayService { @Override public String pay() { return "ali"; } } 添加service 在resouces下创建目录META-INF/services 在services添加文件com.xxx.PayService,
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
linux之seletor/poll/epoll 什么是 I/O 多路复用 把标准输入、套接字等都看做 I/O 的一路,多路复用的意思,就是在任何一路 I/O 有“事件”发生的情况下,通知应用程序去处理相应的 I/O 事件。这样就可以同时监听多个 I/O 事件。 select 介绍 int select(int maxfd, fd_set *readset,
redis telnet连接redis 连接 telnet 10.85.172.48 30901 get get limit:652824:639247269906973726:1166102 set set limit:652824:639247269906973726:1166102 '["java.util.ArrayList",[{"@class":"com.wujie.limit.domain.
JVM之JIT JIT概述 即时编译是一项用来提升应用程序运行效率的技术。通常而言,代码会先被 Java 虚拟机解释执行,之后反复执行的热点代码则会被即时编译成为机器码,直接运行在底层硬件之上。 从 Java 8 开始,Java 虚拟机默认采用分层编译的方式。它将执行分为五个层次,分为为 0 层解释执行,1 层执行没有 profiling 的 C1 代码,2 层执行部分 profiling 的 C1 代码,3 层执行全部 profiling
java之优先级队列 PriorityQueue 案例 package com.kevin.container; import java.util.PriorityQueue; import java.util.Random; public class PriorityQueueCase { public static void main(String[] args) { PriorityQueue priorityQueue = new PriorityQueue(); int count = 10; for (int i
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新增非核心线程 如果新增线程失败,
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
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
险象环生之set remove注意事项 问题 public class PatternTest { public static void main(String[] args) { Set<Person> set = new HashSet<>(); Person p1 = new Person(1,"张三"); set.add(p1); Person p2 = new
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(
linux linux上下文切换 上下文切换 CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。 上下文切换的类型 中断上下文切换 线程上下文切换 用户态内核态上下文切换 进程上下文切换 用户态内核态上下文切换 进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。 从用户态到内核态的转变,需要通过系统调用来完成。比如,当我们查看文件内容时,就需要多次系统调用来完成:首先调用 open() 打开文件,
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()方法是有返回值的,它表示用来尝试获取锁,