图片来源:pexels网站
java4all原创,欢迎关注
摘要:本文主要讲解SpringCloud体系中的网关服务Zuul。
在前面的系列文章中,我们介绍了注册中心,负载均衡,熔断机制等组件,这些组件用来搭建一套微服务体系,已经是可以胜任我们常见的业务需求了。但是,我们再思考一个问题,在单体服务时代,外部请求我们服务的接口时,一般是有个鉴权机制的,比如签名校验,权限校验等,单体服务中,我们自然是把这个部分逻辑写在项目中,请求来的时候直接调方法鉴权即可。
在微服务架构中,比如,我们现在有6个服务,我们是不是把这套鉴权机制写进6个微服务中呢?显然,这样实现不够优雅。改进一下,我们把这套鉴权抽取出来形成一个新的服务,所有的服务请求进来前,先过一下这个鉴权服务的校验。这样,听上去是个不错的主意。
如果鉴权服务挂了呢?防线就崩溃了,那鉴权又得考虑要做高可用。
上述的假想中,我们只假想了简单的鉴权,而且这样的实现,难以实现各种自定义的精准的校验和过滤,因为鉴权服务是同一套,给多个微服务使用。再延伸一下,会发现更多的问题。
幸好,SpringCloud这一套体系,封装了一套API网关的实现,给我们提供了一个更好地解决方案SpringCloud Zuul。网关就像所有微服务系统的门面一样,所有的外部访问都需要经过它来调度和过滤,它除了可以实现请求路由、负载均衡、校验过滤外,还可以和服务治理框架整合,做熔断处理,做服务聚合等。
本文先通过简单的使用示例来体验一下网关Zuul带来的快捷,深入使用,等后面进阶系列再详细介绍。本文主要分为以下几个步骤:
1.创建项目
新建一个Springboot项目zuul_server1。
2.引入依赖
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.修改配置文件
zuul需要注册到eureka上,然后我们配置路由,配置指定类型的请求路由到指定的服务上:
当请求path是以/api-a/开头的,就转发到feign-server1服务上;
当请求path是以/api-b/开头的,就转发到service-ribbon服务上,如果有其他路由,在routes下继续添加即可:
server:
port: 8018
spring:
application:
name: zuul-server
eureka:
client:
service-url:
default: http://localhost:8761/eureka
zuul:
routes:
apia:
path: /api-a/**
serviceId: feign-server1
apib:
path: /api-b/**
serviceId: service-ribbon
4.添加启动注解
//开启zuul的功能
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulServer1Application {
public static void main(String[] args) {
SpringApplication.run(ZuulServer1Application.class, args);
}
}
5.自定义Filter
zuul提供的功能,不仅仅是路由,我们还可以把所有的请求,在网关这一层,做个安全的校验,过滤,或者,也可以做一些其的处理。这里做个简单的token是否为空的校验,我们需要用到Filter.
Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。
我们来自定义一个Filter,用来校验token是否为空,自定义Filter,需要继承ZuulFilter,并实现其中的一些方法。我们在run中,对请求做个校验,当token为空时,就直接返回,不再向后端服务转发。
@Component
public class RequestFilter extends ZuulFilter{
private static Logger logger = LoggerFactory.getLogger(RequestFilter.class);
/**
* pre 路由之前
* routing 路由之时
* post 路由之后
* error 发送错误调用
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**过滤的顺序,数字越小越先执行*/
@Override
public int filterOrder() {
return 0;
}
/**这里可以写逻辑判断,是否要过滤,true表示过滤,false表示不过滤*/
@Override
public boolean shouldFilter() {
return true;
}
/**filter需要执行的具体操作*/
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String token = request.getParameter("token");
if(StringUtils.isEmpty(token)){
logger.warn("=======>token is empty");
currentContext.setSendZuulResponse(false);//不对其进行路由
currentContext.setResponseStatusCode(401);
try {
currentContext.getResponse().getWriter().write("token is empty");
}catch (Exception ex){
ex.printStackTrace();
}
}else {
currentContext.setSendZuulResponse(true);//进行路由
currentContext.setResponseStatusCode(200);
}
return null;
}
}
6.启动项目,测试
我们需要启动前几篇文章创建的几个服务,然后启动本文的服务。然后先访问:http://localhost:8018/api-a/getCompany?id=234, 这个请求中没有带token参数的,查看结果:
会发现,这个请求,并没有到后台服务,直接在网关这里就被拦截返回了。我们再请求一下:http://localhost:8018/api-b/getCompany?id=234&token=qweqew, 查看结果:
由于带的有token,请求顺利的到了后台,请求到了指定服务。
SpringCloud系列:
SpringCloud教程-01:服务注册与发现(Eureka)
SpringCloud教程-04:断路器(Hystrix-Ribbon)
SpringCloud教程-06:断路器(Hystrix-Feign)
SpringBoot资料:SpringBoot系列教程资源
与其相忘江湖,不如点赞关注