概述
网关 Api(接口) Gateway(网关)--接口网关:接口是没有界面的
网关概念:
相当于客户端发送的请求先通过网关服务器,如若网关对该接口没有任何疑问,怎转发到接口服务器进行处理。
网关的作用:
网关可以拦截所有请求,对该请求做权限控制、负载均衡、日志管理、接口调用监控
网关与过滤器的区别:
过滤器适合于单个服务请求拦截,网关能拦截整个微服务请求
Nginx和zuul区别:
相同点:都可以实现负载均衡、反向代理、拦截请求、
不同点:Nginx采用C语言编写,Zuul采用Java语言。Zuul的负载本地均衡实现采用ribbon+eureka,Nginx的负载均衡是采用服务器端实现
Nginx相比Zuul功能会更加强大因为Nginx可以整合一些脚本语言(Lua),Nginx适合于服务器端服务器端负载均衡,Zuul适合用微服务网关
最好建议Nginx+Zuul实现网关,Nginx实现反向代理,Zuul实现请求拦截
接口是在什么情况下产生的?
接口是在面向服务架构,和微服务背景下产生的,目的都是为了解耦,在RPC远程调用中产生的。
接口是如何分类的?
开放接口-- 其他机构合作伙伴访问的接口(必须要在外网访问)例如蚂蚁支付,微信公众平台,需要通过APP-ID + APP-SOCET 生成 ACCESS-TOKEN 进通讯,目的是可以授权一些OAuth2.0(授权机制)接口权限
内部接口
一般只能在局域网之间访问,服务于服务调用之间关系,都在同一个微服务系统中。目的是为了保证安全问题
接口幂等性:
在调用接口的过程中,相同接口地址、相同参数同时发送多次请求
最终只会执行一次。
举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...,这就没有保证接口的幂等性
如若现在让你设计一套接口,你会如何设计?
考虑:接口权限(开发、内部)、幂等性、安全性(HTTPS)防止数据篡改(验签)、使用网关拦截接口(黑名单、白名单)、一般使用http+json格式 目的是为了跨平台、高并发(实现服务降级、熔断、隔离)、最后使用同一的API管理平台进行(api swagger)管理
步入正题,使用Zuul过滤所有请求
pom 文件配置
<!--SprinCloud整合zuul网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--SprinCloud整合Eureka客户端->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml配置
#服务注册地址
eureka:
client:
service-url:
defaultZone: http://localhost:8100/eureka/
#端口号
server:
port: 80
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
# 访问路径
path: /api-member/**
# 服务别名
serviceId: app-member
api-b:
path: /api/order/**
serviceId: app-order
在启动方法中开启网关
@SpringBootApplication
@EnableEurekaClient
//开启网关
@EnableZuulProxy
public class SpringcloudZuulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudZuulApplication.class, args);
}
}
新建Class继承 ZuulFilter(模拟拦截)
public class TokenFilter extends ZuulFilter {
/*过滤器类型 pre标识在请求之前执行*/
@Override
public String filterType() {
return "pre";
}
/*过滤器执行顺序*/
@Override
public int filterOrder() {
return 0;
}
/*判断过滤器是否生效*/
@Override
public boolean shouldFilter() {
return true;
}
/*编写过滤器拦截业务*/
@Override
public Object run() throws ZuulException {
// 案例:拦截所有请求,判断是否有传递Token
// 1.获取上下文
RequestContext currentContext = RequestContext.getCurrentContext();
// 2.获取Request对象
HttpServletRequest request = currentContext.getRequest();
// 获取参数
// request.getParameter()
// 3.获取请求头token
String token = request.getHeader("token");
// 判断token是否符合规范
if (StringUtils.isEmpty(token)) {
// 返回错误提示,不会放行
currentContext.setSendZuulResponse(false);
currentContext.setResponseBody("token 为空");
currentContext.setResponseStatusCode(401);
return null;
}
// 正常调用服务
return null;
}
}
新建ErrorFilter继承ZuulFilter(错误拦截)
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
try {
RequestContext context = RequestContext.getCurrentContext();
ZuulException exception = this.findZuulException(context.getThrowable());
logger.error("进入系统异常拦截", exception);
HttpServletResponse response = context.getResponse();
response.setContentType("application/json; charset=utf8");
response.setStatus(exception.nStatusCode);
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.print("{code:" + exception.nStatusCode + ",message:\"" + "服务器异常" + "\"}");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
}
}
} catch (Exception var5) {
ReflectionUtils.rethrowRuntimeException(var5);
}
return null;
}
ZuulException findZuulException(Throwable throwable) {
if (ZuulRuntimeException.class.isInstance(throwable.getCause())) {
return (ZuulException) throwable.getCause().getCause();
} else if (ZuulException.class.isInstance(throwable.getCause())) {
return (ZuulException) throwable.getCause();
} else {
return ZuulException.class.isInstance(throwable) ? (ZuulException) throwable : new ZuulException(throwable, 500, (String) null);
}
}
发现404无法过滤,新建class继承ErrorController
@RestController
public class GlobalExceptionHandler implements ErrorController {
/**
* 出异常后进入该方法,交由下面的方法处理
*/
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error(HttpServletRequest request, HttpServletResponse response) {
Integer status = (Integer)request.getAttribute("javax.servlet.error.status_code");
System.err.println(status);
return ReturnResult.failure(status,"请求异常");
}
}
网友评论