框架演进历史

  1. 单体应用

Spring + MyBatis/Hibernate/JDBC + Struts/SpringMVC + jsp

打包成 war包,部署到 Tomcat

  1. 前后端分离

SpringBoot + Jquery + ajax

nginx+jar

  1. 负载均衡

  2. 微服务

单一职责,一个服务只解决一个业务领域的问题
服务:对外提供我们能处理的业务服务

优点:
服务职责小,服务间互不影响
代码量小,易于维护,减少代码冲突
便于迭代升级
耦合度低

缺点:
运维成本高
架构复杂
隐形问题变多,如不同服务间调用

常用组件

  • 服务注册发现 Euraka
  • 服务网关 Gateway
  • 客户端负载均衡 Ribbon
  • 熔断器 Hystrix (更换成:Sentinel)
  • 分布式配置 Spring Cloud Config (更换成:Nacos)
  • 服务调用 Feign

服务注册中心 Eureka

Netflix 开发的,基于 REST 服务的,服务注册和发现组件

  • 创建 多 module maven工程,module 间无依赖,均为单体应用

    各组件版本:

    spring boot:2.2.5
    spring cloud:Hoxton.SR3
    Jdk:1.8
    1. 新建项目
    2. 配置 maven 镜像,本地仓库,IDE 构建项目依赖 maven,安装依赖
    3. 新增 module 引入eureka 依赖
    4. pom.xml 配置
    5. application.properties 配置服务信息

服务间调用 Feign

一个 http 请求调用的轻量级框架,可以以 java 接口注释的方式调用 Http 请求,而不需要像 java 中封装 HTTP 请求报文的方式直接调用

app-order:调用方
app-storage:被调用方
mybatis-plus:数据处理框架
lombok:简化代码,常用@Data注解,实体类省略 set、get、toString 方法

注意:集成 mybatis-plus 要把mybatis、mybatis-spring去掉,避免冲突;

服务网关 GateWay

目的:提供简单有效的 API路由,基于 Spring Framework5、Spring Boot 2.0 构建,WebFlux 实现,取代 Zuul

Zuul和Spring Cloud Gateway的对比

  • 配置文件
1
2
3
4
5
6
7
8
9
10
11
# 配置路由唯一 id
spring.cloud.gateway.routes[0].id=app-storage

# app-storage 所在服务,lb:// 表示采用负载均衡
spring.cloud.gateway.routes[0].uri=lb://APP-STORAGE

# 配置请求 app-storage 下所有接口的请求前缀
spring.cloud.gateway.routes[0].predicates[0]=Path=/storage/v1/**

# 配置转发请求路径需要过滤掉几层前缀
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=2

基于网关可以做接口鉴权、限流等

JWT 鉴权

app-auth

  • 生成 token
1
2
3
4
5
6
7
8
9
10
11
String token = this.buildJWT(account, type);

// 向 hash 中放入数值
stringRedisTemplate.opsForHash().put(key, "token", token);
stringRedisTemplate.opsForHash().put(key, "refreshToken", refreshToken);

// 设置 key 过期时间
stringRedisTemplate.expire(key, refreshTokenExpireTime, TimeUnit.MICROSECONDS);
Map<String, String> map = new HashMap<>(2);
map.put("token", token);
map.put("refreshToken", refreshToken);
  • 生成 JWT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private String buildJWT(String account, String prefix ){
Date now = new Date();
Algorithm algo = Algorithm.HMAC256(prefix);

String token = JWT.create()
// 签发人
.withIssuer("userPhone")
// 签发时间
.withIssuedAt(now)
// 过期时间
.withExpiresAt(new Date(now.getTime() + tokenExpireTime))
// 自定义数据
.withClaim("phone", account)
// 签名
.sign(algo);

return token;
}

server-gateway

  1. RequestGlobalFilter 实现 filter

  2. 配置文件增加 ignore.urls

    ignore.urls=/auth/login,/authrefresh

  3. 增加 redis 配置

  4. jwt工具校验 token

  5. 测试请求

    启动server-eureka,server-gateway,app-auth,app-order,app-storage
    先访问http://127.0.0.1:10010/order/v1/order/placeOrder/commit会提示“认证失败”
    然后在请求中增加请求头Authorization和clientType,继续访问会提示“认证失败,请重新登录”。
    请求登录接口http://127.0.0.1:10010/auth/login?account=admin&password=123456a,并添加请求头clientType,得到token和refreshToken
    把上一步得到的token和使用clientType放到第一步的请求中,再次请求,会返回true。

参考文档