前后端分离实践(EOS篇)
2014-10-07 14:23
232 查看
1.简介
作为后端,在整个java的环境中,服务的RPC操作是分布式应用中至关重要的部分,如何让开发人员无成本的接入分布式架构,这是我们在思考的范畴。引入它让我们使用java开放服务轻而易举,规范服务接口标准以及跨语言的服务调用提供了快捷途径~1.1 EOS设计原则
它的核心是接口,希望能有效减少开发资源等待的弊端。规范接口,接口要求前端,后端,测试人员都能看懂,能用。而且接口是直接引用到我们的程序中,任何更改都有相关的记录,相关人员的审核。
接口的可测试性。由测试人员直接介入到接口的测试中来,首先保证接口的正确性,给后面的联调工作带来无法估量的好处,理想情况一次通过。而且测试人员可以直接判断错误的来源,到底是前端还是后端
前端人员不用等待后端的开发,接口中就规定了模拟报文,前端人员直接开发,而且后面联调时代码也不需要调整,只需要更改一个参数,就可以自动切换到后端程序。
前端,后端,测试各司其职,互不影响,很好体现了解耦的思想
后端开发人员技术上跟现在没有变化,技术门槛低,直接开发到Spring的服务层就可以了,采用单元测试,更快提高效率
1.2 角色职责
定义的职责是大部分跟接口相关的,得接口得天下。java后端开发程序员:和前端人员沟通,定义接口;不能私自更改接口,有更改必须更相关的前端人员沟通;对一些重要的调整,请跟小组长联系;
前端开发人员:参与定制接口;
小组长:检查程序员接口的规范性,是否存在没必要的冗余;协助程序员和前端人员的沟通;直接参与一些重要模块的接口定制;切实起到小组长的作用,在做审批接口的时候,最好能够做代码审查,包括单元测试是否完备。
测试人员:参与到接口的测试中来;以后技能提升了以后,可以直接参与单元测试(我们考虑开发一些工具(API),实现自动测试);有一个不成熟想法,前端,后端各自提交成果测试,测试完成后,由测试这边统一联调(个人认为基本上工作量不会很大了)。
1.3 系统组成
eos包含以下几部分:服务提供端Server:后端开发者重点关注,根据提供的eos-server.jar包开发应用。
中心管理端:包含控制端eos程序和界面配置端uddi程序。也是直接部署即可。
客户端Client:前端开发者重点关注,根据eos-client.jar包和jquery.eosremote.js开发应用。
zookeeper:此程序为第三方应用直接下载部署即可,它是整个系统的强依赖,提供服务端服务信息注册,提供eos管理端在线注册。
系统的组成部署图如下:
1.4 系统执行流程
服务端发布服务,挂接到eos,客户端取得挂接有需要调用的服务的eos地址进行服务请求,eos对请求进行过滤代理请求服务端服务数据,返回给客户端,具体图解如下:可见,多了模拟数据获取的支持,eos轻松实现前后端分离开发,两端不需要同步等待。并且,系统采用分布式思想,可以部署多台服务端和eos中心端以及客户端,轻松实现平行扩展。
2.快速开始
2.1 核心部件部署
(1)zookeeper:此程序为第三方应用直接下载部署即可。(2)中心管理端:包含控制端eos程序和界面配置端uddi程序。也是直接部署即可。注意根据配置文件说明更改对应参数即可。
eos程序的配置eos.properties:
#zookeeper服务端的ip地址 zookeeper_ip=192.168.0.224 zookeeper_port=2181 #eos的标识,服务端挂接服务需要知道此id eos_id=ulyn #client能够访问到eos的本机ip地址 local_ip=192.168.0.60 #服务请求的端口号,默认值为5555 eos_port=5555 #eos的模式,默认是为pro部署模式,当为dev开发模式,开发模式允许mock模拟,否则不管客户端是否指定mock请求都直接调用真实服务 eos_mode=dev
2.2 服务端开发
(1)申请应用接入:访问uddi管理界面,注册应用,取得appid,例如:ihome(2)可以使用eos-server-example工程
(3)制定服务接口:根据项目功能需求制定java接口类Test.java,见3.2.1
(3)在uddi管理中进行接口java文件上传,由小组长审核
(4)实现接口类
(5)修改配置文件eos-server.properties参数,启动系统
#zookeeper服务端的ip地址 zookeeper_ip=192.168.0.224 zookeeper_port=2181 #挂接的eos_id,挂接多个eos请使用逗号隔开 eos_id=ulyn #eos能够连接到server的本机ip local_ip=192.168.0.60 #netty服务器的端口,默认是5555 netty_server_port=10085 #应用id app_id=ihome
2.3 客户端开发
(1)在uddi管理中下载接口java文件Test.java,下载得接口文件如下:package com.sunsharing.component.test; import com.sunsharing.eos.common.annotation.ParameterNames; import com.sunsharing.eos.common.annotation.EosService; @EosService(version="1.0",appId="ihome",id="test") public interface Test { /** * 说hello * @param abc * @return * ${ulyn} * ulyn */ @ParameterNames(value = {"abc"}) String sayHello(String abc); }
(2)使用eos-client-example工程,将接口放对应的包路径com.sunsharing.component.test,即下载的java文件的package。
(3)java使用者直接使用接口:
Test test = ServiceContext.getBean(Test.class); test.sayHello("hello");
(4)前端,使用js辅助插件jquer.eosremote.js,依赖jquery.js和json2.js
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script> <script type="text/javascript" src="/js/json2.js"></script> <script type="text/javascript" src="/js/jquery.eosremote.js"></script> <script> $(document).ready(function () { $.eosRemote({ url: "/remote", serviceId: "test", mock: "hexin", method: "sayHello", data: {"abc": "hello"}, success: function (data) { alert("返回结果:" + data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert(errorThrown); } }); }); </script> </body> </html>
(5)修改配置文件eos-client.properties参数,启动系统
zookeeper服务端的ip地址 zookeeper_ip=192.168.0.224 zookeeper_port=2181 #联调服务端ip,当有配置次参数时,可以指定从此server取得数据,不配置则走负载均衡随机取得一台服务 #debugging_server_ip=192.168.100.60 #全局控制是否使用mock,默认值为false use_mock=false
3.使用说明
3.1 中心管理端
3.1.1 控制端eos
对于此系统,部署启动即可。jar包执行类为com.sunsharing.eos.manager.main.Eos,可以使用发包提供的批处理命令启动。它作为客户端桥,调用服务端接口,主要实现以下流程:
(1)判断是否审批通过,不通过则不继续往下处理,直接返回服务未通过审核异常
(2)判断是否需要测试模拟,是模拟则直接返回模拟测试数据,不继续往下处理
(3)调用实际server服务
(4)调用监控逻辑
在此特别需要强调的是配置文件eos.properties, 约定配置文件的第一行请不要配置参数(因为使用校验框架resvalidate,下面的server和client也遵循此约定)。重点关注下eos_mode配置项,它是配置eos的模式,不配置则使用默认值pro部署模式,dev为开发模式,开发模式允许mock模拟。也就是说,当客户端client处指定请求mock数据时,我们eos会根据eos_mode来最终决定给模拟数据还是实际调用数据。假如eos_mode=pro,无论client是否指定mock,都返回实际调用数据。
3.1.2 界面配置端uddi
对于此系统,部署启动即可。可以使用发包提供的批处理命令启动也可以部署在web容器使用。提供服务接口的上传发布并进行审核、下载。
直观显示服务状态
此部分使用一看系统界面便懂,略过。。。
3.2 服务提供端Server
3.2.1 制定服务接口
根据项目功能需求制定java接口类,按照如下规约:(1)接口类有@EosService的注解
(2)对于注解需要配置version参数,接口升级需要升级version参数
(3)对于注解,id参数默认不需要配置,值为类名第一个字母小写,一个应用不能有相同的id的服务
(4)请不要使用方法的重载,也就是说方法函数名不要重复
(5)请使用常用java类型,int,boolean,String,Map,List等,请不要使用自定义POJO类,如User、Animal....
(6)接口方法详细根据javadoc进行注释
(7)配置mock参数,作为模拟测试用的返回值。写在javadoc注释的@return,每一种mock使用${}紧接描述,接着空一行写模拟值,模拟值使用json格式(简单类型直接写值,对象用{}格式,数组对应[]格式)。如:
/** * 取得num条List * * @param num * @return ${success}当入参name="criss"为成功输出 * [{"success":"成功了2", * "haha":"haha2"}] * ${error}当入参为其他时为错误输出 * [{"error":"错误了2"}] */ List getList(int num);
3.2.1 暴露服务端
在服务端系统启动处加入启动代码:com.sunsharing.eos.server.EosInit.start(ctx,"com.sunsharing");
ctx为Spring的ApplicationContext,也就是说服务接口如果是有Spring实现的,必须在启动初始化时候入参传入。上述代码意思是将在com.sunsharing扫描服务接口并进行注册等事件。
服务端开发在于对接口的实现,当接口有多种实现时,系统只默认取一个实现方式,所以,请尽量 不要有接口的多种实现。如果确实有多种接口,请指定,否则可能取的不是你想要的实现方式。指定可以使用配置文件EosServiceConfig.xml,主要在于impl,也就是服务的实现类,当实现类有多种,可以指定一种实现。格式如下:
<beans> <bean id="beanId" impl="com.sunsharing.eos.server.test.TestService"/> </beans>
3.3 客户端Client
对于客户端的使用,支持两种方式,java client和js client。当然,使用的接口均是服务端制定出来的,从uudi系统下载的接口文件。将下载的服务接口文件放在工程相应的位置,对于要快速开发使用的可以直接使用eos-client-example工程,请确保服务接口一定是从uudi系统下载下来的,并且不去更改它。3.3.1 基本功能使用
在客户端端系统启动处加入启动代码进行接口初始化:com.sunsharing.eos.client.EosInit.start("com.sunsharing");
java使用者直接使用接口:
Test test = ServiceContext.getBean(Test.class); test.sayHello("hello");
前端,使用js辅助插件jquer.eosremote.js进行接口调用,依赖jquery.js和json2.js,系统需要在web.xml文件中配置Servlet提供js请求,配置代码如下:
<servlet> <description>remote servlet</description> <display-name>remote servlet</display-name> <servlet-name>remoteServlet</servlet-name> <servlet-class>com.sunsharing.eos.client.RpcServlet</servlet-class> <init-param> <param-name>scanPackage</param-name> <param-value>com.sunsharing</param-value> </init-param> <init-param> <param-name>sysParamVar</param-name> <param-value>com.sunsharing.component.sys.ParamVarImpl</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>remoteServlet</servlet-name> <url-pattern>/remote</url-pattern> </servlet-mapping>
下面对上述配置进行说明:
(1)scanPackage:表示eos-client初始化时候扫描的服务接口的路径,同EosInit.start("com.sunsharing");。所以当有进行RpcServlet配置时,在系统启动不需要再写EosInit.start进行初始化。当然,写了也不会重复初始化。
(2)sysParamVar:配置系统变量获取com.sunsharing.eos.client.sys.SysParamVar的实现类,该实现类需要开发者自行编写,可以根据变量名取得系统缓存的变量的值。此类是为了支持js入参值为${}变量形式获取的接口,当入参为后台变量形式,如入参userId=${userId},那么后台java端将使用该实现类取得userId的值。
jquery.eosremote.js使用参数说明:它借鉴jquery ajax使用的格式,特别的,前端提出的一个需求,前端开发使用跨域取值请求,而实际整合联调项目时走正常同域请求,因此开发时候可以修改下jquery.eosremote.js文件的dataType参数为jsonp即可实现跨域,当实际联调时候再改回json。
Property | Type | Default | Description |
---|---|---|---|
url | String | "/remote" | RpcServlet配置的请求地址 |
serviceId | String | '' | 调用服务id |
method | String | '' | 调用服务的方法 |
mock | String | "" | 指定要获取的模拟的数据值,对应接口文件的@return的${} |
data | Object | null | 方法入参值 |
beforeSend | function | function (XHR) { } | 发送请求前 |
success | function | function (data, textStatus) { } | 请求成功后 |
error | function | function (XMLHttpRequest, textStatus, errorThrown) { if (console) { console.info(XMLHttpRequest); } } | 请求异常时 |
3.3.2 使用数据模拟
eos在设计上一个重要功能就是分离前后端,可以使用模拟的数据返回,使得前后端不需要同步等待。(1)上述前端调用的js插件入参mock即可指定服务的模拟参数
(2)还可以在配置文件EosServiceConfig.xml设置模拟参数,如下:bean上的mock表示此服务接口所有服务都是指定success的模拟,但是当配置具体方法的mock时,则使用具体方法的mock,下面服务的sayHello走的是error的模拟。
<beans> <bean id="testService" mock="success"> <methods> <sayHello mock="error"/> <getList mock="error"/> </methods> </bean> </beans>
(3)关注配置文件eos-client.properties的use_mock:默认不配置时为false。当配置use_mock=false时,任何的mock配置都无效,服务请求直接走真实服务调用。
总结上述三点,mock参数的指定需要有优先级关系,use_mock是全局性的控制,优先级最高。当use_mock为false时,任何mock都无效,这可以节省实际部署时候前端去除mock参数的事情。js接口指定mock参数优先级次之。xml配置的mock参数优先级最低。
3.3.2 服务AOP(后续可能改造重构)
有时候,我们在使用服务接口时候,我们需要在调用服务前或者调用服务后做一些事情,结合aop设计思想,我们提供了简单的aop功能。下面我们以一个例子来说明。例子:我们需要在调用用户登录服务后,将用户记录到Session
(1)实现Advice的实现类LoginAdvice,它有两个方法,分别是调用前和调用后。
public class LoginAdvice implements Advice { @Override public AdviceResult before(ServiceMethod method, Object[] args) { System.out.println(method.getMethodName() + "被执行前,入参为" + Arrays.toString(args)); System.out.println("RpcServletContext.getRequest=" + RpcServletContext.getRequest()); return new AdviceResult(false, null); } @Override public AdviceResult after(ServiceMethod method, Object[] args, Object returnVal) { System.out.println(method.getMethodName() + "被执行后,returnVal=" + returnVal); //设置session RpcServletContext.getRequest().getSession().setAttribute("user",returnVal); return new AdviceResult(true, returnVal); } }
(2)配置EosServiceConfig.xml,指定方法login,采用loginAdvice进行切面
<beans> <bean id="loginService"> <methods> <login advice="com.sunsharing.component.test.LoginAdvice"/> </methods> </bean> </beans>
注:
所有的切面类均要实现Advice接口。
RpcServletContext.getRequest()的使用,它是对ThreadLocal进行简单的封装,对于请求前端从RpcServlet请求的服务,使用此静态方法可以取得请求的HttpRequest。如果是java直接调用那么将得到空值。
相关文章推荐
- 前后端分离的思考与实践(二)
- 基于vue实现网站前台的权限管理(前后端分离实践)
- 前后端分离的思考与实践(1)
- 【第1176期】前后端分离实践
- 前后端分离的思考与实践(一)
- 前后端分离实践(一)
- 前后端分离的思考与实践(二)
- (精)前后端分离的思考与实践(一)
- 前后端分离的思考与实践(三)
- 前后端分离开发实践
- 前后端分离实践有感
- 基于NodeJS的前后端分离的思考与实践(四)安全问题解决方案
- 【C#编程最佳实践 十二】前后端分离的思考
- 基于vue,nodejs前后端分离的建站实践(web app)
- 前后端分离的思考与实践(三)
- [置顶] 前后端分离实践
- 前后端分离的思考与实践(四)
- 基于NodeJS的前后端分离的思考与实践(五)多终端适配
- 前后端分离的思考与实践(四)
- 前后端分离的思考与实践(一)