全部产品
云市场

通道服务

更新时间:2019-02-19 17:43:28

前提

  • 已经搭建了AServer应用集群
  • 已经搭建了ACCS应用集群
  • 已经搭建了Zookeeper Dubbo 注册中心
  • AServer,ACCS应用集群与接入ACCS服务端SDK的业务方应用之间网络互通

    接入步骤

  1. 查阅ACCS相关的名词解释,以便对本文提及的术语及其含义有所了解
  2. 理解架构图,以便对ACCS、SDK接入方及设备所在的位置有所了解
  3. 创建/拥有 一个Java工程来接入SDK
  4. 在Java工程里引入一个maven依赖,不是maven工程的可以手工引入jar类库
  5. 初始化ACCSServiceSDK
  6. 使用ACCSServiceSDK发送下行消息或者接收上行消息
  7. 尝试运行demo工程

架构图

屏幕快照 2018-05-21 下午4.46.37.png | center | 535x548

说明

  1. 使用ACCS SDK的被称为业务方
  2. 业务方可以通过ACCS向设备主动发送消息
  3. 设备收到下行消息后,可以向ACCS发送消息回执,ACCS可以把消息回执进一步投递给业务方
  4. 设备也可以主动向服务端发送消息

接入ACCSService SDK

我们假设端上已经接入了ACCS的SDK,服务端要接收设备上行的数据、或者服务端要主动发送下行数据到设备上,必须要接入服务端的SDK。

引入类库

  1. <dependency>
  2. <groupId>com.emas.accslite.app</groupId>
  3. <artifactId>service-sdk</artifactId>
  4. <version>1.2.6.Final</version>
  5. </dependency>

或者从以下demo工程lib目录中提取service-sdk类库

Maven仓库设置

setting.xml文件中设置

  1. <servers>
  2. <server>
  3. <id>emasRelease</id>
  4. <username>username</username><!-- 请向Emas管理员索取用户名和密码 -->
  5. <password><![CDATA[password]]></password>
  6. </server>
  7. <server>
  8. <id>emasSnapshot</id>
  9. <username>username</username> <!-- 请向Emas管理员索取用户名和密码 -->
  10. <password><![CDATA[password]]></password>
  11. </server>
  12. </servers>

工程的pom.xml文件中设置

  1. <repositories>
  2. <repository>
  3. <name>emas_release</name>
  4. <id>emasRelease</id>
  5. <releases>
  6. <enabled>true</enabled>
  7. </releases>
  8. <url>http://nexus-ce.emas-poc.com/repository/maven-releases/</url>
  9. </repository>
  10. <repository>
  11. <name>emas_snapshot</name>
  12. <id>emasSnapshot</id>
  13. <releases>
  14. <enabled>true</enabled>
  15. </releases>
  16. <url>http://nexus-ce.emas-poc.com/repository/maven-snapshots/</url>
  17. </repository>
  18. </repositories>

配置项

SDK初始化的时候会尝试从应用的classpath路径查找application.properties文件,找不到就会查找工作目录下的config/application.properties文件。从其中加载以下配置项:

  1. emas.accs.service.name=your_own_service
  2. emas.accs.dubbo.registry=zookeeper://11.163.132.16:2181
  3. emas.accs.dubbo.group=emas_dev
  • emas.accs.service.name 是应用上的服务名称,需要跟端上SDK中的服务名称保持一致,否则ACCS将无法把上行消息投递给应用端
  • emas.accs.dubbo.registry dubbo应用的注册中心地址
  • emas.accs.dubbo.group dubbo服务的分组

    Spring Boot应用接入

    服务端的SDK是一个Spring Boot工程,假如你的工程是一个Spring Boot应用,在应用启动时ACCS相关的Bean配置就会被加载到Spring的Bean Factory中。

在添加了service-sdk的maven依赖之后,在应用的业务类中就可以通过Autowired的方式使用ACCSServiceSDK了。如:

  1. @Autowired
  2. private ACCSServiceSDK accsServiceSDK;

非Spring Boot应用接入

1要求业务应用是一个Spring应用2要求在应用Bean初始化的时候加载classpath:accs-service-sdk.xml。3 并且配置文件也提供了classpath:/application.properties或者工作目录下的config/application.properties。在执行上述1-3步骤后,也同样可以通过Autowired方式使用ACCSServiceSDK

appkey serviceId deviceId之间的关系

appkey表示一个唯一的应用,deviceId表示一个唯一的设备,serviceId用来表示唯一的消息路由关系。ServiceId可以跟端细分模块建立起一一映射关系,当消息到达端上时,端上ACSS SDK根据不同的serviceId把消息路由到对应模块。反之,当端上设备发送上行消息时,ACCS服务端会根据ServiceId把消息路由到对应的SDK服务端。

一个应用可以对应多个设备和多个serviceId。当一个应用的业务足够庞大,势必需要多个ServiceId。当一个应用业务相对简单时,一个应用对应一个serviceId就已经足够了,也就是说ACCS端上SDK设置的SDK与接入了ACCS服务端SDK的ServiceId和发送消息的ServiceId都是相同的值。

数据类型

指的是发送上行或者下行消息的类型

  1. public enum ACCSDataType {
  2. /**
  3. * 需要ACK的数据下行
  4. */
  5. DATA_WITH_ACK,
  6. /**
  7. * 需要业务ACK的数据下行
  8. */
  9. DATA_WITH_BIZACK,
  10. /**
  11. * 不需要ACK的数据下行
  12. */
  13. DATA_WITHOUT_ACK,
  14. /**
  15. * 回执
  16. */
  17. ACK
  18. }

数据策略

对应的类是

  1. com.emas.accs.dataobject.ACCSDataStrategy

可以相应指定

  • 过期时间
  • 目标不在线,是否丢弃消息
  • 是否需要业务确认
  • 消息是否存储,直接影响离线消息

发送策略

对应的类是

  1. com.emas.accs.dataobject.ACCSSendStrategy

只在批量发送的方法中使用到

  • SYNC_SEND 同步发送
  • ASYNC_SEND 异步发送

发送下行消息

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class ACCSServiceSDKTest {
  4. @Autowired
  5. private ACCSServiceSDK accsServiceSDK;
  6. @Test
  7. public void testSendData(){
  8. Assert.assertNotNull(accsServiceSDK);
  9. ACCSTarget target = new ACCSTarget();
  10. target.setDeviceId("Vh3zpwqJl08DAOflua8HK2lq");
  11. target.setAppKey("10000031");
  12. target.setServiceName("demo_service");
  13. ACCSDataStrategy accsDataStrategy = new ACCSDataStrategy();
  14. accsDataStrategy.setDiscardIfOffline(false);
  15. accsDataStrategy.setExpiredTime(DateUtils.addDays(new Date(), 3).getTime());
  16. ACCSResult<String> result = accsServiceSDK.sendData(ACCSDataType.DATA_WITHOUT_ACK,
  17. target,
  18. accsDataStrategy,
  19. "hello world".getBytes());
  20. Assert.assertEquals(true, result.isSuccess());
  21. }
  22. }

例子解读:

  1. ACCSTarget target = new ACCSTarget();
  2. target.setDeviceId("Vh3zpwqJl08DAOflua8HK2lq");
  3. target.setAppKey("10000031");
  4. target.setServiceName("demo_service");

上述代码设置了一个接收消息的设备id,deviceId为Vh3zpwqJl08DAOflua8HK2lq,appkey为10000031, serviceName/serviceId 为demo_service

  1. ACCSDataStrategy accsDataStrategy = new ACCSDataStrategy();
  2. accsDataStrategy.setDiscardIfOffline(false);
  3. accsDataStrategy.setExpiredTime(DateUtils.addDays(new Date(), 3).getTime());

上述代码创建了一个发送策略,accsDataStrategy.setDiscardIfOffline(false) 代表这是一个离线消息,过期时间为3天后。潜在含义是设备如果在线则立刻投递;如果不在线且设备在3天内重新连接的话,消息将会被投递,否则将丢弃。

  1. ACCSResult<String> result = accsServiceSDK.sendData(ACCSDataType.DATA_WITHOUT_ACK,
  2. target,
  3. accsDataStrategy,
  4. "hello world".getBytes());

上述代码表示发送的消息内容是:hello world,accsServiceSDK对消息进行了发送,指定的数据类型是需要ACCS回执的,也就是说设备端收到消息后将会发送一个ACK类型的数据给ACCS。

批量发送下行消息

一对多发送

即同一消息群发给多个设备, 结果通过com.emas.accs.handler.ACCSRpcCallBackHandler 返回

例子代码

  1. Assert.assertNotNull(accsServiceSDK);
  2. ACCSDataStrategy accsDataStrategy = new ACCSDataStrategy();
  3. accsDataStrategy.setAccsNotifyMode(ACCSNotifyMode.ALL);
  4. accsDataStrategy.setDiscardIfOffline(false);
  5. accsDataStrategy.setExpiredTime(DateUtils.addDays(new Date(), 3).getTime());
  6. List<ACCSTarget> batch = Lists.newArrayList();
  7. for(int i =0; i < 1000; i++){
  8. ACCSTarget bat = new ACCSTarget();
  9. bat.setDeviceId("WtWhjS5Pog0DAHL3+gEtQ3"+i);
  10. bat.setAppKey("4272");
  11. bat.setServiceName("accs");
  12. batch.add(bat);
  13. }
  14. Map ext = Maps.newHashMap();
  15. ext.put("x", 1341324);
  16. ACCSResult<Map<ACCSBaseTarget, ACCSResult<String>>> mapACCSResult =accsServiceSDK.batchSendData(ACCSDataType.DATA_WITH_ACK, accsDataStrategy,
  17. ACCSSendStrategy.ASYNC_SEND,"hello".getBytes(),batch, ext);

多对多发送

即多个消息对应发送多个设备, 结果通过com.emas.accs.handler.ACCSRpcCallBackHandler 返回

例子代码

  1. Assert.assertNotNull(accsServiceSDK);
  2. ACCSTarget target = new ACCSTarget();
  3. target.setUser("tony");
  4. target.setDeviceId("WthZkejKfXADAIAs680zY9wz");
  5. target.setAppKey("4272");
  6. target.setServiceName("accs");
  7. ACCSTarget target2 = new ACCSTarget();
  8. target2.setUser("kate");
  9. target2.setAppKey("4272");
  10. target2.setServiceName("accs");
  11. ACCSDataStrategy accsDataStrategy = new ACCSDataStrategy();
  12. accsDataStrategy.setAccsNotifyMode(ACCSNotifyMode.ALL);
  13. accsDataStrategy.setDiscardIfOffline(false);
  14. accsDataStrategy.setExpiredTime(DateUtils.addDays(new Date(), 3).getTime());
  15. ACCSBatchData batchData1 = new ACCSBatchData();
  16. batchData1.setData("hello".getBytes());
  17. ACCSBatchData batchData2 = new ACCSBatchData();
  18. batchData2.setData("love".getBytes());
  19. Map ext = Maps.newHashMap();
  20. ext.put("x", 1341324);
  21. ACCSResult<Map<ACCSBaseTarget, ACCSResult<String>>> mapACCSResult = accsServiceSDK.batchSendData(ACCSDataType.DATA_WITH_ACK,
  22. accsDataStrategy,ACCSSendStrategy.ASYNC_SEND, Lists.newArrayList(batchData1,batchData2), Lists.newArrayList(target,target2), ext);

接收上行消息

ACCS SDK定义了多个事件处理接口

  • com.emas.accs.handler.ACCSDataHandler 接收端上上行消息,非ACK和出错事件
  • com.emas.accs.handler.ACCSEventHandler 接收端上上行事件,ACK和出错事件
  • com.emas.accs.handler.ACCSRpcCallBackHandler 批量发送接口的回调

创建一个java类,实现上述接口中的一个或者多个,然后分别注册到ACCSServiceSDK中,如后面章节例子工程中的HelloWorldController就实现了ACCSDataHandler和ACCSEventHandler,并注册到了ACCSServiceSDK中。

  1. public class HelloWorldController implements InitializingBean, ACCSDataHandler, ACCSEventHandler{
  2. private final String iOSDeviceId = "WM9ML+9HONsDAH9L84NW4OBK";
  3. private final String androidDeviceId = "Vh3zpwqJl08DAOflua8HK2lq";
  4. private final String iosAppkey = "10000031";
  5. private final String androidAppkey = "10000078";
  6. private final String userId = "21321124123";
  7. @Autowired
  8. private ACCSServiceSDK accsServiceSDK;
  9. @Override
  10. public byte[] onReceived(byte[] bytes) {
  11. System.out.println("onReceived"+new String(bytes));
  12. return "this string will be send back to the device".getBytes();
  13. }
  14. @Override
  15. public void onEvent(ACCSEvent accsEvent, String s) {
  16. System.out.println("onEvent"+new String(s));
  17. }
  18. @Override
  19. public void afterPropertiesSet() throws Exception {
  20. this.accsServiceSDK.setDataHandler(this);
  21. this.accsServiceSDK.setEventHandler(this);
  22. }
  23. @RequestMapping("/hello")
  24. public ACCSResult<String> hello(@RequestParam(value = "serviceName", defaultValue = "accs", required = false) String serviceName,
  25. @RequestParam(value = "appkey", defaultValue = "4272") String appkey,
  26. @RequestParam(value = "deviceId", defaultValue = "WmGqGZNJnYsDAGSHJ62rSEsO") String deviceId,
  27. @RequestParam(value = "user", required = false) String user,
  28. @RequestParam(value = "msg", defaultValue = "Hello world!", required = false) String msg ){
  29. Assert.notNull(AccsDmAppServiceFactory.getAccsDmAppWriteService(), "null null.");
  30. accsServiceSDK.sendData(null,null,null, null);
  31. AccsDmAppServiceFactory.getAccsDmAppWriteService().registerService("abc", "addd");
  32. ACCSTarget target = new ACCSTarget();
  33. target.setUser(user);
  34. target.setDeviceId(deviceId);
  35. target.setAppKey(appkey);
  36. target.setServiceName(serviceName);
  37. ACCSDataStrategy accsDataStrategy = new ACCSDataStrategy();
  38. accsDataStrategy.setDiscardIfOffline(false);
  39. accsDataStrategy.setExpiredTime(DateUtils.addDays(new Date(), 3).getTime());
  40. ACCSResult<String> result = accsServiceSDK.sendData(ACCSDataType.DATA_WITH_ACK, target, accsDataStrategy, "hello world!".getBytes());
  41. return result;
  42. }
  43. }

问题排查

${user.home}/logs/accs-sdk/ 目录记录着sdk的相关日志

  1. 查看accs-data-in.log日志可得知收到了哪些消息
  2. 查看accs-data-out.log日志可得知发送了哪些消息

集成常见问题

log4j初始化异常

  1. Caused by: java.lang.NoSuchFieldError: fileName
  2. at org.apache.log4j.DailyRollingFileAppender.activateOptions(DailyRollingFileAppender.java:224) ~[log4j-1.2.17.jar:1.7.21]
  3. at org.apache.log4j.DailyRollingFileAppender.<init>(DailyRollingFileAppender.java:205) ~[log4j-1.2.17.jar:1.7.21]
  4. at com.emas.accs.util.ACCSLogFactory.prepareLog(ACCSLogFactory.java:122) ~[classes/:na]
  5. at com.emas.accs.util.ACCSLogFactory.<clinit>(ACCSLogFactory.java:99) ~[classes/:na]
  6. at com.emas.accs.ACCSServiceSDK.<clinit>(ACCSServiceSDK.java:28) ~[classes/:na]
  7. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_152-ea]
  8. at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_152-ea]
  9. at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_152-ea]
  10. at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_152-ea]

解决方法:请确认不要依赖org.slf4j:log4j-over-slf4j如:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. <exclusions>
  5. <exclusion>
  6. <groupId>org.slf4j</groupId>
  7. <artifactId>log4j-over-slf4j</artifactId>
  8. </exclusion>
  9. </exclusions>
  10. </dependency>