arthinking

ChinSyun Pang's blog


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

ZooKeeper的Watcher机制

发表于 2016-06-22   |   分类于 Java , 分布式

ZooKeeper的数据变更通知的流程

Client向Zk注册Watcher,同时将Watcher对象存储在客户端的WatchManger中,服务器端先生成WatcherEvent事件对象,序列化传输,客户端把WatcherEvent还原成WatchedEvent对象,调用Watcher中的回调函数进行处理:process(WatchedEvent event);

下面看看Watcher的注册流程:

创建ZooKeeper客户端实例的时候(getData,getChildren,exist也可以)传入Watcher,会一直保存在客户端ZKWatchManager的defaultWatcher中,作为整个ZooKeeper会话期间的默认Watcher。

客户端发送Watcher:

标记request为使用Watcher监听 --> 封装WatchRegistration对象(保存数据节点和Watcher的对应关系) --> 在ClientCnxn把WatchRegistration封装到Packet中 --> 放入发送队列等待客户端发送 --> SendThread.readResponse方法负责接收来自服务端的响应 --> finishPacket方法从Packet中取出对应的Watcher注册到ZKWatchManger中。(从WatchRegistration对象中提取出Watcher,保存在ZKWatchManger的dataWatches中,dataWatches的key是数据节点的路径。)

底层实际传输的过程中,并没有将WatchRegistration序列化到底层数组中。

服务器端处理Watcher:

FinalRequestProcessor.processRequest中判断是否需要注册Watcher --> 需要注册,则将ServerCnxn对象和数据节点路径传入getData方法。(ServerCnxn代表着一个客户端和服务器的连接,实现:NIOServerCnxn,NettyServerCnxn,并实现了process方法,可以把ServerCnxn看成Watcher对象) --> 数据节点路径和ServerCnxn最终存储在WatchManger的WatchTable和watch2Paths中。

Watcher事件触发:

WatchManager负责Watcher事件触发,并移除已经被触发的Watcher:

  1. 封装WatchedEvent
  2. 查询Watcher
  3. 调用ServerCnxn的process方法来触发

    请求头是-1,表明当前是一个通知;
    将WatchedEvent包装成WatcherEvent对象,以便于网络传输序列化;
    向客户端发送通知

客户端回调Watcher:

SendThread.readResponse()接收事件通知,如果响应头标识了XID为-1,则表明是一个通知类型的响应,处理流程:

反序列化;
处理chrootPath;
还原WatchedEvent;
回调Watcher;

Watcher特性:

一次性;
客户端串行执行;
轻量;

数据库事务使用规范

发表于 2016-06-16   |   分类于 Database

to be continue…

ZooKeeper运维相关

发表于 2016-06-16   |   分类于 Java , 分布式

集群

1、配置zoo.cfg文件,通过git管理zoo.cfg配置文件

2、在dataDir所配置的目录下创建myid文件,里面记录上当前机器编号

3、启动zk:

%ZK_HOME%/bin/zkServer.sh start

4、验证服务器:

1
2
telnet 127.0.0.1 2181
stat

单机

特殊的集群:只有一台机器的集群。

stat输出区别:
Mode: standalone/leader

伪集群模式

集群所有的机器都在一台机器上,但是还是以集群的特性来对外提供服务。配置基本一样,只不过服务器的IP都是同一个IP,但是使用不同的端口号。

zoo.cfg配置文件

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
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
# dataDir=/tmp/zookeeper
dataDir=/usr/local/zookeeper/zookeeper-3.4.8/data
# the port at which the clients will connect
clientPort=2181
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

在同一台计算机创建几个目录,分别解压zookeeper,并把上面的zoo.cfg文件替换配置文件,注意,每个配置文件的clientPort需要不一样;

然后通过telnet或者客户端连接:

./zkCli.sh –server 127.0.0.1:2181

接下来就可以通过客户端连接编程实现各种炫酷的功能啦.

从一笔交易说起,如何处理好数据的一致性问题

发表于 2016-06-10   |   分类于 Java , Database

发表于:从一笔交易说起,如何处理好数据的一致性问题

什么是分布式事务?

互联网应用中,随着系统用户数量的增多,访问压力也不断增大,数据功能相互独立的模块拆分开来,对其进行集群部署。

比如完成一笔交易,分别需要在交易模块,订单模块,用户数据模块中进行处理,分别做一些数据的更新或者入库,当三个模块都处理完毕之后,才算完成了这笔交易的事务。在这种分布式部署的系统中,需要处理的数据分布在不同的物理节点上,怎么去保证能处理完一笔交易之后的数据完整性呢,这就是分布式事务考虑的事情。

分布式事务如何处理

如何用消息系统避免分布式事务?这里提到的方案也是可行的,但是更常见的场景是,我们对接了第三方的支付,需要调用第三方支付接口,而第三方支付的API一般我们不可能有那么高的控制权(第三方支付究竟打款成功没有,我们不可能立刻知道,也不可以在第三方支付那边加上应用消息确认表来防止重复投递的问题(因为第三方对接一般不会跟渠道做一些定制上的数据存储改变)。

不过类似的,我们可以根据第三方API来实现基于消息的分布式事务。具体过程如下:

  • 在需要与第三方系统交互的数据处理,使用编程式事务,在系统异常回滚的时候,取消请求第三方系统,如果第三方系统已经处理了相关数据,则通知其回滚(需要第三方支持该操作);
  • 在本地系统创建一个消息表,处理完一笔订单,则添加一条消息,用于第三方系统的消费,每次投递消息后,则设置消息为处理中;
  • 注意,为了只发送一次请求(如果第三方api有类似的去重机制,那么可以不用限制只发送一次请求,实际上消息去重第三方已经帮你实现了),不应该在处理前请求第三方接口判断是否已经处理,因为假如一个请求到了第三方系统,消息被阻塞了,而另一个查询执行状态的请求已经处理成功了,结果我们得到的还是未处理的状态,于是又尝试请求一次,这样,连续两次请求都提交到了第三方系统;更好的做法应该是提交请求之前就设置消息状态为处理中,间隔久一点的时间定期轮训请求结果,如果还是未执行,则产生报警并生成重发消息的表单之类的,双方确认没问题之后再重新让消息投递过去;不过如果是两个系统都是内部的,则可以很方便控制API来通过消息状态来防止消息重复投递。

关于本地系统线程重复请求的问题

为了防止并发多个线程重复执行,加入了分布式锁。防止重复点击两次,导致重复执行的情况。这样又影响了前端的体验,目前该功能都是财务人员使用的,如果类似的,我们需要让用户主动触发该功能,那么并发量就上来了,这个分布式锁则会引发性能问题,很明显,我们需要通过其他的可靠方式来取代这个分布式锁:

  • 通过数据库行锁的方式:根据支付订单,创建支付消息,根据支付消息发送打款请求,打款请求发出去之前,先通过数据库的行锁update更新支付消息为已提交支付请求,如果更新成功,则表明可以继续往下执行,如果更新失败,则表明已经被另一个线程处理掉了。

  • 根据乐观锁的方式:获取到乐观锁的优先update更新状态,并发送消息,那么下一个线程进来的时候就无法继续执行了,为了让系统更可靠,乐观锁也应该是要保证高可用的,可以通过Redis集群或者zk集群的方式提供分布式锁服务。

关于事务的四个特征

ACID

原子性:购买操作所有数据处理要么都失败,要么都成功;
一致性:一个事务的执行,不能破坏数据库的一致性;
隔离性:不同事务操作相同的数据,每个事务都有各自完整的数据空间,并发执行的各事务不能相互干扰;
持久性:事务提交之后,数据库中的变更是永久的。

怎么设置事务隔离级别

Read Uncommitted: 允许脏读,不可重复读,存在幻读
Read Committed: 没有脏读脏读,不可重复读(提交后可读),存在幻读
Repeatable Read: 没有有脏读,可重复度,存在幻读
Serializable: 没有脏读,可重复度,不存在幻读

Serializable串行执行,效率太低。

一般使用Repeatable Read,这类隔离级别可重复读,保证一个事物内数据处理的一致性,避免了脏读也防止了事务回滚造成的脏数据,更大程度上保证了事务的一致性,至于幻读,如果我们处理好具有竞争关系的数据库资源,则可以很好的避免数据不一致的问题,举个例子:

购买理财产品,可购买余额剩下1000块,A用户购买金额扣减成功了,B用户进来发现没得购买了,然后就提示已募集结束,A接下来执行的时候因为异常,事务回滚了,这个时候又恢复了可购买余额1000块,B用户或者C用户可以继续钱够这1000块,这种情况也是运行出现的,但是最终我们能够确保卖出去的钱不会超额。曾经就遇到过一个这样的系统,购买最后一笔可购买余额被多下了两次单,导致募集金额多了,这就是并发事务和具有竞争关系的资源没控制好的情况,怎么彻底避免这类问题呢?我们先来研究下数据库的隔离级别和事务的传播特性。

事务的隔离级别

假设我们程序里面设置的数据库隔离级别是使用数据库默认的,通过查询:

1
select @@global.tx_isolation;

可以看到,假设查出来的是REPEATABLE_READ,允许重复读。接下来做一组试验。

通过两个事务观察数据库的更新操作

线程1执行事务:

1
2
3
update t_audio set like_num=like_num-10 where id=1 and like_num>0;
// 休眠30秒
...

线程2执行事务:

1
2
3
update t_audio set like_num=like_num-10 where id=2 and like_num>0;
// 休眠30秒
...

可以看到由于这两个线程更新两条不一样的数据,所以都可以立刻更新成功;

而如果把线程2执行更新的记录id也改为1,可以发现线程2一直卡在那里,等地线程1的事务提交。

进一步测试,线程2不更新,只是查询线程1修改的记录:

1
select * from t_audio where id=1;

因为事务隔离级别是REPEATABLE_READ,而线程2执行的时候线程1事务还没有提交,所以看到的是修改前的值。

结论

我们可以看到,在一个事务中修改中的数据库记录是有行锁的,在事务提交之前该锁不会释放,直到事务提交之后才释放.

关于事务的传播特性

我们来假设一下有如下场景

1
2
3
transA(){  // PROPAGATION_REQUIRED
transB(); // PROPAGATION_REQUIRES_NEW
}

假设设置的事务隔离级别为Repeatable Read,transA方法的传播特性为PROPAGATION_REQUIRED,transB的传播特性为PROPAGATION_REQUIRES_NEW,假设这两个方法都同事执行了:

1
update t_audio set like_num=like_num-10 where id=1

也就是说,在两个事务里面,我们都对同一个记录进行了更新,我们执行一下,可以发现,两次改动都成功了,好像两次事务的改动都成功了的样子,PROPAGATION_REQUIRES_NEW是怎样的机制呢,我们先来分析下。

事务嵌套是个什么概念

其实我们所说的嵌套事务(nested transaction),对应的 spring的事务传播特性则是:

PROPAGATION_NESTED

在这篇文章里面已经提到,该传播特性是通过设置savepoint的方式实现的,并不是在事务里面又开启了事务这样的嵌套。

我们有必要再次区分这两种传播特性:

  • PROPAGATION_NESTED:通过savepoint实现了子事务;

  • PROPAGATION_REQUIRES_NEW把当前事务挂起了,然后开启一个新的数据库连接并创建新的事务。说到事务挂起,就要顺便提一下MySQL的事务模型了,Mysql使用了平面事务模型(flat model, not nested model)。

平面事务模型和嵌入事务模型有什么区别呢?

在嵌入式事务模型中,如果你开启了一个事务,并且想在当前事务下继续开启一个新的事务,第一个事务依旧会保持正常的开启状态,也就是说,第二个事务会嵌套在第一个事务里面;而在平面式事务中,是不允许事务嵌套的,如果开启了一个事务之后,继续开启另一个事务,会自动先提交第一个事务。

MySQL使用了平面事务模型:嵌套的事务是不允许的,在连续开启第二个事务的时候,第一个事务自动提交了。

see more and here

为了更具体的说明MySQL不允许这种事务里面嵌套新的事务的情况,我可以做一个实验:

首先设置为不自动提交事务

set autocommit=0

设置事务隔离级别:

set tx_isolation=’REPEATABLE-READ’

1
2
3
4
5
6
7
8
9
START TRANSACTION;
# 外部事务更新记录
update gt_audio set like_num=like_num-10 where id=1;
START TRANSACTION;
# 内部事务更新同一条记录
update gt_audio set like_num=like_num-20 where id=1;
ROLLBACK; # 内部事务回滚
# 外部事务查找记录
COMMIT;

like_num的初始值是30,开启第二个事务之后,在另一个会话中查询,发现like_num的值已经变为了20,可以发现,执行第二个START TRANSACTION的时候,第一个事务的内容已经提交了,继续测试可以发现,无论后面怎么回滚或者提交,都无法撤销第一个事务的改动了。

很明显,Spring的PROPAGATION_REQUIRES_NEW是不可以通过这样简单粗暴的sql来实现的,唯一的一种实现方式就是开启第二个事务的时候,将第一个事务的数据库连接暂时存起来不使用,并开启一个新的数据库连接来创建新的事务,没错,Spring内部就是这样实现的,PROPAGATION_REQUIRES_NEW的挂起操作,带来了一个新的事务状态:Suspend。除了PROPAGATION_REQUIRES_NEW这种传播特性,PROPAGATION_NOT_SUPPORTED也会让事务进入Suspend状态。

什么时候应该使用PROPAGATION_NESTED

使用PROPAGATION_NESTED是有前提的,就是该嵌套事务可能需要做分支处理。像这样:

1
2
3
4
5
6
7
transactionA(){
try{
transactionB();
} catch(SomeException e){
// 处理其他数据
}
}

因为内部事务回滚实际上是返回到了内部事务之前的savepoint,所以外部事务的其他部分还可以继续执行,否则用PROPAGATION_REQUIRED就足够了。

什么时候应该使用PROPAGATION_REQUIRED_NEW

如果不管外部方法执行成功与否,某些内嵌的方法都要执行成功,即提交对数据的修改,那么就可以使用这种传播属性啦,常见的是交易系统中的日志记录,无论方法执行成功或者失败,都需要保存日志;当然了,如果我们有单独的日志系统,也可以通过发送异步消息的方式,在别的系统中保存日志,这样就无需开启这种事务了。

PROPAGATION_SUPPORTS的作用

对于前面使用REPEATABLE_READ的例子,两个事务相互隔离,一个事务对数据的改动,是不会让另一个事务读取到的,如果有这样一个方法:

1
2
3
4
5
6
void updateData(){
// 更新金额相关操作
...
// 读取金额的方法
long amount = readAmount();
}

如果我们设置readAmount()方法的事务传播属性为PROPAGATION_SUPPORTS,那么该方法就会使用updateData()方法的事务了,这样上面改动金额之和,readAmount也可以立刻读取到改动后的值了,因为是处于同一个事务中。

事务隔离级别最佳实践: 当为方法分配事务属性的时候,把类中对大部分方法最具限制性的属性作为类级别的默认属性, 然后再对有特殊需要的方法进行微调.如果默认的,我们的读方法不需要加事务,则可以配置为PROPAGATION_SUPPORTS方式,对于其他写的方法,则使用PROPAGATION_MANDATORY来确保需要事务,这样即使写方法里面又调用了读的方法,也会自动包含在这个事务里面了。

可以考虑如下配置

将PROPAGATION_MANDATORY作为默认属性, 而对查询方法使用PROPAGATION_SUPPORTS属性。

这样所有的方法都默认需要事务了,不会因为编写的过程中忘了考虑事务而产生意向不到的错误,在不需要事务的地方在专门的配置,对于查询方法,使用Supports会避免上面更新金额的例子产生的问题。

设置为可重复读,对于非嵌套的两个事务,如果一个事务的改动没提交,另一个事务读取到的是改动前的值,另外,如果两个事务要改动同一条记录,那么其中一个事务一定会等另一个事务执行完毕之后才可以继续进行修改,由于是可重复度,第一个事务改动后,第二个事务读取到的还是原来的值,但是通过update… set a=a-1这样修改的时候,a却应用了两次的修改,例如:

1
2
3
4
5
6
START TRANSACTION;  # 同时开启事务A和事务B
select like_num from gt_audio where id=1; # 事务A和事务B都读取到的值为10
# 外部事务更新记录
update gt_audio set like_num=like_num-10 where id=1; # 事务A对该值-10
select like_num from gt_audio where id=1;
COMMIT; # 事务A提交之后,事务B再读取值,还是10,但是事务B执行上面的update语句之后,数值直接变为-10了

考虑以上sql,两个事务同事执行,刚开始查出来的like_num都是10,
事务1修改并提交之后,事务2继续查出来的还是10,因为是可重复读的隔离级别,但是事务二执行更新语句之后,like_num却突然直接变为了-10,这说明了两个事务通过a=a-1这种形式进行更新是不会覆盖第一个事务做的改动的。

由于InnoDB引擎实现了MVCC多版本并发控制,所以update之后看到的总是最新的数据。

MySQL的innodb引擎是如何实现MVCC的?

innodb会为每一行添加两个字段,分别表示该行创建的版本和删除的版本,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别(事务的隔离级别请看这篇文章)下,具体各种数据库操作的实现:

  • select:满足以下两个条件innodb会返回该行数据:(1)该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。(2)该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。
  • insert:将新插入的行的创建版本号设置为当前系统的版本号。
  • delete:将要删除的行的删除版本号设置为当前系统的版本号。
  • update:不执行原地update,而是转换成insert + delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。

其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。

If Eventual Consistency Seems Hard, Wait Till You Try MVCC

MVCC浅析

最终一致性其实比MVCC简单

实际运用中推荐可重复读REPEATABLE READ这个隔离级别,这个隔离级别能够保证ACID。

讲到了这里,你真的了解事务嵌套吗?怎么选择事务传播特性?事务隔离级别又需要如何考虑?

java concurrent包中的copyonwrite系列就是专门用于优化读远大于写的场景的。写操作的时候,copy一份数据用于读取,写完后原子替换掉旧的数据,这样写操作不会阻塞读操作。

解惑 spring 嵌套事务
浅谈Spring事务隔离级别
《Java事务设计策略》

什么是脏读,不可重复读,幻读

http://www.cnblogs.com/phoebus0501/archive/2011/02/28/1966709.html

怎么防止幻读带来的问题?

(update … 看是否更新到了,从而进行判断)

通过事务保证一致性,就会影响系统的可用性,如何解决这类问题?

在MySQL中update一个记录,直到该事务提交之前,都会锁住该记录,为了让其他事务能够看到其中的改动,就得使用read uncommit隔离级别了,但这样会引入不可重复读的问题,需要权衡,如果允许这种数据的不一致性,则使用REPEATABLE-READ就够了;

对于有竞争关系的资源,如果两个事务同时这样操作:判断是否符合条件,符合条件则修改,发现两个事务都修改了,修改之后变成不符合我们想要达到的改动效果了;

对于有竞争关系的资源,不可以进行这样判断修改(单线程程序才可以这样搞)。

这种情况最好使用乐观锁进行实现(类似抢购系统那种资源处理方式)。在一些高并发的场景中,采用数据库的行锁不如使用Redis的乐观锁,防止请求累积在数据库,通过redis直接过滤掉了一些不合法的请求,减轻数据库连接资源的消耗。

请求 –> redis乐观锁处理 –> MySQL

这样过滤之后,可以保证每一个时刻只会开启一个数据库连接,不会占用数据库连接资源。

也可以使用数据库行锁来保证:

update t_audio set like_num = 1 where id = id and like_num= 0. 1 row affected 说明更新成功.不管多少并发,数据库都能保证只有一个才事务更新成功。

MySQL高并发下的解决方案

脱离 Spring 实现复杂嵌套事务,之三(REQUIRES_NEW - 独立事务)

user mysql client

发表于 2016-06-09   |   分类于 Database

一般地MySQL安装在

/usr/local/mysql/

通过以下命令链接到mysql客户端:

mysql [-u username] [-h host] [-p[password]] [dbname]

./mysql -uroot -h172.22.23.1 -p123456 -P3306

show databases

show tables

use table

查看表信息

查看某一个库所有表的详细信息:

1
2
use information_schema;
select * from TABLES where TABLE_SCHEMA='my_db';
1
2
3
4
5
6
7
8
9
10
11
use information_schema;
select COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT from information_schema.columns where table_schema ='database' and table_name = 't_user';

# or

select * from TABLES where TABLE_SCHEMA='my_db' and TABLE_NAME= 't_user';

# or

use database;
show full columns from t_user;
1
desc table;

修改表字段,注释

1
2
alter table t comment = '表注释';
alter table t modify column id int comment '主键ID';

查看MySQL DDL

1
show create table table_name\G;

Spring架构设计

发表于 2016-06-08   |   分类于 Java

Spring3自定义环境配置

Zookeeper 基础问题

发表于 2016-06-08   |   分类于 Java , 分布式

第一章

什么是分布式事务

什么是CAP定理,什么是BASE理论?

CAP: Consistency, Availability, Partition tolerance

BASE: Basically Availabel, Soft state, Eventually consistent.

对于本地或者集中式事务,我们可以用很成熟的ACID模型来保证数据的严格一致性。而分布式系统,一致性和可用性无法同时得到保证。CAP既一个分布式系统无法同事满足一致性,可用性和分区容错性。

放弃CAP其中一个特性会有什么问题,哪个特性是分布式服务必须具有的?

最终一致性有哪些变种? 举个最终一致性的例子

BASE与ACID有哪些区别?具体使用的时候怎么考虑两者

第二章 一致性协议

为什么要引入2PC(Two-Pahse Commit),阐述一下2PC的执行过程。

协调者协调参与者

二阶段事务提交有什么优缺点?

提交事务请求,执行事务提交.

原理简单,实现方便

同步阻塞,单点问题,脑裂(数据不一致),太过保守.

为什么要引入3PC,阐述一下3PC的执行过程。

2PC 提交事务请求分为两步.

CanCommit,PreCommit,DoCommit.

3PC比起2PC有什么优点?仍然存在什么问题?

一分为二后降低了阻塞范围,提前检测单点故障,使数据达成一致性.

如果进入阶段三之后出现了故障,最终超时之后,参与者的事务还是会自动提交,还是会导致数据不一致性的问题.

Paxos分布式一致性算法为什么会出现,解决了什么问题?执行原理是怎样的?

拜占庭将军问题 兼职会议

Paxos要解决的问题是在一个分布式系统中,出现宕机或者网络异常,能够快速并且正确的在集群内对某个数据的值达成一致,无论发生什么异常,都不会破坏整个系统的一致性.

第三章 Paxos的工程实践

Chubby是一个分布式锁服务,以Paxos算法为基础.

分布式锁服务具有的优点:

  • 对上层应用程序的侵入性小
  • 便于提供数据的发布与订阅
  • 开发人员对基于锁的接口更为熟悉
  • 更便捷地构建更可靠的服务

Chubby设计目标:

  • 提供一个完整的,独立的分布式锁服务,而非仅仅是一个一致性协议的客户端
  • 提供粗粒度的锁服务

与细粒度的锁的区别:细粒度的锁通常设计为锁服务一旦失效就释放所有锁,但也可以设计更可靠的锁,通过redis集群或者zk实现.
高可用的锁更安全:比如一个线程获取锁之后,锁服务就闪断了,或者单机的锁数据丢失,另一个线程进来也获取到了锁,问题就发生了.

  • 在提供锁服务的同时提供对小文件的读写功能
  • 高可用,高可靠
  • 提供事件通知机制

粗粒度的锁,一般持有数小时或者数天

第四章 Zookeeper与Paxos

ZooKeeper可以保证如下分布式一致性特征:

  • 顺序一致性
  • 原子性
  • 单一视图
  • 可靠性
  • 实时性

ZooKeeper的设计目标

  • 简单数据模型:树形结构,全量存于内存;
  • 可以构建集群:客户端使用TCP和ZooKeeper服务器进行连接
  • 顺序访问:对于每一次请求,通过分配一个全局唯一的递增编号(事务ID:ZXID)实现;
  • 高性能:由于数据存在内存中,尤其适合用于以读操作为主的应用场景,高QPS。

Zookeeper与Master/Slave的集群模式有什么区别

主备模式中,能够处理所有写操作的是Master机器,异步方式获取最新数据并提供读服务的机器是Slave机器;
ZooKeeper中,Leader服务器为客户端提供读和写服务;其他的Follower和Observer提供读服务;Observer不参与Leader选举过程,也不参与写操作的过半写成功策略,因此Observer服务器可以在不影响写性能的情况下提升集群的读性能

ZooKeeper的会话是什么概念?

Sessioin是指客户端会话,客户端连接是TCP长连接,默认端口2181,通过该连接实现与服务器通信,Watch事件通知机制sessionTimeout时间内实现会话重连;

ZooKeeper的算法?

ZAB(ZooKeeper Atmoic Broadcast,原子消息广播协议)

ZooKeeper主要依赖ZAB协议实现分布式一致性,使用一个主进程接收并处理客户端的请求,采用ZAB原子广播协议,将服务器状态变更以Proposal的形式广播到所有的副本上去;另外还需要保证全局的变更序列被顺序应用;ZAB协议还要保证当前主进程异常的时候,集群依旧能够正常工作。(Leader服务器负责将客户端事务请求转换成一个Proposal,并分发给集群中所有Follower服务器,等待反馈,超过半数正确反馈,则Leader服务器向所有Follower服务器分发Commit消息,要求将Proposal进行提交)

Leader服务器接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议。

Leader服务器出现崩溃退出或机器重启,或者集群中已经不存在过半的服务器与该Leader保持通信,重新开始新的一轮原子广播事务操作之前,所有进程会进入崩溃恢复协议来使笔记达到一个一直的状态。

ZAB的消息广播机制

类似于二阶段提交过程,与二阶段事务提交稍有不同的是:移除了中断逻辑,意味着我们可以在过半的Follower服务器反馈Ack之后就开始提交事务Proposal了。

这种模型无法处理Leader服务器崩溃退出带来的数据不一致问题,需要引入崩溃恢复模式。

整个消息协议是基于具有FIFO特性的TCP协议来进行网络通信的,因此能够保证消息广播过程中消息接收与发送的顺序性。

Leader服务器会为每个Follower都分配一个单独的队列,将Proposal通过队列根据FIFO策略发送出去,Follower接收到事务的Proposal会以事务日志的形式存在本地磁盘,接收到Leader的Commit消息之后完成对事务的提交。

崩溃恢复

整个恢复过程结束后需要选举出一个新的Leader服务器,因此ZAB协议需要一个高效且可靠的Leader选举算法。

崩溃出现不一致的场景:

  • ZAB需要确保那些已经在Leader服务器上提交的事务最终被所有服务器提交
    例如LeaderCommit了事务,向Follower发出Commit请求之前,Leader服务器挂了
  • ZAb需要确保丢弃那些只在Leader服务器上被提出的事务

选举出来的Leader拥有集群中所有机器最高编号ZXID的事务Proposal,就可以保证新选举出来的Leader一定具有所欲已提交的提案。

数据同步

对于未同步的事务:Leader服务器会为每个Follower都分配一个单独的队列,将没有被各Follower服务器同步的事务以Proposal消息的形式逐个发给Follower服务器,并在每一个Proposal消息后面紧接着发送一个Commit消息。同步完成之后,Leader会将Follower服务器加入真正可用Follower列表。

对于需要被丢弃的事务:通过ZAB协议的事务编号设计:Leader周期编号 + 低32位递增,需要确保新选举出来的Leader最大事务Proposal的ZXID是最高的,因为肯定存在一个Quorum集合,是包含最高ZXID的,如果最高ZXID是原来的Leader,那么很明显,原来Leader服务器的最大事务Proposal应该被丢弃。

(从ZooKeeper的实现分析分布式系统消息处理的方式之一)

关于Zookeeper的选举

选举机制(FastLeaderElection算法):sid最大且被超过集群中超过半数的机器拥护就会成为leader.
所以只有两种情况无法选出leader:

.    整个集群只有2台服务器(注意不是只剩2台,而是集群的总节点数为2)

.    整个集群超过半数机器挂掉。


所谓的偶数问题其实是另一个集群优化配置问题,即:集群的容灾数量=集群总节点数/2-1
假如集群有5节点,那么最多允许2个节点挂掉,如果有3节点挂了,那么整个集群的选举结果不会满足条件:集群中超过半数的机器拥护。
假如集群有6个节点,那么最多也只能挂掉2台,因为挂了3台时,选举结果也不会满足条件:集群中超过半数的机器拥护。
结果可以看出,多那一台用处并不大。所以集群总数推荐为奇数。

ZAB协议与Paxos的区别

ZAB协议额外添加了一个同步阶段,新的Leader会确保存在过半的Follower已经提交了之前Leader周期中所有事务Proposal,这阶段的引入能够保证Leader在新的周期中提出事务Proposal之前,所有的进程已经完成了对之前事务Proposal的提交。

ZAB主要用于构建高可用的分布式数据主备系统;Paxos算法用于构建分布式的一致性状态机系统。

第五章 使用Zookeeper

集群与单机模式

集群

1、配置zoo.cfg文件,通过git管理zoo.cfg配置文件

2、在dataDir所配置的目录下创建myid文件,里面记录上当前机器编号

3、启动zk:

%ZK_HOME%/bin/zkServer.sh start

4、验证服务器:

1
2
telnet 127.0.0.1 2181
stat

单机

特殊的集群:只有一台机器的集群。

stat输出区别:
Mode: standalone/leader

伪集群模式

集群所有的机器都在一台机器上,但是还是以集群的特性来对外提供服务。配置基本一样,只不过服务器的IP都是同一个IP,但是使用不同的端口号。

运行服务

通过Java命令行启动

java -cp zookeeper-3.4.8.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:log4j-1.2.16.jar:conf org.apache.zookeeper.server.quorum.QuorumPeerMain conf/zoo.cfg

使用ZooKeeper自带的启动脚本来启动ZooKeeper

sh zkServer.sh start
sh zkServer.sh stop

ZooKeeper默认使用3888端口进行Leader选举过程中的投票通信。

客户端脚本

sh zkCli.sh # 连接本地zk
sh zkCli.sh -server ip:port

创建

create [-s] [-e] path data acl

-s 顺序节点 -e 临时节点

create /zk-book 123

读取

ls path [watch]

get path [watch]

更新

set path data [version]

删除

delete path [version]

无法删除一个包含子节点的节点

Java客户端使用

Watcher通知,会话复用

setData() 方法的version参数是由CAS原理衍化而来的。每次设置都存储最新的版本到客户端,下次设置的时候,如果发现服务器的版本已经更新了,那么久不匹配,无法更新了。

权限控制

为了避免存储在ZooKeeper服务器上的数据被其他进程干扰或人为操作修改,需要对ZooKeeper上的数据进行权限访问控制。

权限控制模式:

world auth digest ip super

ZkClient

⁃    ZooKeeper会话创建是一个异步的过程,ZkClient通过内部包装,将这个异步会话创建过程同步化了。

IZkConnection是对ZooKeeper原始接口最直接的包装,报行了增删改查一系列接口定义。
IZkConnection有两个实现:ZkConnection和InMemoryConnection,前者是最常用的实现。

ZkClient定义了ZkSerializer接口,允许用户传入一个序列化实现。

ZkClient不需要提供Watcher,使用Listener取而代之。

Curator

Curator解决了很多ZooKeeper客户端非常底层的细节开发工作,包括连接重连,反复注册Watcher和NodeExistsException异常等。

易用性和可读性更强的Fluent风格的客户端API框架。

删除

如何保证网络问题导致的删除失败?Curator引入了guaranteed()方法,记录删除失败的操作,客户端会话有效的时候,会在后台反复重试,直到节点删除成功,通过这样的措施,就可以保证节点删除操作一定会生效。

异步接口

在ZooKeeper中所有异步通知事件处理都是由EventThread这个线程来处理的,EventThread线程用于串行处理所有的事件通知。保证执行的顺序性,但是如果碰到复杂的处理单元,就会消耗过长时间,从而影响其他事件的处理,所以运行在异步接口中传入一个Executor实例,把一些比较复杂的事件专门放到一个线程池中处理。

典型使用场景

事件监听

Curator引入Cache来实现对ZooKeeper服务端事件的监听,可以近似看做是一个本地缓存试图和远程ZooKeeper试图的对比过程。

NodeCache

PathChildrenCache
新增、删除子节点或者节点数据发生变化就会回调PathChildrenCacheListener,并且根据对应的事件类型进行相关的处理。对于节点本身的变更或者一级子节点以外的变更,不会进行回调。

Master选举

从分布式集群中选举一台服务器处理复杂的任务:多台机器同时向同一个节点创建子节点,最终只有一台机器能够创建成功,成功的那台机器就作为Master。

当一个应用实例成为Master之后其他应用实例会进入等待,知道当前Master挂了或者退出之后才会开始选举新的Master。

分布式锁
分布式计数器
分布式Barrier

工具

ZKPaths
EnsurePath
TestingServer
TestingCluster

第六章 ZooKeeper的典型应用场景

说说MySQL的数据复制组件的工作流程和架构?

MySQL数据复制组件冷热备份对比?

热备份:实时,比较耗机器资源;
冷备份:类备份扫描,不实时,节省机器资源;

单一台MySQL服务器无法保证高可用

分布式系统大部分的性能瓶颈在数据库操作上,有什么方法取代使用数据库的排他性(如行锁,表锁或者事务)造成的性能问题呢?

  • zookeeper 排它锁 共享锁

zookeeper可以通过数据节点来表示一个锁

排它锁的流程

共享锁的流程

优化羊群效果后的流程图

JDK synchronized

Redis mc
redis集群 高可用

通过Future的方式,先并发向5个节点请求,再一起获得响应结果,能缩短响应时间,不过还是比单节点redis锁要耗费更多时间。

聊一聊分布式锁的设计

跟着实例学习ZooKeeper的用法: 分布式锁

Master选举

如果仅仅想实现Master选举功能,那么只要能够有一个保证数据唯一性的组件即可,例如关系型数据库的主键模型;如果希望能够快速进行集群的Master动态选举,那么基于ZooKeeper是一个表现不错的思路。

常见的分布式队列有哪些,有哪两种模型

ActiveMQ,Kafka

先入先出,Barrier模型

ZooKeeper在Dubbo中的应用

ZooKeeper实现的主备切换一般流程是怎样的?

  1. 创建锁节点
  2. 注册Watcher监听
  3. 主备切换

什么是分布式脑裂(brain-Split)现象,怎么解决这类问题?

网络,GC等导致假死问题产生选举了另一个Master,同事出现两个Master各司其职;

Fencing(隔离):创建的跟节点必须携带ZooKeeper的ACL信息

Kafka使用四层负载均衡和使用ZooKeeper进行负载均衡的区别是什么?

研究ZK架构前需要知道的:

zookeeper原理
ZooKeeper解惑

第七章 ZooKeeper技术内幕

不能基于临时节点来创建子节点,只能作为叶子节点

ZooKeeper的版本号是用来干嘛的?

从乐观锁说起,乐观锁控制事务分为三个阶段:数据读取、写入校验、数据写入。 CAS

Watcher

ZooKeeper如何保证数据安全?

通过ACL机制,常用的权限模式:

  • IP
  • Digest(username:passowrd SHA-1加密 BASE64编码)
  • World(最开放权限控制模式,特殊的Digest模式:world:anyone)
  • Super(超级用户,特殊的Digest模式)

通过ACL授权给一个客户端的节点,但是该客户端已经退出不用的情况下,只能使用Super模式清理节点。

常用的序列化组件有哪些?

Jute,是ZooKeeper中的序列化组件,Hadoop最初的默认序列化组件,前身是Hadoop Record IO;
Apache Avro,具有出众的跨语言特性,丰富的数据结构,对MapReduce的天生支持,可以方便地用于RPC,后来Hadoop采用该框架进行序列化;

ZooKeeper的通信协议是怎样的?

基于TCP/IP协议,ZooKeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信。

协议解析:请求部分

请求头:RequestHeader: xid type
请求体:ConnectRequest,GetDataRequest,SetDataRequest

协议解析:响应部分

响应头:RepayHeader: xid zxid err
响应体:ConnectResponse,GetDataResponse,SetDataResponse

ZooKeeper的核心组件以及客户端的初始化和启动过程

  • ZooKeeper实例
  • ClientWatchManager
  • HostProvider
  • ClientCnxn
    • SendThread: I/O线程
    • EventThread: 事件线程

客户端初始化和启动过程:1、设置默认Watcher;2、设置ZooKeeper服务器地址列表;3、创建ClientCnxn;

ClientCnxn网络I/O具体流程

关键概念:Packet ClientCnxn outgoingQueue pengdingQueue ClientCnxnSocket SendThread EventThread

谈谈ZooKeeperSessionId的生成算法

ZooKeeper中的超时检查机制是怎样的,这样的机制有什么好处?

ExpirationInterval

ZooKeeper中的清理会话的机制是怎样的?

重连机制是怎样的?

长连接是通过心跳机制实现的;

会话转移可能会导致什么问题?

网络延时,会话转移的过程中,导致先发送的请求写入的节点数据覆盖了后面发送的请求写入的节点数据。解决方法:判断客户端请求的Owner是不是当前服务器

谈谈ZooKeeper的整体架构

ZooKeeper启动流程

ZooKeeper的选举流程

ZooKeeper中提供了三种选举算法:LeaderElection,UDP版本的FastLeaderElection,TCP版本的FastLeaderElection(3.4开始只保留这个选举算法)。

sendqueue:选票发送队列
recvqueue:选票接收队列
WorkerReceiver:选票接收器

Leader选举算法实现的流程示意图

各个服务器角色介绍

Leader

PreRequestProcessor:预处理器,对事务请求进行一系列预处理;
ProposalRequestProcessor:事物投票处理器,非事务请求直接提交到CommitProcessor;

CommitProcessor:事务提交处理器,非实物请求直接交付给下一级处理器;
ToBeCommitProcessor:包含一个toBeApplied队列,包含可被提交的Proposal,并叫一个下一个处理器进行处理;
FinalRequestprocessor:收尾工作:创建客户端请求的响应,针对事务请求,将事务应用到内存数据库中去;

SynRequestProcessor:事务日志记录处理器;
AckRequestProcessor:Leader特有的处理器,通知投票收集器当前服务器已经完成了对该Proposal的事务日志记录;

Leader服务器会为所有的Follower/Observer服务器建立TCP长连接,同时为每个Follower/Observer服务器创建一个名为LearnHander的对象。

Follower

处理客户端非事务请求,对于事务请求,直接转发给Leader进行处理;参与事务请求Proposal投票,参与Leader选举投票;

FollowerRequestProcessor: 转发事务请求给Leader进行处理

SendAckRequestProcessor: Follower才有该处理器,以ACK消息的形式来向Leader服务器进行反馈。

Observer

Observer与Follower的区别:Observer不参与任何形式的投票(事务请求Proposal和Leader选举的投票),只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

注意:Leader服务器不会将事务请求的投票发送给Observer服务器。

集群间消息通信有哪些类型

数据同步型

服务器初始化型

请求处理型

会话管理型

会话创建处理服务器端流程是怎样的

会话创建服务端流程可分为:请求接收,会话创建,预处理,事务处理,事务应用,会话响应。

setData请求的流程

分为4个步骤:请求的预处理,事务处理,事务应用,请求响应。

ProposalRequestProcessor处理器三个子流程:Sync,Proposal,Commit

getData非事务请求处理流程

大致分为3个步骤:请求预处理,非事务处理,请求响应。

ZooKeeper数据存储

DataTree DataNode nodes ZKDatabase

第八章 Zookeeper运维

JMX监控ZooKeeper运行时数据信息

分布式理论之一:Paxos算法的通俗理解

Paxos算法与Zookeeper分析

如何用消息系统避免分布式事务?

Mac终端介绍和配置

发表于 2016-06-06   |   分类于 Mac

Mac终端配置

颜色配置

Mac OS X的终端默认是黑白的,下面是配置颜色的步骤:

1 设置常用命令语法高亮

修改~/.bash_profile文件,加入以下内容:

1
2
3
alias ls='ls -G'
alias ll='ls -l'
alias grep='grep --color'

这样,ls和grep命令就是彩色的了.

2 设置vim语法高亮

修改~/.vimrc文件,加入以下内容:

1
syntax on

保存退出后再次使用vim就会发现高亮语法了.

/etc/profile

如果想让 ~/.bashrc 文件每次打开bash都立刻执行,可以在 /etc/profile 加上如下配置:

1
2
3
if [ -f ~/.bashrc ];then
. ~/.bashrc
fi

Bash常用脚本

发表于 2016-06-05   |   分类于 Cache

Bash Shell 解析路径获取文件名和目录名

查找文件夹对应关键词所在行,并打印包含异常所在行的下面10行

MyBatis一级缓存引发的问题

发表于 2016-06-02   |   分类于 Database

发表于:MyBatis一级缓存引发的问题

一般的,我们在写一个系统的时候,都会分为持久层,服务层,和控制层,各层之间职责分明,对应的Entity,DTO,VO也是很明确知道用在哪里的,但是有时候为了方便,特别是用MyBatis的时候,直接从持久层返回一个vo,然后这个vo又作为方法的参数在系统之间来回调用,那么会引发什么样的问题呢?

假设我们有如下代码:

/**
 * 确认订单
 */
public boolean confirmOrder(String orderId){
    OrderVo orderVo = orderDao.findByOrderId(orderId);
    // 假设计算订单号需要用到一个特殊的利率
    BigDecimal annualRate = new BigDecimal(0.12);
    // 根据订单号和利率计算实际的价格
    String realAmount = calculateRealAmount(orderId, annualRate);
    System.out.println(orderVo.getAnnualRate.longValue());
}

/**
 * 计算订单实际价格的方法,传入orderVo和实际的利率,用于计算订单价格
 */
private BigDecimal calculateRealAmount(OrderVo orderVo, BigDecimal annualRate){
    OrderVo orderVo = orderDao.findByOrderId(orderId);
    orderVo.setAnnualRate(annualRate);  // 设置新的计算利率
    calculateRealAmount(orderVo);
}

/**
 * 计算订单实际价格的方法,传入orderVo用于计算
 */
private BigDecimal calculateRealAmount(OrderVo orderVo){
    return NumberUtils.mulBigDecimal(orderVo.getAmount, orderVo.getAnnualRate);
}

假设confirmOrder()是处于一个事务里面,confirmOrder()方法里面通过calculateRealAmount()方法计算了实际的订单金额,这两个方法都通过orderDao.findByOrderId(orderId);查找到了OrderVo,由于MyBatis默认开启了以及缓存,实际上这两个方法查找出来的是同一个OrderVo对象,所以calculateRealAmount()里面对orderVo的改动,也会影响到confirmOrder()里面的vo,所以confirmOrder(String orderId)最后一行输出的值也不是刚查找出来的了,使用不当,会造成潜在的bug。

下面就来总结一下这个问题。

代码规范问题引发脏数据

原因是MyBatis xml文件里面读取出来的是一个VO,某一些方法也使用该VO作为参数,在传递VO参数的时候,对VO进行了重新设置,导致接下来的地方读取vo的属性读到了脏数据。

项目规范:

分清各层数据的职责,防止出现类似情况;
Entity查找出来不要更改,更改后则一定要入库处理,也不要作为方法的参数进行传递;
禁止从数据库里面直接查找出VO。

mybatis一级缓存(session cache)引发的问题

【mybatis】多次查询缓存的问题

深入理解mybatis原理

设置IDEA Mybatis xml文件错误提示


发表于 2016-05-31   |   分类于 Database

俗话说,工欲善其事,必先利其器。

设置IDEA Mybatis xml文件错误提示

设置 SQL Dialects

在IDEA里面链接数据库

这样,使用MyBatis plugin之后,xml文件里面就可以是语法错误了,方便排除问题。

去除不必要的错误提示

在mybatis xml文件里面按组合键:alt + enten 弹出意图动作和快速修复菜单,选择Language Injection Settings

因为我们一般通过sql标签定义属性:

1
<sql id="Base_Column_List">id, create_time, update_time, status</sql>

所以为了去掉这种问题的提示错误,应该在Local name配置里面去掉sql,改为如下:

1
select|insert|update|delete

线上排错常用命令

发表于 2016-05-26   |   分类于 Cache

查看cpu使用情况:
top

查看硬盘:
ls -lh

进程相关:
ps aux | grep “”

Linux common command

发表于 2016-05-26   |   分类于 Linux

Author: ChinSyun Pang
Weibo: arthinking_plus
Posted in: http://www.itzhai.com

common linux command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 替换字符串
grep -i -l -r -e 'arthinking.github.io' /Users/arthinking/Dev/informal-essay-github/* | xargs sed -i "" "s/arthinking.github.io/informal-essay/g"


# 去除空格
find /Users/arthinking/Dev/arthinking.github.io/source/_posts/* -name "*.md" -print0 | xargs -0 sed -i "" "s/^\(##*\) *\(.*\)$/\1 \2/g"

https://raw.githubusercontent.com/arthinking/arthinking.github.io/blog/source/_posts/images/20141204-java01.png

# 替换url
find /Users/arthinking/Dev/test/* -name "*.md" -print0 | xargs -0 perl -pi -e "s/(\!\[\]\(.*?jpg\))/test\1/g"


# 注意贪婪模式

http://serverfault.com/questions/268368/how-can-i-handle-spaces-in-file-names-when-using-xargs-on-find-results

sed在mac下使用差异

scp

Linux的scp命令可以在Linux之间复制文件和目录:

scp [可选参数] file_source file_target

可选参数:

  • -v和大多数 linux 命令中的 -v 意思一样 , 用来显示进度 . 可以用来查看连接 , 认证 , 或是配置错误;
  • -C使能压缩选项;
  • -P选择端口 . 注意 -p 已经被 rcp 使用;
  • -4强行使用IPV4地址;
  • -6强行使用IPV6地址。

从本地复制到远程

复制文件:

1
2
3
4
5
6
7
# 指定上传文件夹
scp local_file remote_username@remote_ip:remote_folder
# 指定上传后的文件名
scp local_file remote_username@remote_ip:remote_file
# 以下两个不指定远程服务器的登录名
scp local_file remote_ip:remote_folder
scp local_file remote_ip:remote_file

复制目录

1
2
scp -r local_folder remote_username@remote_ip:remote_folder 
scp -r local_folder remote_ip:remote_folder

会在remote_folder下面创建local_folder文件夹

从远程复制到本地

只要将从本地复制到远程的命令的后2个参数调换顺序即可。

例子

从远程下载文件到本地

1
scp root@182.92.6.82:/home/guitargg.gz ~/Downloads/

把本地文件传递到另一台主机上面:

1
scp ~/Downloads/guitargg.gz root@182.92.6.82:/home/

tar

解压:
tar -zxvf /usr/local/test.tar.gz
tar -zxvf 压缩文件名.tar.gz -C /usr/local/maven

01-.tar格式
解包:[*******]$ tar xvf FileName.tar
打包:[*******]$ tar cvf FileName.tar DirName(注:tar是打包,不是压缩!)

02-.gz格式
解压1:[*******]$ gunzip FileName.gz
解压2:[*******]$ gzip -d FileName.gz
压 缩:[*******]$ gzip FileName

03-.tar.gz格式
解压:[*******]$ tar zxvf FileName.tar.gz
压缩:[*******]$ tar zcvf FileName.tar.gz DirName

04-.bz2格式
解压1:[*******]$ bzip2 -d FileName.bz2
解压2:[*******]$ bunzip2 FileName.bz2
压 缩: [*******]$ bzip2 -z FileName

05-.tar.bz2格式
解压:[*******]$ tar jxvf FileName.tar.bz2
压缩:[*******]$ tar jcvf FileName.tar.bz2 DirName

06-.bz格式
解压1:[*******]$ bzip2 -d FileName.bz
解压2:[*******]$ bunzip2 FileName.bz

07-.tar.bz格式
解压:[*******]$ tar jxvf FileName.tar.bz

08-.Z格式
解压:[*******]$ uncompress FileName.Z
压缩:[*******]$ compress FileName

09-.tar.Z格式
解压:[*******]$ tar Zxvf FileName.tar.Z
压缩:[*******]$ tar Zcvf FileName.tar.Z DirName

10-.tgz格式
解压:[*******]$ tar zxvf FileName.tgz

11-.tar.tgz格式
解压:[*******]$ tar zxvf FileName.tar.tgz
压缩:[*******]$ tar zcvf FileName.tar.tgz FileName

12-.zip格式
解压:[*******]$ unzip FileName.zip
压缩:[*******]$ zip FileName.zip DirName

13-.lha格式
解压:[*******]$ lha -e FileName.lha
压缩:[*******]$ lha -a FileName.lha FileName

14-.rar格式
解压:[*******]$ rar a FileName.rar
压缩:[*******]$ rar e FileName.rar
rar请到:http://www.rarsoft.com/download.htm 下载!
解压后请将rar_static拷贝到/usr/bin目录(其他由$PATH环境变量
指定的目录也行):[*******]$ cp rar_static /usr/bin/rar

cp

复制并且覆盖目标文件的文件
cp -r -f /webapp/* /dist/

lsof

查看端口占用

lsof -i tcp:port

curl

1
curl -H "Content-Type: application/json" -X POST  --data '{"head": {"clientId": "02"},"body": {}}'  http://127.0.0.1/app-mis/api/market/getPosition/

web前端项目搭建说明

发表于 2016-05-20   |   分类于 web前端

Author: ChinSyun Pang
Weibo: arthinking_plus
Posted in: web前端项目搭建说明
项目地址:app-mis

安装webpack:

1
$ npm install webpack -g

关于npm install

初始化项目:

1
$ npm init

执行完这步会在目录下生成package.json文件

为项目安装webpack:

1
$ npm install webpack --save-dev

或者安装特定的版本:

1
$ npm install webpack@1.2.x --save-dev

webpack是一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含less/sass)、图片等都作为模块来使用和处理。

–save-dev 与 –save的区别

–save-dev: Package will appear in your devDependencies.
seeGrunt.js: What does -save-dev mean in npm install grunt –save-dev

安装webpack调试工具:

$ npm install webpack-dev-server –save-dev

启动webpack-dev-server:

1
$ webpack-dev-server --inline --hot

webpack-dev-serverwebpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息作出响应。

webpack构建命令:

1
2
$ webpack --display-modules
$ webpack --display-modules --display-chunks

–display-modules 和 –display-chunks的作用

by default Webpack hides modules that are not yours. To see all the modules compiled by Webpack, we can pass the –display-modules flag.

run webpack with the –display-chunks flag to see what modules are in which chunks.

安装需要的模块:

lodash

1
npm install --save lodash

lodash是一个Javascript工具库。

安装babel:

1
$ npm install babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-2 --save-dev

然后在文件目录下创建 .babelrc 文件。详细配置参考:babel

  • babel是一个Javascript代码转换器,可以把你写的符合 ECMAScript 6 标准的代码完美地转换为 ECMAScript 5 标准的代码,并且可以确保良好地运行在所有主流 JavaScript 引擎中。

安装eslint:

1
$ npm install eslint eslint-loader babel-eslint eslint-plugin-react --save-dev

在团队协作中,为避免低级 Bug、产出风格统一的代码,会预先制定编码规范。使用 Lint 工具和代码风格检测工具,则可以辅助编码规范执行,有效控制代码质量。在以前的项目中,我们选择 JSHint 和 JSCS 结合使用,使用 React JSX 语法时,却遇到了问题:JSHint 不支持 JSX 语法,以有了 ESLint。
eslint

安装react:

1
npm install react react-dom react-router --save

安装antd

1
npm install antd --save

安装需要的webpack loader:

1
2
3
$ npm install css-loader postcss-loader less-loader style-loader html-loader sass-loader node-sass --save-dev
$ npm install url-loader file-loader --save-dev
$ npm install baggage-loader --save-dev

loader配置说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require("./loader!./dir/file.txt");
// => uses the file "loader.js" in the current directory to transform
// "file.txt" in the folder "dir".

require("jade!./template.jade");
// => uses the "jade-loader" (that is installed from npm to "node_modules")
// to transform the file "template.jade"
// If configuration has some transforms bound to the file, they will still be applied.

require("!style!css!less!bootstrap/less/bootstrap.less");
// => the file "bootstrap.less" in the folder "less" in the "bootstrap"
// module (that is installed from github to "node_modules") is
// transformed by the "less-loader". The result is transformed by the
// "css-loader" and then by the "style-loader".
// If configuration has some transforms bound to the file, they will not be applied.

安装需要的webpack plugin:

1
2
3
$ npm install clean-webpack-plugin --save-dev
$ npm install extract-text-webpack-plugin --save-dev
$ npm install html-webpack-plugin
  • clean-webpack-plugin: A webpack plugin to remove/clean your build folder(s) before building.
  • extract-text-webpack-plugin: It moves every require(“style.css”) in entry chunks into a separate css output file. So your styles are no longer inlined into the javascript, but separate in a css bundle file (styles.css).
  • html-webpack-plugin: Simplifies creation of HTML files to serve your webpack bundles

参考:

  • webpack 入门指南
  • webpack-dev-server
  • Webpack-dev-server结合后端分缶的热替换配置
  • WEBPACK DEV SERVER
  • webpack your bags
  • lodash
  • babel
  • Babel-现在开始使用 ES6
  • eslint
  • ESLint 使用入门

SeaJS使用简介

发表于 2016-05-20   |   分类于 web前端

在使用spm之前,首先得安装nodejs,因为spm是nodejs的一个package。使用spm@3构建seajs项目

spm 3官方教程

构建目录:

1
2
3
4
5
6
dist  构建目标文件夹
examples 相关使用文档
spm_modules 依赖的js文件仓库,spm install的时候下载到里面
tests 测试案例
xx.js
package.json spm维护的依赖配置文件,用于生产dist目录的相关配置

expectjs

为什么 SeaJS 模块的合并这么麻烦

安装nodejs

seajs

配置NODE_PATH

nodejs 中的 NODE_PATH

windows下需要安装make

下载demo编译执行

SeaJS遵循CMD规范,一个模块就是一个文件,通过require 关键词引入外部模块,每次加载需要直接使用到的模块的时候,模块内部 require 进来的js也会一并加载,为了让SeaJS在加载之前把所有相关联的JS都能够通过一个JS文件一次性加载进来,就需要用到SeaJS的Transport格式
没转换前Modules/Wrappingsgui规范格式如下:

1
2
3
4
define(function(require, exports, module) {
var a = require("a")
exports.foo = ...
})

CMD模块在正式上线前,通过构建工具(如SPM)先转换为Modules/Transport格式:

1
2
3
define("id", ["dep-1", "dep-2"], function(require, exports, module) {
// source code
})

进一步通过构建工具,就可以一并吧 require 引入进来的模块一起压缩合并到同一个文件中了。

说明

简单来说,通过使用SeaJS,我们可以把JS代码模块化,在上线之前,通过spm这样的构建工具,把自己开发的模块压缩合并,当成SeaJS的一个模块来使用。

Step by step

下面一步一步来实现这个过程:

正面项目接口

整体目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
project   // 项目跟目录 
|
|-assets // web资源文件
|
|- sea-modules // SeaJS相关模块
|
|- static // 模块开发路径
| |
| |- first-module
| |- dist // 我的模块构建目标文件夹
| |- src // 我的模块开发文件夹
| |- package.json // spm编译配置
|
|- app // 我的模块使用例子
|

创建目录结构

step1、创建项目 project

step2、创建web资源文件夹

在project里面新建一个assetsweb资源文件夹,专门用来存放JS,CSS等资源,我们的模块开发就在这个目录下面进行

step3、在assets下面创建sea-modules文件夹

这个文件夹是用来存放SeaJS模块的,我们需要用到的模块,包括自己实现的模块最终都要存放到这里

step4、在assets目录下创建 static 文件夹

这个文件夹用于模块开发

step5、在 static 目录下创建 first-module 文件夹

这个文件夹用于存储我的第一个模块的开发代码

step6、在 first-module 目录下创建 src 目录,这个目录用于存放我们模块开发的代码

step7、在assets下面创建 app 文件夹

这个文件夹存放我们演示调用模块代码的HTML文件

使用spm初始化构建

首先你需要通过npm安装spm,而npm是Node.js里面的一个工具,所以你需要先这样搭建环境:

  • 安装Node.js
  • 安装spm
    目前最新的是3版本的,建议安装最新的,详细可以参考官方文档:
    http://spmjs.io/
    这里只要在cmd里面执行以下一句就可以了
1
$ npm install spm -g
  • 使用spm初始化项目目录

在first-module目录下执行

1
spm init

接下来按照提示分表填写 Package name, Version, Description, Author即可。

执行完之后生成如下目录:

接下来就可以开始开发自己的模块了。

模块开发

编辑我们自己的模块代码

在src目录下创建我们的模块代码 index.js

http://blog.csdn.net/huyoo/article/details/21703701

VIM常用命令

发表于 2016-05-18   |   分类于 IDE

Author: ChinSyun Pang
Weibo: arthinking_plus
Posted in: http://www.itzhai.com

选择

选中: v
取消选择:再按一下v

矩形选区:ctrl + v

全选:ggVG

选中后的操作

d 删除
y 复制
p 粘贴
“+y
“+p

复制

single line:yy
muitl line:nyy
copy block to destination:6,9 co 12

粘贴

paste:p

删除

x 删除一个字符
dd 删除一整行

撤销

u

重做

ctrl + r

合并

J 合并两行

退出保存

ZZ

more

错误汇总

发表于 2016-05-14   |   分类于 问题

解决Mac外接显示器字体模糊的问题

but failed to remove it when the web application was stopped. This is very likely to create a memory leak.

ThreadLocal 内存没有手动回收导致泄露

问题合辑

发表于 2016-05-10   |   分类于 问题
  • 客户端由于受到端口号的限制 理论上最大发出tcp链接是6万多,怎么提高

  • IO复用

未命名

发表于 2016-05-03
{"name":"Arthinking.GitHub.io","tagline":"","body":"arthinking的技术总结\r\n----------\r\n这是一个技术总结对比的思考区\r\n\r\n> Author: arthinking \r\n> E-mail: 1225538383@qq.com\r\n\r\n****\r\n\r\n# [Java](https://github.com/arthinking/arthinking/blob/master/Java.md \"Java\")\r\n\r\n# Mysql\r\n\r\n\r\n----------\r\n\r\n#GitHub相关资源\r\nmarkdown语法:https://github.com/guodongxiaren/test \r\nGitHub上README写法暨GFM语法解读:http://blog.csdn.net/wqvbjhc/article/details/27349209","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."}

MySQL 海量数据架构

发表于 2016-05-03   |   分类于 Database

Author: ChinSyun Pang
Weibo: arthinking_plus
Posted in: http://www.itzhai.com

MySQL要存储大数据,一般都是按照分库分表的方式进行的,类似的MongoDB也是一样的。

而淘宝版开源了一个数据库访问组件TDDL,主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制,它是一个基于集中式配置的JDBC datasource实现,具有主备份,读写分离,动态数据库配置等功能。

Cobar

MyCat

数据库分区分片框架

Github上看到了这个开源的也不错。

Cobar

MyCat

123…6
arthinking

arthinking

Struggle for HighPay zoom.
主站 http://www.itzhai.com/

120 日志
33 分类
51 标签
itzhai GitHub Twitter Weibo
Creative Commons
© 2016 arthinking
由 Hexo 强力驱动
主题 - NexT.Pisces