当前位置: 首页 > news >正文

前后端分离实践(EOS篇)

为什么80%的码农都做不了架构师?>>>   hot3.png

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管理端在线注册。

  •     系统的组成部署图如下:142749_qQC3_167767.png

1.4 系统执行流程

    服务端发布服务,挂接到eos,客户端取得挂接有需要调用的服务的eos地址进行服务请求,eos对请求进行过滤代理请求服务端服务数据,返回给客户端,具体图解如下:   142818_FSjT_167767.png          

    可见,多了模拟数据获取的支持,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。

PropertyTypeDefaultDescription
urlString"/remote"RpcServlet配置的请求地址
serviceIdString''调用服务id
methodString''调用服务的方法
mockString""指定要获取的模拟的数据值,对应接口文件的@return的${}
dataObjectnull方法入参值
beforeSendfunctionfunction (XHR) {     }发送请求前
successfunctionfunction (data, textStatus) {     }请求成功后
errorfunctionfunction (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>

注:

  1. 所有的切面类均要实现Advice接口。                

  2. RpcServletContext.getRequest()的使用,它是对ThreadLocal进行简单的封装,对于请求前端从RpcServlet请求的服务,使用此静态方法可以取得请求的HttpRequest。如果是java直接调用那么将得到空值。                         

转载于:https://my.oschina.net/ulyn/blog/324626

相关文章:

  • 备份和还原邮件(01)
  • 【进阶修炼】——改善C#程序质量(1)
  • Ubuntu端口开放
  • 二叉树详解
  • Photoshop打造唯美的蓝色古装外景人物图片
  • ASP.NET-FineUI开发实践-6
  • HDU-2069-Coin Change
  • javaweb学习总结(三十)——EL函数库
  • Bandizip---解压缩工具(可替代RAR)
  • 阿里云服务器购买完整流程
  • ireport默认参数和变量的含义
  • js几种排序算法
  • 2014年spark开发者大赛火热进行中!
  • 网卡别别名设置
  • Ping过程详解
  • 【RocksDB】TransactionDB源码分析
  • 【翻译】babel对TC39装饰器草案的实现
  • 【刷算法】求1+2+3+...+n
  • bootstrap创建登录注册页面
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • Git 使用集
  • js算法-归并排序(merge_sort)
  • leetcode98. Validate Binary Search Tree
  • MySQL的数据类型
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Swoft 源码剖析 - 代码自动更新机制
  • vue数据传递--我有特殊的实现技巧
  • WePY 在小程序性能调优上做出的探究
  • 阿里研究院入选中国企业智库系统影响力榜
  • 从输入URL到页面加载发生了什么
  • 如何合理的规划jvm性能调优
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 少走弯路,给Java 1~5 年程序员的建议
  • 使用 @font-face
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 异常机制详解
  • 从如何停掉 Promise 链说起
  • # 达梦数据库知识点
  • #QT(一种朴素的计算器实现方法)
  • (1)Nginx简介和安装教程
  • (C语言)fgets与fputs函数详解
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • ****Linux下Mysql的安装和配置
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .htaccess 强制https 单独排除某个目录
  • .NET Micro Framework初体验(二)
  • .net 流——流的类型体系简单介绍
  • @Query中countQuery的介绍
  • @selector(..)警告提示
  • [ JavaScript ] JSON方法
  • [AI]ChatGPT4 与 ChatGPT3.5 区别有多大
  • [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存
  • [ASP.NET MVC]如何定制Numeric属性/字段验证消息
  • [bbk5179]第66集 第7章 - 数据库的维护 03
  • [boost]使用boost::function和boost::bind产生的down机一例