iyihua

  • 首页

  • 标签

  • 分类

  • 归档

系统分布式情况下最终一致性方案梳理

发表于 2016-08-10 | 更新于 2019-05-22 | 分类于 architecture

前言

目前的应用系统,不管是企业级应用还是互联网应用,最终数据的一致性是每个应用系统都要面临的问题,随着分布式的逐渐普及,数据一致性更加艰难,但是也很难有银弹的解决方案,也并不是引入特定的中间件或者特定的开源框架能够解决的,更多的还是看业务场景,根据场景来给出解决方案。根据笔者最近几年的了解,总结了几个点,更多的应用系统在编码的时候,更加关注数据的一致性,这样系统才是健壮的。

基础理论相关

说起事务,目前的几个理论,ACID事务特性,CAP分布式理论,以及BASE等,ACID在数据库事务中体现,CAP和BASE则是分布式事务的理论,结合业务系统,例如订单管理,例如仓储管理等,可以借鉴这些理论,从而解决问题。

  • ACID 特性
    A(原子性)事务的原子操作单元,对数据的修改,要么全部执行,要么全部不执行;
    C(一致性)在事务开始和完成时,数据必须保持一致状态,相关的数据规则必须应用于事务的修改,以保证数据的完整性,事务结束时,所有的内部数据结构必须正确;
    I(隔离性)保证事务不受外部并发操作的独立环境执行;
    D(持久性)事务完成之后,对于数据的修改是永久的,即使系统出现故障也能够保持;
  • CAP
    C(一致性)一致性是指数据的原子性,在经典的数据库中通过事务来保障,事务完成时,无论成功或回滚,数据都会处于一致的状态,在分布式环境下,一致性是指多个节点数据是否一致;
    A(可用性)服务一直保持可用的状态,当用户发出一个请求,服务能在一定的时间内返回结果;
    P(分区容忍性)在分布式应用中,可能因为一些分布式的原因导致系统无法运转,好的分区容忍性,使应用虽然是一个分布式系统,但是好像一个可以正常运转的整体
  • BASE
    BA: Basic Availability 基本业务可用性;
    S: Soft state 柔性状态;
    E: Eventual consistency 最终一致性;

最终一致性的几种做法

单数据库情况下的事务

如果应用系统是单一的数据库,那么这个很好保证,利用数据库的事务特性来满足事务的一致性,这时候的一致性是强一致性的。对于java应用系统来讲,很少直接通过事务的start和commit以及rollback来硬编码,大多通过spring的事务模板或者声明式事务来保证。

基于事务型消息队列的最终一致性

借助消息队列,在处理业务逻辑的地方,发送消息,业务逻辑处理成功后,提交消息,确保消息是发送成功的,之后消息队列投递来进行处理,如果成功,则结束,如果没有成功,则重试,直到成功,不过仅仅适用业务逻辑中,第一阶段成功,第二阶段必须成功的场景。对应上图中的C流程。

基于消息队列+定时补偿机制的最终一致性

前面部分和上面基于事务型消息的队列,不同的是,第二阶段重试的地方,不再是消息中间件自身的重试逻辑了,而是单独的补偿任务机制。其实在大多数的逻辑中,第二阶段失败的概率比较小,所以单独独立补偿任务表出来,可以更加清晰,能够比较明确的直到当前多少任务是失败的。对应上图的E流程。

业务系统业务逻辑的commit/rollback机制

这一点说的话确实不难,commit和rollback是数据库事务中的比较典型的概念,但是在系统分布式情况下,需要业务代码中实现这种,成功了commit,失败了rollback。

业务应用系统的幂等性控制

为啥要做幂等呢? 原因很简单,在系统调用没有达到期望的结果后,会重试。那重试就会面临问题,重试之后不能给业务逻辑带来影响,例如创建订单,第一次调用超时了,但是调用的系统不知道超时了是成功了还是失败了,然后他就重试,但是实际上第一次调用订单创建是成功了的,这时候重试了,显然不能再创建订单了。

  • 查询
    查询的API,可以说是天然的幂等性,因为你查询一次和查询两次,对于系统来讲,没有任何数据的变更,所以,查询一次和查询多次一样的。

  • MVCC方案
    多版本并发控制,update with condition,更新带条件,这也是在系统设计的时候,合理的选择乐观锁,通过version或者其他条件,来做乐观锁,这样保证更新及时在并发的情况下,也不会有太大的问题。例如update tablexxx set name=#name#,version=version+1 where version=#version# ,或者是 update tablexxx set quality=quality-#subQuality# where quality-#subQuality# >= 0 。

  • 单独的去重表
    如果涉及到的去重的地方特别多,例如ERP系统中有各种各样的业务单据,每一种业务单据都需要去重,这时候,可以单独搞一张去重表,在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。

  • 分布式锁
    还是拿插入数据的例子,如果是分布是系统,构建唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统,在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。

  • 删除数据
    删除数据,仅仅第一次删除是真正的操作数据,第二次甚至第三次删除,直接返回成功,这样保证了幂等。

  • 插入数据的唯一索引
    插入数据的唯一性,可以通过业务主键来进行约束,例如一个特定的业务场景,三个字段肯定确定唯一性,那么,可以在数据库表添加唯一索引来进行标示。

  • API层面的幂等
    这里有一个场景,API层面的幂等,例如提交数据,如何控制重复提交,这里可以在提交数据的form表单或者客户端软件,增加一个唯一标示,然后服务端,根据这个UUID来进行去重,这样就能比较好的做到API层面的唯一标示。

  • 状态机幂等
    在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机,就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。

异步回调机制的引入

A应用调用B,在同步调用的返回结果中,B返回成功给到A,一般情况下,这时候就结束了,其实在99.99%的情况是没问题的,但是有时候为了确保100%,记住最起码在系统设计中100%,这时候B系统再回调A一下,告诉A,你调用我的逻辑,确实成功了。其实这个逻辑,非常类似TCP协议中的三次握手。上图中的B流程。

类似double check机制的确认机制

还是上图中异步回调的过程,A在同步调用B,B返回成功了。这次调用结束了,但是A为了确保,在过一段时间,这个时间可以是几秒,也可以是每天定时处理,再调用B一次,查询一下之前的那次调用是否成功。例如A调用B更新订单状态,这时候成功了,延迟几秒后,A查询B,确认一下状态是否是自己刚刚期望的。上图中的D流程。

总结

上面的几点总结,更多的在业务系统中体现,在超复杂的系统中,数据的一致性,不是说简单的引入啥中间件能够解决的,更多的是根据业务场景,来灵活应对。

SOA分布式事务解决方案

发表于 2016-08-10 | 更新于 2019-05-22 | 分类于 architecture

这里是转的一段摘录:

传统分布式系统与当代的面向SOA的分布式系统有一定区别,论概念上来讲SOA是以服务为中心,既然以服务为中心就会有很多面向服务的设计原则。而传统的分布式系统没有服务的概念,也没有所谓的一切皆是服务的原则。而当代SOA则首要原则就要以服务为中心,针对服务的设计又有了很多服务设计原则。
SOA对服务还进行了类型的划分,按照服务的应用层次来分类:业务服务、组合服务、应用服务,包装服务等。再按照管理与运维的层面来分类:控制服务、调度服务、监控服务等等。传统的分布式系统是没有这些的,我们谈论的是当代SOA的分布式系统,所以我们强调的是以服务为中心,以服务设计原则为架构设计的指导要求,当代SOA是对传统分布式系统的一个迭代进化,不是一个时代的产物,SOA更加强调了以服务为首要原则,已经提升到了另外一个更加高级的层面。
本节我们交流一下在当代SOA分布式系统中的数据一致性问题,在SOA中这主要涉及两个层面来考虑,一个是服务层面、一个数据持久化层面。再按照一致性的基本要求,可以分为:读一致性、写一致性、会话一致性、最终一致性、实时一致性等几个维度,当然还有其他几个维度的一致性要求。
我们这里重点讨论在企业应用中实施SOA时遇到的一些比较棘手的数据一致性问题和解决方案,对于刚才提到的几个维度的一致性要求均具有重要的参考价值。
1.分布式事务(基于DTC的分布式事务)
以往包括目前很多项目还是倾向于使用DTC来处理分布式事务,这个方案多数适用于一般的企业应用,业务、访问量、数据量要求都不是很高的情况下。用DTC很方便,事务的自动传播、事务的自动感知、事务的自动回滚和提交,这都是中央DTC帮我们管理好了。
由于有中央DTC的统一协调,看似好像帮我们解决了很多我们需要考虑的问题,但是它也是整个平台的致命的瓶颈,一旦DTC由于某个问题出现错误,而且这种错误都是系统层面的错误,很多问题我们是无能为力的。如果出现问题,整个应用平台都无法完成任何一个跨服务的业务流程,这其实很危险,你不无法控制系统的稳定性。
这里总结,DTC用于一般的小型企业应用,不建议用在中等规模的企业应用中,不是说这个东西不好,而是无法控制它。
2.事务补偿(提供正向或反向的操作来让数据在业务上是一致的)
世界级SOA专家所编写的书籍里都提到了使用“补偿”操作来完成数据的不一致性,当我们编写了一个服务方法A,就需要一个服务方法A1的补偿接口来完成A服务的补偿操作。但是真实的业务情况下很难实施这种看起来好像很优美很柔性的设计。没有实践就没有发言权,我们公司的技术团队就实施过这种方案,但是很不理想,这跟技术本身及技术团队没关系,只是我们的平台业务太复杂,很难去“补偿”一个已经做过的操作。
这当然也要看你所面对的项目情况,量变引起质变,如果你的各种量都上去了,这个“补偿”方案不实际,而且很难在数据层面进行“补偿“。总之,这不是一个中长期的方案。
3.异步EDA(基于异步事件流来实现柔性的分布式事务)
EDA简称”事件驱动架构“。多个系统之间通过传播”事件“来驱动整个业务的运转。系统之间没有紧耦合的同步调用的操作,都是通过发出异步的“事件”来通知下一个业务环节。
可能你会有一个疑问,异步操作,是不是系统之间延迟会很长,其实不是,现在有很多成熟的消息中间件在内网内几乎是毫秒级别的延迟,至于跨机房就看物理上的距离了。
异步操作有很多好处,这里我就不浪费大家时间重复那些好处。使用EDA实现系统之间的一个松散的事务关系,要把控好项目的质量,对系统的非功能需求、BUG数等等可能会影响业务操作中断的地方都要建立起适当的机制,让这些问题尽早的在线下解决。比如可以实施UnitTest、持续集成等一些敏捷的方法论。

What's the simplest way to print a Java array?

发表于 2016-08-08 | 更新于 2019-05-22 | 分类于 java

Examples:

  • Simple Array:
    1
    2
    String[] array = new String[] {"John", "Mary", "Bob"};
    System.out.println(Arrays.toString(array));

Output:

1
[John, Mary, Bob]

  • Nested Array:
    1
    2
    3
    4
    String[][] deepArray = new String[][] {{"John", "Mary"}, {"Alice", "Bob"}};
    System.out.println(Arrays.toString(deepArray));
    //output: [[Ljava.lang.String;@106d69c, [Ljava.lang.String;@52e922]
    System.out.println(Arrays.deepToString(deepArray));

Output:

1
[[John, Mary], [Alice, Bob]]

  • double Array:
    1
    2
    double[] doubleArray = { 7.0, 9.0, 5.0, 1.0, 3.0 };
    System.out.println(Arrays.toString(doubleArray));

Output:

1
[7.0, 9.0, 5.0, 1.0, 3.0 ]

  • int Array:
    1
    2
    int[] intArray = { 7, 9, 5, 1, 3 };
    System.out.println(Arrays.toString(intArray));

Output:

1
[7, 9, 5, 1, 3 ]

Java 中int和Integer的区别

发表于 2016-08-04 | 更新于 2019-05-22 | 分类于 java

Java中int和Integer的区别

Java各种数据类型详细介绍及其区别

  • 基本类型,或者叫做内置类型,是JAVA中不同于类的特殊类型。

Java中的简单类型从概念上分为四种:实数、整数、字符、布尔值。但是有一点需要说明的是,Java里面只有八种原始类型,其列表如下:

实数:double、float

整数:byte、short、int、long

字符:char

布尔值:boolean

复杂类型和基本类型的内存模型本质上是不一样的,简单数据类型的存储原理是这样的:所有的简单数据类型不存在“引用”的概念,简单数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,而Java语言里面只有这八种数据类型是这种存储模型;而其他的只要是继承于Object类的复杂数据类型都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的。

  • Java的简单数据讲解列表如下:

int:int为整数类型,在存储的时候,用4个字节存储,范围为-2,147,483,648到2,147,483,647,在变量初始化的时候,int类型的默认值为0。

short:short也属于整数类型,在存储的时候,用2个字节存储,范围为-32,768到32,767,在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。

long:long也属于整数类型,在存储的时候,用8个字节存储,范围为-9,223,372,036,854,775,808到9,223,372,036, 854,775,807,在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。

byte:byte同样属于整数类型,在存储的时候,用1个字节来存储,范围为-128到127,在变量初始化的时候,byte类型的默认值也为0。

float:float属于实数类型,在存储的时候,用4个字节来存储,范围为32位IEEEE 754单精度范围,在变量初始化的时候,float的默认值为0.0f或0.0F,在初始化的时候可以写0.0。

double:double同样属于实数类型,在存储的时候,用8个字节来存储,范围为64位IEEE 754双精度范围,在变量初始化的时候,double的默认值为0.0。

char:char属于字符类型,在存储的时候用2个字节来存储,因为Java本身的字符集不是用ASCII码来进行存储,是使用的16位Unicode字符集,它的字符范围即是Unicode的字符范围,在变量初始化的时候,char类型的默认值为’u0000’。

boolean:boolean属于布尔类型,在存储的时候不使用字节,仅仅使用1位来存储,范围仅仅为0和1,其字面量为true和false,而boolean变量在初始化的时候变量的默认值为false。

Integer是int的封装类,里面有很多进行处理的静态方法

Integer是对象而int不是,内存的分配位置也不一样

Integer的属性和其他类一样的!在方法里都是引用传递,而原始类型是值传递!

jdk1.5以后可以从int自动装箱Integer类。

int是为了兼容以前的编程语言使用的基本类型,目的是让程序效率更高,以为它是直接分配到栈上的。所以它不是对象,不能有类似 int.operation()的操作。

Integer是java中一切都是对象这个大前提下的int的包装类型,可以使用方法,是个对象,是用new分配到堆上的。

jdk1.5后,引入了类似c#中的自动装、拆箱,使得Integer i = 1;这样的表达直接可行。

int是一种基本数据类型,而Integer是相应于int的类类型,称为对象包装。

实现这种对象包装的目的主要是因为类能够提供必要的方法,用于实现基本数据类型的数值与可打印字符串之间的转换,以及一些其他的实用程序方法;

另外,有些数据结构库类只能操作对象,而不支持基本数据类型的变量,包装类提供一种便利的方式,能够把基本数据类型转换成等价的对象,从而可以利用数据结构库类进行处理。

int 是基本类型,(int)(Math.Random()*100)就是一个数,可以进行加见乘除。 Integer是class ,那么 new Integer(temp)就是一个对象了,可以用到Integer这个class的方法,例如用intvalue()可以返回这个int的值。int a=1;这是基本数据类型是能参与运算的.而Integer b= new Integer(1);c=b.floatvalue;Float a= new Float(null);是可以的用Float初始化一个对象这个a有很多方法而float a;就没有因为原始没有引用类,java 提供两种不同的类型:引用类型(或者封装类型,Warpper)和原始类型(或内置类型,Primitive)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。

  • 原始类型 封装类

boolean Boolean

char Character

byte Byte

short Short

int Integer

long Long

float Float

double Double

引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。

1 、Boolean VS boolean

public final class Boolean extends Object implementsSerializable,Comparable

Boolean 类将基本类型为boolean的值包装在一个对象中。一个Boolean类型的对象只包含一个类型为boolean的字段。此外,此类还为boolean和String的相互转换提供了许多方法,并提供了处理 boolean时非常有用的其他一些常量和方法。

2、 Byte VS byte

public final class Byte extends Number implements Comparable Byte类将基本类型 byte的值包装在一个对象中。一个Byte类型的对象只包含一个类型为 byte的字段。此外,该类还为 byte和 String的相互转换提供了几种方法,并提供了处理 byte时非常有用的其他一些常量和方法。

3、 Character VS char

public final class Character extends Object implements Serializable, Comparable

Character类在对象中包装一个基本类型char的值。

Character类型的对象包含类型为char的单个字段。此外,该类提供了几种方法,以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然。

4 、Double VS double

public final class Double extends Number implements Comparable Double类在对象中包装了一个基本类型double的值。每个Double类型的对象都包含一个double类型的字段。此外,该类还提供了多个方法,可以将double转换为String,将String转换为double,还提供了其他一些处理double时有用的常量和方法。

5、 Float VS float

public final class Float extends Number implements Comparable

Float类在对象中包装了一个float基本类型的值。Float类型的对象包含一个float类型的字段。此外,此类提供了几种方法,可在float类型和String类型之间互相转换,并且还提供了处理float类型时非常有用的其他一些常量和方法。

6、 Integer VS int

public final class Integer extends Number implements Comparable

Integer类在对象中包装了一个基本类型int的值。Integer类型的对象包含一个int类型的字段。

此外,该类提供了多个方法,能在int类型和String类型之间互相转换,还提供了处理int类型时非常有用的其他一些常量和方法。

7 Long VS long

public final class Long extends Number implements Comparable

Long类在对象中封装了基本类型long的值。每个Long类型的对象都包含一个long类型的字段。

此外,该类提供了多个方法,可以将long转换为String,将String转换为long,除此之外,还提供了其他一些处理long时有用的常量和方法。

8、 Short VS short

public final class Short extends Number implements Comparable

Short类在对象中包装基本类型short的值。一个Short类型的对象只包含一个short类型的字段。另外,该类提供了多个方法,可以将short转换为String,将String转换为short,同时还提供了其他一些处理short时有用的常量和方法。

9、public final class Voidextends Object

Void 类是一个不可实例化的占位符类,它保持一个对代表 Java 关键字 void 的 Class 对象的引用。

类的对象才能为null,不能把null赋值给一个变量不能,如int m=null;但可以String s=null;因为String是个类。

[dev][collect][2016-07]collect-of-dev

发表于 2016-08-03 | 更新于 2019-05-22 | 分类于 collect

2016-07

2016-07-29

  • 搭建ElasticSearch-2.x Logstash-2.x Kibana-4.5.x Kafka为消息中心的ELK日志平台
    https://www.iamle.com/archives/2058.html

2016-07-23

  • discover meteor
    http://zh.discovermeteor.com/chapters/introduction/

  • webpack gitbook
    https://hulufei.gitbooks.io/react-tutorial/content/webpack.html

2016-07-22

  • Spring Boot应用的测试——Mockito
    http://www.jianshu.com/p/972cd6b93206

  • 利用Mockito模拟DB
    http://www.jianshu.com/p/c1c495f231ea

  • 在Spring Boot项目中使用Spock框架
    http://www.jianshu.com/p/f1e354d382cd

  • Spring Boot Testing
    http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

  • Integration Testing a Spring Boot Application
    https://www.jayway.com/2014/07/04/integration-testing-a-spring-boot-application/

2016-07-21

  • Growth: 全栈增长工程师指南
    http://growth.phodal.com/#

2016-07-19

  • 微服务架构的分布式事务解决方案
    http://www.roncoo.com/details?cid=7ae3d7eddc4742f78b0548aa8bd9ccdb

2016-07-16

  • 使用 Vaadin 实现全堆栈 Java Web 开发
    http://www.ibm.com/developerworks/cn/java/j-full-stack-java-web-dev-vaadin/

  • Creating CRUD UI with Vaadin
    https://spring.io/guides/gs/crud-with-vaadin/
    git clone https://github.com/spring-guides/gs-crud-with-vaadin.git

  • 当当网开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用
    http://www.infoq.com/cn/news/2014/10/dubbox-open-source

  • 在Dubbo中开发REST风格的远程调用(RESTful Remoting)
    http://dangdangdotcom.github.io/dubbox/rest.html

  • 使用JavaConfig方式配置dubbox
    http://dangdangdotcom.github.io/dubbox/java-config.html

  • Microservices with Spring
    https://spring.io/blog/2015/07/14/microservices-with-spring

2016-07-14

  • Import/Read excel file 2003 or 2007 with Spring MVC
    http://lvtutorial.com/spring-mvc/read-excel-file-2003-or-2007-with-spring-mvc.html
  • SPRING BOOT FILE UPLOAD WITH AJAX
    http://blog.netgloo.com/2015/02/08/spring-boot-file-upload-with-ajax/
    https://github.com/netgloo/spring-boot-samples

2016-07-13

  • cSphere Docker 实训课程
    http://git.oschina.net/dockerf/docker-training

  • Spring Cloud方面的博客
    http://blog.didispace.com/

  • 白话 IT 之浅谈 ELK 日志系统
    https://mp.weixin.qq.com/s?__biz=MzA4ODgwNjk1MQ==&mid=2653788351&idx=1&sn=a38a0b95b63186b789423195fa09f91d&scene=0&key=77421cf58af4a653195d70dcdaaed6e78c06fe4711f95c19349f5ca630c1e7f8481f6f21ba392beedcc329dbcc331cc8&ascene=0&uin=MTM4NTI5NTI4Mg%3D%3D&devicetype=iMac+MacBookAir6%2C2+OSX+OSX+10.9.5+build(13F1808)&version=11020201&pass_ticket=MxW0cGHvKj7WTMRpzBpE%2FND%2BPkUf91pVVetULpXmjhLbsP9btRTYFt3DF6QSZ5Ct

  • 用户行为分析项目
    https://github.com/YihuaWanglv/piwik

2016-07-12

  • 使用API网关构建微服务
    http://www.infoq.com/cn/articles/construct-micro-service-using-api-gateway

  • 使用Spring Cloud和Docker构建微服务
    http://www.dockone.io/article/510
    原文链接:
    http://www.kennybastani.com/2015/07/spring-cloud-docker-microservices.html?mkt_tok=3RkMMJWWfF9wsRonuqTMZKXonjHpfsX57ukoWaC0lMI%2F0ER3fOvrPUfGjI4ATcdqI%2BSLDwEYGJlv6SgFQ7LMMaZq1rgMXBk%3D

  • The API Gateway Pattern: Angular JS and Spring Security Part IV
    https://spring.io/blog/2015/01/28/the-api-gateway-pattern-angular-js-and-spring-security-part-iv

  • git demo for spring-boot-microservices
    https://github.com/YihuaWanglv/spring-boot-microservices
    https://github.com/YihuaWanglv/spring-cloud-microservice-example

  • Implementing Netflix Zuul in Spring Cloud
    http://kubecloud.io/apigatewaypattern/
    http://start.spring.io/

2016-07-11

  • 使用 Vuex + Vue.js 构建单页应用
    https://segmentfault.com/a/1190000005891026
    https://github.com/lichenbuliren/vuex-notes-app

  • 两篇关于微服务api gateway的著名文章
    http://microservices.io/patterns/apigateway.html
    https://www.nginx.com/blog/building-microservices-using-an-api-gateway/

2016-07-08

  • 使用spring boot和thrift、zookeeper建立微服务
    http://www.cnblogs.com/skyblog/p/5535418.html

  • Jenkins+Docker搭建持续集成测试环境
    http://www.dockone.io/article/1464

  • spring boot+spring cloud服务化系列博客
    http://www.cnblogs.com/skyblog/category/774535.html

2016-07-06

  • 使用Jenkins搭建iOS/Android持续集成打包平台
    http://debugtalk.com/post/iOS-Android-Packing-with-Jenkins

  • React-Native For Android 环境搭建及踩坑
    http://www.imbeta.cn/react-native-for-android-huan-jing-da-jian-ji-cai-keng.html

  • React Native 开发培训免费书
    https://unbug.gitbooks.io/react-native-training/content/

  • Android Studio插件整理
    https://ydmmocoo.github.io/2016/06/28/Android-Studio%E6%8F%92%E4%BB%B6%E6%95%B4%E7%90%86/

  • FastDFS + Nginx 反向代理缓存 安装与配置
    http://www.linux178.com/storage/fastdfs-nginx-cache.html

How to synchronize ArrayList in java with example

发表于 2016-07-28 | 更新于 2019-05-22 | 分类于 java

There are two ways to synchronize explicitly:

  • Using Collections.synchronizedList() method
  • Using thread-safe variant of ArrayList: CopyOnWriteArrayList

Example 1: Collections.synchronizedList() method for Synchronizing ArrayList

In this example we are using Collections.synchronizedList() method. The important point to note here is that iterator should be in synchronized block in this type of synchronization as shown in the below example.

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
package beginnersbook.com;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;

public class Details {

public static void main(String a[]){
List<String> syncal =
Collections.synchronizedList(new ArrayList<String>());

//Adding elements to synchronized ArrayList
syncal.add("Pen");
syncal.add("NoteBook");
syncal.add("Ink");

System.out.println("Iterating synchronized ArrayList:");
synchronized(syncal) {
Iterator<String> iterator = syncal.iterator();
while (iterator.hasNext())
System.out.println(iterator.next());
}
}
}
  • Output:
    1
    2
    3
    4
    Iterating synchronized ArrayList:
    Pen
    NoteBook
    Ink

Method 2: Using CopyOnWriteArrayList

CopyOnWriteArrayList is a thread-safe variant of ArrayList.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package beginnersbook.com;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Iterator;

public class Details {

public static void main(String a[]){
CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>();

//Adding elements to synchronized ArrayList
al.add("Pen");
al.add("NoteBook");
al.add("Ink");

System.out.println("Displaying synchronized ArrayList Elements:");
//Synchronized block is not required in this method
Iterator<String> iterator = al.iterator();
while (iterator.hasNext())
System.out.println(iterator.next());
}
}
  • Output:
    1
    2
    3
    4
    Displaying synchronized ArrayList Elements:
    Pen
    NoteBook
    Ink

多版本部署如何使用nginx根据url参数api_version值进行路由

发表于 2016-07-26 | 更新于 2019-05-22 | 分类于 deploy

目标:实现多版本部署,并且使用nginx根据HTTP请求中的url参数’api_version’来进行请求的路由,将对应版本的请求分发到目标版本实例当中。

实际测试场景:

tomcat部署启动了2个实例:

  • 127.0.0.1:8080
  • 127.0.0.1:8081

本地部署一个nginx服务器,对所有请求进行代理和路由。
部署的nginx简单配置如下:

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
worker_processes  1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
location /client {
if ( $arg_api_version = '1.0.0' ){
proxy_pass http://localhost:8080;
break;
}
if ( $arg_api_version = '1.0.1' ){
proxy_pass http://localhost:8081;
break;
}
proxy_pass http://localhost:8080;
}
location / {
proxy_pass http://localhost:8080;
}
}
}

预先配置好本地host,以便直接使用域名访问。

1
127.0.0.1 local.xxx.com

说明:

  • 1.“listen 80;”配置nginx监听80端口,接收所有请求。
  • 2.“location /client { … }”部分将会匹配到所有以“/client”开头的请求
  • 3.“location /client { … }”部分,通过$http_api_version获取到url中字段名为’api_version’的参数值,用if判断版本值,做相应的路由。最后如果都没有匹配到,就会匹配最后一行“proxy_pass http://localhost:8080;”走8080实例。
  • 4.“location / { … }” 部分将会匹配其余的所有请求,路由到“http://localhost:8080”。这样,除“/client”前缀开头的请求外,都会默认匹配到一个固定的实例。别的请求都不会受到多版本路由配置的影响。

Transform objects with guava(使用Guava库转换对象)

发表于 2016-07-23 | 更新于 2019-05-22 | 分类于 java

Transform objects with guava

All the function is a specific type of class that has an apply method that can be overridden. In the example, the apply method accepts a string as a parameter and returns an integer or the length of the string. Scrolling a bit further in the documentation, the most common use of functions is transforming collections.

1
2
3
4
5
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
};

Convert string to Enum

I have seeded some data so we can get right to the examples and you don’t have to watch me type. The first example to show is transforming a string to Enum. Enums are quite popular and you may want to transform as it might be stored as a string in a database or some value.

Taking a look at the Day enum we created, it is a simple class that represents the days of the week:

1
2
3
4
5
public enum Day {

SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY;
}

We may have a list of strings with various strings representing days, Wednesday, Sunday, Monday… What we want to do is convert the list of strings to enums. There is a function in the Enum utility class in guava valueOfFunction that allows you to pass in the enum you want to convert to. In our case, Day.Class. Since we just upgraded to Guava 16.0, the valueOfFunction has been deprecated in preference of stringConverter. The stringConverter will return a function that converts a string to an Enum. Now we have a function that can be passed to guava utilities, in this case Iterables utility class by calling the transform method which will convert each string of list to a day enum.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void transform_string_to_enum () {

List<String> days = Lists.newArrayList(
"WEDNESDAY",
"SUNDAY",
"MONDAY",
"WEDNESDAY");

Function<String, Day> stringToDayEnum = Enums.stringConverter(Day.class);

Iterable<Day> daysAsEnum = Iterables.transform(days, stringToDayEnum);

for (Day day : daysAsEnum) {
System.out.println(day);
}
}

Output

1
2
3
4
WEDNESDAY
SUNDAY
MONDAY
WEDNESDAY

Convert from one object to another

The next example will look at is how to convert one object to another. Quite often we need to go to a legacy system to get data and populate a new set of domains for our new system. Or we may get data from a web service or whatever it may be. For this example, I created two objects. ETradeInvestment and TdAmeritradeInvestment which contain similar attributes of different types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ETradeInvestment {

private String key;
private String name;
private BigDecimal price;

...
}

public class TdAmeritradeInvestment {

private int investmentKey;
private String investmentName;
private double investmentPrice;

...
}

There is a number of other utilities such as BeanUtils.copyProperties in apache commons or written your own if statements and have made it work that way. If we don’t have a list of objects, we can call a method on function that will return a just a single object. We want to create the function that will map the TdAmeritradeInvestment to ETradeInvestment. If you ever get lost, you can use code assist or just remember the <F, T> just means from object, to object. For each iteration of the list, a new ETradeInvestment object will be created while mapping the TdAmeritradeInvestment to it. For the key, we will use the Ints.stringConverter which allows us to convert from a string to an int.

If we want to get some reuse out of this function, we could extract it outside this method or we can just use it inline. We will use the Lists utility to transform the list, above we use Iterables and there is also FluentIterables and Collections2. There is a lot of different ways which guava provides to use a function. If we run this code, we have EtradeInvestment object with the key, name and price. As a disclosure, I don’t own any of these investments and were pulled from Google home page under the trending section. That did it, we converted from the TdAmeritradeInvestment to ETradeInvestment.

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
@Test
public void convert_tdinvestment_etradeinvestment () {

List<TdAmeritradeInvestment> tdInvestments = Lists.newArrayList();
tdInvestments.add(new TdAmeritradeInvestment(555, "Facebook Inc", 57.51));
tdInvestments.add(new TdAmeritradeInvestment(123, "Micron Technology, Inc.", 21.29));
tdInvestments.add(new TdAmeritradeInvestment(456, "Ford Motor Company", 15.31));
tdInvestments.add(new TdAmeritradeInvestment(236, "Sirius XM Holdings Inc", 3.60));

// convert a list of objects
Function<TdAmeritradeInvestment, ETradeInvestment> tdToEtradeFunction = new Function<TdAmeritradeInvestment, ETradeInvestment>() {

public ETradeInvestment apply(TdAmeritradeInvestment input) {
ETradeInvestment investment = new ETradeInvestment();
investment.setKey(Ints.stringConverter().reverse()
.convert(input.getInvestmentKey()));
investment.setName(input.getInvestmentName());
investment.setPrice(new BigDecimal(input.getInvestmentPrice()));
return investment;
}
};

List<ETradeInvestment> etradeInvestments = Lists.transform(tdInvestments, tdToEtradeFunction);

System.out.println(etradeInvestments);
}

Output

1
2
3
4
5
6
[
ETradeInvestment{key=555, name=Facebook Inc, price=57.50},
ETradeInvestment{key=123, name=Micron Technology, Inc., price=21.28},
ETradeInvestment{key=456, name=Ford Motor Company, price=15.31},
ETradeInvestment{key=236, name=Sirius XM Holdings Inc, price=3.60}
]

Convert an object

If you have one single investment or object you want to convert, you can call the apply method directly on the function that will do the conversion.

ETradeInvestment faceBookInvestment = tdToEtradeFunction
.apply(new TdAmeritradeInvestment(555, “Facebook Inc”, 57.51));
Output

1
ETradeInvestment{key=555, name=Facebook Inc, price=57.50}

Convert list to map

One other really neat way to use function convert a list to a map. You may want to look up an object based on some object property. In this example, we want to create a map of TdAmeritradeInvestment based on the investment key. Taking a look we can use the Maps utility calling the uniqueIndex method which accepts a list and a function. The function, or the keyfunction, is used to produce the key for each value in the iterable. In this instance, the function will map from a TdAmeritradeInvestment and return an integer which will represent the key in the map.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void transform_list_to_map () {

List<TdAmeritradeInvestment> tdInvestments = Lists.newArrayList();
tdInvestments.add(new TdAmeritradeInvestment(555, "Facebook Inc", 57.51));
tdInvestments.add(new TdAmeritradeInvestment(123, "Micron Technology, Inc.", 21.29));
tdInvestments.add(new TdAmeritradeInvestment(456, "Ford Motor Company", 15.31));
tdInvestments.add(new TdAmeritradeInvestment(236, "Sirius XM Holdings Inc", 3.60));

ImmutableMap<Integer, TdAmeritradeInvestment> investmentMap = Maps
.uniqueIndex(tdInvestments,
new Function<TdAmeritradeInvestment, Integer>() {

public Integer apply(TdAmeritradeInvestment input) {
return new Integer(input.getInvestmentKey());
}
});

System.out.println(investmentMap);

}
`

Output

1
2
3
4
5
6
{
555=TdAmeritradeInvestment{key=555, name=Facebook Inc, price=57.51},
123=TdAmeritradeInvestment{key=123, name=Micron Technology, Inc., price=21.29},
456=TdAmeritradeInvestment{key=456, name=Ford Motor Company, price=15.31},
236=TdAmeritradeInvestment{key=236, name=Sirius XM Holdings Inc, price=3.6}
}

原地址:http://www.leveluplunch.com/java/tutorials/002-transform-objects-with-guava/

Springboot应用中如何针对springmvc的controller写单元测试

发表于 2016-07-23 | 更新于 2019-05-22 | 分类于 java-spring

An example test for your controller can be something as simple as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DemoApplicationTests {

final String BASE_URL = "http://localhost:8080/";
private MockMvc mockMvc;

@Before
public void setup() {
this.mockMvc = standaloneSetup(new HelloWorld()).build();
}

@Test
public void testSayHelloWorld() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));

}
}

The new testing improvements that debuted in Spring Boot 1.4.M2 can help reduce the amount of code you need to write situation such as these.

The test would look like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RunWith(SpringRunner.class)
@WebMvcTest(HelloWorld.class)
public class UserVehicleControllerTests {

@Autowired
private MockMvc mvc;

@Test
public void testSayHelloWorld() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));

}

}

还有一种方式是使用TestRestTemplate

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
45
46
47
package controller;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;

import ipicture.service.post.AppServicePost;
import ipicture.service.post.model.JsonObject;
import ipicture.service.post.model.Subject;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AppServicePost.class)
@WebIntegrationTest("server.port:8083")// 使用0表示端口号随机,也可以具体指定如8888这样的固定端口
public class SubjectControllerTest {

private RestTemplate template = new TestRestTemplate();
@Value("${server.port}")// 注入端口号
private int port;

private String getBaseUrl() {
return "http://localhost:" + port;
}

@Test
public void test() {
Subject s = new Subject();
s.setCreator(1l);
s.setCreated(new Date());
s.setSubjectName("test5");
s.setDescr("test subject");
s.setDeleted(0);
s.setParent_id(0);
s.setStatus(0);
s.setType(0);
String url = getBaseUrl() + "/subject/save";
String result = template.postForObject(url, s, String.class);
}
}

关于java项目中如何读取配置文件

发表于 2016-07-22 | 更新于 2019-05-22 | 分类于 java

关于java项目中如何读取配置文件

- Using multiple property files (via PropertyPlaceholderConfigurer) in multiple projects/modules

If you ensure that every place holder, in each of the contexts involved, is ignoring unresolvable keys then both of these approaches work. For example:

1
2
3
4
5
<context:property-placeholder
location="classpath:dao.properties,
classpath:services.properties,
classpath:user.properties"
ignore-unresolvable="true"/>

or

1
2
3
4
5
6
7
8
9
10
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:dao.properties</value>
<value>classpath:services.properties</value>
<value>classpath:user.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>

- Spring MVC : read file from src/main/resources

1
2
Resource resource = new ClassPathResource(fileLocationInClasspath);
InputStream resourceInputStream = resource.getInputStream();

- How do I load a resource and use its contents as a string in Spring

1
2
3
<bean id="contents" class="org.apache.commons.io.IOUtils" factory-method="toString">
<constructor-arg value="classpath:path/to/resource.txt" type="java.io.InputStream" />
</bean>

This solution requires Apache Commons IO.

Another solution, suggested by @Parvez, without Apache Commons IO dependency is

1
2
3
4
5
6
7
<bean id="contents" class="java.lang.String">
<constructor-arg>
<bean class="org.springframework.util.FileCopyUtils" factory-method="copyToByteArray">
<constructor-arg value="classpath:path/to/resource.txt" type="java.io.InputStream" />
</bean>
</constructor-arg>
</bean>

1…456…9
Wanglv Yihua

Wanglv Yihua

82 日志
20 分类
206 标签
RSS
© 2019 Wanglv Yihua
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v6.4.1