SpringCloud服務(wù)網(wǎng)關(guān)zuul

考慮一個(gè)問題汁展,外部的應(yīng)用如何來訪問內(nèi)部各種各樣的微服務(wù)呢?在微服務(wù)架構(gòu)中厌殉,后端服務(wù)往往不直接開放給調(diào)用端食绿,而是通過一個(gè)API網(wǎng)關(guān)根據(jù)請求的url,路由到相應(yīng)的服務(wù)公罕。當(dāng)添加API網(wǎng)關(guān)后器紧,在第三方調(diào)用端和服務(wù)提供方之間就創(chuàng)建了一面墻,這面墻直接與調(diào)用方通信進(jìn)行權(quán)限控制楼眷,后將請求均衡分發(fā)給后臺服務(wù)端铲汪。

Spring Cloud Zuul

Spring Cloud Zuul路由是微服務(wù)架構(gòu)的不可或缺的一部分,提供動態(tài)路由罐柳,監(jiān)控掌腰,彈性,安全等的邊緣服務(wù)张吉。Zuul是Netflix出品的一個(gè)基于JVM路由和服務(wù)端的負(fù)載均衡器齿梁。

下面我們通過代碼來了解Zuul是如何工作的

簡單使用

1、添加依賴

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

引入spring-cloud-starter-zuul

2肮蛹、配置文件

spring.application.name=gateway-service-zuul
server.port=8888

#這里的配置表示士飒,訪問/it/** 直接重定向到http://127.0.0.1:8080/**
zuul.routes.baidu.path=/it/**
zuul.routes.baidu.url=http://127.0.0.1:8080/

3、啟動類

@EnableZuulProxy
@SpringCloudApplication
public class GatewayServiceZuulApplication {

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

啟動類添加@EnableZuulProxy蔗崎,支持網(wǎng)關(guān)路由。

史上最簡單的zuul案例就配置完了

4扰藕、測試

啟動vicente-cloud-zuul項(xiàng)目缓苛,在瀏覽器中訪問:http://localhost:8764/it/test,看到頁面返回了:http://127.0.0.1:8080/test 頁面的信息邓深,如下:

image.png

image.png

說明訪問vicente-cloud-zuul的請求自動轉(zhuǎn)發(fā)到了vicente-boot未桥,并且將結(jié)果返回。


服務(wù)化

通過url映射的方式來實(shí)現(xiàn)zull的轉(zhuǎn)發(fā)有局限性芥备,比如每增加一個(gè)服務(wù)就需要配置一條內(nèi)容冬耿,另外后端的服務(wù)如果是動態(tài)來提供,就不能采用這種方案來配置了萌壳。實(shí)際上在實(shí)現(xiàn)微服務(wù)架構(gòu)時(shí)亦镶,服務(wù)名與服務(wù)實(shí)例地址的關(guān)系在eureka server中已經(jīng)存在了日月,所以只需要將Zuul注冊到eureka server上去發(fā)現(xiàn)其他服務(wù),就可以實(shí)現(xiàn)對serviceId的映射缤骨。

我們結(jié)合示例來說明爱咬,在上面示例項(xiàng)目vicente-cloud-zuul的基礎(chǔ)上來改造。

1绊起、添加依賴

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

增加spring-cloud-starter-eureka包精拟,添加對eureka的支持。
2虱歪、配置文件

配置修改為:

eureka:
  client:
    serviceUrl:
      defaultZone: http://192.168.243.21:9001/eureka/
server:
  port: 8764
spring:
  application:
    name: vice-zuul-local
zuul:
  routes:
    client-hi:
      path: /hhhi/**
      serviceId: service-hi
    consumer:
      path: /bd-its/**
      serviceId: bd-its-deg-service
    baidu:
      path: /it/**
      url: http://192.168.243.21:9090/

啟動完成后蜂绎,zuul會自動注冊發(fā)現(xiàn)的服務(wù)


image.png

3、測試

訪問:http://127.0.0.1:8764/it/itsdeg/select/style,http://127.0.0.1:8764/bd-its/itsdeg/select/style,http://127.0.0.1:8764/bd-its-deg-service/itsdeg/select/style笋鄙,
實(shí)際均是訪問的是服務(wù)名為:bd-its-deg-service的項(xiàng)目师枣,
url地址:http://192.168.243.21:9090/itsdeg/select/style
返回:

image.png

說明訪問/bd-its-deg-service的請求自動轉(zhuǎn)發(fā)到了bd-its-deg-service項(xiàng)目,并且將結(jié)果返回局装。

為了更好的模擬服務(wù)集群坛吁,我們復(fù)制BD-ITS-DEG-SERVICE項(xiàng)目,啟動2個(gè)同名的項(xiàng)目铐尚,修改方法拨脉,使兩個(gè)同名項(xiàng)目返回的結(jié)果不一樣


image.png

修改完成后啟動項(xiàng)目。測試多次訪問http://127.0.0.1:8764/bd-its-deg-service/itsdeg/select/style宣增,依次返回:

image.png

image.png

說明通過zuul成功調(diào)用了BD-ITS-DEG-SERVICE服務(wù)并且做了均衡負(fù)載玫膀。

網(wǎng)關(guān)的默認(rèn)路由規(guī)則

但是如果后端服務(wù)多達(dá)十幾個(gè)的時(shí)候,每一個(gè)都這樣配置也挺麻煩的爹脾,spring cloud zuul已經(jīng)幫我們做了默認(rèn)配置帖旨。默認(rèn)情況下,Zuul會代理所有注冊到Eureka Server的微服務(wù)灵妨,并且Zuul的路由規(guī)則如下:http://ZUUL_HOST:ZUUL_PORT/微服務(wù)在Eureka上的serviceId/**會被轉(zhuǎn)發(fā)到serviceId對應(yīng)的微服務(wù)解阅。

我們注銷掉gateway-service-zuul-eureka項(xiàng)目中關(guān)于路由的配置:
zuul.routes.api-a.path=/producer/**
zuul.routes.api-a.serviceId=spring-cloud-producer
重新啟動后,訪問http://localhost:8888/spring-cloud-producer/hello?name=%E5%B0%8F%E6%98%8E泌霍,測試返回結(jié)果和上述示例相同货抄,說明Spring cloud zuul默認(rèn)已經(jīng)提供了轉(zhuǎn)發(fā)功能。

到此zuul的基本使用我們就介紹完了朱转。

上篇文章主要介紹了Zuul網(wǎng)關(guān)使用模式蟹地,以及自動轉(zhuǎn)發(fā)機(jī)制,但其實(shí)Zuul還有更多的應(yīng)用場景藤为,比如:鑒權(quán)怪与、流量轉(zhuǎn)發(fā)、請求統(tǒng)計(jì)等等缅疟,這些功能都可以使用Zuul來實(shí)現(xiàn)分别。

Zuul的核心

Filter是Zuul的核心遍愿,用來實(shí)現(xiàn)對外服務(wù)的控制。Filter的生命周期有4個(gè)茎杂,分別是“PRE”错览、“ROUTING”、“POST”煌往、“ERROR”倾哺,整個(gè)生命周期可以用下圖來表示。

image

Zuul大部分功能都是通過過濾器來實(shí)現(xiàn)的刽脖,這些過濾器類型對應(yīng)于請求的典型生命周期羞海。

  • PRE: 這種過濾器在請求被路由之前調(diào)用。我們可利用這種過濾器實(shí)現(xiàn)身份驗(yàn)證曲管、在集群中選擇請求的微服務(wù)却邓、記錄調(diào)試信息等。
  • ROUTING:這種過濾器將請求路由到微服務(wù)院水。這種過濾器用于構(gòu)建發(fā)送給微服務(wù)的請求腊徙,并使用Apache HttpClient或Netfilx Ribbon請求微服務(wù)。
  • POST:這種過濾器在路由到微服務(wù)以后執(zhí)行檬某。這種過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header撬腾、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端等恢恼。
  • ERROR:在其他階段發(fā)生錯(cuò)誤時(shí)執(zhí)行該過濾器民傻。 除了默認(rèn)的過濾器類型,Zuul還允許我們創(chuàng)建自定義的過濾器類型场斑。例如漓踢,我們可以定制一種STATIC類型的過濾器,直接在Zuul中生成響應(yīng)漏隐,而不將請求轉(zhuǎn)發(fā)到后端的微服務(wù)喧半。

Zuul中默認(rèn)實(shí)現(xiàn)的Filter

類型 順序 過濾器 功能
pre -3 ServletDetectionFilter 標(biāo)記處理Servlet的類型
pre -2 Servlet30WrapperFilter 包裝HttpServletRequest請求
pre -1 FormBodyWrapperFilter 包裝請求體
route 1 DebugFilter 標(biāo)記調(diào)試標(biāo)志
route 5 PreDecorationFilter 處理請求上下文供后續(xù)使用
route 10 RibbonRoutingFilter serviceId請求轉(zhuǎn)發(fā)
route 100 SimpleHostRoutingFilter url請求轉(zhuǎn)發(fā)
route 500 SendForwardFilter forward請求轉(zhuǎn)發(fā)
post 0 SendErrorFilter 處理有錯(cuò)誤的請求響應(yīng)
post 1000 SendResponseFilter 處理正常的請求響應(yīng)

禁用指定的Filter

可以在application.yml中配置需要禁用的filter,格式:

zuul:
    FormBodyWrapperFilter:
        pre:
            disable: true

自定義Filter

實(shí)現(xiàn)自定義Filter青责,需要繼承ZuulFilter的類薯酝,并覆蓋其中的4個(gè)方法。

public class MyFilter extends ZuulFilter {
    @Override
    String filterType() {
        return "pre"; //定義filter的類型爽柒,有pre、route者填、post浩村、error四種
    }

    @Override
    int filterOrder() {
        return 10; //定義filter的順序,數(shù)字越小表示順序越高占哟,越先執(zhí)行
    }

    @Override
    boolean shouldFilter() {
        return true; //表示是否需要執(zhí)行該filter心墅,true表示執(zhí)行酿矢,false表示不執(zhí)行
    }

    @Override
    Object run() {
        return null; //filter需要執(zhí)行的具體操作
    }
}

自定義Filter示例

我們假設(shè)有這樣一個(gè)場景,因?yàn)榉?wù)網(wǎng)關(guān)應(yīng)對的是外部的所有請求怎燥,為了避免產(chǎn)生安全隱患瘫筐,我們需要對請求做一定的限制,比如請求中含有Token便讓請求繼續(xù)往下走铐姚,如果請求不帶Token就直接返回并給出提示策肝。

首先自定義一個(gè)Filter,在run()方法中驗(yàn)證參數(shù)是否含有Token隐绵。

public class TokenFilter extends ZuulFilter {

    private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);

    @Override
    public String filterType() {
        return "pre"; // 可以在請求被路由之前調(diào)用
    }

    @Override
    public int filterOrder() {
        return 0; // filter執(zhí)行順序之众,通過數(shù)字指定 ,優(yōu)先級為0,數(shù)字越大依许,優(yōu)先級越低
    }

    @Override
    public boolean shouldFilter() {
        return true;// 是否執(zhí)行該過濾器棺禾,此處為true,說明需要過濾
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String token = request.getParameter("token");// 獲取請求的參數(shù)

        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); //對請求進(jìn)行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); //不對其進(jìn)行路由
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }

}

將TokenFilter加入到請求攔截隊(duì)列峭跳,在啟動類中添加以下代碼:

@Bean
public TokenFilter tokenFilter() {
    return new TokenFilter();
}

這樣就將我們自定義好的Filter加入到了請求攔截中膘婶。

測試

我們依次啟動示例項(xiàng)目:spring-cloud-eurekaspring-cloud-producer蛀醉、spring-cloud-zuul悬襟,這個(gè)三個(gè)項(xiàng)目均為上一篇示例項(xiàng)目,spring-cloud-zuul稍微進(jìn)行改造滞欠。

訪問地址:http://localhost:8888/spring-cloud-producer/hello?name=neo古胆,返回:token is empty ,請求被攔截返回筛璧。
訪問地址:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx逸绎,返回:hello neo,this is first messge夭谤,說明請求正常響應(yīng)棺牧。

通過上面這例子我們可以看出,我們可以使用“PRE”類型的Filter做很多的驗(yàn)證工作朗儒,在實(shí)際使用中我們可以結(jié)合shiro颊乘、oauth2.0等技術(shù)去做鑒權(quán)、驗(yàn)證醉锄。

路由熔斷

當(dāng)我們的后端服務(wù)出現(xiàn)異常的時(shí)候乏悄,我們不希望將異常拋出給最外層,期望服務(wù)可以自動進(jìn)行一降級恳不。Zuul給我們提供了這樣的支持檩小。當(dāng)某個(gè)服務(wù)出現(xiàn)異常時(shí),直接返回我們預(yù)設(shè)的信息烟勋。

我們通過自定義的fallback方法规求,并且將其指定給某個(gè)route來實(shí)現(xiàn)該route訪問出問題的熔斷處理筐付。主要繼承ZuulFallbackProvider接口來實(shí)現(xiàn),ZuulFallbackProvider默認(rèn)有兩個(gè)方法阻肿,一個(gè)用來指明熔斷攔截哪個(gè)服務(wù)瓦戚,一個(gè)定制返回內(nèi)容。

public interface ZuulFallbackProvider {
   /**
     * The route this fallback will be used for.
     * @return The route the fallback will be used for.
     */
    public String getRoute();

    /**
     * Provides a fallback response.
     * @return The fallback response.
     */
    public ClientHttpResponse fallbackResponse();
}

實(shí)現(xiàn)類通過實(shí)現(xiàn)getRoute方法丛塌,告訴Zuul它是負(fù)責(zé)哪個(gè)route定義的熔斷较解。而fallbackResponse方法則是告訴 Zuul 斷路出現(xiàn)時(shí),它會提供一個(gè)什么返回值來處理請求姨伤。

后來Spring又?jǐn)U展了此類哨坪,豐富了返回方式,在返回的內(nèi)容中添加了異常信息乍楚,因此最新版本建議直接繼承類FallbackProvider 当编。

我們以上面的spring-cloud-producer服務(wù)為例,定制它的熔斷返回內(nèi)容徒溪。

@Component
public class ProducerFallback implements FallbackProvider {
    private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);

    //指定要處理的 service忿偷。
    @Override
    public String getRoute() {
        return "spring-cloud-producer";
    }

    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("The service is unavailable.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }

    @Override
    public ClientHttpResponse fallbackResponse(Throwable cause) {
        if (cause != null && cause.getCause() != null) {
            String reason = cause.getCause().getMessage();
            logger.info("Excption {}",reason);
        }
        return fallbackResponse();
    }
}

當(dāng)服務(wù)出現(xiàn)異常時(shí),打印相關(guān)異常信息臊泌,并返回”The service is unavailable.”鲤桥。

啟動項(xiàng)目spring-cloud-producer-2,這時(shí)候服務(wù)中心會有兩個(gè)spring-cloud-producer項(xiàng)目渠概,我們重啟Zuul項(xiàng)目茶凳。再手動關(guān)閉spring-cloud-producer-2項(xiàng)目,多次訪問地址:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx播揪,會交替返回:

hello neo贮喧,this is first messge
The service is unavailable.
...

根據(jù)返回結(jié)果可以看出:spring-cloud-producer-2項(xiàng)目已經(jīng)啟用了熔斷,返回:The service is unavailable.

Zuul 目前只支持服務(wù)級別的熔斷猪狈,不支持具體到某個(gè)URL進(jìn)行熔斷箱沦。

路由重試

有時(shí)候因?yàn)榫W(wǎng)絡(luò)或者其它原因,服務(wù)可能會暫時(shí)的不可用雇庙,這個(gè)時(shí)候我們希望可以再次對服務(wù)進(jìn)行重試谓形,Zuul也幫我們實(shí)現(xiàn)了此功能,需要結(jié)合Spring Retry 一起來實(shí)現(xiàn)疆前。下面我們以上面的項(xiàng)目為例做演示寒跳。

添加Spring Retry依賴

首先在spring-cloud-zuul項(xiàng)目中添加Spring Retry依賴。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

開啟Zuul Retry

再配置文件中配置啟用Zuul Retry

#是否開啟重試功能
zuul.retryable=true
#對當(dāng)前服務(wù)的重試次數(shù)
ribbon.MaxAutoRetries=2
#切換相同Server的次數(shù)
ribbon.MaxAutoRetriesNextServer=0

這樣我們就開啟了Zuul的重試功能竹椒。

測試

我們對spring-cloud-producer-2進(jìn)行改造冯袍,在hello方法中添加定時(shí),并且在請求的一開始打印參數(shù)。

@RequestMapping("/hello")
public String index(@RequestParam String name) {
    logger.info("request two name is "+name);
    try{
        Thread.sleep(1000000);
    }catch ( Exception e){
        logger.error(" hello two error",e);
    }
    return "hello "+name+"康愤,this is two messge";
}

重啟 spring-cloud-producer-2和spring-cloud-zuul項(xiàng)目。

訪問地址:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx舶吗,當(dāng)頁面返回:The service is unavailable.時(shí)查看項(xiàng)目spring-cloud-producer-2后臺日志如下:

2018-01-22 19:50:32.401  INFO 19488 --- [io-9001-exec-14] o.s.c.n.z.f.route.FallbackProvider       : request two name is neo
2018-01-22 19:50:33.402  INFO 19488 --- [io-9001-exec-15] o.s.c.n.z.f.route.FallbackProvider       : request two name is neo
2018-01-22 19:50:34.404  INFO 19488 --- [io-9001-exec-16] o.s.c.n.z.f.route.FallbackProvider       : request two name is neo

說明進(jìn)行了三次的請求征冷,也就是進(jìn)行了兩次的重試。這樣也就驗(yàn)證了我們的配置信息誓琼,完成了Zuul的重試功能检激。

注意

開啟重試在某些情況下是有問題的,比如當(dāng)壓力過大腹侣,一個(gè)實(shí)例停止響應(yīng)時(shí)叔收,路由將流量轉(zhuǎn)到另一個(gè)實(shí)例,很有可能導(dǎo)致最終所有的實(shí)例全被壓垮傲隶。說到底饺律,斷路器的其中一個(gè)作用就是防止故障或者壓力擴(kuò)散。用了retry跺株,斷路器就只有在該服務(wù)的所有實(shí)例都無法運(yùn)作的情況下才能起作用复濒。這種時(shí)候,斷路器的形式更像是提供一種友好的錯(cuò)誤信息乒省,或者假裝服務(wù)正常運(yùn)行的假象給使用者巧颈。

不用retry,僅使用負(fù)載均衡和熔斷袖扛,就必須考慮到是否能夠接受單個(gè)服務(wù)實(shí)例關(guān)閉和eureka刷新服務(wù)列表之間帶來的短時(shí)間的熔斷砸泛。如果可以接受,就無需使用retry蛆封。

跨域問題處理

在項(xiàng)目中添加上ZuulFilter的文件

public class PreFilter extends ZuulFilter{
    private Logger LOG =Logger.getLogger(PreFilter.class);
    @Override
    public Object run() {
        // TODO Auto-generated method stub
//       return checkToken();
        RequestContext ctx = RequestContext.getCurrentContext();
         HttpServletResponse response = ctx.getResponse();
         response.setHeader("Access-Control-Allow-Origin", "*");  
         response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");  
         response.setHeader("Access-Control-Max-Age", "3600");  
         response.setHeader("Access-Control-Allow-Headers", "x-requested-with");  
        ctx.setSendZuulResponse(true);// 對該請求進(jìn)行路由  
        ctx.setResponseStatusCode(200);  
        ctx.set("isSuccess", true);// 設(shè)值唇礁,讓下一個(gè)Filter看到上一個(gè)Filter的狀態(tài)
        LOG.info(" PreFilter1 ok");
        return null;       
        }

    @Override
    public boolean shouldFilter() {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public int filterOrder() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String filterType() {
        // TODO Auto-generated method stub
         return "pre";
    }

}

這樣前端通過zuul訪問服務(wù)器接口就可以跨域訪問。
還有個(gè)問題:就是設(shè)置跨域訪問只能設(shè)置一處娶吞,如果zuul調(diào)用的cloud服務(wù)也設(shè)置了跨域訪問垒迂,這樣就會報(bào)錯(cuò),這個(gè)要怎么解決呢?可以在application配置文件中使用ZUUL配置忽略頭部信息

zuul:
#需要忽略的頭部信息妒蛇,不再傳播到其他服務(wù)
  sensitive-headers: Access-Control-Allow-Origin
  ignored-headers: Access-Control-Allow-Origin,H-APP-Id,Token,APPToken

404頁面處理

springboot2中可以通過ErrorPageRegistrar接口來實(shí)現(xiàn)錯(cuò)誤跳轉(zhuǎn)不同頁面
自定義404和500等頁面:

@Configuration
public class ErrorPageConfig {
 
    @Bean
    public ErrorPageRegistrar errorPageRegistrar() {
        return new ErrorPageRegistrar() {
            @Override
            public void registerErrorPages(ErrorPageRegistry registry) {
//              registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error404"),
                registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error404"),
                        new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error500"));
            }
        };
    }
 
}

ErrorPage中第一個(gè)參數(shù)是錯(cuò)誤的狀態(tài)机断,第二個(gè)參數(shù)是發(fā)生這個(gè)錯(cuò)誤要跳轉(zhuǎn)的鏈接,比如NOT_FOUND是404的錯(cuò)誤代碼绣夺,發(fā)生404的時(shí)候就會跳轉(zhuǎn)到error404的鏈接吏奸,我們再配置一個(gè)controller,用于控制跳轉(zhuǎn)錯(cuò)誤頁面

@Controller
public class ErrorController {

    @RequestMapping("/error404")
    public String error() {
        return "redirect:/bd-portal-view/";
    }

}

第二種方法
springboot2我們不需要自定義error的filter,最后都會進(jìn)入到/error映射的方法里面毁涉。
然后我們看/error娘汞,對應(yīng)的文件是BasicErrorController漫仆,里面有兩個(gè)方法:
errorHtml和error泊碑,看方法名就知道一個(gè)是返回html一個(gè)是返回json坤按、xml對象的。他們是根據(jù)客戶端請求是的accept進(jìn)行選擇具體返回內(nèi)容的馒过。這里面返回的參數(shù)可以這樣進(jìn)行配置,當(dāng)然如果你想自己重新/error這個(gè)映射也是可以的臭脓,如下配置:

@Controller
public class GlobalErrorController implements ErrorController {
 
    @Override
    public String getErrorPath() {
        return "/error";
    }
     
    @ResponseBody
    @RequestMapping("/error")
    public String error() {
        return "自定義錯(cuò)誤信息";
    }
 
}

Zuul高可用

image

我們實(shí)際使用Zuul的方式如上圖,不同的客戶端使用不同的負(fù)載將請求分發(fā)到后端的Zuul腹忽,Zuul在通過Eureka調(diào)用后端服務(wù)来累,最后對外輸出。因此為了保證Zuul的高可用性窘奏,前端可以同時(shí)啟動多個(gè)Zuul實(shí)例進(jìn)行負(fù)載嘹锁,在Zuul的前端使用Nginx或者F5進(jìn)行負(fù)載轉(zhuǎn)發(fā)以達(dá)到高可用性。

Zuul的高可用

Zuul的高可用非常關(guān)鍵着裹,因?yàn)橥獠空埱蟮胶蠖宋⒎?wù)的流量都會經(jīng)過Zuul领猾。故而在生產(chǎn)環(huán)境中,我們一般都需要部署高可用的Zuul以避免單點(diǎn)故障求冷。

筆者分兩種場景討論Zuul的高可用瘤运。

Zuul客戶端也注冊到了Eureka Server上

這種情況下,Zuul的高可用非常簡單匠题,只需將多個(gè)Zuul節(jié)點(diǎn)注冊到Eureka Server上拯坟,就可實(shí)現(xiàn)Zuul的高可用。此時(shí)韭山,Zuul的高可用與其他微服務(wù)的高可用沒什么區(qū)別郁季。

image
                                                                                                  Zuul高可用架構(gòu)圖

當(dāng)Zuul客戶端也注冊到Eureka Server上時(shí),只需部署多個(gè)Zuul節(jié)點(diǎn)即可實(shí)現(xiàn)其高可用钱磅。Zuul客戶端會自動從Eureka Server中查詢Zuul Server的列表梦裂,并使用Ribbon負(fù)載均衡地請求Zuul集群。

Zuul客戶端未注冊到Eureka Server上

現(xiàn)實(shí)中盖淡,這種場景往往更常見,例如褪迟,Zuul客戶端是一個(gè)手機(jī)APP——我們不可能讓所有的手機(jī)終端都注冊到Eureka Server上冗恨。這種情況下,我們可借助一個(gè)額外的負(fù)載均衡器來實(shí)現(xiàn)Zuul的高可用味赃,例如Nginx掀抹、HAProxy、F5等心俗。

image

Zuul高可用架構(gòu)圖

Zuul客戶端將請求發(fā)送到負(fù)載均衡器傲武,負(fù)載均衡器將請求轉(zhuǎn)發(fā)到其代理的其中一個(gè)Zuul節(jié)點(diǎn)蓉驹。這樣,就可以實(shí)現(xiàn)Zuul的高可用揪利。

參考:
springcloud(十):服務(wù)網(wǎng)關(guān)zuul初級篇
springcloud(十一):服務(wù)網(wǎng)關(guān)Zuul高級篇
Zuul的高可用
zuul統(tǒng)一異常處理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末态兴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疟位,更是在濱河造成了極大的恐慌诗茎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件献汗,死亡現(xiàn)場離奇詭異,居然都是意外死亡王污,警方通過查閱死者的電腦和手機(jī)罢吃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昭齐,“玉大人尿招,你說我怎么就攤上這事≮寮荩” “怎么了就谜?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長里覆。 經(jīng)常有香客問我丧荐,道長,這世上最難降的妖魔是什么喧枷? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任虹统,我火速辦了婚禮,結(jié)果婚禮上隧甚,老公的妹妹穿的比我還像新娘车荔。我一直安慰自己,他們只是感情好戚扳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布忧便。 她就那樣靜靜地躺著,像睡著了一般帽借。 火紅的嫁衣襯著肌膚如雪珠增。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天宜雀,我揣著相機(jī)與錄音切平,去河邊找鬼。 笑死辐董,一個(gè)胖子當(dāng)著我的面吹牛悴品,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苔严,長吁一口氣:“原來是場噩夢啊……” “哼定枷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起届氢,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤欠窒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后退子,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岖妄,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年寂祥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荐虐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丸凭,死狀恐怖福扬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惜犀,我是刑警寧澤铛碑,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站虽界,受9級特大地震影響汽烦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浓恳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一刹缝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颈将,春花似錦梢夯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至死姚,卻和暖如春人乓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背都毒。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工色罚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人账劲。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓戳护,卻偏偏與公主長得像金抡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子腌且,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 6月26日 星期一 雨 先表揚(yáng)一下自己梗肝,昨晚上為了完成作業(yè),堅(jiān)持工作到凌晨2點(diǎn)铺董,必須要做到今日事今日畢巫击,因?yàn)榈诙?..
    雪媚閱讀 108評論 0 0
  • 小時(shí)候的事情大都太過久遠(yuǎn)重付,現(xiàn)在說起來也只能含糊其辭什黑;未來的日子呢,我們又說了不算堪夭,所以我們給自己找了個(gè)詞,叫活在當(dāng)...
    林目秀閱讀 237評論 0 0
  • 驀然的想起拣凹,攪亂心扉森爽。回頭望嚣镜,走過的路爬迟,不禁傷感,心頭愁緒綿綿菊匿! 看看自己的生活付呕,不知道何時(shí)這么漫不經(jīng)心,看看路友...
    泥洼丶滴雨閱讀 414評論 0 0
  • 一去家山幾許年跌捆?揮毫欲寫錦云篇徽职。 今宵客里逢鄉(xiāng)友,把酒題詩憶竹泉佩厚。
    135d90d4563f閱讀 417評論 0 0