目标
新建gateway网关,作为后期项目的统一网关服务
简介
zuul提供统一网关入口,屏蔽底层服务地址,规范入口地址,统一进行授权管理
部署步骤
-
按照前几章内容创建新子项目gateway
添加依赖,入口配置注解
org.springframework.boot
spring-boot-starter-test
org.springframework.boot
spring-boot-starter-logging
test
org.springframework.cloud
spring-cloud-starter-zuul
org.springframework.cloud
spring-cloud-starter-eureka
org.springframework.boot
spring-boot-starter-log4j2
open.template.work
commons
0.0.1-SNAPSHOT
入口配置
@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
- application.yml中添加配置,zuul自动集成了Eureka,并我们屏蔽了底层服务名,采用统一前缀规范化入口,对udm-server进行格式化
eureka:
client:
service-url:
defaultZone: http://root:123@peer1:8761/eureka/,http://root:123@peer2:8762/eureka/,http://root:123@peer3:8763/eureka/
instance:
prefer-ip-address: true
logging:
config: classpath:log4j2-spring.xml
zuul:
#配置zuul统一前缀
prefix: /api
#禁止所有eureka服务通过服务名直接访问
ignored-services:
"*"
routes:
#将微服务需要暴露服务进行路由映射
udm-server: /udm/**
-
此时访问gateway服务,访问成功
添加过滤器,进行模拟统一权限管理,需要实现集成ZuulFilter
package open.template.work.gateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
@Component
public class AuthorizationFilter extends ZuulFilter {
@Override
public String filterType() {
// 在进行Zuul过滤的时候可以设置其过滤执行的位置,那么此时有如下几种类型:
// 1、pre:在请求发出之前执行过滤,如果要进行访问,肯定在请求前设置头信息
// 2、route:在进行路由请求的时候被调用;
// 3、post:在路由之后发送请求信息的时候被调用;
// 4、error:出现错误之后进行调用
return "pre";
}
@Override
public int filterOrder() {
// 设置优先级,数字越大优先级越低
return 0;
}
@Override
public boolean shouldFilter() {
// 该Filter是否要执行
return true;
}
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext() ; // 获取当前请求的上下文
String appKey = currentContext.getRequest().getParameter("appKey");
if(StringUtils.isEmpty(appKey)){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(200);
currentContext.setResponseBody("{\"result\":\"权限验证失败!\"}");
currentContext.getResponse().setContentType("text/html;charset=UTF-8");
return null;
}
return null;
}
}
如上就实现了过滤器逻辑,下面再添加个异常过滤器进行统一异常处理
package open.template.work.gateway.filter;
import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import open.template.work.commons.exception.BaseTemplateException;
import open.template.work.commons.exception.ExceptionCodeEnum;
import org.springframework.stereotype.Component;
@Component
public class ExceptionFilter extends ZuulFilter {
@Override
public String filterType() {
// 在进行Zuul过滤的时候可以设置其过滤执行的位置,那么此时有如下几种类型:
// 1、pre:在请求发出之前执行过滤,如果要进行访问,肯定在请求前设置头信息
// 2、route:在进行路由请求的时候被调用;
// 3、post:在路由之后发送请求信息的时候被调用;
// 4、error:出现错误之后进行调用
return "error";
}
@Override
public int filterOrder() {
// 该Filter是否要执行
return 0;
}
@Override
public boolean shouldFilter() {
// 该Filter是否要执行
return true;
}
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
currentContext.setSendZuulResponse(true);
currentContext.setResponseStatusCode(200);
currentContext.setResponseBody(JSON.toJSONString(new BaseTemplateException(ExceptionCodeEnum.S99999)));
currentContext.getResponse().setContentType("text/html;charset=UTF-8");
return null;
}
}
此时访问根据我们模拟的临时权限拦截,不传appKey将会报错
但我们发现当有异常时,通过异常过滤器后会调用/error页面,此时我们还需配置ErrorHandler,此时有异常时,最后都会在/error的controller进行处理
package open.template.work.gateway.controller;
import open.template.work.commons.exception.BaseTemplateException;
import open.template.work.commons.exception.ExceptionCodeEnum;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error(){
return new BaseTemplateException(ExceptionCodeEnum.S99999).toString();
}
}
- 根据上一章内容,网关一样存在后端服务崩溃无法访问问题,由于网关服务属于共用服务,资源宝贵,故一样需要容错机制,下面配置zuul的fallback类
package open.template.work.gateway.fallback;
import com.netflix.hystrix.exception.HystrixTimeoutException;
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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
@Component
public class UdmServerFallBack implements FallbackProvider {
@Override
public String getRoute() {
// 表明是为哪个微服务提供回退,*表示为所有微服务提供回退
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return this.fallbackResponse();
}
}
@Override
public ClientHttpResponse fallbackResponse() {
return this.response(HttpStatus.INTERNAL_SERVER_ERROR);
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//编码保持一致,防止中文乱码
return new ByteArrayInputStream("服务升级中....".getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() {
// headers设定
HttpHeaders headers = new HttpHeaders();
MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
headers.setContentType(mt);
return headers;
}
};
}
}
好了,到此zuul在微服务的使用基本介绍完了。当然还有zuul的一些其他应用并没有细说,如对非jvm应用的支持(sidecar技术)、各种路由规则等等,可以参考《spring cloud 微服务实战》学习