API网关 Gateway
- 类似于设计模式中的Facade模式
- 微服务系统中的正门
- 微服务的重要组成部分
常见作用
- 身份验证和安全 JSON Web Token(JWT)
- 审查和检测
- 动态路由
- 压力测试
- 负载均衡
- 静态相应处理
业务总结
必须先启动服务提供者,否则会报错
启动检查:服务启动过程中验证服务提供者的可用性,如果验证出现问题,则阻止整个spring容器的初始化,还有个好处就是服务启动检查可以尽可能早的发现服务问题
check = false
如果我们将用户模块部署多台,消费者会如何访问
Dubbo负载均衡
Protocol
Dubbo支持多协议
最常见dubbo
RMI、Hessian、Http、Redis、Memerycached
网关gateway逻辑处理
外部服务请求的入口
1、使用ThreadLocal进行用户的id存储,配合jwt进行用户校验
public class CurrentUser {
// 线程绑定的存储空间
private static final ThreadLocal<String> threadlocal = new ThreadLocal<>();
// 将用户信息放入存储空间
public static void saveUserId(String userId) {
threadlocal.set(userId);
}
// 将用户信息取出
public static String getCurrentUserId() {
return threadlocal.get();
}
/* 考虑到jvm内存大小 存储对象过多 使用存储id */
// public static void saveUserInfo(UserInfoModel userInfoModel) {
// threadlocal.set(userInfoModel);
// }
// 将用户信息取出
// public static UserInfoModel getCurrentUser() {
// return threadlocal.get();
// }
}
@RequestMapping(value = "${jwt.auth-path}")
public ResponseVO createAuthenticationToken(AuthRequest authRequest) {
boolean validate = true;
// 去掉guns自带用户名密码验证,使用自己的
int userId = userAPI.login(authRequest.getUserName(), authRequest.getPassword());
if (userId == 0) {
validate = false;
}
if (validate) {
// randomKey token 生成完毕
final String randomKey = jwtTokenUtil.getRandomKey();
final String token = jwtTokenUtil.generateToken(""+userId, randomKey);
//
return ResponseVO.success(new AuthResponse(token, randomKey));
} else {
return ResponseVO.serviceFail("用户名或密码错误");
}
}
// JWT 处理
final String requestHeader = request.getHeader(jwtProperties.getHeader());
String authToken = null;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
// 通过token获取userId 并且存入threadlocal 以便后续业务使用
String userId = jwtTokenUtil.getUsernameFromToken(authToken);
if (userId == null) {
return;
} else {
CurrentUser.saveUserId(userId);
}
2、忽略路径的配置
rest:
auth-open: true #jwt鉴权机制是否开启(true或者false)
sign-open: true #签名机制是否开启(true或false)
jwt:
header: Authorization #http请求头所需要的字段
secret: mySecret #jwt秘钥
expiration: 604800 #7天 单位:秒
auth-path: auth #认证请求的路径
md5-key: randomKey #md5加密混淆key
ignore-url: /user/register,/user/check # 忽略列表
private String ignoreUrl = "";
public String getIgnoreUrl() {
return ignoreUrl;
}
public void setIgnoreUrl(String ignoreUrl) {
this.ignoreUrl = ignoreUrl;
}
// 配置忽略列表
String ignoreUrl = jwtProperties.getIgnoreUrl();
String[] ignoreUrls = ignoreUrl.split(",");
for (int i = 0; i < ignoreUrls.length; i++) {
if (request.getServletPath().equals(ignoreUrls[i])) {
chain.doFilter(request, response);
return;
}
}
3、dubbo配置
spring:
application:
name: meeting-gateway
dubbo:
server: true
registry: zookeeper://localhost:2181
protocol:
name: dubbo # 协议名称
port: 20880 # 端口号
datasource:
url: jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: root
filters: log4j,wall,mergeStat
4、pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>com.stylefeng</groupId>
<artifactId>guns-core</artifactId>
</dependency>
<dependency>
<groupId>com.stylefeng</groupId>
<artifactId>guns-api</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
注:期间遇到引入dubbo-spring-boot-starter提示某个包找不到,解决办法是找到maven仓库中的包删掉,然后project中maven clean 重新install
5、公用接口请求返回类 封装
package com.stylefeng.guns.rest.modular.vo;
public class ResponseVO<M> {
// 状态 【0-成功 1-失败 999-系统异常】
private int status;
// 返回信息
private String msg;
// 实体
private M data;
// 不允许外部创建实体 单例
private ResponseVO() {}
public static<M> ResponseVO success(M m) {
ResponseVO responseVO = new ResponseVO();
responseVO.setStatus(0);
responseVO.setData(m);
return responseVO;
}
public static<M> ResponseVO success(String msg) {
ResponseVO responseVO = new ResponseVO();
responseVO.setStatus(0);
responseVO.setMsg(msg);
return responseVO;
}
public static<M> ResponseVO serviceFail(String msg) {
ResponseVO responseVO = new ResponseVO();
responseVO.setStatus(1);
responseVO.setMsg(msg);
return responseVO;
}
public static<M> ResponseVO appFail(String msg) {
ResponseVO responseVO = new ResponseVO();
responseVO.setStatus(999);
responseVO.setMsg(msg);
return responseVO;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public M getData() {
return data;
}
public void setData(M data) {
this.data = data;
}
}
6、API模块抽离
7、User模块实现
copy guns-rest 服务模块 修改pom文件,修改project structure 删除多余引用,修改pom配置,application.properties,api对应的UserAPi实现类处理
8、理解 gateway api user 模块的调用原理,其实就是api作为公用模块,修改之前做的直接提供者,采用zookeeper注册中心,user模块实现api接口,gateway通过调用api模块接口,注册中心监测到user中有对应的实现,直接执行userApiImpl中的实现方法
坑:
1、遇到java.util.Date 数据库写入问题,替换java.sql.Date后得到解决,倒是数据库时分秒全为0,后续排查
2、mysql链接不起 jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false,使用serverTimezone=GMT%2B8 确定时区
3、接口名 使用 value = “register”
4、服务检查:@Reference(interfaceClass = UserAPI.class, check = false)
5、service负载均衡 @Service(interfaceClass = UserAPI.class, loadbalance = “roundrobin”)