Zuul


學習筆記

作用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;
    }
}

測試過濾器


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM