博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[EDAS][HSF]开发 HSF 应用(EDAS SDK)
阅读量:6239 次
发布时间:2019-06-22

本文共 15305 字,大约阅读时间需要 51 分钟。

hot3.png

开发 HSF 应用(EDAS SDK)

更新时间:2019-04-19 12:01:13

  • 本页目录
    • 快速开始
    • 开发高级特性
    • 单元测试

您可以使用 EDAS-SDK 开发 HSF 应用,实现服务注册发现,还可以实现隐式传参、异步调用、泛化调用、调用链路 Filter 扩展等高级 HSF 特性,以及 HSF 单元测试。

快速开始

介绍如何使用 EDAS-SDK 快速开发 HSF 应用,完成服务注册与发现。

下载 Demo 工程

本章中介绍的代码均可以通过官方 Demo 获取。

  1. 下载 Demo 工程。
  2. 解压下载的压缩包,可以看到carshop文件夹,里面包含 itemcenter-api,itemcenter 和 detail 三个 Maven 工程文件夹。
  • itemcenter-api:提供接口定义
  • itemcenter:生产者服务
  • detail:消费者服务

说明:请使用 JDK 1.7 及以上版本。

定义服务接口

HSF 服务基于接口实现,当接口定义好之后,生产者将使用该接口实现具体的服务,消费者也是基于此接口去订阅服务。 在 Demo 的itemcenter-api工程中,定义了一个服务接口 com.alibaba.edas.carshop.itemcenter.ItemService,内容如下:

public interface ItemService {    public Item getItemById(long id);    public Item getItemByName(String name);}

该服务接口将提供两个方法:getItemById 与 getItemByName。

开发生产者服务

生产者将实现服务接口以提供具体服务。同时,如果使用了 Spring 框架,还需要在 .xml 文件中配置服务属性。

说明:Demo 工程中的 itemcenter 文件夹为生产者服务的示例代码。

实现服务接口

可以参考ItemServiceImpl.java文件中的示例:

package com.alibaba.edas.carshop.itemcenter;public class ItemServiceImpl implements ItemService {    @Override    public Item getItemById( long id ) {        Item car = new Item();        car.setItemId( 1l );        car.setItemName( "Mercedes Benz" );        return car;    }    @Override    public Item getItemByName( String name ) {        Item car = new Item();        car.setItemId( 1l );        car.setItemName( "Mercedes Benz" );        return car;    }}

配置服务属性

上述示例主要实现了 com.alibaba.edas.carshop.itemcenter.ItemService,并在两个方法中返回了一个 Item 对象。 代码开发完成之后,除了在 web.xml 中进行必要的常规配置,您还需要增加相应的 Maven 依赖, 同时在 Spring 配置文件使用 <hsf /> 标签注册并发布该服务。具体内容如下: 在pom.xml中添加如下 Maven 依赖:

javax.servlet
servlet-api
2.5
provided
com.alibaba.edas.carshop
itemcenter-api
1.0.0-SNAPSHOT
org.springframework
spring-web
2.5.6(及其以上版本)
com.alibaba.edas
edas-sdk
1.5.0

在 hsf-provider-beans.xml 文件中增加 Spring 关于 HSF 服务的配置。

interface=“com.alibaba.edas.carshop.itemcenter.ItemService"
ref=“itemService"
version=“1.0.0"

上面的示例为基本配置,您也可以根据您的实际需求,参考下面的生产者服务属性列表,增加其它配置。

生产者服务属性列表

属性 描述
interface 必须配置,类型为 [String],为服务对外提供的接口。
version 可选配置,类型为 [String],含义为服务的版本,默认为 1.0.0。
clientTimeout 该配置对接口中的所有方法生效,但是如果客户端通过 methodSpecials 属性对某方法配置了超时时间,则该方法的超时时间以客户端配置为准。其他方法不受影响,还是以服务端配置为准。
serializeType 可选配置,类型为 [String(hessian|java)],含义为序列化类型,默认为 hessian。
corePoolSize 单独针对这个服务设置核心线程池,从公用线程池中划分出来。
maxPoolSize 单独针对这个服务设置线程池,从公用线程池中划分出来。
enableTXC 开启分布式事务 GTS。
ref 必须配置,类型为 [ref],为需要发布为 HSF 服务的 Spring Bean ID。
methodSpecials 可选配置,用于为方法单独配置超时时间(单位 ms),这样接口中的方法可以采用不同的超时时间。该配置优先级高于上面的 clientTimeout 的超时配置,低于客户端的 methodSpecials 配置。

服务创建及发布限制

名称 示例 限制大小 是否可调整
{服务名}:{版本号} com.alibaba.edas.testcase.api.TestCase:1.0.0 最大192字节
组名 aliware 最大32字节
一个Pandora应用实例发布的服务数 N/A 最大800个 可在应用基本信息页面单击应用设置部分右侧的设置,在下拉列表中选择JVM,在弹出的应用设置对话框中进入自定义->自定义参数,在输入框中添加 -DCC.pubCountMax=1200 属性参数(该参数值可根据应用实际发布的服务数调整)。

生产者服务属性配置示例

开发消费者服务

消费者订阅服务从代码编写的角度分为两个部分。

  1. Spring 的配置文件使用标签hsf:consumer/定义好一个 Bean。
  2. 在使用的时候从 Spring 的 context 中将 Bean 取出来。

说明:Demo 工程中的 detail 文件夹为消费者服务的示例代码。

配置服务属性

与生产者一样,消费者的服务属性配置分为 Maven 依赖配置与 Spring 的配置。

  1. pom.xml文件中添加 Maven 依赖。 Maven 依赖配置与生产者相同,详情请参见开发生产者服务的配置服务属性。
  2. hsf-consumer-beans.xml 文件中添加 Spring 关于 HSF 服务的配置。 增加消费者的定义,HSF 框架将根据该配置文件去服务中心订阅所需的服务。
id="item"
interface="com.alibaba.edas.carshop.itemcenter.ItemService"
version="1.0.0"

配置服务调用

可以参考StartListener.java文件中的示例:

public class StartListener implements ServletContextListener{    @Override    public void contextInitialized( ServletContextEvent sce ) {        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( sce.getServletContext() );        // 根据 Spring 配置中的 Bean ID “item” 获取订阅到的服务        final ItemService itemService = ( ItemService ) ctx.getBean( "item" );        ……        // 调用服务 ItemService 的 getItemById 方法        System.out.println( itemService.getItemById( 1111 ) );        // 调用服务 ItemService 的 getItemByName 方法        System.out.println( itemService.getItemByName( "myname is le" ) );        ……    }}

上面的示例中为基本配置,您也可以根据您的实际需求,参考下面的服务属性列表,增加其它配置。

消费者服务属性列表

属性 描述
interface 必须配置,类型为 [String],为需要调用的服务的接口。
version 可选配置,类型为 [String],为需要调用的服务的版本,默认为1.0.0。
methodSpecials 可选配置,为方法单独配置超时时间(单位 ms)。这样接口中的方法可以采用不同的超时时间,该配置优先级高于服务端的超时配置。
target 主要用于单元测试环境和开发环境中,手动地指定服务提供端的地址。如果不想通过此方式,而是通过配置中心推送的目标服务地址信息来指定服务端地址,可以在消费者端指定 -Dhsf.run.mode=0。
connectionNum 可选配置,为支持设置连接到 server 连接数,默认为1。在小数据传输,要求低延迟的情况下设置多一些,会提升 TPS。
clientTimeout 客户端统一设置接口中所有方法的超时时间(单位 ms)。超时时间设置优先级由高到低是:客户端 methodSpecials,客户端接口级别,服务端 methodSpecials,服务端接口级别 。
asyncallMethods 可选配置,类型为 [List],设置调用此服务时需要采用异步调用的方法名列表以及异步调用的方式。默认为空集合,即所有方法都采用同步调用。
maxWaitTimeForCsAddress 配置该参数,目的是当服务进行订阅时,会在该参数指定时间内,阻塞线程等待地址推送,避免调用该服务时因为地址为空而出现地址找不到的情况。若超过该参数指定时间,地址还是没有推送,线程将不再等待,继续初始化后续内容。

注意,在应用初始化时,需要调用某个服务时才使用该参数。如果不需要调用其它服务,请勿使用该参数,会延长启动启动时间。 消费者服务属性配置示例

发布服务

完成代码、接口开发和服务配置后,在 Eclipse 或 IDEA 中,可直接以 Ali-Tomcat 运行该服务 (具体请参照文档开发工具准备中的配置 Eclipse 开发环境和配置 IDEA 开发环境)。

在开发环境配置时,有一些额外 JVM 启动参数来改变 HSF 的行为,具体如下: 属性 描述 -Dhsf.server.port 指定 HSF 的启动服务绑定端口,默认值为 12200。 -Dhsf.serializer 指定 HSF 的序列化方式,默认值为 hessian。 -Dhsf.server.max.poolsize 指定 HSF 的服务端最大线程池大小,默认值为 600。 -Dhsf.server.min.poolsize 指定 HSF 的服务端最小线程池大小。默认值为 50。 -DHSF_SERVER_PUB_HOST 指定对外暴露的 IP,如果不配置,使用 -Dhsf.server.ip 的值。 -DHSF_SERVER_PUB_PORT 指定对外暴露的端口,该端口必须在本机被监听,并对外开放了访问授权,默认使用 -Dhsf.server.port 的配置,如果 -Dhsf.server.port 没有配置,默认使用12200。

开发环境查询 HSF 服务

在开发调试的过程中,如果您的服务是通过轻量配置中心进行服务注册与发现,就可以通过 EDAS 控制台查询某个应用提供或调用的服务。

假设您在一台 IP 为 192.168.1.100 的机器上启动了 EDAS 配置中心。

  1. 进入 。
  2. 在左侧菜单栏单击服务列表,输入服务名、服务组名或者 IP 地址进行搜索,查看对应的服务提供者以及服务调用者。

说明:配置中心启动之后默认选择第一块网卡地址做为服务发现的地址,如果开发者所在的机器有多块网卡的情况,可设置启动脚本中的 SERVER_IP 变量进行显式的地址绑定。

常见查询案例

  • 提供者列表页
    • 在搜索条件里输入 IP 地址,单击搜索即可查询该 IP 地址的机器提供了哪些服务。
    • 在搜索条件里输入服务名或服务分组,即可查询哪些 IP 地址提供了这个服务。
  • 调用者列表页
    • 在搜索条件里输入 IP 地址,单击搜索即可查询该 IP 地址的机器调用了哪些服务。
    • 在搜索条件里输入服务名或服务分组,即可查询哪些 IP 地址调用了这个服务。

开发高级特性

在您参照上面的快速开始开发完基本的应用之后,下面将向您介绍如何在上面的应用中实现隐式传参、异步调用、泛化调用和 Filter 链路扩展等 HSF 的高级特性。您可以直接下载 Demo。 隐式传参(目前仅支持字符串传输)

隐式传参一般用于传递一些简单 KV 数据,又不想通过接口方式传递,类似于 Cookie。 单个参数传递 服务消费者: RpcContext.getContext().setAttachment("key", "args test"); 服务提供者: String keyVal=RpcContext.getContext().getAttachment("key"); 多个参数传递

服务消费者:

Map
map=new HashMap
();map.put("param1", "param1 test");map.put("param2", "param2 test");map.put("param3", "param3 test");map.put("param4", "param4 test");map.put("param5", "param5 test");RpcContext rpcContext = RpcContext.getContext();rpcContext.setAttachments(map);

服务提供者:

Map
map=rpcContext.getAttachments();Set
set=map.keySet();for (String key : set) { System.out.println("map value:"+map.get(key));}

说明:隐式传参只对单次调用有效,当消费端调用返回后,会自动擦除 RpcContext 中的信息。

异步调用

支持 callback 和 future 两种异步调用方式。 callback 调用方式 客户端配置为 callback 方式时,需要配置一个实现了 HSFResponseCallback 接口的 listener。结果返回之后, HSF 会调用 HSFResponseCallback 中的方法。 注意:这个 HSFResponseCallback 接口的 listener 不能是内部类,否则 Pandora 的 classloader 在加载时就会报错。 XML 中的配置:

其中 AsynABTestCallbackHandler 类实现了 HSFResponseCallback 接口。DemoApi 接口中有一个方法是 ayncTest 。 代码示例

public void onAppResponse(Object appResponse) {  //获取到异步调用后的值 String msg = (String)appResponse; System.out.println("msg:"+msg);}

注意: 由于只用方法名字来标识方法,所以并不区分重载的方法。同名的方法都会被设置为同样的调用方式。 不支持在 call 里再发起 HSF 调用。这种做法可能导致 IO 线程挂起,无法恢复。 future 调用方式 客户端配置为 future 方式时,发起调用之后,通过 HSFResponseFuture 中的 public static Object getResponse(long timeout) 来获取返回结果。 XML 中的配置:

代码示例如下。 单个调用异步处理:

//发起调用demoApi.ayncTest();// 处理业务...//直接获得消息(若无需获得结果,可以不用操作该步骤)String msg=(String) HSFResponseFuture.getResponse(3000);

多个调用需要并发处理: 若是多个业务需要并发处理,可以先获取 future,存储起来,等调用完毕后再使用。

//定义集合List
futures = new ArrayList
();方法内进行并行调用:试用//发起调用demoApi.ayncTest();//第一步获取 future 对象HSFFuture future=HSFResponseFuture.getFuture();futures.add(future);//继续调用其他业务(同样采取异步调用)HSFFuture future=HSFResponseFuture.getFuture();futures.add(future);// 处理业务...//获得数据并做处理for (HSFFuture hsfFuture : futures) { String msg=(String) hsfFuture.getResponse(3000); //处理相应数据 ...}

泛化调用

通过泛化调用可以组合接口、方法、参数进行 RPC 调用,无需依赖任何业务 API。 步骤一:在消费者 XML 配置中加入泛化属性

说明:generic 代表泛化参数,true 表示支持泛化,false 表示不支持,默认为 false。

DemoApi 接口方法:

public String dealMsg(String msg);public GenericTestDO dealGenericTestDO(GenericTestDO testDO);

步骤二:获取 demoApi 进行强制转换为泛化服务

导入泛化服务接口

import com.alibaba.dubbo.rpc.service.GenericService

获取泛化对象 XML 加载方式

//若 WEB 项目中,可通过 Spring bean 进行注入后强制转换,这里是单元测试,所以采用加载配置文件方式  ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext("hsf-generic-consumer-beans.xml");  //强制转换接口为 GenericService  GenericService svc = (GenericService) consumerContext.getBean("demoApi");

代码订阅方式

HSFApiConsumerBean consumerBean = new HSFApiConsumerBean();  consumerBean.setInterfaceName("com.alibaba.demo.api.DemoApi");consumerBean.setGeneric("true"); // 设置 generic 为 trueconsumerBean.setVersion("1.0.0");consumerBean.init();// 强制转换接口为 GenericServiceGenericService svc = (GenericService) consumerBean.getObject();

步骤三:泛化接口

Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException;

接口参数说明: methodName:需要调用的方法名称。 parameterTypes:需要调用方法参数的类型。 args:需要传输的参数值。

步骤四:泛化调用

String 类型参数

svc.$invoke("dealMsg", new String[] { "java.lang.String" }, new Object[] { "hello" })

对象参数,服务端和客户端需要保证相同的对象

// 第一步构造实体对象 GenericTestDO,该实体有 id、name 两个属性  GenericTestDO genericTestDO = new GenericTestDO();  genericTestDO.setId(1980l);  genericTestDO.setName("genericTestDO-tst");  // 使用 PojoUtils 生成二方包 pojo 的描述  Object comp = PojoUtils.generalize(genericTestDO);  // 服务泛化调用  svc.$invoke("dealGenericTestDO",new String[] { "com.alibaba.demo.generic.domain.GenericTestDO" }, new Object[] { comp });

调用链路 Filter 扩展

下载 Demo。

基础接口

public interface ServerFilter extends RPCFilter {}public interface ClientFilter extends RPCFilter {}public interface RPCFilter {    ListenableFuture
invoke(InvocationHandler invocationHandler, Invocation invocation) throws Throwable; void onResponse(Invocation invocation, RPCResult rpcResult);}

实现步骤

实现 ServerFilter 进行服务端拦截。 实现 ClientFilter 进行客户端拦截。 业务通过标准的 META-INF/services/com.taobao.hsf.invocation.filter.RPCFilter 文件来注册 Filter。 实现示例

import com.taobao.hsf.invocation.Invocation;import com.taobao.hsf.invocation.InvocationHandler;import com.taobao.hsf.invocation.RPCResult;import com.taobao.hsf.invocation.filter.ServerFilter;import com.taobao.hsf.util.PojoUtils;import com.taobao.hsf.util.concurrent.ListenableFuture;public class HSFServerFilter implements ServerFilter {    public ListenableFuture
invoke(InvocationHandler invocationHandler, Invocation invocation) throws Throwable { //process args String[] sigs = invocation.getMethodArgSigs(); Object [] args = invocation.getMethodArgs(); System.out.println("#### intercept request"); for(String sig : sigs) { System.out.print(sig); System.out.print(";"); } System.out.println(); for(Object arg : args) { System.out.println(PojoUtils.generalize(arg)); System.out.print(";"); } System.out.println(); return invocationHandler.invoke(invocation); } public void onResponse(Invocation invocation, RPCResult rpcResult) { System.out.println("#### intercept response"); Object resp = rpcResult.getHsfResponse().getAppResponse(); System.out.println(PojoUtils.generalize(resp)); }}

配置 META-INF/services/com.taobao.hsf.invocation.filter.RPCFilter

com.alibaba.edas.carshop.itemcenter.filter.HSFServerFilter

运行效果

intercept request

long 1111 intercept response

试用 {itemId=1, itemName=Mercedes Benz, class=com.alibaba.edas.carshop.itemcenter.Item} 可选的 Filter

在一些场景下,您定制了 filter,但是只希望在某些服务上使用,这时可以使用可选的 Filter。具体做法是在对应的 Filter上 增加 @Optional 注解,如下: 试用

@Optional@Name("HSFOptionalServerFilter")public class HSFOptionalServerFilter implements ServerFilter {    public ListenableFuture
invoke(InvocationHandler invocationHandler, Invocation invocation) throws Throwable { System.out.println("#### HSFOptionalServerFilter intercept request"); return invocationHandler.invoke(invocation); } public void onResponse(Invocation invocation, RPCResult rpcResult) { System.out.println("#### HSFOptionalServerFilter intercept response"); }}

当指定服务需要使用该 Filter 时,只需要在配置的 Bean 上声明即可,配置如下:

HSFOptionalServerFilter
NoFilter

上述配置的服务,将会使用所有的非 @Optional 修饰的 ServerFilter ,并且会包括 HSFOptionalServerFilter 和 NoFilter,而 HSFOptionalServerFilter 的名称是来自于对应的 Filter 配置上的 修饰。

如果无法找到该名称的 Filter ,只会提醒您,但是不会导致您无法启动或者运行。

单元测试

在测试环境中,有两种方式做单元测试。 方式一 通过 LightApi 代码发布和订阅服务 方式二 通过 XML 配置发布订阅服务 相关样例请下载 Demo。 方式一 通过 LightApi 代码发布和订阅服务

在 Maven 中添加 LightApi 依赖。

com.alibaba.hsf
LightApi
1.0.5

注意: 请使用 1.0.5 或以上版本的 LightApi,否则可能遇到 hsf: can not load class {com.taobao.hsf.address.AddressService} after all phase的错误。 创建 ServiceFactory。 这里需要设置 Pandora 的地址,参数是 SAR 包所在目录。如果 SAR 包地址是 /Users/Jason/Work/AliSoft/PandoraSar/DevSar/taobao-hsf.sar,则参数如下:

private static final ServiceFactory factory = ServiceFactory.getInstanceWithPath("/Users/Jason/Work/AliSoft/PandoraSar/DevSar");

通过代码进行发布和订阅服务。

// 进行服务发布(若有发布者,无需再在这里写) factory.provider("helloProvider")// 参数是一个标识,初始化后,下次只需调用 provider("helloProvider")即可提供对应服务         .service("com.alibaba.edas.unit.service.UnitTestService")// 接口全类名         .version("1.0.0")// 版本号         .impl(new UnitTestServiceImpl())// 对应的服务实现         .publish();// 发布服务,至少要调用 service()和 version()才可以发布服务 // 进行服务消费 factory.consumer("helloConsumer")// 参数是一个标识,初始化后,下次只需调用 consumer("helloConsumer")即可直接提供对应服务         .service("com.alibaba.edas.unit.service.UnitTestService")// 接口全类名         .version("1.0.0")// 版本号         .subscribe(); factory.consumer("helloConsumer").sync();// 同步等待地址推送,最多6秒。 UnitTestService log4jService = (UnitTestService) factory.consumer("helloConsumer").subscribe();// 用 ID 获取对应服务,subscribe()方法返回对应的接口 // 调用服务方法 System.out.println("bean -> msg rec success:-"+log4jService.print());

方式二 通过 XML 配置发布订阅服务

编写好 HSF 的 XML 配置。 通过代码方式加载配置文件。

//XML 方式加载服务提供者new ClassPathXmlApplicationContext("hsf-provider-beans.xml");//XML 方式加载服务消费者ClassPathXmlApplicationContext consumerContext=new ClassPathXmlApplicationContext("hsf-consumer-beans.xml");//获取 BeanUnitTestXMLConsumer unitTestXMLConsumer=(UnitTestXMLConsumer) consumerContext.getBean("unitTestConsumer");//服务调用 unitTestXMLConsumer.testUnitProvider();

参考

阿里云官网 - 帮助文档:

转载于:https://my.oschina.net/u/2464371/blog/3041571

你可能感兴趣的文章
php cookie
查看>>
linux下redis安装
查看>>
量子通信和大数据最有市场突破前景
查看>>
如何申请开通微信多客服功能
查看>>
Sr_C++_Engineer_(LBS_Engine@Global Map Dept.)
查看>>
非监督学习算法:异常检测
查看>>
jquery的checkbox,radio,select等方法总结
查看>>
Linux coredump
查看>>
Ubuntu 10.04安装水晶(Mercury)无线网卡驱动
查看>>
我的友情链接
查看>>
ElasticSearch 2 (32) - 信息聚合系列之范围限定
查看>>
VS2010远程调试C#程序
查看>>
windows查看端口占用
查看>>
Yii用ajax实现无刷新检索更新CListView数据
查看>>
App 卸载记录
查看>>
JavaScript变量和作用域
查看>>
开源SIP服务器加密软件NethidPro升级
查看>>
Apache Pulsar中的地域复制,第1篇:概念和功能
查看>>
python pip install 出现 OSError: [Errno 1] Operation not permitted
查看>>
从源码分析scrollTo、scrollBy、Scroller方法的区别和作用
查看>>