学习笔记

作用api网关,路由转发,过滤器,负载均衡等多种作用

简介:类似Nginx,反向代理的功能,不过netflix自己增加了一些配合其他组件的特性。在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务器。

 

在 Spring Cloud 微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Zuul、Ngnix),再到达服务网关(Zuul 集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由分布式配置中心管理,配置中心的配置文件放在 Git 仓库,方便开发人员随时改配置。

Zuul与Nginx区别

相同点:Zuul和Nginx都可以实现负载均衡、反向代理、过滤请求、实现网关效果。

不同点

Zuul采用java语言编写,采用ribbon+eureka实现本地负载均衡,适合微服务中实现网关。

Nginx采用C语言编写,采用微服务端实现负载均衡,适合于服务端负载均衡,也可以实现网关。Nginx比Zuul功能会更加强大,因为Nginx整合一些脚本语言(Nginx+Lua)。

(建议nginx+zuul实现网关,nginx作用实现反向代理,zuul对微服务实现网关拦截。)

 

创建路由网关

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.8.RELEASE</version>
  </parent>
  
  <!-- 管理依赖 -->
  <dependencyManagement>
      <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
      </dependencies>
  </dependencyManagement>
  
  <dependencies>
    <!-- SpringCloud eureka-server -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
  </dependencies>
  
  <!-- 注意:这里必须要添加,否则各种依赖有问题 -->
  <repositories>
      <repository>
          <id>spring-milestones</id>
          <name>Spring Milestones</name>
          <url>https://repo.spring.io/libs-milestone</url>
          <snapshots>
              <enabled>false</enabled>
          </snapshots>
      </repository>
  </repositories>

启动类增加 @EnableZuulProxy 注解开启 Zuul 功能

application.yml

spring:
  application:
    name: mobileshop-zuul

server:
  port: 8771

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

zuul:
  routes:
    api-seller:
      path: /api/seller/**
      serviceId: mobileshop-api-seller-feign

路由说明:以 /api/seller 开头的请求都转发给 moblieshop-api-seller-feign 服务

测试访问:

 

 

配置网关路由失败时的回调

@Component
public class ApiSellerFeignbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        return "mobileshop-api-seller-feign";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            /**
             * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的,
             * 不应该把 api 的 404,500 等问题抛给客户端
             * 网关和 api 服务集群对于客户端来说是黑盒
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("message", "无法连接,请检查您的网络");
                return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                // 和 getBody 中的内容编码一致
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }
}

测试

 

创建服务过滤器

@Component
public class LoginFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);
    /**
     * 配置过滤类型,有四种不同生命周期的过滤器类型
     * 1. pre:路由之前
     * 2. routing:路由之时
     * 3. post:路由之后
     * 4. error:发送错误调用
     */
    @Override
    public String filterType() {
        return "pre";
    }
    /**
     * 配置过滤的顺序
     */
    @Override
    public int filterOrder() {
        return 0;
    }
    /**
     * 配置是否需要过滤:true/需要,false/不需要
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    /**
     * 过滤器的具体业务代码
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getParameter("token");
        if (token == null) {
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            try {
                HttpServletResponse response = context.getResponse();
                response.setContentType("text/html;charset=utf-8");
                context.getResponse().getWriter().write("非法请求");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

测试过滤器

 posted on 2019-10-10 23:30  tripleWen  阅读(744)  评论(0编辑  收藏  举报