您的位置:首页 > 编程语言 > Java开发

SpringCloud之API网关服务Spring Cloud Zuul实例

2018-01-12 16:07 1471 查看

一、前言

有前几章的介绍,我们对于Spring Cloud Netflix下的核心组件已经了解了一大半。这些组件基本涵盖了微服务架构中最为基础的几个核心设施,利用这些组件我们已经可以构建起一个简单的微服务架构系统,比如,通过使用Spring Cloud Eureka实现高可用的服务注册中心以及实现微服务的注册与发现;通过Spring Cloud Ribbon或Feign实现服务间负载均衡的接口调用;同时,为了使分布式系统更为健壮,对于依赖的服务调用使用Spring
Cloud Hystrix来进行包装,实现线程隔离并加入熔断机制,以避免在微服务架构中因个别服务出现异常而引起级联故障蔓延。通过上述,可设计如下基础系统架构:



在该架构中,我们的服务集群包含内部服务ServiceA和ServiceB, 它们都会向Eureka Server集群进行注册与订阅服务,而OpenService是一个对外的RESTfulAPI服务,它通过FS、 Nginx等网络设备或工具软件实现对各个微服务的路由与负载均衡,并公开给外部的客户端调用。

二、简介

API网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、 负载均衡、 校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。

在SpringCloud中了提供了基于NetflixZuul实现的API网关组件Spring Cloud Zuul。那么,它是如何解决上面这两个普遍问题的呢?

首先,对于路由规则与服务实例的维护间题。SpringCloud Zuul通过与SpringCloud Eureka进行整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获得了所有其他微服务的实例信息。这样的设计非常巧妙地将服务治理体系中维护的实例信息利用起来,使得将维护服务实例的工作交给了服务治理框架自动完成,不再需要人工介入。
其次,对千类似签名校验、登录校验在微服务架构中的冗余问题。SpringCloud Zuul提供了一套过滤器机制,它可以 很好地支持这样的任务。开发者可以通过使用Zuul来创建各种校验过滤器,然后指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口,不然就返回错误提示。通过这样的改造,各个业务层的微服务应用就不再需要非业务性质的校验逻辑了,这使得我们的微服务应用可以更专注千业务逻辑的开发,同时微服务的自动化测试也变得更容易实现。

简单来说,就是既具备路由转发功能,又具备过滤器功能,比如将/aaa/**路径请求转发到service-ribbon服务上,将/bbb/***路径请求转发到service-feign服务上,比如过滤,对请求参数的信息进行过滤,不符合的进行过滤拦截等。

三、前期准备

一个服务注册中心,EUREKASERVER,端口为5555;
HELLOSERVER工程跑了三个实例,端口分别为5556、5557、5558,分别向服务注册中心注册;
SERVICE-RIBBON端口为5560,向服务注册中心注册;
SERVICE_FEIGN端口为5565,向服务注册中心注册;
修改SERVICE_FEIGN服务中的HelloController访问路径,将value = "/feign-hello"改为value = "/hello",以保持一致。

@RestController
public class HelloController {

@Autowired
HelloService helloService;

@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(){
return helloService.hello();
}

}


四、实例

(1)新建一个springcloud项目。命名无要求,并在pom.xml中引入spring-cloud-starter-zuul依赖。

(2)pom.xml

由于要在Eureka-Server中进行注册,所以还需要引入spring-cloud-starter-eureka的依赖。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

(3)入口类

@EnableEurekaClient
@EnableZuulProxy
@SpringBootApplication
public class SpringcloudzuulApplication {

public static void main(String[] args) {
SpringApplication.run(SpringcloudzuulApplication.class, args);
}

@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
}

使用@EnableZuulProxy注解开启Zuul的API网关服务功能。

(4)application.yml

server:
port: 5580  #端口号

spring:
application:
name: service-zuul  #服务注册中心测试名

zuul:
routes:
api-a:
path: /ribbon/**
serviceId: service-ribbon  #如果是/ribbon/**路径下的请求,则跳转到service-ribbon
api-b:
path: /feign/**
serviceId: service-feign  #如果是/feign/**路径下的请求,则跳转到service-feign

eureka:
client:
serviceUrl:
defaultZone: http://localhost:5555/eureka/  #服务注册中心


首先指定服务注册中心的地址为http://localhost:5555/eureka/,服务的端口为5580,服务名为service-zuul;以/ribbon/
开头的请求都转发给service-ribbon服务;以/feign/开头的请求都转发给service-feign服务;




我们可以通过下表中的示例来进一步理解这三个通配符的含义并进行参考使用。



(5)过滤器类

public class AccessFilter extends ZuulFilter {

private static Logger log = LoggerFactory.getLogger(AccessFilter.class);

@Override
public String filterType() {
return "pre";
}

@Override
public int filterOrder() {
return 0;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("send {} request to{}", request.getMethod(), request.getRequestURL().toString());
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
log.warn("accessToken is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("accessToken is empty");
} catch (Exception e) {
}
return null;
}
log.info("access is ok");
return null;
}
}

filterType: 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为pre,代表会在请求被路由之前执行。
filterOrder: 过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行。
shouldFilter: 判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。
run: 过滤器的具体逻辑。这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然也可以进 一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回的body内容进行编辑等。

五、测试

(1)访问:http://localhost:5555/



(2)访问:http://localhost:5580/ribbon/hello



(3)访问:http://localhost:5580/ribbon/hello?accessToken=ribbon



(4)访问:http://localhost:5580/feign/hello



(5)访问:http://localhost:5580/feign/hello?accessToken=feign



通过以上的测试,可以得出Zuul的路由和过滤都起作用了。

参考资料《Spring Cloud微服务实战》


新手一枚,欢迎拍砖~ ~ ~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息