`
hehaibo
  • 浏览: 410517 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

java应用中的-幂等性

 
阅读更多

[原创链接: http://www.smithfox.com/?e=16 转载请保留此声明, 谢谢]

绝大部分网络上对幂等性的解释类似于:

"幂等性是指重复使用同样的参数调用同一方法时总能获得同样的结果。比如对同一资源的GET请求访问结果都是一样的。"

我认为这种解释是非常错误的, 幂等性强调的是外界通过接口对系统内部的影响, 外界怎么看系统和幂等性没有关系. 就上面这种解释, System.getCPULoad(), 这两次调用返回能一样吗? 但因为是只读接口, 对系统内部状态没有影响, 所以这个函数还是幂等性的.

首先了解一下什么是幂等性,如果你没有兴趣可以直接跳过这段代数概念解释 :)

幂等(idempotence)是来自于高等代数中的概念。

定义如下(加入了自己理解):

单目运算, x为某集合内的任意数, f为运算子如果满足f(x)=f(f(x)), 那么我们称f运算为具有幂等性(idempotent)

比如在实数集中,绝对值运算就是一个例子: abs(a)=abs(abs(a))

双目运算,x为某集合内的任意数, f为运算子如果满足f(x,x)=x, f运算的前提是两个参数都同为x, 那么我们也称f运算为具有幂等性

比如在实数集中,求两个数的最大值的函数: max(x,x) = x, 还有布尔代数中,逻辑运算 "与", "或" 也都是幂等运算, 因为他们符合AND(0,0) = 0, AND(1,1) = 1, OR(0,0) = 0, OR(1,1) = 1

在将幂等性应用到软件开发中,需要一些更深的理解. 我的理解如下:

数学处理的是运算和数值, 程序开发中往往处理的是对象和函数. 但是我们不能简单地理解为数学幂等中的运算就是函数,而数值就是对象!!

比如有Person对象有两个属性weight和age,但是所有的function只能对其中一个属性操作. 所以从这个层面我们可以理解为: 函数只对该函数所操作的对象某个属性具有幂等性, 而不是说对整个对象有运算幂等性.

Person {
 private int weight;
 private int age;
 //是幂等函数
 public void setAge(int v){
     this.age = v; 
 }
 //不是幂等函数
 public void increaseAge(){
     this.age++;
 } 
 //是幂等函数
 public void setWeight(int v){
     this.weight=v+10;//故意加10斤!!
 }
}

还有一点必须要澄清的是: 幂等性所表达的概念关注的是数学层面的运算和数值, 并没有提及到数值的安全性问题.

比如上面的Person的setAge函数, 有两种case不是幂等性所关心的, 但程序开发却又必须要关心的:

1. 两个线程同时调用

2. 因为age从业务上讲不可能递减, 如果前一次调用设置是30岁, 后一次调用变成了10岁或是更离谱的 -1 岁

所以RESTful设计中将幂等性和安全性是作为两个不同的指标来衡量POST,PUT,GET,DELETE操作的:

重要方法 安全? 幂等?
GET
DELETE
PUT
POST

幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的. 声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试.

就象cache有cache基本实现范式一样, 幂等也有自己的固定外部调用范式
cache实现范式:
value getValue(key){
    value = getValueFromCache(key);

    if( value == null ){
        value = readFromPersistence(key);
        saveValueIntoCache(key,value);
    }

    return value;
}
幂等外部调用范式
client.age = 30;

while(一些退出条件){
    try{
        if(socket.setPersonAge(person,client.age) == FAILED){
	        int newAge = socket.getPersonAge();
	        //处理冲突问题: 因为age只可能越来越大,所以将client的age更新为server端更大的age
	        if(newAge>30){
			client.age = newAge;
			break;
		} else{
		//无法进行冲突解决,再次尝试
		}
        } else return;
    } catch(Exception){
        //发生网络异常, 再次尝试
    }
}

幂等接口的内部实现需要有对内保护机制, 一般情况是用类似于乐观锁的版本机制.版本重点是体现时间的先后.

[原创链接: http://www.smithfox.com/?e=16 转载请保留此声明, 谢谢]

 

 

理解HTTP幂等性

http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html

 

基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议,无论是服务器端提供Web服 务,还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设 计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的Java语言并不意味着高质量的Java程序,简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。

幂等性定义

本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

从定义上看,HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一 样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要的概念,而 HTTP的分布式本质也决定了它在HTTP中具有重要地位。

分布式事务 vs 幂等设计

为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为:

bool withdraw(account_id, amount)

withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount; 如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请 求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认 为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:

图1

这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简 单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的 ACID性质,而但却无法提供性能和可用性的保证。

另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

int create_ticket() 
bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。 idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一 次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用 idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw 是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:

图2

和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

HTTP的幂等性

HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用 层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后 在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协 议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。

HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是 一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足 幂等性的。

HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此, 调用者可以多次调用或刷新页面而不必担心引起错误。

比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:

The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖 子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法 不具备幂等性。而PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次 PUT是相同的;因此,PUT方法具有幂等性。

在介绍了几种操作的语义和幂等性之后,我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单,用POST /tickets来实现create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx来实现idempotent_withdraw。值得注意 的是严格来讲amount参数不应该作为URI的一部分,真正的URI应该是/accounts/account_id/ticket_id,而 amount应该放在请求的body中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。

总结

上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。

参考

RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions
The Importance of Idempotence
Stackoverflow - PUT vs POST in REST

 

分享到:
评论

相关推荐

    java8源码-tcc:TCC分布式事务框架

    java8 源码 TCC分布式事务框架 基于服务层补偿的Tcc分布式事务解决方案,基于java 1.8编写,目前支持springcloud应用框架。...内置本地事务状态机,实现confirm和cancel操作不会被多次执行,放宽了幂等性要求。

    java8看不到源码-Muster:Muster-集中您的JavaAWSLambda函数

    java8 看不到源码Muster - 集中您的 Java AWS Lambda 函数 概述 Muster 是一个概念验证框架,旨在简化 Java 中的 ...而不是传统应用程序服务器的一些问题。...函数中的多个服务/方法, 内置幂等性, ...内置的幂等性

    某果学院 微服务分布式事物解决方案

    7、消息重复发送问题及业务接口的幂等性设计 8、可靠消息最终一致性方案1(本地消息服务) 9、可靠消息最终一致性方案2(独立消息服务)的设计 10、可靠消息服务的设计与实现--消息服务子系统 11、可靠消息服务的...

    动力节点RabbitMQ教程配套文档分享

    RabbitMQ是一个被广泛使用在生产中的消息中间件,可以应用在异步处理、系统解耦、流量削峰等场景。 本套视频适合具有一定Linux常用命令操作基础,熟悉SpringBoot基本开发的程序开发设计人员。 课程内容主要分为:...

    Java常见面试问题.docx

    1.ribbon 负载均衡 项目客户端的负载均衡采用ribbon,去看一看ribbon怎么配置的 2.服务端的负载均衡采用zuul ...3.Springboot的自动配置是怎么实现的? 4.库存并发控制采用什么方法?...17.幂等性 18.Mysql存储过程

    企业级消息队列Kafka视频教程

    7.1 幂等性 第八章 分区和副本机制 8.1 生产者分区写入策略 8.2 消费者组Rebalance机制 8.3 消费者分区分配策略 8.4 副本机制 第九章 高级(High Level)API与低级(Low Level)API 9.1 高级API 9.2 低级API 9.3 ...

    Kafka生产环境问题总结与性能优化实践

    Kafka,作为一个高吞吐量的分布式发布-订阅消息系统,经常用于处理大规模数据流,但在实际应用中会遇到消息丢失、重复消费、乱序、积压和延时队列等问题。为有效应对这些挑战,需要对Kafka进行细致的配置和调优。 ...

    interview-lagou:Java面试真题与原始码解析-源码解析

    31.如何保证接口的幂等性?常见的实现方案有什么? 32.TCP为什么需要三次握手? 33.Nginx的负载均衡模式有什么?它的实现原理是什么? 34.Docker有什么优点?使用时需要注意什么问题? 彩蛋:如何提高面试成功率...

    开涛高可用高并发-亿级流量核心技术

    1.3.2 幂等设计 13 1.3.3 流程可定义 13 1.3.4 状态与状态机 13 1.3.5 后台系统操作可反馈 14 1.3.6 后台系统审批化 14 1.3.7 文档和注释 14 1.3.8 备份 14 1.4 总结 14 第2部分高可用 17 2 负载均衡与反向代理 18 ...

    TCP-IP详解卷1:协议

    15.4 安全性 161 15.5 小结 162 第16章 BOOTP: 引导程序协议 163 16.1 引言 163 16.2 BOOTP的分组格式 163 16.3 一个例子 164 16.4 BOOTP服务器的设计 165 16.5 BOOTP穿越路由器 167 16.6 特定厂商信息 167 16.7 小...

    系统设计:准备系统设计面试问题

    路线图十月19-26 NoSQL 新SQL 弹性搜索数据库迁移 共享中间件差异27-31岁分布式速率限制-Moimoc API等幂-imooc 分布式文件系统-imooc 分布式交易-imooc 多线程系统设计面试书梳理八月01-02 CDN 负载均衡检测故障03-...

    算法导论(part1)

    书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如...

    算法导论(part2)

    书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如...

    高效算法:竞赛、应试与提高必修128例.[法] Christoph Dürr Jill-Jênn Vie(带书签文字版).pdf

    详细阐述了经典算法和特殊算法的实现、应用技巧和复杂度验证过程,内容由浅入深,能帮助读者快速掌握复杂度适当、正确率高的高效编程方法以及自检、自测技巧,是参加ACM ICPC、Google Code Jam等国际编程竞赛、备战...

    .NET 事件总线,简化项目、类库、线程、服务等之间的通信,代码更少,质量更好 ‎

    ‎特性(简化组件之间通信,支持事件监视器,支持动作执行器,支持自定义消息存储组件,支持自定义策略执行,支持单消费、多消费消息,支持消息幂等性处理,高内聚,低耦合,使代码更简单,非常快速,每秒可处理 ...

    kermoss:响应式业务流的传奇交易实现

    为了提高分布式环境中的性能和一致性,应用程序不应采用诸如两阶段提交之类的方式,否则您将发现自己处于类似于两个一般问题的情况下,而应寻求最终的一致性。在这种模型中,交易操作分为多个步骤。在执行过程中。 ...

    model-map:模型图

    DomainModel.stream() 会话说明: 数据处理的理想表示法是基于集合,列表或地图,但是大多数应用程序都是基于对象域模型... 这里有一些例子: 映射幂等性(对象实体和键值模型之间的一致性) 在基于列的数据存储中的

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    简单来说是本身可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。 常见的数据模型 1. 层次结构模型: 层次结构模型实质上是一种有根结点的定向有序树,IMS...

Global site tag (gtag.js) - Google Analytics