Java基础之《微服务(9)—服务网关》
时间:2023-12-31 02:37:02
为什么要使用服务网关?
乐队,每个人演奏自己的音乐是一种服务,多个人使用相同的乐器是一个微服务集群
指挥家是服务网关,全局统筹处理
它解决了什么问题
网关职责:
1.统一入口:为所有微服务提供唯一入口点,网关内外隔离,保证后台服务安全
2、鉴定验证:识别每个请求的权限,拒绝不符合要求的请求
3.动态路由:动态路由将被要求到不同的后端集群
4.通过网关层减少客户端与服务的耦合:服务可以独立发展
三、网关服务访问
例如,直接访问服务是:
http://127.0.0.1:8011/InstanceInfo
访问网关:
http://127.0.0.1:8012/service-provider1/InstanceInfo
返回结果是一样的
四、网关路由器四种规则
1、采用URL指定路由方法
URL如果匹配关键字URL跳转到指定的关键字,包括指定的关键字URL中
zuul.routes.service-provider.path=/service-provider/** zuul.routes.service-provider.url=http://127.0.0.1:8011/
访问:http://127.0.0.1:8012/service-provider/InstanceInfo
参数说明:
(1)
通配符:?
含义:匹配任何单个字符
举例:/service-provider/?
说明:匹配/service-provider/a,/service-provider/b,/service-provider/c等
(2)
通配符:*
含义:匹配任何数量的字符
举例:/service-provider/*
说明:匹配/service-provider/aaa,/service-provider/bbb,/service-provider/ccc等等,无法匹配/service-provider/a/b/c
(3)
通配符:**
含义:匹配任何数量的字符
举例:/service-provider/**
说明:匹配/service-provider/aaa,/service-provider/bbb,/service-provider/ccc等等,可以匹配/service-provider/a/b/c
2.使用服务指定路由方法
方式一
将路径的/service-provider/引到eureka的service-provider1服务上
规则:
zuul.routes.路径名.path=路径
zuul.routes.路径名.serviceId=eureka的服务名
zuul.routes.service-provider.path=/service-provider/** zuul.routes.service-provider.serviceId=service-provider1
方式二
zuul.routes后面是服务名,后面是路径规则,更简单
zuul.routes.service-provider1.path=/service-provider/**
3.排除路由的方法
方式一
排除一些服务,将多个服务逗号分开
zuul.ignored-services=service-provider1
排除后,地址为空http://127.0.0.1:8012/service-provider1/InstanceInfo
方式二
排除所有服务
由于服务太多,不可能手工添加,所以路由排除所有服务,然后手动添加路由服务
zuul.ignored-services=* zuul.routes.服务名.path=路径
方式三
排除指定关键字的路径
排除所有包含/排除/排除/排除/排除所有包含/排除/排除/排除/排除所有包含/排除/排除/排除/排除所有包含/排除/排除/排除/排除所有包含/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/排除/list/的路径
zuul.ignored-patterns=/**/list/** zuul.routes.service-provider1.path=/service-provider/**
4.添加路由前缀的方法
zuul.prefix=/api zuul.routes.service-provider1.path=/service-provider/**
访问时必须改成http://127.0.0.1:8012/api/service-provider1/InstanceInfo
五、过滤器
1.如何定义自己的过滤器?
添加LogFilter.java
package com.example.mycloud.run; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; @Component public class LogFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(LogFilter.class); @Override public boolean shouldFilter() { //打开过滤器,设置为true return true; } @Override public Object run() throws ZuulException { //过滤器的内容,打印请求信息 RequestContext rc = RequestContext.getCurrentContext(); HttpServletRequest request = rc.getRequest(); logger.info("method={},url={}", request.getMethod(), request.getRequestURL().toString()); return null; } @Override public String filterType() { //过滤器类型 return "pre"; } @Override public int filterOrder() { // TODO Auto-generated method stub return 0; } }
日志:
2022-06-09 17:01:42.086 INFO 22424 --- [nio-8012-exec-1] com.example.mycloud.run.LogFilter : method=GET,url=http://127.0.0.1:8012/service-provider1/InstanceInfo
2.过滤器的类型是什么?
2-1、filterType
用字符串表示过滤器的类型。zuul默认定义了四种不同生命周期的过滤器类型:
(1)pre
请求路由前可调用。一般用于身份权限验证、记录调用日志等。
(2)routing
路由执行后调用。
(3)post
在routing和error调用后的过滤器。可用于信息收集和统计。例如,性能指标,对response特殊处理结构。
(4)error
在处理请求时发生错误时被调用。用于包装异常处理。
2-2、filterOrder
用int定义过滤器的执行顺序,值越小,优先级越高。
2-3、shouldFilter
返回一个boolean判断过滤器是否要执行类型。
2-4、run
逻辑处理。一般有两个用途:
第一,请求前拦截,验证判断请求,请求无效时直接断路;如有效,可再加工。
二是要求结果后处理,即对结果进行一些处理。
3、http请求过滤器生命周期表
4.权限验证的例子
@Override public Object run() throws ZuulException { //过滤器的内容,打印请求信息 RequestContext rc = RequestContext.getCurrentContext(); HttpServletRequest request = rc.getRequest(); logger.info("method={},url={}", request.getMethod(), request.getRequestURL().toString()); //权限过滤例 // String token = request.getParameter("token"); // if (token == null) { // logger.warn("token is null..."); // rc.setSendZuulResponse(false); //代表结束请求,不继续下级传递 // rc.setResonseStatusCode(401);
// rc.setResponseBody("{\"result\":\"token is null\"}");
// rc.getResponse().setContentType("text/html;charset=utf-8");
// } else {
// //校验token,redis验证
// logger.info("token is ok");
// }
return null;
}
5、采用网关过滤器对系统异常统一处理
添加异常类ErrorFilter.java
package com.example.mycloud.run;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
@Override
public boolean shouldFilter() {
//开启过滤器,设置为true
return true;
}
@Override
public Object run() throws ZuulException {
//过滤器的内容,打印请求的信息
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("--------------------error--------------------");
return null;
}
@Override
public String filterType() {
//过滤器的类型
return "error";
}
@Override
public int filterOrder() {
// TODO Auto-generated method stub
return 0;
}
}
抛出异常:
throw new RuntimeException();
触发异常过滤器
会跳转到/error路径上,添加错误提示类:
package com.example.mycloud.run;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ErrorGatewayController implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error() {
return "{\"result\":\"500 error!\"}";
}
}
六、网关容错
1、zuul和hystrix无缝衔接
zuul默认集成了hystrix,访问:http://127.0.0.1:8012/actuator/hystrix.stream
2、网关如何实现服务降级
springboot2.1需要实现FallbackProvider接口
添加服务降级类ServiceProvider1Fallback.java
package com.example.mycloud.run;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
@Component
public class ServiceProvider1Fallback implements FallbackProvider {
@Override
public String getRoute() {
//代表为哪个服务fallback
return "service-provider1";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public InputStream getBody() throws IOException {
//返回的文本信息
String input = "服务不可用,请联系管理员!";
return new ByteArrayInputStream(input.getBytes());
}
@Override
public HttpHeaders getHeaders() {
//http头部类型
HttpHeaders header = new HttpHeaders();
MediaType mt = new MediaType("application","json",Charset.forName("utf-8"));
header.setContentType(mt);
return header;
}
@Override
public HttpStatus getStatusCode() throws IOException {
//http状态码
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
//http状态值
return this.getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
//http状态文本
return this.getStatusCode().getReasonPhrase();
}
@Override
public void close() {
// TODO Auto-generated method stub
}
};
}
}
停止service-provider1服务,访问:http://127.0.0.1:8012/api/sp1/InstanceInfo
七、网关限流
限制某个IP请求流量
1、pom文件添加,zuul限流的jar包
com.marcosbarbero.cloud
spring-cloud-zuul-ratelimit
2.4.2.RELEASE
2、添加限流的配置
#开启限流
zuul.ratelimit.enabled=true
#60s内请求超过3次,服务端就抛出异常,60s后可以恢复正常请求
zuul.ratelimit.policies.service-provider.limit=3
zuul.ratelimit.policies.service-provider.refresh-interval=60
#针对某个IP进行限流,不影响其他IP
zuul.ratelimit.policies.service-provider.type=origin
3、限流配置说明
(1)zuul.ratelimit.enabled
默认值:
false
备注:
开启限流
(2)zuul.ratelimit.repository
默认值:
in_memory
备注:
支持的存储方式={redis, consul, jpa, in_memory}
in_memory=使用ConcurrentHashMap作为数据存储
consul=使用consul作为数据存储
redis=使用redis作为数据存储
jpa=使用数据库作为数据存储
(3)zuul.ratelimit.policies.ServiceId.refresh-interval
默认值:
60
备注:
刷新时间窗口的时间,默认值(秒)
(4)zuul.ratelimit.policies.ServiceId.limit
默认值:
无
备注:
每个刷新时间窗口对应的请求数量限制
(5)zuul.ratelimit.policies.ServiceId.quota
默认值:
无
备注:
单位时间内允许访问的总时间(统计每次请求的时间总和)
例子:refresh-interval=60、limit=10、quota=30,60秒内允许10个访问,并且要求总请求时间小于30秒
(6)zuul.ratelimit.policies.ServiceId.type
默认值:
无
备注:
type={origin, user, url}
origin=对访问IP进行限流(例如:某个IP每分钟只允许请求多少次)
url=对请求的url进行限流(例如:某个url每分钟只允许调用多少次)
user=对某些特定用户或者用户组进行限流(例如:非VIP用户限制每分钟只允许调用100次某个API等)
4、添加限流全局配置
zuul.ratelimit.enabled=true
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
zuul.ratelimit.default-policy.type=origin
八、网关的2层超时调优
1、原因
2、修改配置
#第一层hystirx超时时间设置
#默认情况下是线程池隔离,超时时间1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60*1000
#第二层ribbon超时时间设置,设置比第一层小
#请求连接的超时时间,默认5s
ribbon.ConnectTimeout=10*1000
#请求处理的超时时间,默认5s
ribbon.ReadTimeout=10*1000