SpringCloud-網關ZUUL
項目結構
父工程(每一個服務都是一個子工程)
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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.xiaoge.demo</groupId> <artifactId>zuul-demo</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>user-service</module> <module>consumer-demo</module> <module>eureka-server</module> <module>gateway</module> </modules> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.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.SR1</spring-cloud.version> <mapper.starter.version>2.0.3</mapper.starter.version> <mysql.version>5.1.32</mysql.version> </properties> <dependencyManagement> <!-- 所有子工程, 必須引下面的依賴纔會有 --> <dependencies> <!-- spring cloud 引用了它, 以後cloud依賴版本就不用寫了 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 通用Mapper啓動器 --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>${mapper.starter.version}</version> </dependency> <!-- mysql驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 因爲不會再dependencyManagement標籤裏, 所以所有的子工程都可以用 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
consumer-demo服務
-
配置文件
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>zuul-demo</artifactId> <groupId>com.xiaoge.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-demo</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入eureka客戶端依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 導入hystrix依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- 引入feign依賴 裏面自帶hystrix熔斷器基本依賴(所以我這裏還是需要導入hystrix-SpringCloud相關的依賴)和ribbon負載均衡依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
-
application.yml
server: port: 8088 spring: # 在eureka中註冊的服務名稱 application: name: consumer-service # 註冊方 # eureka配置 因爲它要去註冊中心註冊, 所以要配置 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka, http://127.0.0.1:10087/eureka # 配置所有的eureka地址, 防止其中一個eureka掛掉 registry-fetch-interval-seconds: 3 # 設置拉取服務列表的週期 爲3秒 fetch-registry: true # 默認爲true 拉取列表, 改爲false 不拉取 instance: prefer-ip-address: true # 我希望使用ip地址 ip-address: 127.0.0.1 # 設置ip地址 # 開啓feign裏面的hystrix feign: hystrix: enabled: true # feign裏面的ribbon配置 ribbon: # 連接超時時長 500毫秒, 500毫秒沒建立連接, 拋出異常 ConnectionTimeOut: 500 # 讀取超時時長 2秒, 2秒沒有讀取到數據, 拋出異常 ReadTimeOut: 2000 # 設置 hystrix默認指令 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 # 設置全局方法 的 超時 時間 爲 3秒 超過 3秒 執行錯誤方法
-
-
啓動類
-
ConsumerApplication
package com.xiaoge; import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @program: cloud-demo * @description: 服務調用方 * @author: Mr.Xiao * @create: 2020-05-04 16:04 **/ // @EnableCircuitBreaker // hystrix註解 // @EnableDiscoveryClient // 它也是客戶端, 它捷能兼容eureka 又能consul、zookeeper等等, 更通用, 更廣泛 // @SpringBootApplication @EnableFeignClients // feign註解-開啓feign功能 @SpringCloudApplication // 它的功能就是上面三個註解的功能 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } }
-
-
實體類
-
User
package com.xiaoge.Consumer.pojo; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 16:06 **/ @Data public class User implements Serializable { // 主鍵 private Long id; // 用戶名 private String username; // 密碼 private String password; // 姓名 private String name; // 年齡 private Integer age; // 性別,1男性,2女性 private Integer sex; // 出生日期 private Date birthday; // 創建時間 private Date created; // 更新時間 private Date updated; // 備註 private String note; }
-
-
Feign客戶端
-
UserClient接口
package com.xiaoge.Consumer.client; import com.xiaoge.Consumer.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @program: feign_demo * @description: 告訴feign 請求服務 請求方式 請求路徑 請求參數 返回結果 * @author: Mr.Xiao * @create: 2020-05-18 20:43 **/ @FeignClient(value = "user-service", fallback = UserClientFallback.class) // 告訴feign服務名稱, 告訴feign方法異常要執行的類 public interface UserClient { @GetMapping("user/{id}") User queryById(@PathVariable("id") Long id); }
-
UserClientFallback實現類
package com.xiaoge.Consumer.client; import com.xiaoge.Consumer.pojo.User; import org.springframework.stereotype.Component; /** * @program: feign_demo * @description: * @author: Mr.Xiao * @create: 2020-05-18 21:07 **/ @Component public class UserClientFallback implements UserClient { /** * 寫熔斷的業務邏輯 * @param id * @return */ @Override public User queryById(Long id) { User user = new User(); user.setUsername("未知用戶!"); return user; } }
-
-
表現層
-
ConsumerController
package com.xiaoge.Consumer.controller; import com.xiaoge.Consumer.client.UserClient; import com.xiaoge.Consumer.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: cloud-demo * @description: 動態拉取服務列表 * @author: Mr.Xiao * @create: 2020-05-04 16:08 **/ @RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private UserClient userClient; @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id){ // 1. 引入feign依賴 // 2. 在啓動器上加上feign註解 @EnableFeignClients // 3. 編寫UserClient接口, 用spring mvc的註解方式告訴feign 請求服務 請求方式 請求路徑 請求參數 返回結果 // 4. 調用 return userClient.queryById(id); } }
-
eureka-server服務
-
配置文件
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>zuul-demo</artifactId> <groupId>com.xiaoge.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>eureka-server</artifactId> <dependencies> <!-- springcloud 註冊中心 eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>
-
application.yml
# 這是eureka的默認端口號 server: port: 10086 # 配置eureka服務名稱 spring: application: name: eureka-server # 註冊方 # 告訴eureka要註冊的地址在哪裏 (這樣eureka就不會報一個com.sun.jersey.api.client.ClientHandlerException異常) eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka # 配置所有的eureka地址, 防止其中一個eureka掛掉 register-with-eureka: false # 默認爲true 註冊自己, 改爲false 不 註冊自己 instance: prefer-ip-address: true # 我希望使用ip地址 ip-address: 127.0.0.1 # 設置ip地址 server: eviction-interval-timer-in-ms: 60000 # 設置eureka失效剔除時長, 每隔60秒失效剔除一次, 失效剔除掛掉的eureka
-
-
啓動類
-
EurekaServer
package com.xiaoge; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @program: cloud-demo * @description: EurekaServer端 * @author: Mr.Xiao * @create: 2020-05-04 16:34 **/ @EnableEurekaServer // 啓動Eureka服務 @SpringBootApplication public class EurekaServer { public static void main(String[] args) { SpringApplication.run(EurekaServer.class); } }
-
user-service服務
-
配置文件
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>zuul-demo</artifactId> <groupId>com.xiaoge.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>user-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> </dependency> <!-- 引入eureka(註冊中心)-客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> </project>
-
application.yml
server: port: 9090 spring: # 在eureka中註冊的服務名稱 application: name: user-service # 數據庫連接信息配置 datasource: url: jdbc:mysql://localhost:3306/yun6 username: root password: 123456 hikari: maximum-pool-size: 20 minimum-idle: 10 # 別名包 mybatis: type-aliases-package: com.xiaoge.user.pojo # 註冊方 # eureka配置 因爲它要去註冊中心註冊, 所以要配置 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka, http://127.0.0.1:10087/eureka # 配置所有的eureka地址, 防止其中一個eureka掛掉 instance: prefer-ip-address: true # 我希望使用ip地址 ip-address: 127.0.0.1 # 設置ip地址 lease-renewal-interval-in-seconds: 30 # 每隔30秒發一次心跳 lease-expiration-duration-in-seconds: 90 # 每隔30秒發一次心跳, 如果隔了90秒都沒發, 那就證明掛了
-
-
啓動類
-
UserApplication
package com.xiaoge; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import tk.mybatis.spring.annotation.MapperScan; /** * @program: cloud-demo * @description: 服務的提供方 * @author: Mr.Xiao * @create: 2020-05-04 15:43 **/ @EnableDiscoveryClient // 它也是客戶端, 它捷能兼容eureka 又能consul、zookeeper等等, 更通用, 更廣泛 @SpringBootApplication @MapperScan("com.xiaoge.user.mapper") public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class); } }
-
-
實體類
-
User
package com.xiaoge.user.pojo; import lombok.Data; import tk.mybatis.mapper.annotation.KeySql; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; import java.util.Date; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 15:46 **/ @Table(name = "tb_user") @Data public class User implements Serializable { @Id @KeySql(useGeneratedKeys = true) // id自動增長 private Long id; // 用戶名 private String username; // 密碼 private String password; // 姓名 private String name; // 年齡 private Integer age; // 性別,1男性,2女性 private Integer sex; // 出生日期 private Date birthday; // 創建時間 private Date created; // 更新時間 private Date updated; // 備註 private String note; }
-
-
表現層
-
UserController
package com.xiaoge.user.controller; import com.xiaoge.user.pojo.User; import com.xiaoge.user.service.UserService; import org.apache.tomcat.jni.Time; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 15:51 **/ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id) { User user = userService.queryById(id); return user; } }
-
-
業務層
-
UserService接口
package com.xiaoge.user.service; import com.xiaoge.user.pojo.User; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 15:50 **/ public interface UserService { public User queryById(Long id); }
-
UserServiceImpl實體類
package com.xiaoge.user.service.impl; import com.xiaoge.user.mapper.UserMapper; import com.xiaoge.user.pojo.User; import com.xiaoge.user.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 15:51 **/ @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User queryById(Long id) { return userMapper.selectByPrimaryKey(id); } }
-
-
Mapper
-
UserMapper
package com.xiaoge.user.mapper; import com.xiaoge.user.pojo.User; import tk.mybatis.mapper.common.Mapper; /** * @program: cloud-demo * @description: * @author: Mr.Xiao * @create: 2020-05-04 15:49 **/ public interface UserMapper extends Mapper<User> { }
-
gateway網關
-
配置文件
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>zuul-demo</artifactId> <groupId>com.xiaoge.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>gateway</artifactId> <dependencies> <!-- 引入eureka依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!-- 引入zuul依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> </project>
-
application.yml
server: port: 10010 spring: # 在eureka中註冊的服務名稱 application: name: gateway # 從eureka中拉取服務列表 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka # 配置路由規則 # zuul: # routes: # user-service: # 是個map結構, 鍵是個字符串可以隨便寫, 不重複就行, 所以我這裏寫了個hehe, 值是路由規則 # path: /user-server/** # 路由規則 # serviceId: user-service # 應用id # 當用戶請求到達, 首先匹配路徑, 匹配成功轉發到user-service服務, # 底層調用eureka拉去服務列表, 然後利用負載均衡算法, 動態從裏面獲取一個地址 # 簡化寫法 # zuul: # routes: # user-service: /user-service/** # key是服務的id, value是這個服務的映射路徑 # 重點: 但是zuul 默認是把每一個微服務都是這樣配置的, 你不配置, 默認這樣的路徑, 也是可以訪問到每一個微服務的(但是這樣每一個微服務都對外暴露了) # zuul: # routes: # user-service: # path: /user/** # 服務映射路徑 # serviceId: user-service # 服務名稱 # strip-prefix: false # 忽略前綴, 默認爲true 忽略前綴以後就不用寫重複的了 列(/user/9, 不忽略, 你訪問要/user/user/9, 忽略, user/9) # 配置忽略的服務(不對外暴露的服務) 是一個集合, 可以把不想暴露的微服務都這樣配置下去 # ignored-services: # - consumer-service # consumer-service不對外暴露 zuul: prefix: /api # 配置全局前綴, 訪問時必須攜帶前綴 routes: user-service: path: /user/** # 服務映射路徑 serviceId: user-service # 服務名稱 strip-prefix: false # 配置忽略的服務(不對外暴露的服務) 是一個集合, 可以把不想暴露的微服務都這樣配置下去 ignored-services: - consumer-service # consumer-service不對外暴露 # 設置熔斷超時時間, 訪問時超過6秒沒反應, 直接報錯 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 ribbon: # 請求連接的超時時間 ConnectTimeout: 500 # 讀取超時時長 ReadTimeout: 2000 # 設置從新訪問次數爲0, 這樣連接不上時, 不會重新發起連接(不重試) MaxAutoRetriesNextServer: 0 # 注意: ribbon的超時時長, 真實值(read + connect) * 2, 必須小於hystrix時長. # 源碼: 計算公式 ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
-
-
啓動類
-
GatewayApplication
package com.xiaoge; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; /** * @Author: 瀟哥 * @DateTime: 2020/5/19 下午8:58 * @Description: zuul啓動器 */ @EnableZuulProxy // 引入zuul註解 @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class); } }
-
-
自定義過濾器
-
LoginFilter
package com.xiaoge.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.apache.commons.lang.StringUtils; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * @Author: 瀟哥 * @DateTime: 2020/5/21 下午8:22 * @Description: 自定義過濾器 */ @Component public class LoginFilter extends ZuulFilter { /** * 過濾器類型 * pre: 請求在被路由之前執行 * routing: 在路由請求時調用 * post: 在routing和error過濾器之後調用 * error: 處理請求時發生錯誤調用 * @return */ @Override public String filterType() { return FilterConstants.PRE_TYPE; // 設置爲前置 pre 這是個常量 } /** * 過濾器順序 * 通過返回的int值來定義過濾器的執行順序, 數字越小優先級越高 * @return */ @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; // 請求參數處理完, 然後攔截 } /** * 要不要過濾 * 返回一個boolean值, 判斷該過濾器是否需要執行. 返回true執行, 返回false不執行. * @return */ @Override public boolean shouldFilter() { return true; } /** * 過濾邏輯 * 過濾器的具體業務邏輯 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { // 獲取請求上下文 RequestContext ctx = RequestContext.getCurrentContext(); // 獲取request HttpServletRequest request = ctx.getRequest(); // 獲取請求參數access-token String token = request.getParameter("access-token"); // 判斷是否存在 if (StringUtils.isBlank(token)) { // 不存在, 未登入, 則攔截 ctx.setSendZuulResponse(false); // 爲true放行, 爲false攔截, 默認爲true ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value()); // 狀態碼, 設置爲403 } return null; } }
-