本文收集了Linux相关的性能指标和工具。大部分资料来源于极客时间课程《Linux性能优化实战》,工具使用资料大部分来源于:linux-command。
问题背景
假设要开发一款基于地理位置的交友软件,其中最核心的功能为用户匹配其邻近的用户。为了实现这个功能,可以把每个用户的经纬度记录下来,之后,可以使用半正矢公式计算两个用户经纬度之间的距离。
可以把用户经纬度数据都存到数据库里,然后用SQL查询附近的用户:
select * from user where latitude between LAT-D and LAT+D and longitude between LNG-D and LNG+D;
如果有网络分区partition (P),系统就必须在可用性availability和一致性consistency (A and C)之间取得平衡; 否则else (E) 当系统运行在无分区情况下,系统需要在 延迟latency (L) 和 consistency (C)之间取得平衡。
PACELC定理是CAP定理的扩展。
MongoDB
MongoDB(默认配置)下是PA/EC系统,mongo读写都在主节点上,它只需在主节点上写入成功就返回(不像raft需要多数复制成功),当主节点和备节点分区时,可能会丢失数据,所以说PA,在无分区的情况下,由于读写都在主节点,肯定能读到最新写入的数据,所以是EC。另外Mongo可以配置为写入大多数节点读主节点,这样就是PC/EC系统了。
生产环境的JVM进程经常被运维报告有OOM的情况,运维的描述是,内存一直在缓慢增长,1-2天就会出现OOM的情况。因为已经严重影响到客户的使用,所以采取由运维定时监控,与客户交流,开发负责排查问题的策略。
Step1:测试环境复现问题
由于开发是没权限进入生产环境的,要高效率解决问题,必须能在测试环境复现。查看生产环境的日志,确认容器出发OOM的接口,然后使用Jmeter
在测试环境压测该接口,发生OOM的情况,问题能够复现。
Step2: 在测试环境复现
在测试环境建一个和生产环境规格一样的容器,使用Jmeter
压测,确认问题能复现。
无锁编程里最复杂,最难理解的莫过于是内存顺序。
乱序
程序不一定会按照源代码的顺序执行,这称之为乱序。乱序的必须遵循的原则是:在单线程执行下,乱序与不乱序执行的结果必须相同。所以,在单线程的环境里不需要注意乱序的问题,而到了多线程环境就需要考虑乱序。
乱序产生的原因有好几种:
- 编译器优化,在编译阶段将源码交换。
- 程序执行期间,指令流水被CPU乱序执行。
- inherent cache 的分层及刷新策略使得有时候某些写读操作的从效果上看,顺序被重排。
Release和Acquire语义
无锁编程是一项挑战,不仅因为任务本身的复杂性,还因为要深入理解这个主题是非常困难的。
我第一次接触无锁(lock-free,又称为lockless)编程是Bruce Dawson优秀而全面的白皮书《Lockless Programming Considerations》。和很多人一样,我也有机会将Bruce的建议付诸实践,在Xbox 360等平台上开发和调试无锁代码。
灰度发布又称为金丝雀(canary)发布,是一种版本更新的平滑过度方式。在微服务里,一般是更新服务时,先更新部分实例,通过配置一些用户访问新版本,新版本稳定后,再更新所有实例,把所有流量切到新版本。灰度发布也可以用来做AB测试,例如想测试一个新的算法的效果,可以把部分流量切到新算法,和旧版本效果对比。
此外,还有蓝绿发布,一般来说,蓝绿发布是有两个集群,更新时更新其中的一个集群。原集群和新集群同时保持运行一段时间,在此期间,新集群出现问题,可以立马把流量切回原集群。新集群稳定后,再下线原集群,新集群变为稳定集群。
Spring Cloud 灰度发布原理
本文主要记录docker和k8s常用的一些命令之类的,方便查阅。
容器不退出
容器在执行完CMD
或者ENTRYPOINT
之后,或者执行过程中出错,都会使容器退出。一般来说,执行出错时,我们希望进入容器调试报错原因。可以把容器的Command
改为:
/bin/bash -c "while true; do sleep 30; done"
在微服务系统中,往往需要一些手段来应对流量激增的情况。例如,弹性伸缩,在高流量时自动扩容。但弹性伸缩往往只能在无状态服务上比较容易实现,在有状态的服务,例如数据库、消息中间件、分布式缓存上,是比较难实现的。这时就需要流量控制,只接受系统能处理的流量,拒绝或排队处理不过来的流量,从而保护微服务系统的可用性。
服务在自己处理不过来时,应该拒绝其它服务的请求,保护自己,这就是限流降级。当服务发现它调用一个服务,在发现这个服务“不行”时,应该不再去请求它,从而保护这个服务,但其实也是在保护自己,因为服务“不行”时,往往响应很慢,拒绝请求它避免大量请求在自己服务内堆积,这就是熔断降级。一般而言,限流侧重于流量控制,预防系统被压垮,一般通过拒绝或者排队等流量整形手段应付暂时不能处理的流量。而熔断侧重于在发现依赖的服务“不行”时,如:每秒请求异常数超过多少,每秒请求错误率超过多少时,每秒平均耗时超过多少时,在一个时间窗口内拒绝请求该服务,在一个时间窗口之后再恢复请求,从而保护依赖的服务。当然,服务也可以自己统计自己的错误率,平均耗时等,从而熔断其它服务的调用。
虚拟线程是轻量级线程,可以减少编写、维护和调试高吞吐量并发应用程序的工作量。虚拟线程在JDK 19
中作为预览特性引入,在JDK 21
中作为正式特性引入。
在Spring中启用虚拟线程
在最新版本的Spring Framework
、Spring boot
和Apache Tomcat
中,你可以使用以下代码去自定义你的应用程序去使用虚拟线程去处理servlet请求:
@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor asyncTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}