你的位置:首页 > Java教程

[Java教程]open api应用开发最佳实践


        在公司的内部系统中,会存在一些接入第三方数据的场景,例如在携程的app里能看到艺龙的机票,在美团的酒店列表里能找到去哪儿的酒店等。在自己的客户端或者H5里展示这些数据,一般会通过open-api对接的方式完成。最近正好在做一个open-api项目,场景是第三方可以通过调用open-api提供的http服务,在我方app里面提供第三方的团购服务。本文主要总结一下系统设计时的一些实践经验。

系统设计

        系统包括2个应用,一个是web应用,一个是server应用,web应用对外提供http服务,service应用提供内部调用的api。为什么不在一个应用里面完成呢?这是为了保证项目的整洁干净。web应用提供对外的api,不直接与数据库打交道,而server为web项目提供rpc服务。系统设计的时序图如下:

                                                图1

        第三方调用web应用提供的restful api,按照我们提供的参数格式发起请求。open-api-web在对参数进行校验之后,根据校验结果返回错误信息or发起rpc调用。open-api-service为每个请求落地数据到数据库,包括请求log、团单初始信息等。如果没有异常,则会异步调用公司内部上单系统服务,同时返回一个初步的处理结果:团单初始化中or错误信息,由web应用返回给第三方。当异步调用结果出来之后,回调第三方提供的rest api,发送处理结果。

        为什么要异步处理与内部上单系统的对接呢?这里主要考虑到api的可用性和相应速度。在上单的过程中,涉及到图片的上传这样的耗时操作,如果采用同步调用的方式,势必会大大增加api的相应时间,影响api的可用性。

如何异步

        实现异步的方式有很多,总的来说,就是不要在同一个线程中把所有的事情完成。之前考虑了以下几种方式:

        1. 封装一个task,通过ExecutorService提交一个任务给线程池,由线程池分配一个线程,处理与上单系统对接的逻辑。

        2. 使用spring event,由于spring对Event的默认是同步的,可以对multicastEvent方法进行重写,实现异步。

        3. 使用消息队列,server端对团单做本地保存后,发送消息,同时server接收消息,处理消息。

        对这三种方式进行了分析,1和2比较接近,本质上都是另开线程处理,但是2比1更好,因为2实现了业务的解耦,发布event和处理event可以由不同的开发独立完成。发布者只需要关心发布的逻辑,而接收者专注于与内部系统的对接。最后我们采用了第三种方式,因为第三种方式更稳定,原因在于消息队列会对消息做持久化处理,当应用重启或者宕机的时候,消息不会丢失,从而保证每一个消息都可以得到处理。

       对于消息的处理,要注意的是幂等处理。例如第三方创建团单,如果不进行幂等,可能会出现重复创建的问题。每次接受到创建请求时,先查询数据库中是否已保存相同团单的数据,如果已经存在则丢弃。而更新团单的时候,我们采用了版本号的方式。为每个已创建的团单分配一个版本号,初始值为0,当收到更新请求时,消息生产者先把版本号+1,再发送消息。消费者接受到消息后,与数据库中版本号对比,只接受大于原始版本号的请求。如果满足要求,则进行团单更新,更新成功后,把最新的版本号持久化到数据库中。这样就保证了,不重复处理请求,同时只处理最新的更新请求。

异常处理

        发生数据增删改的api,需要将处理结果返回。对于请求执行过程中产生的业务逻辑错误或者运行时异常,如何处理呢?我们采用了定义ErrorCode和异常统一处理的方式。ErrorCode是一个枚举类,定义了不同的业务异常,每个枚举都包括了错误码code和错误信息msg,例如 Parnert_NOT_FOUND(2001, “第三方不存在")。同时定义一个异常类BizException,当业务执行的过程中,发生与预期不符时,抛出该异常,同时把ErrorCode信息带上。最后利用Spring AOP技术,定义一个切面,对所有执行的业务代码进行代理,统一catch异常,对不同的异常进行包装处理成Response,再将response返回。这样做的好处是什么呢?我认为主要有2点:1 异常code收拢到一个枚举类,便于异常code统一定义和查询 2 在切面对异常进行处理,避免异常处理代码散落到各处,不利于维护。

        开发业务代码时,只需要关心业务逻辑,出现与预期不符的情况时,抛出异常即可,至于怎么处理无需关心。

安全控制

        open api需要对第三方做严格的校验,避免恶意用户随意使用公司内部系统。为了保证其安全性,我们主要考虑3个方面:

        1. 录入第三方基础信息,落到本地数据库,当第三方调用open api时,需要传入第三方信息做校验;

        2. http请求采用post方式;

        3. 签名校验。使用对称加密MD5加密的方式,动态生成签名。

问题排查

        第三方使用open api出现问题时,如何排查?首先,直接根据api返回的code和msg(由response对象封装),就可以知道处理结果以及失败原因。如果对于某次请求有异议,怎么办呢?我们希望可以还原现场,以便排查问题到底出在哪里。所以我设计了一张log表,把每个请求的路径和参数都记下来,这样可以第三方id找到相应的原始请求数据。现场都还原了,到底是哪方的问题,也就清清楚楚了。同时,在本地的团单表中,对于处理失败的单子,也记录下失败的原因,以做备查。

其他总总

       当然,还有很多值得思考的地方,例如流量控制、系统监控等,这些都是可以完善的地方。一个看似简单的功能,想要做好,也是要多多思考才行😊

 

作者:mayday芋头
出处:http://www.cnblogs.com/maypattis/
本博客中未标明转载的文章归作者mayday芋头和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利