學習筆記
作用: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; } }
測試過濾器