服务端开发(7) REST服务、微服务开发与部署
2023-08-09 14:53:19 # NJU # 服务端开发

1. 一个 Web App 的开发和快速反馈

image-20230406005903378

华为云软件开发生产线 CodeArts

  • 集合业界先进理念,华为30年研发经验,可操作可落地的端到端一站式开发方法论和工具链。
  • 学习链接: https://support.huaweicloud.com/devcloud/index.html
  • 集华为研发实践、前沿研发理念、先进研发工具于一体,使能软件企业/开发者简单高效地向最终用户交付有价值的软件。

2. 开发模式

前、后端不分离的开发模式

image-20230406134413889

前后端分离的开发模式

image-20230406134433520

3. 单体应用程序

  • 数据库的表对所有模块可见
  • 一个人的修改整个应用都要重新构建、测试、部署
  • 整体复制分布式部署,不能拆分按需部署

4. 微服务架构模式的特征

  • 应用程序分解为具有明确定义了职责范围的细粒度组件
  • 完全独立部署,独立测试,并可复用
  • 使用轻量级通信协议,HTTP 和 JSON,松耦合
  • 服务实现可使用多种编程语言和技术
  • 将大型团队划分成多个小型开发团队,每个团队只负责他们各自的服务

5. Spring Boot 和 Spring Cloud

  • Spring Boot 提供了基于 Java 的、面向 REST 的微服务框架
  • Spring Cloud 使实施和部署微服务到私有云或公有云变得更加简单

5.1 Spring Boot

  • 简化 Spring Web 开发
  • Spring Boot Starter
    • 自动管理依赖、版本号
  • 自动配置
    • 根据类路径加载的类自动创建需要的Bean
    • 如: DataSource、JdbcTemplate、视图解析器等
  • Actuator

    • /autoconfig 使用了哪些自动配置(positiveMatches)
    • /beans,包含bean依赖关系
  • Spring initializer: 生成初始框架代码

6. Rest原则

  • Representational State Transfer,表现层状态转移
  • 资源(Resources),就是网络上的一个实体,标识: URI
  • 表现层(Representation): json、xml、html、pdf、excel
  • 状态转移(State Transfer): 服务端—客户端
  • HTTP协议的四个操作方式的动词: GET、POST、PUT、DELETE
    • CRUD: Create、Read、Update、Delete
  • 如果一个架构符合REST原则,就称它为RESTful架构。

7. 客户端与服务的交互

image-20230406140432047

7.1 请求报文

image-20230406140607318

请求头与请求体

请求头: 请求头由 key/value 对组成,每行为一对,key 和 value 之间通过冒号(:)分割。请求头的作用主要用于通知服务端有关于客户端的请求信息。

  • User-Agent: 生成请求的浏览器类型
  • Accept: 客户端可识别的响应内容类型列表
    • 星号 $\ast$ 用于按范围将类型分组
    • $\ast / \ast$表示可接受全部类型
    • $\text{type}/\ast$ 表示可接受 type 类型的所有子类型
  • Accept-Language: 客户端可接受的自然语言
  • Accept-Encoding: 客户端可接受的编码压缩格式
  • Accept-Charset: 可接受的字符集
  • Host: 请求的主机名,允许多个域名绑定同一 IP 地址
  • connection: 连接方式(close 或 keepalive)
  • Cookie: 存储在客户端的扩展字段
  • Content-Type:标识请求内容的类型
  • Content-Length:标识请求内容的长度

请求体: 请求体主要用于 POST 请求,与 POST 请求方法配套的请求头一般有 Content-Type 和 Content-Length

7.2 响应报文

image-20230406141433436

响应头与响应体

状态行:由 HTTP 协议版本、状态码、状态码描述三部分构成,它们之间由空格隔开

状态码:由 3 位数字组成,第一位标识响应的类型,常用的 5 大类状态码如下:

  • 1xx:表示服务器已接收了客户端的请求,客户端可以继续发送请求
  • 2xx:表示服务器已成功接收到请求并进行处理
  • 3xx:表示服务器要求客户端重定向
  • 4xx:表示客户端的请求有非法内容
  • 5xx:标识服务器未能正常处理客户端的请求而出现意外错误

响应头:

  • Location:服务器返回给客户端,用于重定向到新的位置
  • Server:包含服务器用来处理请求的软件信息及版本信息Vary:标识不可缓存的请求头列表
  • Connection: 连接方式
    • close 是告诉服务端,断开连接,不用等待后续的请求了
    • keep-alive 则是告诉服务端,在完成本次请求的响应后,保持连接
  • Keep-Alive: 300,期望服务端保持连接多长时间(秒)

响应内容:服务端返回给请求端的文本信息。

7.3 客户端表述的两种方式

内容协商(Content negotiation)

  • ContentNegotiatingViewResolver 是要创建的bean,基于内容协商生成表述,判断的依据有请求头的 Accept,URL请求路径加扩展名(优先)
  • 然后会转向具体的视图解析器生成不同的视图表述
  • ContentNegotiationManager(配置的作用)
    • 通过 setter 注入到 ContentNegotiatingViewResolver
    • 创建这个 Bean 的方式是继承自 WebMvcConfigerAdapter(基于spring mvc)
    • 覆盖方法 configureContentNegotiation,配置缺省内容类型等。

消息转换器(Message conversion)

  • 使用注解 @ResponseBody 或类级 @RestController,作用:指定使用消息转换器
  • 没有 model 和视图,控制器产生数据,然后消息转换器转换数据之后的资源表述
  • spring 自动注册一些消息转换器(HttpMethodConverter),不过类路径下要有对应转换能力的库,如:Jackson Json processor、JAXB库
  • 请求传入,@RequestBody 以及 HttpMethodConverter

7.4 提供资源以外的其它内容

  • @ResponseStatus(HttpStatus.CREATED) 指定返回的状态码
  • 控制器方法返回 ResponseEntity 对象,指定业务对象(负载)、状态码、响应头
    • 不用使用 @ResponseBody 注解
  • 异常处理器,@ExceptionHandler(异常类型),加在方法上
  • 指定响应头信息,通过返回 ResponseEntity 对象的方式
    • HttpHeaders 类型
      • 注意与发出请求时的类型 MultiValueMap 区别
    • setLocation

8. Rest客户端

  • new RestTemplats()
  • getForObject(), 指定返回类型,自动转换
  • getForEntity(),返回 ResponseEntity, 有头部信息,getBody()可以转换
  • put(),传递的对象存在转换问题,String 转成 test/plain,MultiValueMap 转成 x-www-form-urlncoded,对象可能转成json,要看 classpass 类路径下有无库
  • delete(),删除一个资源,一般提供资源路径即可
  • postForObject()/postForEntity()/postForLocation()
    • 因为需要返回值
    • postForLocation,只需要路径,不需要body,路径信息来源头部 Location 信息
  • exchange(), 可指定请求头信息
    • MultiValueMap headers;
    • HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
    • ResponseEntity<Spitter> response = rest.exchange()

例子代码

  • @SpringBootApplication
    • 配置类 @Configuration
    • @ComponentScan
  • @RestController
    • @Controller
    • 请求响应,JSON编解码(序列化)
  • mvn spring-boot:run
  • 健康检查:http://localhost:8080/actuator/health

9. 现实和挑战

  • 程序规模越来越大、越来越复杂
  • 客户期望快速频繁交付
  • 性能和可伸缩性
  • 弹性,应用程序中某个部分的故障或问题不应该导致整个应用程序崩溃
  • 小型的、简单的和解耦的服务 = 可伸缩的、有弹性的和灵活的应用程序

9.1 云计算平台

  • 基础设施即服务(Infrastructure as a Service , IaaS)
  • 平台即服务(Platform as a Service, PaaS)
  • 软件即服务(Software as a Service, SaaS)
  • 函数即服务(Functions as a Service, FaaS),将代码块以“无服务器”(serverless)的形式部署,无须管理任何服务器基础设施
  • 容器即服务(Container as a Service, CaaS),如亚马逊ECS(Amazon’s Elastic Container Service)

9.2 微服务开发要考虑的问题

  • 微服务划分,服务粒度、通信协议、接口设计、配置管理、使用事件解耦微服务
  • 服务注册、发现和路由
  • 弹性,负载均衡,断路器模式(熔断),容错
  • 可伸缩
  • 日志记录和跟踪
  • 安全
  • 构建和部署,基础设施即代码

9.3 Spring Cloud的工具集成

  • spring cloud alibaba
    • 数据配置,与 Nacos 集成
    • 服务注册与发现,与 Nacos 集成
      • Spring Cloud Loadbalancer
      • Spring Cloud openfeign
    • 限流、熔断,与 Sentinel 集成
  • Spring Cloud gateway,网关服务
  • Spring Cloud Stream,与 RabbitMQ、Kafka 集成
  • Spring Cloud Sleuth,与日志聚合工具Papertrail、跟踪工具Zipkin集成
  • Spring Cloud Security,与OAuth2集成

9.4 微服务划分

  • 可以从数据模型入手,每个域的服务只能访问自己的表
  • 刚开始粒度可以大一点,不要太细,由粗粒度重构到细粒度是比较容易的
  • 设计是逐步演化的

9.5 接口设计

  • 使用标准 HTTP 动词:GET、PUT、POST、DELETE,映射到 CRUD
  • 使用 URI 来传达意图
  • 请求和响应使用 JSON
  • 使用 HTTP 状态码来传达结果

9.6 运维实践

  • 都在源代码库中
  • 指定 JAR 依赖的版本号
  • 配置与源代码分开放
  • 已构建的服务是不可变的,不能再被修改
  • 微服务应该是无状态的
  • 并发,通过启动更多的微服务实例横向扩展,多线程是纵向扩展