历史介绍部分:
  远程调用大致经过了corba、webservice、二进制跟restful四个阶段
  corba时代,corba(Common Object Request Broker Architecture,通用对象请求代理架构)算是公认的所有远程调用的鼻祖,是由OMG(object mangement group)制定的一套远程调用机制。rpc中的一些概念,例如 调用方、服务、提供方等都是corba最早定义的。但其定义过于繁琐,兼容不太好且效率一般,后来逐渐被其它技术取代。
  webservice时代(http+soap),java在90年代末广泛应用后,webservice随即迅速发展,第一个webservice框架aixs诞生,aixs诞生在盛产神油的神奇国度--印度,其实印度一直是一个it外包产业发达的国家,虽然我们日常更多关注的是他的奇闻异事;但aixs做的质量不好,架构跟性能都有点问题,apache觉得这合作伙伴有些丢人,就整理了一下,做了aix2。后来又有了Xfire跟进化版的cxf。webservice是基于http传输的,协议采用soap。soap使用xml来承载,而且不属于任意公司或组织,因此很多公司自己实现了一套解析方案,经常会有jar包冲突的问题。而且xml数据传输效率(里边太多用于维护结构等接口无关的数据)跟对象转为xml的序列化效率都不高,导致webservice注定不是一款高性能的rpc协议。
  二进制时代(tcp+byte),鉴于webservice有上述问题,性能更优秀(理论上)的二进制rpc协议应运而生,代表有hession跟rmi。二进制自然涉及到对象的序列化,其实这个性能也比较差,单纯就这一点来说,webservice甚至略有优势,因为有些机型的cpu为对象跟xml的序列化提供了硬件支持。二进制解决了网络传输效率问题,但没解决对象序列化效率问题。二进制协议后来并没有遮住webservice的光芒,跟伟大的IBM也不无关系,2005年IBM提出SOA的设计概念,这个思想对java产生了深远的影响,而IBM的SOA实现采用的是webservice。
  restful时代(http+json),json开始是用在前端,后来发现在后端对象的转换效率也非常高,于是传输效率跟转换效率都解决了。
  自定义协议,dubbo协议等,值得注意的是一些二进制协议也在不断发展,比如hessian2,性能也有一些进步,但效果仍然一般。
-------------------------------------------------
  代码编写部分,远程调用并不麻烦,无论使用dubbo还是webservice,使用起来都非常方便,但仍然有一些细节处理需要我们注意。 
  业务场景:有新订单生产,用户点击结算进行付款,后台调用远程支付接口划款,更新订单状态。
  1、第一种写法:
@Transactional
public void tixian(OrderInfo orderInfo){
try {
OrderVO order = new OrderVO();
order.setAmount(orderInfo.getAmount());
order.setId(orderInfo.getId());
order.setName(orderInfo.getName());
//调用远程接口,从用户账号扣款,转账到自己账户
//此处response只是模拟写法,具体要参照api来写,而且此处可能有异常,该处的异常暂不考虑
//此处认为,response为null是网络异常的情况
BankResponse response = bankService.moneyOut(order);
//根据远程接口返回的状态,判断是否支付成功
if(response !=null && response.getCode().equalsIgnoreCase("s")){
orderInfo.setStatus(2);//支付成功
}else {
orderInfo.setStatus(3);//支付失败
}
//保存订单状态
orderInfoDao.updateByPrimaryKey(orderInfo);
}catch (Exception e){
e.printStackTrace();
}
}

  BankService是个远程接口,本地demo配置为:

<bean name="bankService" class="com.istack.base.httpService.client.HttpProxyFactoryCglib">
<property name="targetClass" value="com.xxx.bank.BankService"></property>
<property name="endPoint" value="http://localhost:18080/rpcTest"></property>
<property name="timeOut" value="35000"></property><!--超时会返回null-->
</bean>

  OrderInfo的status,1未支付,2支付成功,3支付失败,4处理中(支付过,但结果未知)

  这段代码逻辑上是比较清晰的,但会两点问题:
  a、事务范围过大
  事务直接加在方法上,那么一旦进入该方法,即启用事务,直至该方法结束才对事务进行提交或者回滚。而事务是依赖于数据库连接的。一般来说,远程调用的耗时都会相对较长(毕竟不在本地,网络延迟可能会比较严重,而且有的业务的确复杂,处理需要一段时间),那么,当短时间内访问量较大的情况下,每个用户一个线程,每个线程占用一个数据库连接,会在短时间内耗光数据库连接池的连接,这时候,会导致后续的用户无法进行结算(因为没有数据库连接,需要等待),或者数据库连方面连接已满,导致其它应用无法连接数据库。这两种情况,对于业务系统来说都是灾难性的。
  b、状态判断不合理
  只判断了接收到远程反馈的情况,如果由于网络或者黑客等原因,导致没有接收到远程反馈的情况没有处理。比如发送了支付请求,银行方面进行了处理,也进行了反馈,但这个反馈信息中途被拦截,导致超时,或者某些支付系统返回的是个处理中这类模糊信息,而不是处理成功或者处理失败的明确信息,该代码则无法应对。
  如何解决这两个问题?
  2、第二种写法:
  针对第一种写法中的问题,我们改进代码如下:
public void tixian2(OrderInfo orderInfo){
try {
OrderVO order = new OrderVO();
order.setAmount(orderInfo.getAmount());
order.setId(orderInfo.getId());
order.setName(orderInfo.getName()); //调用远程接口,从用户账号扣款,转账到自己账户
BankResponse response = bankService.moneyOut(order);
//根据远程接口返回的状态,判断是否支付成功
if(response !=null){//有返回值
if(response.getCode().equalsIgnoreCase("s")){
orderInfo.setStatus(2);
}else{
orderInfo.setStatus(3);
}
}else {//无返回值,这里这么写,代指超时或者网络异常等,不一定支付失败,实际情况要根据接口api来写这个判断
orderInfo.setStatus(4);
}
//保存订单状态,(编程式事务)
template.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
orderInfoDao.updateByPrimaryKey(orderInfo);
return null;
}
});
}catch (Exception e){
e.printStackTrace();
}
}

  该代码避免了第一种写法的两个问题,貌似完美但仍有一个问题,如果由于某种原因,比如用户点击了两次,或者该订单是通过mq发过来的,而mq有可能重发消息,这样的话就会导致同一个用户多次支付,如何避免?

  3、第三种写法

public void tixian3(OrderInfo orderInfo){
try {
int rows = orderInfoDao.update("update order_info set status=4 where order_info_id=? and status=1",new Object[]{orderInfo.getId()});
if(rows == 1){
OrderVO order = new OrderVO();
order.setAmount(orderInfo.getAmount());
order.setId(orderInfo.getId());
order.setName(orderInfo.getName()); //调用远程接口,从用户账号扣款,转账到自己账户
BankResponse response = bankService.moneyOut(order);
//根据远程接口返回的状态,判断是否支付成功
if(response !=null){//有返回值
if(response.getCode().equalsIgnoreCase("s")){
orderInfo.setStatus(2);
}else{
orderInfo.setStatus(3);
}
}else {//无返回值,不一定支付失败,可能是超时或者网络异常等
orderInfo.setStatus(4);
}
//保存订单状态,(编程式事务)
template.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
orderInfoDao.updateByPrimaryKey(orderInfo);
return null;
}
});
}
}catch (Exception e){
e.printStackTrace();
}
}

  跟2的区别是开始的时候修改了状态,使得后续的相同订单处理无效。这是一种乐观锁的处理方式,这样就避免了2中所说的多次支付问题。

  该部分内容,仅用来示例,提醒自己要培养互联网思维,不要仅仅用代码罗列一下业务逻辑就算了,要深入思考,考虑各种复杂情况下的处理,提高系统性能跟健壮性。

远程调用历史及代码编写demo的更多相关文章

  1. 轻量级远程调用框架-Hessian学习笔记-Demo实现

    Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...

  2. dotNET使用DRPC远程调用运行在Storm上的Topology

    Distributed RPC(DRPC)是Storm构建在Thrift协议上的RPC的实现,DRPC使得你可以通过多种语言远程的使用Storm集群的计算能力.DRPC并非Storm的基础特性,但它确 ...

  3. SpringCloud使用Nacos服务发现实现远程调用

    本文使用SpringCloud结合Nacos服务发现,Feign远程调用做一个简单的Demo. 1 Nacos 关于Nacos之前写了两篇文章关于SpringBoot对它的使用,感兴趣可以查看一下. ...

  4. 2019.12.4 Hystix熔断&Feign进行远程调用&Zuul

    0.学习目标 会配置Hystix熔断 会使用Feign进行远程调用 能独立搭建Zuul网关 能编写Zuul的过滤器 1.Hystrix 1.1.简介 Hystrix,英文意思是豪猪,全身是刺,看起来就 ...

  5. [转载] 基于Dubbo的Hessian协议实现远程调用

    转载自http://shiyanjun.cn/archives/349.html Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行 ...

  6. 基于Dubbo的Hessian协议实现远程调用

    Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务, ...

  7. Dubbo搭建HelloWorld-搭建服务提供者与服务消费者并完成远程调用(附代码下载)

    场景 Dubbo简介与基本概念: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103555224 Dubbo环境搭建-ZooKe ...

  8. 【lombok】使用lombok注解,在代码编写过程中可以调用到get/set方法,但是在编译的时候无法通过,提示找不到get/set方法

    错误如题:使用lombok注解,在代码编写过程中可以调用到get/set方法,但是在编译的时候无法通过,提示找不到get/set方法 报错如下: 解决方法: 1.首先查看你的lombok插件是否下载安 ...

  9. ZooKeeper伪分布集群安装及使用 RMI+ZooKeeper实现远程调用框架

    使用 RMI + ZooKeeper 实现远程调用框架,包括ZooKeeper伪集群安装和代码实现两部分.  一.ZooKeeper伪集群安装: 1>获取ZooKeeper安装包 下载地址:ht ...

随机推荐

  1. appium自动化安装(二)

    第二节  安装Android开发环境 如果你的环境是MAC那么可以直接跳过这一节.就像我们在用Selenium进行web自动化测试的时候一样,我们需要一个浏览器来执行测试脚本.那么移动端自动化测试,我 ...

  2. RGB转灰度的几种算法

    这里我用的摄像头是OV2640,这款摄像头的像素是200万.通过对摄像头拍摄图像的进行图像的转化,先把图像的格式转化为RGB565,然后在进行灰度的转化,最后进行二值化处理,设定阈值,进行二值化的处理 ...

  3. Gym - 100792C Colder-Hotter(三分交互)

    Colder-Hotter Statements This is an interactive problem. Egor and Petr are playing a game called «Co ...

  4. windows10系统连接蓝牙鼠标自动断开解决方案

    环境: Windows 10 企业版 2016 长期服务版 罗技M590 问题: 鼠标长时间未使用,会自动断开 解决步骤: 参考链接: https://zhidao.baidu.com/questio ...

  5. 图像特征提取之Haar特征

    1.Haar-like特征 Haar-like特征最早是由Papageorgiou等应用于人脸表示,Viola和Jones在此基础上,使用3种类型4种形式的特征. Haar特征分为三类:边缘特征.线性 ...

  6. C语言和sh脚本的杂交代码

    在网上看到了一个把 C语言和bash杂并起来的例子,这个示子如下所示.在下面这个例子中,我们把脚本用#if 0这个预编译给起来,这样就不会让其编译到C语言中了. #if 0 echo "He ...

  7. P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA

    \(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...

  8. 树链剖分【洛谷P2590】 [ZJOI2008]树的统计

    P2590 [ZJOI2008]树的统计 题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把 ...

  9. 洛谷P1894 [USACO4.2]完美的牛栏The Perfect Stall

    题目描述 农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术.不幸的是,由于工程问题,每个牛栏都不一样.第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在 ...

  10. php字符串截取中文出现乱码解决

    在截取中文字符串时使用substr()容易出现乱码 可以使用mb_substr()用法与substr类似,但是比substr多了一个参数,第四个参数指定网页编码