0%

悲观锁和乐观锁

悲观锁

悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据是都会上锁,如数据库中的行锁、表锁、读锁、写锁等操作都会上锁,java中的synchronized和ReentrantLock等独占锁就是悲观锁的思想

乐观锁

乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候回判断一下在此期间别人有没有更新这条数据,可以使用版本号机制和CAS算法实现。乐观锁适用于读多写少的情况,可以提升吞吐量,如java的atomic包下的原子变量类就是使用了CAS来实现的乐观锁

阅读全文 »

elasticsearch过程分析

索引文档

当用户向一个节点提交了一个创建文档的请求,节点会计算文档应该加入到哪个分片中,每个节点都存储了每个分片存储在哪个节点的信息,因此协调节点会将请求发送到对应的节点,当主分片完成索引,会将请求发送到所有的副本,保持每个分片的数据都是最新的

每次写入新文档时,都会先写到内存中,然后将这一操作写入translog文件中,此时如果执行搜索,是无法索引到对应文档的。elasticsearch会每隔一段时间(index.translog.sync_interval)进行一次刷新操作,此时内存中的文档会被写入一个文件系统缓存中,并构成一个segment,此时segment内的文档可以被搜索到,但是还未写入磁盘,如果发生断电,这些文档仍可能丢失。

随着越来越多的文档写入,将会生成很多的segment,而translog也在不断的增大,当经过一段时间(index.refresh_interval)则执行fsync操作,将所有文件系统缓存中的segment写入磁盘,而translog会被删除(如果fsync还没有执行突然断电了,此时的translog是保留的,elasticsearch可以根据translog来进行数据的还原和恢复)

segment过多也会导致查询性能的下降,所以elasticsearch会自动的进行segment的合并

数据一致性

数据一致性的问题其实是对分布式事务的另一种解释,分布式事务就是希望在多级环境下可以像单机一样实现强一致性,但是这种强一致性付出的代价很大,而在很多场景下,其实是不需要做到强一致性的,只要最终一致就行。

这里来了解一下数据一致性的基础理论CAP和BASE

CAP

CAP表示三个单词,Consistency、Availability、Partition-Tolerance,这里就三个单词分别进行说明

  • Consistency 所有的节点在同一时间读到同样的数据,也就是数据的一致性,当数据写入成功后,所有的节点会同时看到这个新的数据
  • Availability 保证无论成功还是失败,每个请求都能够收到一个反馈,也就是数据的可用性
  • Partition-Tolerance 即便系统中有部分问题或者有消息的丢失,但是系统仍能够继续运行,也就是分区容忍性,在系统的一部分出现问题时,系统仍能继续工作

在系统中,我们不能够同时满足上述三项,只能满足两项

  • CA 放弃分区容忍性,加强一致性和可用性,也就是传统的单机架构
  • AP 放弃一致性,追求分区容忍性以及可用性
  • CP 放弃可用性,追求一致性和分区容忍性
阅读全文 »

分布式事务

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上。

分布式事务模型和规范

The Open Group提出了一个分布式事务规范-XA,以及分布式事务处理模型-X/OpenDTP模型

X/OpenDTP模型

分布式事务DTP模型

该模型中定义了三个组件,即Application Program,Resource Manager和Transaction Manager

  • Application Program AP,即应用程序,可以理解为使用DTP模型的程序,定义了事务边界,并定义了构成该事务的应用程序的特定操作

  • Resource Manager RM,即资源管理器,可以理解为DBMS系统,应用程序通过资源管理器对资源进行控制,资源管理器能够提供单数据库的事务能力,通过XA接口将本数据库的提交、回滚等能力提供给事务管理器调用,以帮助事务管理器实现分布式的事务管理,资源必须实现XA定义的接口,资源管理器提供了存储共享资源的支持

    XA接口是DTP模型定义的接口,用于向事务管理器提供该资源管理器的提交、回滚能力

  • Transaction Manager TM,即事务管理器,负责协调和管理事务,提供给AP应用程序编程接口并管理资源管理器。事务管理器向事务指定标识,监视它们的的进程,并负责处理事务的完成和失败。事务分支标识(XID)由TM指定,以标识一个RM内的全局事务和特定分支,它是TM中日志与RM中日志之间的相关标记。两阶段提交或者回滚需要XID,以便在系统启动时执行再同步操作

    事务管理器还管理着所有的资源管理器,通过它们提供的XA接口来统一调度这些资源管理器,以实现分布式事务

    DTP只是一套实现分布式事务的规范,并没有定义具体如何实现分布式事务,TM可以采用2PC、3PC、Paxos等协议实现分布式事务。

阅读全文 »

maven转为web项目

只需要三步,就可以将一个普通的maven项目变为一个web项目

  • 在main目录下,添加webapp目录
  • 在webapp目录下,添加WEB-INF目录
  • 在WEB-INF目录下,添加web.xml文件

ELKF

ELKF是指ELK+Filebeat,流程为Filebeat->Logstash->(Elasticsearch<->Kibana),由应用程序产生日志,Filebeat进行处理,将日志输出到Logstash中

  • Elasticsearch: 能对大容量的数据进行接近实时的存储,搜索和分析操作。
  • Logstash: 数据收集引擎,它支持动态的的从各种数据源获取数据,并对数据进行过滤,分析,丰富,统一格式等操作,然后存储到用户指定的位置。
  • Kibana: 数据分析与可视化平台,对Elasticsearch存储的数据进行可视化分析,通过表格的形式展现出来。
  • Filebeat: 轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装Filebeat,并指定目录与日志格式,Filebeat就能快速收集数据,并发送给logstash进行解析,或是直接发给Elasticsearch存储

修改配置filebeat.yml:

1
2
3
4
5
6
filebeat.inputs:
type: log
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /var/log/*.log
- /opt/ywxt/log/*.log #修改扫描输入路径(可以配置多个路径)

分布式session

为了解决单点问题,现在很多的大型网站都是用的是分布式集群来进行项目搭建的,但是分布式集群会出现很多的分布式问题,这篇文章说的是分布式session问题

分布式session是指的当一个带有session标识的HTTP请求到了web服务器后,需要在http请求的处理过程中找到对应的会话数据,但是有一个问题就是,会话数据是需要保存在单机上的,如果每次访问的服务器不同的话,如何获取到session呢,这里给大家说几种解决的思路

阅读全文 »

Hystrix执行流程

  • 每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中
  • 执行execute()/queue做同步或异步调用
  • 判断熔断器(circuit-breaker)是否打开,如果打开则执行fallback进行降级策略,如果关闭继续执行
  • 判断线程池/队列/信号量是否跑满,如果跑满执行fallback进行降级策略,否则继续后续步骤
  • 调用HystrixCommand的run方法,运行依赖逻辑,如果逻辑调用超时,则执行fallback逻辑
  • 判断逻辑是否调用成功,如果调用成功,则返回调用结果;如果调用出错,则执行fallback逻辑
  • 计算熔断器状态,所有的运行状态(成功、失败、拒绝、超时)上报给熔断器,用于统计从而判断熔断器状态

有四种情况将触发fallback逻辑

  • run()方法抛出非HystrixBadRequestException异常
  • run()方法调用超时
  • 熔断器开启拦截调用
  • 线程池/队列/信号量是否跑满

性能优化

设置操作系统禁止swap

SpringApplicationRunListener

在看源码的时候经常看到

1
2
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

这个SpringApplicationRunListeners是用来干嘛的呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public interface SpringApplicationRunListener {

/**
* run方法首次启动的时候立刻调用,可以进行早期的初始化操作
*/
void starting();

/**
* 环境被准备好的时候被调用,但是在ApplicationContext创建之前
*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}

/**
* ApplicationContext已经被创建且准备好了,但是源还没被加载
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}

/**
* ApplicationContext已经被加载但是在刷新之前
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}

/**
* @since 2.0.0 ApplicationContext已经被刷新且启动但是CommandLineRunner和ApplicationRunner还没有被调用
*/
default void started(ConfigurableApplicationContext context) {
}

/**
* @since 2.0.0 run方法完成之前被调用,ApplicationContext已经被刷新且启动,CommandLineRunner和ApplicationRunner已经被调用
*/
default void running(ConfigurableApplicationContext context) {
}

/**
* @since 2.0.0 运行ApplicationContext出错
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}

}