幂等(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操作的:
幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的. 声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试.
就象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){ //发生网络异常, 再次尝试 } } 幂等接口的内部实现需要有对内保护机制, 一般情况是用类似于乐观锁的版本机制.版本重点是体现时间的先后 |