Spring Boot 2.0集成和使用CSE

点击上方“微服务蜂巢”可以订阅哦

本文通过一个IoT的应用展现在SpringBoot 2.0中集成和使用CSE。IoT应用原来使用SpringBoot 2.0开发,通过少量的步骤集成CSE,然后展现了集成后带来了哪些新特性,以及中间存在的一些变化和问题。通过这个例子,我们快速的将一个SpringBoot 2.0的原型系统,改造为具备通用可靠性和运维能力的微服务,加快了业务系统的建设速度。

//

集成步骤

//

基本集成过程分为三步。

1

增加依赖关系

在dependencyManagement里面导入CSE对于springboot 2提供的组件包依赖管理以及CSE自身的依赖管理。需要注意将spring boot 2的依赖管理放到前面,因为CSE缺省的依赖管理是spring boot 1的。如果业务需要引入特定的spring boot或者springcloud版本,也可以通过这个机制将spring boot或者springcloud的dependencyManagement引入。[这里](http://servicecomb.apache.org/cn/docs/maven_dependency_management/) 有一个介绍dependencyManagement的文章,使用dependencyManagement解决三方件冲突,是非常有用的技巧,正确掌握它的原理,能够帮助开发者快速定位和解决常见的三方件冲突问题。

<dependencyManagement>

    <dependencies>

        <dependency>

             <groupId>org.apache.servicecomb</groupId>

             <artifactId>java-chassis-dependencies-springboot2</artifactId>

            <version>1.1.0.B018</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

        <dependency>

             <groupId>com.huawei.paas.cse</groupId>

             <artifactId>cse-dependency</artifactId>

            <version>2.3.46</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

    </dependencies>

</dependencyManagement>

   

然后引入CSE对于springboot 2提供的支持组件包。

<dependencies>

    <dependency>

         <groupId>com.huawei.paas.cse</groupId>

         <artifactId>cse-solution-service-engine</artifactId>

        <exclusions>

            <exclusion>

                 <groupId>org.slf4j</groupId>

                 <artifactId>slf4j-log4j12</artifactId>

            </exclusion>

        </exclusions>

    </dependency>

    <dependency>

         <groupId>org.apache.servicecomb</groupId>

         <artifactId>spring-boot2-starter-servlet</artifactId>

    </dependency>

</dependencies>

   

2

修改运行参数

在SpringBoot 2中,开发者会将服务通过DispatcherServlet发布为REST接口。集成CSE后,服务通过CSE提供的RestServlet进行发布。需要在运行参数里面启用CSE功能和禁用DispatcherServlet。@EnableServiceComb标签会启用和加载CSE,exclude参数会禁用DispatcherServlet。

@SpringBootApplication(scanBasePackages  = {exclude = DispatcherServletAutoConfiguration.class)

@EnableServiceComb

public class  Application {

     public static void main(String[] args) {

         SpringApplication.run(Application.class, args);

    }

}

   

3

发布服务接口

CSE发布REST接口的方式和SpringBoot 2有少量差异,需要将@RestController修改为@RestSchema,并且显示的声明@RequestMapping。

@RestSchema(schemaId=  "EquipmentInfoController")

@RequestMapping(value  = "/equipmentInfo")

public class  EquipmentInfoController

   

服务会通过服务中心进行发布,在application.yml中指定服务中心地址,并提供简单的微服务信息。其中service_description.name可以使用原来项目的名字,cse.rest.address采用tomcat的监听端口server.port进行发布。

## CSE configurations

APPLICATION_ID: houseapp

service_description:

  name: ${spring.application.name}

  version: 0.0.1

cse:

  service:

    registry:

      address: http://localhost:30100

      instance:

        watch: false

  rest:

    address: 0.0.0.0:9091

//

集成效果

//

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

1

运行时变化

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

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

改造后,运行时简图如下。CSE客户端和服务端均提供了统一一致的处理链,并默认实现了

[负载均衡]

(https://docs.servicecomb.io/java-chassis/zh_CN/references-handlers/loadbalance.html)

[熔断容错]

(https://docs.servicecomb.io/java-chassis/zh_CN/build-provider/configuration/downgrade-strategy.html)

[流量控制]

(https://docs.servicecomb.io/java-chassis/zh_CN/build-provider/configuration/ratelimite-strategy.html)等功能。

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

CSE本身也提供了丰富的开发和扩展能力,可以从

[设计]

(https://bbs.huaweicloud.com/blogs/1fc9427c088611e89fc57ca23e93a89f)

和[指南]

(https://docs.servicecomb.io/java-chassis/zh_CN/)了解CSE的更多信息。

2

开发方式变化

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

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

private Hello hello;

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

这种方式适用于采用CSE框架开发的微服务之间的访问。如果访问第三方,CSE也提供了类似Feign的机制,并且提供了比Feign强大的负载均衡管理和治理能力(Consumer处理链)。相关例子可以参考ServiceComb代码

[示例]

(https://github.com/apache/incubator-servicecomb-java-chassis/blob/master/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/thirdparty/Test3rdPartyInvocation.java)。

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

3

周边工具变化

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

商业解决方案

华为[微服务引擎]

(https://console.huaweicloud.com/cse/)

提供了一站式微服务管理功能。将改造的应用部署到微服务引擎,即可实现微服务目录查看、接口管理、动态治理等多种功能。

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

[买房系统](https://github.com/huaweicse/HouseApp/tree/spring-boot-2.0-with-zipkin)提供了一个本地使用华为云服务的例子。开发者只需要在application.yml中配置AK/SK信息和服务中心地址信息等。

开源解决方案

[服务中心]

(https://github.com/apache/incubator-servicecomb-service-center)

服务中心提供了注册发现服务,还提供了前端服务,用于查看服务目录和进行接口测试。

[配置中心]

(https://github.com/ctripcorp/apollo)

这个是携程开发的配置中心。CSE支持通过NetflixArchaius扩展,使用各种配置服务。

[调用链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)。

可以从

[开发指南]

( https://docs.servicecomb.io/java-chassis/zh_CN/)

了解如何集成和使用这些开源组件。

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

4

负载均衡和隔离能力展示

这里通过一个例子展现改造后,负载均衡能力和隔离能力。

这个

[例子]

(https://bbs.huaweicloud.com/blogs/72a312f09c8911e89fc57ca23e93a89f)

展现了在多实例部署情况下,滚动升级零中断。

[购房系统]

(https://github.com/huaweicse/HouseApp/tree/spring-boot-2.0-with-zipkin)

实现了一个完整的springcloud应用,可以使用这个系统进行体验。

5

使用zikpin调用链追踪能力展示

这里演示快速在系统中构建调用链跟踪能力。改造后的引用只需要引入调用链jar包和在application.yml中增加处理链配置和zipkin服务器地址,并安装zipkin服务器,就能够使用调用链了。

[开发步骤]

(https://docs.servicecomb.io/java-chassis/zh_CN/general-development/microservice-invocation-chain.html)

[购房系统]

(https://github.com/huaweicse/HouseApp/tree/spring-boot-2.0-with-zipkin)实现了调用链。

6

开启metrics监控

CSE提供了强大的

[metrics功能]

(https://docs.servicecomb.io/java-chassis/zh_CN/general-development/metrics.html)

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

通过接口查询

地址: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

输出到日志文件

配置项:

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  


//

常见问题

//

在使用新的框架对业务进行改造的时候,都会碰到一些问题。

[原理]

(https://bbs.huaweicloud.com/blogs/ba7b62178cb811e89fc57ca23e93a89f)介绍了改造过程中可能碰到问题,以及一些通用的模式,可以阅读这个文章以评估改造的工作量。下面列举了在改造IoT系统中碰到的几个常见问题及其解决方案。

1

三方件冲突

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

建议阅读[maven管理技巧](http://servicecomb.apache.org/cn/docs/maven_dependency_management/)。

2

SpringMVC数据类型支持

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

详细说明[参考]

(https://docs.servicecomb.io/java-chassis/zh_CN/using-java-chassis-in-spring-boot/diff-spring-mvc.html)。

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

import  org.springframework.data.domain.Page;

@GetMapping(params =  { "pageNumber", "pageSize" })

public ResponseEntity<Page<T>>  listByPagination(@RequestParam int pageNumber, @RequestParam int  pageSize)

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

import  org.springframework.data.domain.Page;

@GetMapping(params =  { "pageNumber", "pageSize" })

public ResponseEntity<Page<T>>  listByPagination(@RequestParam int pageNumber, @RequestParam int  pageSize)

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

CSE的数据类型支持[说明]

(https://docs.servicecomb.io/java-chassis/zh_CN/build-provider/interface-constraints.html)

解决方案

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

@Service

@Service

public class MyService {

  public Page<T>  findAll(Pageable pageable)

}

   

发布为SpringMVC的Endpoint:

@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

}

}

   

发布为CSE的接口:

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);

}

}

   

1

依赖于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异常。

解决方案

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

轻戳“阅读原文”,玩转微服务,快来体验吧!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值