什麼是網關
隨着互聯網的快速發展,當前以步入移動互聯、物聯網時代。用戶訪問系統入口也變得多種方式,由原來單一的PC客戶端,變化到PC客戶端、各種瀏覽器、手機移動端及智能終端等。同時系統之間大部分都不是單獨運行,經常會涉及與其他系統對接、共享數據的需求。所以系統需要升級框架滿足日新月異需求變化,支持業務發展,並將框架升級爲微服務架構。“API網關”核心組件是架構用於滿足此些需求。
很多互聯網平臺已基於網關的設計思路,構建自身平臺的API網關,國內主要有京東、攜程、唯品會等,國外主要有Netflix、Amazon等。
網關框架框架
業界爲了滿足這些需求,已有相關的網關框架。
1、基於nginx平臺實現的網關有:KONG、API Umbrella
2、自研發的網關有:apigee、Zuul
API網關設計
API網關是微服務架構(Microservices Architecture)標準化服務的模式。API網關定位爲應用系統服務接口的網關,區別於網絡技術的網關,但是原理則是一樣。API網關統一服務入口,可方便實現對平臺衆多服務接口進行管控,對訪問服務的身份認證、防報文重放與防數據篡改、功能調用的業務鑑權、響應數據的脫敏、流量與併發控制,甚至基於API調用的計量或者計費等等。組件設計如下:
網關的應用場景
- 黑白名單:實現通過IP地址控制禁止訪問網關功能,此功能是應用層面控制實現,再往前也可以通過網絡傳輸方面進行控制訪問。
- 日誌:實現訪問日誌的記錄,可用於分析訪問、處理性能指標,同時將分析結果支持其他模塊功能應用。
- 協議適配:實現通信協議校驗、適配轉換的功能。
- 身份認證:負責網關訪問身份認證驗證,此模塊與“訪問認證中心”通信,實際認證業務邏輯交移“訪問認證中心”處理。
- 計流限流:實現微服務訪問流量計算,基於流量計算分析進行限流,可以定義多種限流規則。
- 路由:路由是API網關很核心的模塊功能,此模塊實現根據請求,鎖定目標微服務並將請求進行轉發。此模塊需要與“服務發佈管理中心”通信。“服務發佈管理中心”實現微服務發佈註冊管理功能,與其通信獲得目標微服務信息。
API網關部署
API網關是一個公共基礎組件,無狀態,可支持多套分佈式部署。如下圖所示:
基於Nginx實現API網關
Nginx配置
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location /api-a { proxy_pass http://127.0.0.1:8000/; index index.html index.htm; } location /api-b { proxy_pass http://127.0.0.1:8001/; index index.html index.htm; } } } |
基於Zuul實現API網關
搭建註冊中心
創建項目eureka_server
eureka_server pom依賴信息
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itmayiedu</groupId> <artifactId>eureka_server</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent>
<!-- 管理依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</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>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
application.yml 配置信息
server: port: 8100 eureka: instance: hostname: server1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ register-with-eureka: false fetch-registry: false |
server: port: 8100 eureka: instance: hostname: server1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ register-with-eureka: false fetch-registry: false |
啓動類
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(final String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
創建A服務項目
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class AIndexController {
@RequestMapping("/")
public String index() {
return "我是A項目....";
}
public static void main(String[] args) {
SpringApplication.run(AIndexController.class, args);
}
}
server: port: 8000 spring: application: name: itmayiedu_a eureka: client: service-url: defaultZone: http://localhost:8100/eureka |
創建B服務項目
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class AIndexController {
@RequestMapping("/")
public String index() {
return "我是B項目....";
}
public static void main(String[] args) {
SpringApplication.run(AIndexController.class, args);
}
}
server: port: 8000 spring: application: name: itmayiedu_a eureka: client: service-url: defaultZone: http://localhost:8100/eureka |
創建 zuul-gateway 項目
創建 zuul-gateway Pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-conf</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <encoding>UTF-8</encoding> <outputDirectory>${project.build.directory}/ext/conf</outputDirectory> <resources> <resource> <directory>ext/conf</directory> <includes> <include>logback.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.5.201505241946</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>default-prepare-agent-integration</id> <goals> <goal>prepare-agent-integration</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.3</version> <configuration> <imageName>hy_uav_gateway</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> <include>ext/conf/logback.xml</include> </resource> </resources> </configuration> </plugin> </plugins> </build>
<repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> |
配置application 依賴信息
eureka: client: serviceUrl: defaultZone: http://localhost:8100/eureka/ server: port: 8769 spring: application: name: service-zuul zuul: routes: api-a: path: /api-a/** serviceId: itmayiedu_a api-b: path: /api-b/** serviceId: itmayiedu_b |
ZuulApplication啓動運行
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public TokenFilter accessFilter() {
return new TokenFilter();
}
}
過濾器攔截參數
// 使用網關攔截Token參數
public class TokenFilter extends ZuulFilter {
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
// 返回錯誤信息
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("accessToken is null");
return null;
}
return null;
}
public boolean shouldFilter() {
return true;// 是否執行該過濾器,此處爲true,說明需要過濾
}
@Override
public int filterOrder() {
return 0;// 優先級爲0,數字越大,優先級越低
}
@Override
public String filterType() {
return "pre"; // 前置過濾器
}
}