1. 程式人生 > >SpringCloud踩坑筆記(四)------- zuul基礎

SpringCloud踩坑筆記(四)------- zuul基礎

本文建立在上一篇文章《SpringCloud踩坑筆記(三)-------服務註冊與消費》之上。我們將引入 Spring Cloud Zuul 來做路由的轉發,訪問之前在 Eureka Server 中註冊的服務。       

        Spring Cloud Zuul路由是微服務架構的不可或缺的一部分,提供動態路由,監控,彈性,安全等的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。在常用的伺服器架構中,我們會使用 nginx 來做路由的轉發,在 Spring Cloud 體系中,zuul 就是類似於 nginx 的存在。

        首先還是按照常規方式建立一個 Spring Boot 專案,然後引入依賴 spring-cloud-starter-eureka 和 spring-cloud-starter-zuul

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

        在 application.yml 中將 zuul 服務註冊到 Eureka Server 中,並且配置路由規則,zuul 以 8081 埠啟動。 

spring:
    application:
        name: spring-cloud-zuul
server:
    port: 8081
# zuul 配置
zuul:
    routes:
        api-a:
            path: /api/**
            serviceId: spring-cloud-service
# eureka 配置    
eureka:
    client:
        serviceUrl:
            defaultZone: http://localhost:8080/eureka/

        上述配置中,我們配置了將 /api/** 的請求,都轉發到 Eureka Server 中已經註冊的服務 spring-cloud-service 上。

        啟動函式中增加 @EnableZuulProxy 的註解宣告。

@SpringBootApplication
@EnableZuulProxy
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

        最後,啟動 Eureka Server 、zuul 以及上一篇文章中建立的服務 spring-cloud-service。服務 spring-cloud-service 中我們建立了兩個介面,以 GET 介面為例,本身服務的地址應該是 http://localhost:9090/service/hello?name=walli,而通過 zuul 來訪問的話,地址就應該變成 http://localhost:8081/api/service/hello?name=walli,那麼我們在瀏覽器中依次訪問這兩個地址,可以看到得到的都是同樣的結果:

        那麼這也就意味著,我們的 zuul 服務成功搭建了。在 Eureka Server 的管理後臺中,也能看到對應的服務。

 

配置多個服務

        很簡單,application.yml 中,增加配置即可。如下,訪問 api/** 時,呼叫 spring-cloud-service 服務,訪問 api2/** 時,訪問 spring-cloud-service2 服務。api-c-url 示例瞭如何直接配置 url 的轉發。

# zuul 配置
zuul:
    routes:
        api-a:
            path: /api/**
            serviceId: spring-cloud-service
        api-b:
            path: /api2/**
            serviceId: spring-cloud-service2
        api-c-url:
            path: /api3/**
            url: http://localhost:9999/

        那麼, 我們就有一個問題了,如果我有100個服務,那麼豈不是要一個一個配置?其實,Spring Cloud Zuul 已經幫我們做了預設的配置,URL規則就是 http://${zuul_host}:${zuul_port}/${service_id}/${path},那麼,我們訪問 spring-cloud-service 和 spring-cloud-service2 的預設URL就是:

http://localhost:8081/spring-cloud-service/service/hello?name=walli

http://localhost:8081/spring-cloud-service2/service/hello?name=walli

 

負載均衡

        到目前學習到的 Spring Cloud 體系元件構成中,我們已經不難想象,基礎的架構就是 一個請求首先到 zuul,然後由 zuul 的路由規則配置,去查詢 Eureka 中對應的服務,轉到到實際的應用伺服器上獲得資料,反饋給呼叫者。那麼如果我對某一個服務部署了多臺應用伺服器做橫向擴充套件的時候,如何去做負載均衡呢?

        首先說到負載均衡,就要先提一下伺服器負載均衡和客戶端負載均衡。

        服務端負載均衡:客戶端請求到負載均衡伺服器,負載均衡伺服器根據自身的演算法將該請求轉給某臺真正提供業務的伺服器,該伺服器將響應資料給負載均衡伺服器,負載均衡伺服器最後將資料返回給客服端。典型的例子就是nginx。

        客戶端負載均衡:基於客戶端的負載均衡,簡單的說就是在客戶端程式裡面,自己設定一個排程演算法,在向伺服器發起請求的時候,先執行排程演算法計算出向哪臺伺服器發起請求,然後再發起請求給伺服器。

        zuul 就是客戶端負載均衡,zuul 通過路由規則匹配,從 Eureka 獲取到某個服務名稱對應的所有例項,然後才去輪詢的策略,去訪問應用伺服器。

        我們拷貝前一篇文章中的 spring-cloud-service,製作相同的服務作為演示用例,將啟動埠號改為9091,名字不變,再把 controller 的返回值做一下修改,以便於區分當前呼叫的是 服務1 還是 服務2。

spring:
    application:
        name: spring-cloud-service      
server:
    port: 9091
eureka:
    client:
        serviceUrl:
            defaultZone: http://www.eureka.local.com:8080/eureka/
@RestController
@RequestMapping("service")
public class ProviderController {
    
    private final static Logger LOGGER = LoggerFactory.getLogger(ProviderController.class);
    
    @RequestMapping(value = "hello", method = RequestMethod.GET)
    @ResponseBody
    public String hello(@RequestParam String name) throws AnkonException {
        String result = "hello " + name + ", you get the service, this is service2";
        return new RestJson(result).toJson();
    }
    
    @RequestMapping(value = "hello2", method = RequestMethod.POST)
    @ResponseBody
    public String hello2(HttpServletRequest request) throws AnkonException, IOException {
        String charset = request.getCharacterEncoding();
        String param;
        try (InputStream input = request.getInputStream();
                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            // out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = input.read(buffer)) != -1) {
                out.write(buffer, 0, length);
            }
            
            param = out.toString(charset);
        }

        LOGGER.debug("Reqeust body:" + param);
        
        String result = "hello, you post the service with param " + param + ", this is service2";
        return new RestJson(result).toJson();
    }
}

        啟動 Eureka Server, Zuul,以及兩個應用伺服器。然後可以在 Eureka 的管理後臺看到 zuul 服務 和兩個應用服務。

        之後我們不停的呼叫 http://localhost:8081/api/service/hello?name=walli,返回結果會在兩個服務之間交替的返回。

        至此,負載均衡完成,其實並不需要我們做額外的處理,只要保證兩個應用的名稱一樣就行了,zuul 會自動輪詢排程。

        另外,在上一篇文章中,我們使用了 Fegin 來呼叫遠端服務,其實 Fegin 也是可以做負載均衡的,其過程跟 zuul 差不多,都是通過 服務名稱,從 Eureka Server 中獲取到例項資訊,然後輪詢呼叫每個應用伺服器。