基于Spring Boot 2.0的IoT应用集成和使用servicecomb实践

举报
liubao68 发表于 2018/10/09 10:49:58 2018/10/09
【摘要】 本文介绍了基于Spring Boot 2.0的IoT应用集成和使用servicecomb的步骤,收益以及问题。使用servicecomb,可以快速的让一个原型系统,具备微服务治理和运维能力,加快了业务上线速度,降低服务运行风险。



目录

- 集成步骤

- 集成效果

  - 运行时变化

  - 开发方式变化

  - 周边工具变化

  - 开启metrics监控

- 常见问题

  - 三方件冲突

  - SpringMVC数据类型支持

  - 依赖于Spring MVC特定机制的业务逻辑



本文通过一个IoT的应用展现在Spring Boot 2.0中集成和使用servicecombIoT应用原来使用Spring Boot 2.0开发,然后通过少量的步骤集成servicecomb。本文说明集成servicecomb以后给应用带来了哪些新特性,中间存在的一些变化和改造过程中碰到的问题。本文档根据servicecomb 2.0.1版本刷新,详细的版本介绍请参考SDK配套关系说明

 

集成步骤

在Spring Boot中集成servicecomb的基本原理,可以参考在spring boot中使用java chassis的描述。将一个现有 Spring Boot的应用改造为servciecomb,一般可以采用下面的步骤。需要注意下面的步骤是一般的改造步骤,完成这些步骤后,可以识别出常见问题,逐个解决这些问题。


  1. 增加依赖关系


在dependencyManagement里面导入servicecomb的依赖管理,dependencyManage能够简化依赖管理的工作量,减少三方件冲突,详细可以参考使用maven管理复杂依赖关系的技巧


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.servicecomb</groupId>
                <artifactId>java-chassis-dependencies</artifactId>
                <version>2.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

 

然后引入servicecomb对于spring boot 2提供的支持组件包。

<dependencies>
    <dependency>
      <groupId>org.apache.servicecomb</groupId>
      <artifactId>java-chassis-spring-boot-starter-servlet</artifactId>
    </dependency>
</dependencies>


  2.     启用ServiceComb能力 


通过@EnableServiceComb标签在Spring Boot 2中启用和加载servicecomb。在Spring Boot 2中,开发者会将服务通过DispatcherServlet发布为REST接口,在Spring Boot 2默认实现中,REST接口通过DispatcherServlet发布,集成servicecomb后,服务通过servicecomb提供的RestServlet进行发布。集成后,两个Servlet可以并存,可以将他们的上下文设置为不同的前缀,以避免冲突。具体参考步骤3。

@SpringBootApplication    
@EnableServiceComb    
public class UserMain {    
  public static void main(String[] args) throws Exception {    
    try {    
      SpringApplication.run(UserMain.class, args);    
    } catch (Exception e) {    
      e.printStackTrace();    
    }    
  }    
}


3.       发布服务接口


和Spring Boot 2一样,servicecom也会扫描@RestContoller标签的类,并将这些类发布为REST接口。同时将接口信息注册到服务中心。可以在application.yml中指定服务中心地址,并提供简单的微服务信息。其中service_description.name可以使用原来项目的名字,servicecomb.rest.address采用tomcat的监听端口server.port进行发布。其中server.servlet.path=/mvc和servicecomb.rest.servlet.urlPattern=/cse/*分别是Spring Boot的DispatcherServlet和servicecom的RestServlet前缀。

APPLICATION_ID:   MY_APPLICATION
service_description:
  name: ${spring.application.name}
  version: 0.0.1
 
servicecomb:
  service:
    registry:
      address: http://localhost:30100
      instance:
        watch: false
  rest:
    address: 0.0.0.0:9091
  
  servlet:
    urlPattern: /cse/*
    
server:
  servlet: 
    path: /mvc


 

集成效果

在上面的例子中,很简单的将原来的Spring Boot 2.0应用的运行时转换成了了servicecom。但是转换本身不是目的,下面通过一些变化来说明转换后给业务系统带来的收益以及可能的功能丢失和工作量,以方便用户评估是否值得做这样的集成。

 

运行时变化

运行时变化是收益和损失的直接来源。在上面的改造中,客户端代码和服务端代码均没有变化(或者极少变化),客户端仍然使用RestTemplate调用服务端代码,服务端仍然提供的是REST接口,可以使用post man等工具访问。运行时变化不修改微服务对外的接口行为,这个是改造的基础。

改造前,Spring Boot 2的运行时简图如下。在客户端,可以使用Spring RestTemplate提供的各种MessageConverters处理请求消息编码和响应消息解码,在服务端,可以使用Spring MVC提供的各种功能对用户参数进行校验。


改造后,运行时简图如下。servicecomb客户端和服务端均提供了统一一致的处理链,并默认实现了负载均衡熔断容错灰度发布等功能。

 

运行时功能的一个核心特征是快速完成了应用微服务化改造。微服务的一个基本特征是多实例,通过网络接口进行通信。因此解决服务发现问题以及通信不可靠性问题是进行微服务化的一个基本保障。通过集成servicecomb,帮用户快速构建这些能力,减少了需要学习和开发其他Spring BootSpring Cloud组件的成本。

 

servicecomb本身也提供了丰富的开发和扩展能力,可以从设计选型参考了解servicecomb的更多信息。

 

开发方式变化

使用Spring Boot 2,开发者可以使用RestTemplate或者使用Feign来访问服务端接口。servicecomb也支持两种方式RestTemplateRPC。相比较于FeignservicecombRPC更加简洁,无需在客户端定义和使用REST标签。

@RpcReference(microserviceName   = "hello", schemaId = "hello")

private   Hello hello;

System.out.println(hello.sayHi("Java   Chassis"));

 

这种方式适用于采用servicecomb框架开发的微服务之间的访问。如果访问第三方,servicecomb已提供非常简单的方式,详细参考调用第三方服务

 

通过集成servicecomb,开发者可以方便的在代码中随时使用RESTRPC,非常灵活,大大节省书写代码的时间。

 

周边工具变化

上面的讨论都限制在微服务本身。为了保证业务功能能够持续高效运行运维,还需要给微服务提供一个功能强大的运行环境,实现微服务运行状况的监控、微服务功能的实时调整(流量控制、灰度发布、熔断容错等)。这里可以借助于商业解决方案快速实现或者开源解决方案自行搭建。

 

l  商业解决方案

华为[微服务引擎]( https://console.huaweicloud.com/cse/)提供了一站式微服务管理功能。将改造的应用部署到微服务引擎,即可实现微服务目录查看、接口管理、动态治理等多种功能。

 

微服务引擎的核心组件包括服务中心、配置中心和治理中心。除了将业务部署到云上,开发者可以在本地使用这些服务,只需要有可用的网络连接,注册华为云微服务引擎并获取AK/SK身份信息。

 

l  开源解决方案

[服务中心]( https://github.com/apache/incubator-servicecomb-service-center)。服务中心提供了注册发现服务,还提供了前端服务,用于查看服务目录和进行接口测试。

[配置中心]( https://github.com/ctripcorp/apollo)。这个是携程开发的配置中心。CSE支持通过Netflix Archaius扩展,使用各种配置服务。

[调用链zipkin]( https://github.com/openzipkin/zipkin)。这个标准的zipkin调用链服务。CSE支持通过Hanlder扩展实现了与zipkin调用链对接。

[调用链skywalking]( https://github.com/apache/incubator-skywalking/tree/master/apm-sniffer/apm-sdk-plugin/servicecomb-plugin)

 

可以从开发指南了解如何集成和使用这些开源组件。

 

servicecomb本身作为Spring Boot 2.0的一个Servlet运行,因此Spring BootSpring Cloud提供的大多数组件,用户也可以选择使用。Spring Cloud场景会选择eureka等作为服务发现服务,CSE需要使用服务中心作为服务发现。在服务中心选择上,目前还未有统一的标准,配套的SDK只能采用推荐的服务发现工具。

 

开启metrics监控

servicecomb提供了强大的metrics功能,能够帮助开发者分析性能问题。改造后的引用只需要引入metrics相关jar包,并在application.yml增加开关启用metrics,即可使用。metrics数据可以通过两种方式获取。

 

l  通过接口查询

地址: http://localhost:9091/metrics

输出参考:(截取了执行时间和平均调用次数,统计周期为60s

"servicecomb.invocation(operation=huawei-cloud-ioccity-app-basemgmt.EquipmentInfoController.getEquipmentList,role=PRODUCER,stage=execution,statistic=totalTime,status=200,transport=rest)":0.011417577750000001,

"servicecomb.invocation(operation=huawei-cloud-ioccity-app-basemgmt.EquipmentInfoController.getEquipmentList,role=PRODUCER,stage=total,statistic=count,status=200,transport=rest)":0.016666666666666666

 

 

l  输出到日志文件

 

配置项: cse.metrics.publisher.defaultLog.enabled=true

输出参考:(截取了性能部分)

producer:

    tps     latency(ms)   max-latency(ms) queue(ms) max-queue(ms) execute(ms) max-execute(ms) operation

    rest.200:

    0       0.000       727.756         0.000     23.542        0.000       704.214           huawei-cloud-ioccity-app-basemgmt.EquipmentInfoController.getEquipmentList  

 

常见问题

在使用新的框架对业务进行改造的时候,都会碰到一些问题。原理介绍了改造过程中可能碰到问题,以及一些通用的模式,可以阅读这个文章以评估改造的工作量。下面列举了在改造IoT系统中碰到的几个常见问题及其解决方案。

 

三方件冲突

在引入新的框架或者组件的时候,三方件冲突是非常常见的问题。解决三方件冲突的前提是版本之间能够兼容。冲突的原因可能很多,解决这类问题没有固定的思路,但掌握了maven依赖关系管理技巧以及理解冲突产生的原因后,通常分析和解决这类问题会变得更加简单。建议阅读maven管理技巧

 

SpringMVC数据类型支持

servicecomb支持SpringMVC的标签定义REST接口,一般情况下,将SpringMVC的应用改造为servicecomb都会非常简单。但是servicecombSpringMVC的运行时不同,设计目标也有差异,所以还会存在一些差异的地方。这块的主要差异点是使用CSE定义REST接口,支持的annotation和数据类型相对于SpringMVC变少了,详细说明参考

 

比如,下面的一些REST接口定义在servicecomb中是不能使用或者有限制使用的。

 

import   org.springframework.data.domain.Page;
@GetMapping(params   = { "pageNumber", "pageSize" })
public ResponseEntity<Page<T>>   listByPagination(@RequestParam int pageNumber, @RequestParam int   pageSize)

 

该接口在定义的时候,Page是一个interfaceservicecomb不支持在接口定义上使用泛型,包括interfaceabstract class等。servicecomb做出这个限制的原因是因为servicecomb需要遵循open API规范,接口定义必须能够存在对应的swagger描述。如果使用interfaceabstract class,那么则无法通过swagger来描述这个接口。

 

l  解决方案

业务代码应该采用合理的分层设计,这样就可以保证代码能够非常灵活的在不同框架下进行迁移。如果使用Spring开发,分层设计可以遵循Spring的一些建议。业务逻辑在@Service中实现,框架接口发布在@Controller实现。当在不同框架发布服务的时候(比如ServletSpring MVCservicecomb等),只需要简单的调整@Controller代码,不需要修改@Service代码。以上面的接口举个例子。


@Service
public class MyService {
  public Page<T> findAll(Pageable   pageable)
}



发布为
Spring MVCEndpoint: 

@RestController
public class   SpringMVCEndpoint {
  @Autowired
private MyService service;
 
  @PostMapping(value = "/search")
public ResponseEntity<Page<T>> findAll(@RequestParam   int pageNumber, @RequestParam int pageSize) {
    return   new ResponseEntity<Page<T>>(service.findAll(pageable),   HttpStatus.OK);
}
}



发布为
Servlet: 

public class   ServletEndpoint extends HttpServlet {
  @Autowired
   private MyService service;
 
   void doPost(HttpServletRequest req, HttpServletResponse resp)   {
    //   extract request
     service.findAll(request, HttpStatus.OK);
     // send response
   }
}



发布为
servicecomb的接口: 

Class PageModel<T>   {
  private int totalPages;
  private int totalElements;
  private int number;
  private int size;
  private int numberOfElements;
  private List<T> content;
  private boolean first;
  private boolean last;
}
 
Class Record   {
  private long id;
}
 
@RestSchema(schemaId=   "CSEEndpoint")
@RequestMapping(value   = "/cseEndpoint")
  public class   CSEEndpoint {
  @Autowired
   private MyService service;
 
  @PostMapping(value = "/search")
   public ResponseEntity<PageModel<Record>> findAll(@RequestParam   int pageNumber, @RequestParam int pageSize) {
      Page<Record>   pages = service.findAll(example);
  //   convert pages to PageModel
     PageModel<Page> result = ;
    return   new ResponseEntity<List<T>>(result, HttpStatus.OK);
  }
}



依赖于Spring MVC特定机制的业务逻辑

在我们的改造步骤二中,去掉了DispatcherServletAutoConfiguration,即Spring MVC REST 提供的相关功能被移除了。Spring MVC提供的有些接口需要依赖于这个功能,移除后,会导致这部分功能无法使用。比如:


import   org.springframework.web.context.request.RequestContextHolder;
import   org.springframework.web.context.request.ServletRequestAttributes;
@Before("webLog()")
public   void doBefore(JoinPoint joinPoint) throws Throwable {
    // 接收到请求,记录请求内容
    ServletRequestAttributes attributes =   (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request =   attributes.getRequest();
 
    // 记录下请求内容
log.info("URL : " +   request.getRequestURL().toString());
}


上面这段代码通过RequestContextHolder获取Servlet请求,并从请求中获取一些头信息来记录日志。RequestContextHolder依赖于Spring MVC REST来设置请求上下文,在整改后,获取到的attributes为空,从而导致抛出NPE异常。


l  解决方案


这种依赖于平台提供的特定功能,在进行改造的时候,都需要结合使用的平台,看是否有替代方案。这段代码的本意是实现审计日志或者调用链等逻辑,servicecomb提供Handler来获取这些信息,并已经实现了调用链等功能,所以可以直接使用,或者通过自定义HandlerHttpFilter等机制实现类似的功能。

 

 


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。