Orika-Java Bean 映射框架

Orika

今天主要介绍一款Java Bean 映射框架—Orika

在此之前,先抛出几个问题,以便加深对orika的理解

  • Q1:什么是Java Bean 映射框架?
    • A:简单地说,就是从一个Bean A映射(转换)成另一个Bean B。举个粟子,实际开发场景中,很多时候都用到VO、BO、PO、DTO等POJO,我们需将其中一个POJO转换成另一个POJO。
  • Q2:如果要从一个Bean转换成另一个Bean,使用b.setXXX(a.getXXX()),手动转换不可以吗?
    • A:当然可以,但可以试想一下,当Bean的属性非常多(十几二十个)的时候,手动get set 不旦麻烦,还容易出错,浪费时间,纯体力劳动。
  • Q3:我听说过BeanUtils.copyProperties(a,b)也可以实现Bean属性的拷贝,它跟Orika有什么区别?
    • A:无论spring的BeanUtils.copyProperties还是apache.commons的BeanUtils.copyProperties,其底层实现机制都是运行时反射,其性能可想而知。在映射场景不频繁的场景或许还可以用一用,但是在一个频繁转换的场景,这样的性能损失,是不能接受的。

Orika是什么?

Orika是一个Java Bean映射框架,它将数据从一个对象递归拷贝到另一个对象。在开发多层应用程序时可能非常有用。其目的在于简化一个对象到另一个对象之间的映射过程。

Orika能干什么?

Orika能提供如下的功能:

  1. 映射复杂和深层结构的对象
  2. 无论是“Flatten”(扁平的)或“Expand”(深层次的)对象,都可以将嵌套属性映射到顶级属性,反之亦然。
  3. 随时快速地创建映射器,并使用自定义来控制部分或全部映射
  4. 处理代理或增强对象(如Hibernate或各种模拟框架)
  5. 通过一个配置,就可以实现双向映射
  6. 可以将任意POJO属性映射到List、Arrays、Map

Orika实现原理

Orika的设计思路就是预先通过javaassist把Java Bean之间的映射关系一次性生成目标拷贝方法代码。这样就可以避免在Bean映射环节一次次的读取映射规则。这就是Orika性能提升根本的原因。因此,理论上以生成的目标Java代码来运行映射是拷贝模式所能取到性能最大值的方法

注:Orika是我做组卷服务时候新学的一个框架,当时发现BeanUtils.copyProperties太慢了,根本不能忍。偶然的机会发现了这么一个东西,这真是刚想瞌睡就有人送上枕头。但是网上关于这个框架的资料非常少,没什么太实用的资料。可能原因有俩,一是知道的人少,二是它太简单易用了,根本就不需要详细地了解它,简单地配置后就能使用。

在此附上一个不知道几年前的参考资料:http://wangbt5191-hotmail-com.iteye.com/blog/1632444

时间有限,今天只做简单的理论介绍,下次将给出实战性的代码。

                                                                                                                           周雄

                                                                                                           搞技术,我们是认真的

浅谈对“分布式事务”的理解

  • 什么是数据库“事务”?

一句话简述一组sql操作,要么都成功执行,要么都不执行。

  • “事务”存在的意义是什么?

保证数据一致性。
eg:A转账给B 100块钱,A余额减少100 并且B余额增加100。确保不出现 A减少100 B没增加 或 A没减少 B增加的情况。


~~~~~~~~~~上面讲的啰嗦了一些基础知识 下面步入正题~~~~~~~~~~


  • 什么是“分布式事务”?

一组sql操作,分别写在不同的系统中,甚至这些sql在操作不同的数据库,在这种情况下,要么都执行成功,要么都不执行。

  • 举例描述当前问题

eg:机构开教师账号。
这个动作在系统中分为两步。
步骤1:将教师账号的业务数据写入DB。
步骤2:将开账号这个动作以及消耗爱豆数记录到流水表中,并减少机构爱豆余额。

目前步骤1由业务系统实现,步骤2由财务系统实现,是两个微服务,这时想使用事务并不是简单的一个@Transactional就能实现了。

  • 数据一致性处理方案

1.强一致性:

a.业务整合:
将开账号业务与财务扣费逻辑写在一个服务中,这也是老系统的实现逻辑,虽然保证了数据一致性 也规避了分布式事务,但把多个相关性不大的业务放在同一个系统中,业务职责不清晰,不利于维护。(极不推荐)

b.使用分布式事务 3次握手方案:
(1)询问服务方是否可执行 得到回应(yes/no)。
(2)服务方回应可执行后,发起预备执行申请,得到预备申请是否成功(true/false)。
(3)服务方回应申请成功后,发起提交申请,得到提交申请是否成功(true/false)。

3次握手
优点:保证数据强一致性,执行成功后立即查询,即可获得正确数据。

缺点:这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高。

2.终一致性:

MQ消息队列

(由于本人对此方案了解较少,并且远没有达到对网上的技术“一看就懂”的地步,所以此方案的原理下周再续。)

技术与方案参考地址:

http://www.cnblogs.com/dinglang/p/5679542.html

http://blog.csdn.net/bluishglc/article/details/7612811

粗谈Patton的《软件测试》

Ron Patton的《软件测试》是一本经典的软件测试方面的书籍,作者是一位有着多年软件测试经验的测试工程师,他深入浅出的讲了许多他对软件测试的见解与知识。虽然这是一本十多年前出版的老书,许多技术已经被更替,但是他对于测试的讲解还是令我受益匪浅,我总结了一些可能对大家有用的部分。
首先我们了解一下Patton对软件缺陷做的定义,他认为符合下面5个规则的才能叫软件缺陷:
1.软件未实现产品说明书要求的功能。
2.软件出现了产品说明书指明不应该出现的错误。
3.软件实现了产品说明书未达到的功能。
4.软件未事先产品说明书虽未明确提及但应该实现的目标。
5.软件测试员认为软件难以理解、不易使用、运行速度缓慢,或者最终用户认为不好。
那么为什么会出现软件缺陷
1.首先可能是产品说明书不够全面,经常更改,或者整个开发小组没有很好的沟通。为软件做计划是极其重要的,这一步没做好就可能会产生上述第4、第5类的缺陷。
2.然后可能是设计,这是程序员规划软件的过程,这个过程好比是建筑师规划蓝图的过程,这个过程产生缺陷的原因跟产品说明书是一样的–随意,易变,沟通不足
3.最后是编码错误,通常代码错误可以归咎于软件的复杂性、文档不足、进度压力或者普通的低级错误。
软件测试的目的就是找到软件的缺陷,但是书中却说软件是不可能完美的,软件测试也不仅仅是技术的问题。
1.完全测试是不可能的。主要有以下4个原因:
a.输入量太大
b.输出结果太多
c.软件执行路径太多
d.软件说明书是主观的,而使用者是客观的
2.软件测试是有风险的行为。
软件测试不可能测试所有的状况,不完全测试的地方又有可能存在软件缺陷。软件终归是要发布的,所以测试需要停止,但如果过早的停下来,就还有一部分没有测试到。那么选择一些关键的部分测试就是冒险的行为。测试员就要学会一个关键的思想:如何把巨大规模的测试减少到可控制的范围,以及针对风险做明确的抉择。
3.测试无法显示潜伏的软件缺陷。
就像除虫公司来家里杀虫不能把房子里所有的虫子杀完–总有些虫子藏在墙里没有找到。软件测试也没办法找到一些隐藏起来的缺陷。
4.找到的缺陷越多,就说明软件缺陷越多
很多时候中找到一个缺陷,就会接二连三地找到更多。其原因是:
a.程序员也有心情不好的时候,烦躁的人容易犯一些自己无法发现的小错误。一个缺陷的出现很可能表明附近还有更多的缺陷。
b.程序员容易犯同样的错误,每个人都有自己的习惯,在没有发现这个习惯性错误之前,程序员可能已经造成好几个一样的缺陷。
c.某些缺陷很可能是冰山一角,一个找不到原因的、不明显的错误很可能后面隐藏了一个巨大的缺陷。
5.杀虫剂怪事
就像虫子会对杀虫剂产生抗体一样,测试员用同样的方法测程序也会漏过新的错误,所以测试员要不断编写新的、不同的测试程序。
6.并非所有软件缺陷都要修复,有许多原因会使一个团队放弃修复某些缺陷:
a.没有足够的时间,产品必须要马上上线,但还有一些缺陷没有修复。
b.不算真正的软件缺陷。测试可能错误的把某些缺陷当作功能看待,而没有上报。
c.修复风险太大。当软件本身脆弱复杂时或者产品发布迫在眉睫,修复一个缺陷很肯能导致新一个甚至更多个缺陷,而这个缺陷又不会造成太大的危害。那么放弃修复这个缺陷无疑是明智的选择。
7.产品说明书从没有最终版
IT行业竞争太大,更新太快,所以我们的产品也在不断的更新,测试也要不断的测试新的功能并且保证以前的功能没有发生新的缺陷。
8.软件测试员在团队中不受欢迎
测试的目标就是寻找程序员的错误,不断的否定同事成果的人总是不受欢迎的。那么就需要我们做到以下几点:
a.早点找出缺陷,尽早的找出缺陷,使其产生的影响尽量减小。
b.控制情绪,诚然,测试员喜欢自己的工作,找出缺陷时非常大兴奋,但是也不能兴冲冲的去找同事:“你的程序出bug了”,更不能到处宣扬,他肯定会不高兴的。
c.不要总是报告坏消息,当同事做出一个厉害的功能时,应该毫不吝啬的夸奖。
希望我总结的一些内容能对大家有一点帮助。

Failed to save registry store file, cause: Can not lock the registry cache file错误日志解决

[] 2017-04-14 09:59:38,015 [DubboSaveRegistryCache-thread-1] WARN [com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry] – [DUBBO] Failed to save registry store file, cause: Can not lock the registry cache file /root/.dubbo/dubbo-registry-zookeeper.aixuexi.com.cache, ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties, dubbo version: 2.8.3, current host: 101.201.78.226
java.io.IOException: Can not lock the registry cache file /root/.dubbo/dubbo-registry-zookeeper.aixuexi.com.cache, ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties
at com.alibaba.dubbo.registry.support.AbstractRegistry.doSaveProperties(AbstractRegistry.java:193)
at com.alibaba.dubbo.registry.support.AbstractRegistry$SaveProperties.run(AbstractRegistry.java:150)

出现这个的原因是服务向ZK注册的同时,会缓存Consumer的列表,写入user.home/.dubbo/dubbo-registry-” + url.getHost() + “.cache 这个文件,当在同一个机器上启动多个Provider的时候,就会出现文件锁争用的问题,报上面这个错误。

解决办法:

既然是由于竞争文件锁导致的,那么让服务模块各自缓存自己的cache文件就可以避免这样的问题了。

具体做法是:在provider的xml配置文件中加入 file=”${catalina.home}/dubbo-registry/dubbo-registry.properties” ,如下:

<dubbo:registry address=”${zookeeper_registry}” transporter=”curator” file=”${catalina.home}/dubbo-registry/dubbo-registry.properties”/>

浅谈java设计模式

一、设计模式的分类

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:

 

 

二、设计模式的六大原则

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

 

个人见解:在开发一些通用型工具时,或者一些特殊的业务场景,用一些设计模式会大大提高可扩展性和高可用性,通过复用已经公认的设计,我能够在解决问题时取得先发优势,而且避免重蹈前人覆辙。我可以从学习他人的经验中获益,用不着为那些总是会重复出现的问题再次设计解决方案了。设计模式还为我们提供了观察问题、设计过程和面向对象的更高层次的视角,这将使我们从“过早处理细节”的桎梏中解放出来。大多数设计模式还能使软件更容易修改和维护。其原因在于,它们都是久经考验的解决方案。所以,它们的结构都是经过长期发展形成的,比新构思的解决方案更善于应对变化。而且,这些模式所用代码往往更易于理解,从而使代码更易维护。

java内存区域介绍

1、什么是JDK、JRE、JVM

JVM(Java Virtual Machine):主要功能就把字节码解释成具体平台上的机器指令执行,目的是实现跨平台,一次编译,到处运行。
JRE(Java Runtime Environment):支持Java程序运行的标准环境,Java SE API + JVM。
JDK(Java Development Kit):java程序设计语言 + java虚拟机 + java API类库。
 lip_image002

2、Java内存区域

Jvm在执行java程序的过程中会把它所管理的内存划分成若干个不同的数据区域,他们有各自的用途和生命周期,如下图
ip_image004

程序计数器

它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变计算器的值里获取下一条要执行的指令的,分支、循环、跳转、异常处理等等都是依赖计数器来完成的。Java虚拟机规范中唯一没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈

每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)。
ip_image006 ip_image008
  1. 局部变量表:是一组局部变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java文件编译为Class文件时,就在方法表的Code属性的max_locals数据项中确定了该方法需要分配的最大局部变量表的容量
  1. 操作数栈:JVM底层字节码指令集是基于栈类型的,所有的操作码都是对操作数栈上的数据进行操作,对于每一个方法的调用,JVM会建立一个操作数栈,以供计算使用。和局部变量一样。操作数栈的最大深度也是编译的时候写入到方法表的code属性的max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long、double。
  1. 动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。在Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期期间转化为直接引用,这部分称为动态连接。
  1. 返回地址:当一个方法被执行后,有两种方式退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者(调用当前方法的的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法方式称为正常完成出口(Normal Method Invocation Completion)。另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用athrow字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的调用都产生任何返回值的。     无论采用何种方式退出,在方法退出之前,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。 方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用都栈帧的操作数栈中,调用PC计数器的值以指向方法调用指令后面的一条指令等
  1. 附加信息:虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧中,例如与高度相关的信息,这部分信息完全取决于具体的虚拟机实现。在实际开发中,一般会把动态连接,方法返回地址与其它附加信息全部归为一类,称为栈帧信息。
JAVA虚拟机规范中,对这个区域规定了两种异常状况:1、如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;2、如果虚拟机可动态扩展,如果扩展时无法申请到足够的内存,将抛出OutOfMemoryError异常。
如递归方法层级过多就会出现StackOverflowError

本地方法栈

本地方法栈( Native Method Stack) 与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如 Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError 和 OutOfMemoryError 异常。

Java堆

对于大多数应用来说,Java堆( Java Heap)是Java 虚拟机所管理的内存中最大的一块。 Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java 虚拟机规范中的描述 是:所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、 标量替换优化技术将会导致一些微妙的变化发生, 所有的对象都分配在堆上也渐渐变得不是那么“ 绝对”了。
Java 堆 是 垃圾 收集 器 管理 的 主要 区域, 因此 很多 时候 也 被 称做“ GC 堆” 从内 存 回收 的 角度 来看, 由于现在收集器基本都采用分代收算法, 所以 Java 堆 中 还可以细分为:新生代和老年代;再细致一点的有Eden空间、 From Survivor空间、 To Survivor 空间等。从内存分配的角度来看,线程共享的Java 堆中可能划分出多个线程私有的分配缓冲区( Thread Local Allocation Buffer, TLAB)。不过无论如何划分,都与存放内容无关,无论 哪个区域,存储的都仍然是对象实例,进一步划分的目的是为了更好地回收内存,或者更快 地分配内存。
根据 Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是 连续的即可,就像我们的磁盘空间一样。 在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的( 通过- Xmx 和- Xms 控制)。 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

方法区

方法区( Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时将抛出 OutOfMemoryError 异常。
运行时常量池( Runtime Constant Pool)是方法区的一部分。 Class文件中除了有类的版本、 字段、 方法、 接口等描述信息外,还有一项信息是常量池( Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量 池中存放。运行期间也可能将新的常量放入池中,如 String 类的intern() 方法。

直接内存

直接 内存( Direct Memory) 并不是 虚拟 机 运行时 数据区 的 一部分, 也不 是 Java 虚拟 机 规范 中 定义 的 内存 区域。在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 类, 引入 了 一种 基于 通道( Channel) 与 缓冲区( Buffer) 的 I/ O 方式, 它可 以 使用 Native 函数 库 直接 分配 堆 外 内存, 然后 通过 一个 存储 在 Java 堆 中的 DirectByteBuffer 对象 作为 这块 内存 的 引用 进行 操作。 这样 能在 一些 场景 中 显著 提高 性能, 因为 避免 了 在 Java 堆 和 Native 堆 中 来回 复制 数据。
本机 直接 内存 的 分配 不会 受到 Java 堆 大小 的 限制, 但是,既然 是 内存, 肯定 还是 会受 到 本机 总 内存( 包括 RAM 以及 SWAP 区 或者 分页 文件) 大小 以及 处理器 寻址 空间 的 限制。也可能 导致 OutOfMemoryError 异常 出现。

分布式系统事务一致性解决方案

原文链接:http://www.cnblogs.com/dinglang/p/5679542.html

我的理解:

首先传统的两阶段提交的分布式事务对性能的影响会比较大,不适合高并发和高性能要求的场景。
考虑了阿里的GTS/TXC,发现使用的人太少,暂时先不用它。
考虑了阿里云ons的事务消息,发现功能不是很完善,而且实现本地事务的策略有点繁琐,担心会加重负担,所以先不用。
最后决定使用非事务消息,将本地业务和发送消息先后执行,用try catch包起来,或者用spring的本地事务注解。如果出错则执行回滚策略。可能的情况有三种:
1.操作数据库成功,向MQ中投递消息也成功。
2.操作数据库失败,不会向MQ中投递消息了。
3.操作数据库成功,但是向MQ中投递消息时失败,向外抛出了异常,刚刚执行的更新数据库的操作将被回滚。
因为要保证最终一致性,因此有两个问题需要关注:
1.消息出列后,消费者对应的业务操作要执行成功。如果下游业务执行失败,消息不能失效或者丢失,要么重复执行到成功为止,要么通知人工介入。
2.尽量避免消息重复消费。如果重复消费,也不能因此影响业务结果。
保证消息与下游业务操作一致,不丢失,主流的MQ产品都具有持久化消息的功能。如果消费者宕机或者消费失败,都可以执行重试机制。其中阿里云的MQ可以自定义重试次数,我开发的消息中间件已经集成进来了,如果需要,可以后续加入超过重试上限后自动通知人工介入的功能。
避免消息被重复消费造成的问题,需要保证消费者调用业务的服务接口的幂等性,消息中间件已经通过全局唯一id的方式保证了幂等性,重复消息不会执行。
这种非事务消息的方式比较常见,如果MQ自身和业务都具有高可用性,理论上是可以满足大部分的业务场景的。

谈运维认知读后感

读完京东高级PE王超的应用运维认知,觉得京东对自动化运维的理解与实施很值得我们学习。他们的团队从几个人到20几个人,从传统运维到自动化到智能运维,从开发和运维的矛盾点到一个标准实时的流程。这就是我们想要的! 目前我们虽然我们虽然还在初阶段,但我们有了明确的目标,还有这些大神们的经验,我们要快速学习应用到爱学习当中。 困难还是有的,老项目的复杂凌乱,项目的繁琐众多,但是看到力勇说的一个字:干!我也精神振奋! 我们先定个小目标:做好项目发布自动化。加快速度与业务连续性,降低风险提高安全。让爱学习更好很快更强!

Gerrit使用规范

本机master上的代码坚决不动,开发的代码永远在本机切换新的分支进行新的业务开发(只让master保持跟服务器master上的同步,能省去很多不必要的问题)。下面是一个正常情况下的栗子🌰
  git checkout master
  git pull #拉远端最新版本代码到本机
  git checkout -b dev #假设你要创建一个叫dev的分支进行开发
  git rebase master #合并远端最新代码到开发的分支(常见是在gerrit后台review后merge时发生冲突后需要本机rebase后再次追加提交)
  #如果有冲突(rebase后会有提示),手动解决冲突后,进行以下操作
  git add -A
  git rebase - -continue
  #本地代码已经可以提交到gerrit,执行以下push操作,(注意不能直接像以前那样push到远端的master上,而是一个特殊的gerrit分支)
  git push origin HEAD:refs/for/master


如果你在master上status出现了以下情况:
 (master) git status
 On branch master
 Your branch is ahead of 'origin/master' by 2 commits.
   (use "git push" to publish your local commits)
 nothing to commit, working tree clean
 出现这个说明没有严格按照以上规范,这时需要通过git reset 进行回滚后再pull(回滚前确保这上面的代码再gerrit上已有,已有的分支可以checkout下来继续在个人分支上继续开发)
a.多个开发任务并行时,不要都提交在一个commit里(相互有依赖的可以)
 b.需要在gerrit中同时维护多个未merge的分支(同时提交多个commit,或者上一个提交因为一些原因还没有merge又需要再提交一个新的),对于每个分支均从master上checkout一个新的分支进行开发,不在一个开发分支里commit多个,这样能最大程度避免依赖。避免出现你的后提交的代码需要上线,但所依赖的代码还不能merge的情况。
 c.提交的subject规范:1.格式:“关键字 正文”  2.关键字,固定以下几个(如有新的需要定义找CTO协商):added(新增,简写add)、fixed(修复,简写fix)、changed(修改原来的业务逻辑,简写change或cg)、upgraded(优化或组件升级,简写upgrade或up) 3.正文:解释是什么和为什么的  。例如:“cg 同步数据接口增加几个表单项”,“fix 同步数据接口缺乏必要校验”
我们使用gerrit的最大的好处,就在与可以review代码,可以看到我们本次提交所涉及的改动,对比老代码能更容易发现本次改动的疏漏或bug。
 因此提出建议规范:自己的代码无论改动多少,+2前必须先给指定人review+1(组员的代码给组长review,组长的代码自己指定组内他人review)。自己review可以在研发过程中的任何时候,不一定要等到开发完了再review