若依springcloud安装文档
先看若依框架的架构图:
网关模块在Spring Cloud通常中又叫做Spring Cloud Gateway ,虽然是 Spring Cloud 生态系统的一部分,专为在微服务架构中工作而设计,但并不意味着它只能在 Spring Cloud 项目或环境中实现或使用。
一个完整健全的Spring Cloud微服务系统需要网关(Spring Cloud Gateway )。
上面的架构图完美的解释了下面网关所扮演的角色和发挥的作用:
角色和作用:
网关可以根据请求的不同路径或其他属性将请求路由到不同的下游服务。这样客户端只需要知道网关的地址,而无需知道后端服务的复杂结构。
在微服务架构中,服务可能部署在不同的域中,Spring Cloud Gateway 可以处理跨域请求,避免前端直接与多个服务交互时出现的跨域问题。
在请求转发之前,通过身份验证和授权功能,确保只有合法请求被代理到下游服务。
通过集成限流组件如Redis RateLimiter等,对流量进行控制,预防下游服务因为过载而崩溃。
集成熔断机制,当下游服务不可用时自动切断请求,保护系统稳定性。
提供对网关路由、过滤器等的监控,并且可以收集和展示各种指标数据。
使用响应式编程模型来处理请求和响应,提高了系统的吞吐量和伸缩性。
网关的核心概念
下面解析若依是如何实现一个微服务网关系统的 ⛹️♂️
这里面最核心的在于全局过滤器(GlobalFilter),所有实现了此接口的类都会成为Spring Cloud网关中的全局过滤器组件,作用于所有的路由。
看一下具体的实现:
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求对象
ServerHttpRequest request = exchange.getRequest();
//用来创建 ServerHttpRequest.Builder的一个可变实例,以便对请求做出改动
ServerHttpRequest.Builder mutate = request.mutate();
//提取请求路径:比如返回 /api/data
String url = request.getURI().getPath();
String token = getToken(request);
.....
//调用 chain.filter(...) 并传递更新后的交换对象(exchange)来调用链中的下一个过滤器
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
@Override
public int getOrder() {
return -200;
}
}
上面只展示了AuthFilter 过滤器的实现思路,省略的部分是验证请求是否包含token,token是否过期,以及请求是否在白名单内等等。
在这段代码中重写了全局过滤器(GlobalFilter)的filter方法,其中**Mono**是 一个核心概念,这里的 filter 方法返回的 Mono 表示的是一个可能会进行一些副作用操作但不返回任何值的异步序列,这样如果还有其他过滤器就可以链式调用.下面是一个简略的过滤器流程图:
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.captcha")
public class CaptchaProperties
{
private Boolean enabled;
private String type;
public Boolean getEnabled()
{
return enabled;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
}
上面的注解@RefreshScope 和 @ConfigurationProperties 一起构成了微服务下发配置文件的注入,prefix = “security.captcha” 表明来自于配置文件的目录结构,找到resources文件夹下面的bootstrap.yml配置文件发现并没有security.captcha,其实这个配置来自于Nacos,下面是来自于Nacos配置文件的截图:
可以看到关于security.captcha的配置, 在点击发布之后,网关会自动更新相关配置,无须重启服务。
其他的配置文件也是类似的道理。
若依微服务采用的是Nacos,上面提到的自动注入配置无须启动服务就是由Nacos实现的,下面是Nacos启动之后的客户端界面
Nacos可以做什么事情:
网关集成Nacos:
若依的配置文件中已经标明了配置信息:
shared-configs:
- application- s p r i n g . p r o f i l e s . a c t i v e . {spring.profiles.active}. spring.profiles.active.{spring.cloud.nacos.config.file-extension}
这里的共享配置在maven构建后会指的是ruoyi-gateway-dev.yml,这个可以在Nacos配置列表中找到。
在Nacos的网关配置文件中有关于路由的配置如下:
```yaml
routes:
# 认证中心
- id: ruoyi-auth
uri: lb://ruoyi-auth
predicates:
- Path=/auth/**
filters:
# 验证码处理
- CacheRequestFilter
- ValidateCodeFilter
- StripPrefix=1
# 代码生成
- id: ruoyi-gen
uri: lb://ruoyi-gen
predicates:
- Path=/code/**
filters:
- StripPrefix=1
# 定时任务
- id: ruoyi-job
uri: lb://ruoyi-job
predicates:
- Path=/schedule/**
filters:
- StripPrefix=1
# 系统模块
- id: ruoyi-system
uri: lb://ruoyi-system
predicates:
- Path=/system/**
filters:
- StripPrefix=1
# 文件服务
- id: ruoyi-file
uri: lb://ruoyi-file
predicates:
- Path=/file/**
filters:
- StripPrefix=1
举例说明用法:
在网关中有一个关于生成验证码的业务:具体的实现逻辑如下:
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService {
//通过name匹配Bean
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
//通过类型匹配Bean
@Autowired
private RedisService redisService;
@Autowired
private CaptchaProperties captchaProperties;
/**
* 生成验证码
*/
@Override
public AjaxResult createCaptcha() throws IOException, CaptchaException {
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = captchaProperties.getEnabled();// 当前是否要产生验证码,该配置在nacos中可以在不重启服务的情况下修改
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled) {
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID(); // 生成唯一的uuid用于验证;
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
String captchaType = captchaProperties.getType();
if ("math".equals(captchaType)) {
String capText = captchaProducerMath.createText(); // 生成文本
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
} else if ("char".equals(captchaType)) {
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
FastByteArrayOutputStream os = new FastByteArrayOutputStream(); // 在内存中创建一个缓冲区,所有发送到输出流的数据都保存在该缓冲区中
try {
ImageIO.write(image, "jpg", os); // 尝试将BufferedImage对象以jpg格式写入输出流
} catch (IOException e) {
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
/**
* 校验验证码
*/
@Override
public void checkCaptcha(String code, String uuid) throws CaptchaException {
if (StringUtils.isEmpty(code)) {
throw new CaptchaException("验证码不能为空");
}
if (StringUtils.isEmpty(uuid)) {
throw new CaptchaException("验证码已失效");
}
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisService.getCacheObject(verifyKey);
redisService.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha)) {
throw new CaptchaException("验证码错误");
}
}
}
上面的业务代码涉及到的注解有:
@Resource:这是一个用于匹配Bean的注解,比如@Resource(name = “captchaProducer”)表示当前的service中引入captchaProducer依赖,在网关中captchaProducer指向了CaptchaConfig类,这个类中实现了字符串验证码方法
@Autowired:同样的也是个匹配Bean的注解,但是和@Resource不同的地方在于该注解通过类型匹配,而且**@Autowired**是spring特有的注解,在spring cloud中这两种方法可以替换使用。
上面的代码已经做了必要的注释,可以对照若依网关文件查看。