Spring Boot 与 Kubernetes 云原生微服务实践 笔记 (2)

305 阅读7分钟

*time.geekbang.org/course/intr…

课程和背景介绍

课程重点是:微服务和云原生架构,运用Spring boot 和 kubernetes。_________________________________________________________________________________________________

Staffjoy项目结构组织

虽然是微服务架构,但是所有代码都在一个仓库当中,称之为单体仓库(monorepo). 并采用multi module的方式组织项目。每个项目优先使用父POM(如果用gradle的话,就是build.gradle)的依赖,其次使用自己定义的依赖。

common-lib模块是公共共享模块,是基础框架类的封装,其他的微服务都依赖它。

每一个微服务都有两个子模块组成,例如account服务分为'account-api 服务接口模块'和'account-svc 服务实现模块',account-svc是依赖于account-api的。这种接口和实现分离的组织方式,会使代码组织结构比较清晰,也利用接口模块的重用(reuse)。一般会把接口模块上传到mavern仓库进行管理,如果某个应用依赖于某个服务,直接引用它的接口模块就可以了,因为接口模块是强类型的,依赖方就可以使用强类型的接口直接调用目标服务,不需要手动解析json. 所以使用强类型客户端可以大幅度提高开发效率。

config文件中包含一些私密配置,比如访问阿里云的api key的配置,jwt的secret等等。这应用了spring支持的本地私密配置方式,不会被推到远端repo。

Mutli-repo 还是 Mono-repo(单体仓库)

Multi-repo 优点:指责单一,复杂度可控,可以有不同的团队独立维护,可独自部署。
Multi-repo 缺点:不容易形成代码规范,随意引入依赖;项目集成和部署复杂度上升;开发人员缺乏对项目整体的认知;项目间冗余代码多;

Mono-repo的优点:易于规范代码,统一代码风格;一键构建;易于整体项目理解,包含技术架构和业务目标的理解;支持更好的重构;

所以微服务架构并不是倡导所有东西都分离的,单体仓库就是很好的例子,谷歌就采用单体仓库,并且google和facebook都有自己的单体仓库构建的工具。

微服务接口参数校验很重要

我们主要在控制层进行参数校验,以保证入参都是经过校验的。参数校验和业务场景相关,所以属于业务开发人员的职责,开发框架应该提供必要的支持,以方便开发人员。Spring框架对接口参数校验的支持是比较完善的,有很多annotion可供使用。

Staffjoy是在控制层对参数进行校验的,以保证所有入参都是经过校验的。定制的校验annotation也会用在controller这一层。

如何实现统一异常处理

不同的开发人员有不同的异常处理风格,如果不统一的话,会造成debug困难,并且丧失规范,所以企业级的应用都会统一异常处理,也尽量减少开发手动做异常处理。

在后端Spring项目中,可以编写一个GlobalExceptionHandler类,用@RestControllerAdvice注解作标注,来统一捕获框架异常或者自定义异常(业务异常)并作统一处理。最后可以捕获Throwable异常来做兜底。

DTO和DMO为什么要互转

DTO:Data Transfer object 数据传输对象,指在做数据传输时用到的对象
DMO:Data Model object 数据模型对象,又分为domain object 领域对象 和 entity object 数据实体对象。

在大部分情况下,DTO和DMO承载的字段是一样的,但有的时候会有差别,比如DTO会屏蔽掉一些字段。modelmapper这个开源库提供了DTO和DMO的转化。分成两种数据对象的优点:不用做额外的逻辑去屏蔽掉不想要的字段,缺点:转化多了很多平板代码。

怎么设计强类型接口

语言类型有强弱类型之分,Java属于强类型静态语言。对于服务api接口也有强弱类型之分,Spring boot通常采用json作为传输消息,http作为传输协议,restful没有严格的契约的概念,使用httpclient就可以调用,但是调用方需要对json消息的手动编码解码的工作。市面上的大部分框架都是弱类型restful的框架。服务器端和客户端不强耦合,缺点是:需要调用方手动编码解码,没有编译期接口类型检查,代码不容易规范,效率低,容易出现服务端和客户端接口类型不匹配。

规避弱类型的方法:
使用spring feign这个强类型的机制。Spring feign是一个动态代理,它负责了对象的序列化和反序列化(这个的性能对框架的性能最大),拼装出来强类型客户端。

怎么处理正常响应返回数据,异常响应返回错误信息,这两种不同返回类型呢?可以使用继承,如下图所示。所以这种强类型可以支持接口返回正常异常两种返回类型。(封装消息➕捎带)调用方的代码结构统一为:

即: 1)先用feign去调用 2)处理异常 3)判断状态码 4)如果是正确状态码,就获取值

为什么框架层要考虑分环境配置

原因1: CI/CD 的基础就是标准化的环境,交付流程和自动化工具。
原因2: 有些花费高的服务在低环境不需要。并且像短信服务,邮件服务这些,在低环境只能发送给交付团队成员,不能发送给真实用户,需要在配置中加白名单来做限制。

微服务异步处理

微服务主要采用请求响应,也就是同步调用的风格,但有些场景并不需要同步,或者同步的开销比较大,这时就可以使用异步处理。比如说邮件短信的发送,客服事件的发送等等。Spring boot通过Java线程池原生支持异步调用。步骤是:1. 配置一个异步线程池的Bean(ThreadPoolTaskExecutor)。 2. 在异步调用的方法中用Async方法标注(@Async("Executor Bean的名字")),之后这个Thread会被提交到我们之前定义的线程池当中去。

引入异步之后,调用和被调用方会在不同的线程执行,线程上下文会发生变化,有些信息就会被断开,比如用户认证信息一般是放在请求线程上下文里的,调用链(tracing)监控一般也放在线程上下文里。当线程切换后,这些信息就会丢失。处理方法,用executor.setTaskExecutor方法把上下文信息传递给后面的线程:

Swagger

swagger 3.0文档

使用springboot启用swagger,只需要引入Spring-fox-swagger即可,然后添加一个用EnableSwagger2注解的SwaggerConfig文件即可。

Swagger不止可以生成swagger ui,还可以生成swagger json文档,可以看editor.swagger.io, 用json也可以反向生成代码,很方便。

主流服务框架概览

Spring boot,Dubbo,Motan,gRpc

gRpc是多语言支持,强类型,契约有限,RPC框架,并且支持HTTP2,谷歌背书的。如果要暴露RESTFUL接口出去,需要引入gRPC api gateway,有一些额外的工作。

Spring boot原本就是restful的,因为不是强类型的,所以不会用它生成客户端。

_________________________________________________________________________________________________

cover了课程第三章的内容