用互联网思维来开发客户端软件——项目开发小结

      随着智能手机、平板电脑的快速发展,台式电脑在个人用户那里已经没落了,但是台式电脑仍然是企业用户工作中的主要工具,且具有不可替代的作用。客户端软件在企业级用户那里有着不可替代的作用,结合时代发展,我们应以互联网思维来做好企业级应用客户端软件?研发快速迭代、快速试错,把大功能拆分成小功能,分阶段实现,追求微创新。

    通常企业级应用的客户端,就是企业管理应用系统,一般分为BSCS两种架构,CS架构要求在用户的电脑上装上客户端与数据库,或者数据库安装在数据库服务器上。这种方式我们经常会碰到一些问题,比如用户的数据库坏,要求服务商帮助进行恢复;用户说我的数据无法保存到数据库中了,你们来看一下,这样就要派出人力到现场进行问题诊断,或者简单问题使用远程维护的方式,不管哪种方式,一旦用户的数量超过了一定的量,那么公司的运维成本就上去了。那么有没有一种方式,改变这种情况呢?云服务风起云升,云数据库的兴起,宽带速率的大幅度的提升,宽带价格的不断降低。让我看到了一种解决方式。这次公司开发的新项目中,力主采用数据库放在云端,客户端通过服务端的Rest Ful接口去云端访问数据,然后加载到本地,用户进行业务操作。  

      说起互联网,大家一个反应便会想到网站,例网易、新浪、淘宝、京东。大家想必也知道网站与客户端软件各有优劣吧,比如:网站可以运行在任何设备的浏览器上,不需要用户安装,网站有更新了,也不需要用户去升级,只要刷新一下浏览器就可以了,能在各种终端(平板电脑,智能手机,台式电脑等)保持统一的用户体验(当然需要有非常好的前端工程师);而相比之下,安装在台式电脑上的客户端软件会运行得更快,并且能够充分利用本地资源为用户带来更多的功能、更方便的操作。BSCS各有优劣,那么如何能把两者进行一些融合呢,让客户端软件具有一些BS架构的优点呢。我试着把一些互联网的优点融合进了此次的客户端软件中。

项目介绍

     这是一个很大的项目,企业级用户数在1000以上,业务多而且复杂。我做为项目的客户端负责人参与系统需求分析至测试和部署的整个过程。负责沟通需求,建立客户端开发团队,确定系统架构风格和技术实现方案。由于项目开发资源(比如时间、人力)紧张,项目的业务逻辑复杂,项目组对业务并不熟悉,难以在一开始预估将所有需求开发完成的时间。因此,在开发模型选择上,采用螺旋式增量开发。

     在开发技术的选择上,由于本公司以往开发中,客户端都是使用微软的开发技术,服务端使用的是Java。 所以此次的项目还是采用微软技术+java技术结合。

      整个系统部署共有12台服务器:四台前置机用于运行REST服务+前台管理系统+MQ+报文生成;四台Web服务器Linux + Weblogic 11, 分别运行后台管理系统+MQ+报文处理;四台数据库服务器 Linux + Oracle 12C;经过试运行,于10月份投入正式使用。目前系统状况良好,经运行评估,实现了全部必须功能,性能、安全性等质量均达到了原定设计要求。目前系统正在根据业务需求,做二次开发中。

 

快速迭代开发、快速试错

 

      这次我们对客户端的开发采用了Scrum敏捷开发流程——两周一次迭代,每两周发布一个版本。我们称之为小步快进”——用户需求拆分,分阶段实现,先让用户看到我们的工作在不断的推进。比较一下传统的软件开发模式:先制定一份详尽冗长的详细设计文档、项目任务计划,然后是少则12个月,多则是56个月甚至更久的开发周期,再加上一个较长的稳定期来修复足够多的bug,然后再给用户布署。

      那么如何快速试错呢?这就要需要靠数据说话了!网站是可以很方便地收集到相关数据:用户在网站上停留了多久,点了什么网页,在网页上点了什么按钮,用户使用的是什么浏览器,同时有多少人在线,访问的峰值出现在什么时间段,等等。此次我们在客户端的一些主要操作都是要调用服务端REST接口进行操作的,我们在服务端加入了一些收集信息的代码,同时在客户端中也加入了类似的收集信息的代码,不断的收集我们开发的客户端软件中出现的BUG让我们对客户端软件的性能以及用户行为了如指掌。我们就能知道用户使用客户端时哪些查询比较慢,哪些操作是不合理的需要改进等信息。每次我们做了一些改进或是增加了新功能之后,我们都会先在小范围内给用户更新,然后在后台观察各种统计数据,同时也会派人去用户那里收集用户的使用反馈。以此来帮助我们决定是否应该继续完善这个功能。

如何解决版本碎片化问题?

       大家如果有过客户端软件运维的经历,那么肯定会知道客户端软件到了用户那里,版本控制就是一个非常麻烦的事情了,在用户那里会有很多版本,这个用户的版本可能是2.1,那个用户的版本可能是2.2。如果版本更新比较频繁的话,特别是项目进行运维初期,我们每两周就对外发布一个版本,那么用户那里的版本就更加不可控了。这次我们在客户端中加入了强制更新机制,一旦在服务器端设置了这个参数之后,就可通过软件自带的升级机制把最新版本的客户端送到用户手上。

      在功能实现方面,我们时刻保持着一种意识:尽量不把业务逻辑在客户端里写死。否则的话,一旦业务逻辑有变,我们必须修改客户端的代码,然后再做强制升级,一来周期比较长,二来万一有什么BUG就会影响用户使用,就必须不得不立即再次更新一个版本,同时通知用户更新,结果就比较被动了。举一个用户提交数据的例子吧。在客户端,当用户对单证数据进行了变更,提交数据的时候,要判断一下是否有变更?具体的业务逻辑是:如果用户对当前单证的数据进行了修改,则保存数据,调用保存接口;如果用户对当前单证数据没有做变更,则不用保存到数据库,同时给出提示。如果是以往的CS架构,这样做多少也会有点问题,因为存在并发的问题,更何况是访问的是云端数据。一些经验欠缺的开发人员 会把上述逻辑直接实现在客户端软件里。遗憾的是,这样做无法解决并发的问题,并发情况下那会死的很惨的!更灵活的实现方式应该是客户端只要把用户的单证数据做为参数传给保存接口,而具体的业务逻辑实现则由服务器端来实现。

    当然,上面只是一个很小的例子。为了在客户端里获得足够的灵活性(不通过发布新版本就能满足业务需求),我们给很多功能加上了云端配置。只要我们发现某些功能的用户使用上有问题,比如查询超时了,我们就可以随时都可以调整控制参数,甚至可以控制用户每分钟的请求数。我们始终牢记,我们在做的不是一个单纯的、孤立的客户端软件——我们提供的是一种互联网服务,我们提供的是云端功能。 

   

为什么采用REST做为数据访问接口

     我们采用REST服务方式来实现客户端与云端的交互,是因为考虑到项目开发的两个因素:简单和灵活。

      REST (Representation State Transfer) 描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之一。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

      使用REST做为业务逻辑接口是因为,从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。

      在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI (Universal Resource Identifier) 得到一个惟一的地址。客户端使用的是标准的 HTTP协议进行资源访问,同时还可以使用标准的HTTP方法,比如 GET、PUT、POST 和 DELETE。

      REST的一个重要原则是系统分层,这表示组件无法了解它与之交互的中间层以外的组件。通过将系统的某些功能限制在某一层,由此可以限制整个系统的复杂性,促进了底层的独立性。

      当 REST 架构的约束条件作为一个整体应用时,将生成一个可以扩展到大量客户端的应用程序。它还降低了客户端和服务器之间的交互延迟。统一界面简化了整个系统架构,改进了子系统之间交互的可见性。REST 简化了客户端和服务器的实现。

REST架构风格可以为开发者带来三方面的利益:

  • 简单性

  采用REST架构风格,对于开发、测试、运维人员来说,都会更简单。可以充分利用大量HTTP服务器端和客户端开发库、功能测试/性能测试工具、HTTP缓存、HTTP代理服务器、防火墙。这些开发库和基础设施早已成为了日常用品,不需要昂贵小型机、大型机等硬件,使用增加平常使用的便宜的PC服务器,就能解决大多数可伸缩性方面的问题。

  • 可伸缩性

  充分利用好通信链各个位置的HTTP缓存组件,可以带来更好的可伸缩性。其实很多时候,在客户端做性能优化,产生的效果不亚于仅仅在服务器端做性能优化,但是HTTP协议层面的缓存常常被一些资深的架构师完全忽略掉。

  • 松耦合

  统一接口+超文本驱动,带来了最大限度的松耦合。允许服务器端和客户端程序在很大范围内,相对独立地进行开发,并行开发。对于设计面向企业内网的API来说, 松耦合并不是一个很重要的设计关注点。但是对于设计面向互联网的API来说,松耦合变成了一个必选项,不仅在设计时应该关注,而且应该放在最优先位置。

     REST服务具有非常出色地灵活性,同时方便用浏览器和 firebug等工具进行测试。我们在项目中,并没有将REST服务单纯视为一串地址的响应,基于HTTP协议,可以最大地利用HTTP协议的语义特性。如数据的增删改查操作对应不同Http Method(Put/Delete/Update/Get)。用户可以用相同访问URI,根据需要,通过在请求头中设置不同的 Accept-Type,获取不同形式的数据结果,比如JSON(用于Ajax)或XML(用于后台)。

     REST服务请求由于不需要构造SOAP消息,开销更小。 REST服务可以利用高速缓存控制头,从而减少带宽的需求,从而REST可以改善响应时间和改进用户体验。
    REST的每个请求都是独立的。一旦被调用,服务器不保留任何会话,这样就可以更具响应性。通过减少事件后通讯状态的维护工作,提高了服务器的可扩展性。 

在此系统开发过程中,也遇到了一些问题:

一、安全性方案。特别是指Restful Api的访问控制方法。REST既可以被服务器端调用又能被客户端调用,所以一开始就要明确地区分用户访问权限和系统访问权限,区分Web页面权限和REST API接口权限。但是在开发过程中经常会混为一谈,今后要加强设计阶段这方面的文档和评估工作。

二、服务接口规范性。REST服务基于URI地址访问,URI不仅仅指明了被标识资源所在的位置,而且通过这个URI可以直接获取目标资源。REST服务接口的每个操作都基于一个URI模板。在实际业务中,功能类似的操作被做成多个重载,随之重载的增多,URI模板如何约定,如何扩展便成为一个规范性问题。开始时,对此未予以足够重视,REST服务接口由不同的人来进行开发,以致于一些REST服务接口定义上产生了混乱,影响了理解和正确使用。后来,又额外花费时间定义了操作URI的规范。通过此次的项目让我知道了应该从设计时就应该全面考虑URI模板的语义扩展方式。

三、由于服务端使用的是Spring与Hibernate这些JAVA的第三方组件,所以造成了一个问题,效率问题,一旦数据量稍微大一点,就会出现问题。我仔细研究了一下服务端工程师写的JAVA程序,发现原来他们的写法很成问题,我不知道这是不是JAVA使用Hibernate就是这样使用的。我举个例子:

表结构与要显示的结果如下图。

 

一般我们,应该使用SQL语句,使用SQL的(inner join 、left join、right join)三种中的一种,把三个表关联起来,一条SQL语句,把结果查询出来。

服务端的实际写法却是,先查询了“单证明细表”,然后循环去查询货物信息表,得到品名,查询企业信息表,得到名称。也就是说,由一条SQL语句,变成了

明细数量*2条SQL语句,最后,造成了很大的性能瓶颈。服务端的负责人最后为了达到性能上的指标,使用了内存数据库,来解决这个问题。

 

posted @ 2015-04-11 21:17  DotNet菜园  阅读(7459)  评论(7编辑  收藏  举报