這個例子主要是將zuul和eureka結合起來使用,zuul作為反向代理,同時起到負載均衡的作用,同時網關後面的消費者也作為服務提供者,同時提供負載均衡。 ...
這個例子主要是將zuul和eureka結合起來使用,zuul作為反向代理,同時起到負載均衡的作用,同時網關後面的消費者也作為服務提供者,同時提供負載均衡。
一.API網關(摘自百度)
API網關是一個伺服器,是系統的唯一入口。從面向對象設計的角度看,它與外觀模式類似。API網關封裝了系統內部架構,為每個客戶端提供一個定製的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。
API網關方式的核心要點是,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的訪問API。服務端通過API-GW註冊和管理服務。
二. 整體架構
(1)http://localhost:40000/provider/hello?name=ljq3經過zuul網關之後,由於zuul對路徑映射
zuul.routes.api-a.path=/provider/**
zuul.routes.api-a.serviceId=ribbon-consumer
(2)把provider映射到ribbon-cunsumer這個服務上,zuul利用負載均衡的方式選一個服務地址,然後將路徑替換,得到
http://localhost:40001/hello?name=ljq3
(3)ribbon-consummer再利用ribbon負載均衡選擇一個provider,但是因為我在代碼中只把地址傳遞,而沒有傳遞參數,所以得到的url是
http://localhost:20003/
(4)github地址:https://github.com/linjiaqin/scdemo
三. zuul代碼結構
這裡把zuul的服務作為一個服務提供者去註冊到eureka中,要使用這個註解表名是一個服務提供者@EnableEurekaClient
1.引導類
package com.ljq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@EnableZuulProxy
@SpringBootApplication
@EnableEurekaClient
//把zuul作為服務提供者到eureka註冊
public class GatewayApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(GatewayApplication.class);
GatewayApplication(){
LOGGER.info("app init");
}
public static void main(String[] args) {
LOGGER.info("app start");
SpringApplication.run(GatewayApplication.class, args);
}
}
2.配置文件
這裡把的路徑匹配規則是當訪問的符合provider這個路徑時,自動映射到serviceId上,去eureka找到serviceID的所有可用地址,負載均衡選取一個後替換成這個地址
spring.application.name=gateway-service-zuul server.port=40000 eureka.client.serviceUrl.defaultZone=http://mu01:8761/eureka,http://cu01:8762/eureka,http://cu02:8763/eureka zuul.routes.api-a.path=/provider/** zuul.routes.api-a.serviceId=eureka-client-service-provider
3. beanconfig
package com.ljq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Service; @Service public class MyBaenConfig { private static final Logger LOGGER = LoggerFactory.getLogger(MyBaenConfig.class); MyBaenConfig(){ LOGGER.info("service init"); } @Bean public MyFilter myFilter() { LOGGER.info("bean init"); return new MyFilter(); } }
4. zuul的核心filter類,用來過濾請求
package com.ljq; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class MyFilter extends ZuulFilter { private final Logger LOGGER = LoggerFactory.getLogger(MyFilter.class); MyFilter(){ LOGGER.info("filter init"); } @Override public String filterType() { return "pre"; // 可以在請求被路由之前調用 } @Override public int filterOrder() { return 0; // filter執行順序,通過數字指定 ,優先順序為0,數字越大,優先順序越低 } @Override public boolean shouldFilter() { return true;// 是否執行該過濾器,此處為true,說明需要過濾 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); LOGGER.info("--->>> MyFilter {},{}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("name");// 獲取請求的參數 if (StringUtils.isNotBlank(token)) { ctx.setSendZuulResponse(true); //對請求進行路由 ctx.setResponseStatusCode(200); ctx.set("isSuccess", true); return null; } else { ctx.setSendZuulResponse(false); //不對其進行路由 ctx.setResponseStatusCode(400); ctx.setResponseBody("parameter name is empty"); ctx.set("isSuccess", false); return null; } } }
5.mvn spring-boot:run起來之後,就可以看到網關服務在eureka上註冊了
curl http://localhost:40000/provider 可以看到負載均衡的效果
6.網關的預設路由規則
但是如果後端服務多達十幾個的時候,每一個都這樣配置也挺麻煩的,spring cloud zuul已經幫我們做了預設配置。
預設情況下,Zuul會代理所有註冊到Eureka Server的微服務,
並且Zuul的路由規則如下:http://ZUUL_HOST:ZUUL_PORT/微服務在Eureka上的serviceId/**
會被轉發到serviceId對應的微服務。
二 .Ribbon Consumer
這裡的consummer不僅是服務消費者去後面拿取provider的內容,同時也作為一個服務提供者對外提供服務
1.引導類
@SpringBootApplication @EnableDiscoveryClient @EnableEurekaClient public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
2.beanconfig類
package com.ljq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ljqConfig { private static final Logger logger = LoggerFactory.getLogger(ljqConfig.class); ljqConfig(){ logger.info("config init"); } @Bean @LoadBalanced public RestTemplate restTemplate(){ logger.info("restTemplate function"); return new RestTemplate(); } }
3.controller
package com.ljq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; @RestController public class ljqController { private static final Logger logger = LoggerFactory.getLogger(ljqController.class); ljqController(){ logger.info("controller init"); } @Autowired private RestTemplate restTemplate; //這裡不寫eureka的註冊中心,而是寫服務提供者的應用名 @GetMapping(value = "/hello") public String hello(HttpServletRequest request){ logger.info("hello function"); logger.info(request.getPathInfo()); logger.info("--->>> consumer contorller {},{}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("name");// 獲取請求的參數 logger.info(token); return restTemplate.getForEntity("http://eureka-client-service-provider/", String.class).getBody(); } }
配置與上篇文章一致
spring.application.name=ribbon-consumer server.port=30001 eureka.client.serviceUrl.defaultZone=http://mu01:8761/eureka,http://cu01:8762/eureka,http://cu02:8763/eureka
springboot的執行順序
註解
三. provider
代碼與上篇文章基本一直
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController public class ljqController { private final Logger logger = LoggerFactory.getLogger(ljqController.class); @Value("${server.port}") String port; @RequestMapping("/") public String home(HttpServletRequest request){ logger.info(request.getPathInfo()); logger.info("--->>> consumer contorller {},{}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("name");// 獲取請求的參數 logger.info(token); return "Hello world, port is:" + port; } }
一鍵啟動腳本
#首先開啟eureka,上篇文章中我們把eureka放在集群上,並單獨寫了一個腳本了,這裡不在贅述 #然後開啟zuul cd /home/linjiaqin/log_stream_platform/source/scdemo/gateway; nohup mvn spring-boot:run > /dev/null 2>&1 & #開兩個ribbon-consumer cd /home/linjiaqin/log_stream_platform/source/scdemo/consumer nohup mvn spring-boot:run -Dserver.port=30001 > /dev/null 2>&1 & nohup mvn spring-boot:run -Dserver.port=30002 > /dev/null 2>&1 & #開啟三個provider cd /home/linjiaqin/log_stream_platform/source/scdemo/provider nohup mvn spring-boot:run -Dserver.port=20001 > /dev/null 2>&1 & nohup mvn spring-boot:run -Dserver.port=20002 > /dev/null 2>&1 & nohup mvn spring-boot:run -Dserver.port=20003 > /dev/null 2>&1 &
測試結果
linjiaqin@linjiaqin-computer:~$ curl http://localhost:40000/provider/hello?name=ljq2 Hello world, port is:20003
linjiaqin@linjiaqin-computer:~$ curl http://localhost:40000/provider/hello?name=ljq3 Hello world, port is:20003
linjiaqin@linjiaqin-computer:~$ curl http://localhost:40000/provider/hello?name=ljq4 Hello world, port is:20003
linjiaqin@linjiaqin-computer:~$ curl http://localhost:40000/provider/hello?name=ljq5 Hello world, port is:20002
linjiaqin@linjiaqin-computer:~$ curl http://localhost:40000/provider/hello?name=ljq6 Hello world, port is:20002