SpringCloud基礎概念(二)git
4、負載均衡Robbingithub
假設有多個user-service集羣,那麼訪問的時候到底該訪問哪個呢?spring
1.開啓負載均衡mybatis
架構
1 @Bean 2 @LoadBalanced 3 public RestTemplate restTemplate() { 4 return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); 5 }
2.修改負載均衡規則app
負載均衡
1 user-service: 2 ribbon: 3 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3.重試機制dom
當請求的服務掛掉之後,不會當即拋出錯誤,而是再次重試另外一個服務,若是還不行就繼續切換,最終若是仍是失敗,則返回失敗。ide
配置以下:函數
1 spring: 2 cloud: 3 loadbalancer: 4 retry: 5 enabled: true # 開啓Spring Cloud的重試功能 6 user-service: 7 ribbon: 8 ConnectTimeout: 250 # Ribbon的鏈接超時時間 9 ReadTimeout: 1000 # Ribbon的數據讀取超時時間 10 OkToRetryOnAllOperations: true # 是否對全部操做都進行重試 11 MaxAutoRetriesNextServer: 1 # 切換實例的重試次數 12 MaxAutoRetries: 1 # 對當前實例的重試次數
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
5、Hystix熔斷器
熔斷是消費者熔斷,並非服務端。
1.服務降級
當服務繁忙時,若是服務出現異常,不是粗暴的直接報錯,而是返回一個友好的提示,雖然拒絕了用戶的訪問,可是會返回一個結果。
2.引入依賴
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 4 </dependency>
3.開啓熔斷
consumer的啓動器上添加註解@EnableHystrix
4.改造消費者,提供失敗時的回滾函數
1 @Autowired 2 private RestTemplate restTemplate; 3 4 private static final Logger logger = LoggerFactory.getLogger(UserDao.class); 5 6 @HystrixCommand(fallbackMethod = "queryUserByIdFallback") 7 public User queryUserById(Long id){ 8 long begin = System.currentTimeMillis(); 9 String url = "http://user-service/user/" + id; 10 User user = this.restTemplate.getForObject(url, User.class); 11 long end = System.currentTimeMillis(); 12 // 記錄訪問用時: 13 logger.info("訪問用時:{}", end - begin); 14 return user; 15 } 16 17 public User queryUserByIdFallback(Long id){ 18 User user = new User(); 19 user.setId(id); 20 user.setName("用戶信息查詢出現異常!"); 21 return user; 22 }
5.改造服務提供者,隨機休眠一段時間,以觸發熔斷
1 @Service 2 public class UserService { 3 4 @Autowired 5 private UserMapper userMapper; 6 7 public User queryById(Long id) throws InterruptedException { 8 // 爲了演示超時現象,咱們在這裏然線程休眠,時間隨機 0~2000毫秒 9 Thread.sleep(new Random().nextInt(2000)); 10 return this.userMapper.selectByPrimaryKey(id); 11 } 12 }
熔斷的默認時間是1000ms,Ribbon的超時時間必定要小於Hystix的超時時間才能觸發重試機制。
6、Feign
Feign能夠把Rest的請求進行隱藏,假裝成相似SpringMVC的Controller同樣。你不用再本身拼接url,拼接參數等等操做,一切都交給Feign去作。
1.導入依賴
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-openfeign</artifactId> 4 </dependency>
2.Fegin的客戶端
1 @FeignClient("user-service") 2 public interface UserFeignClient { 3 4 @GetMapping("/user/{id}") 5 User queryUserById(@PathVariable("id") Long id); 6 }
@FeignClient
,聲明這是一個Feign客戶端,相似@Mapper
註解。同時經過value
屬性指定服務名稱
3.開啓Fegin功能
在啓動類上添加註解@EnableFeignClients。
4.Feign中已經集成了負載均衡和熔斷,只需簡單配置便可。
<1>負載均衡
1 user-service: 2 ribbon: 3 ConnectTimeout: 250 # 鏈接超時時間(ms) 4 ReadTimeout: 1000 # 通訊超時時間(ms) 5 OkToRetryOnAllOperations: true # 是否對全部操做重試 6 MaxAutoRetriesNextServer: 1 # 同一服務不一樣實例的重試次數 7 MaxAutoRetries: 1 # 同一實例的重試次數
1 feign: 2 hystrix: 3 enabled: true # 開啓Feign的熔斷功能
定義一個類,實現剛纔編寫的UserFeignClient,做爲fallback的處理類
1 @Component 2 public class UserFeignClientFallback implements UserFeignClient { 3 @Override 4 public User queryUserById(Long id) { 5 User user = new User(); 6 user.setId(id); 7 user.setName("用戶查詢出現異常!"); 8 return user; 9 } 10 }
而後在UserFeignClient中,指定剛纔編寫的實現類
1 @FeignClient(value = "user-service",path = "user",fallback = UserClientFallback.class) 2 public interface UserClient { 3 4 @GetMapping("/{id}") 5 User getOne(@PathVariable("id") Integer id); 6 }
7、Zuul網關
1.Zuul簡介
2.Zuul加入後的架構
不論是來自於客戶端(PC或移動端)的請求,仍是服務內部調用。一切對服務的請求都會通過Zuul這個網關,而後再由網關來實現 鑑權、動態路由等等操做。Zuul就是咱們服務的統一入口。
3.簡單路由選擇
1 @SpringBootApplication 2 @EnableZuulProxy // 開啓Zuul的網關功能 3 public class ZuulDemoApplication { 4 5 public static void main(String[] args) { 6 SpringApplication.run(ZuulDemoApplication.class, args); 7 } 8 }
編寫配置
1 zuul: 2 routes: 3 user-service: # 這裏是路由id,隨意寫 4 path: /user-service/** # 這裏是映射路徑 5 url: http://127.0.0.1:8081 # 映射路徑對應的實際url地址
4.面向服務的路由
咱們應該根據服務的名稱,去Eureka註冊中心查找 服務對應的全部實例列表,而後進行動態路由。
<1>開啓並配置Eureka
<2>修改配置
1 zuul: 2 routes: 3 user-service: # 這裏是路由id,隨意寫 4 path: /user-service/** # 這裏是映射路徑 5 serviceId: user-service # 指定服務名稱
5.過濾器
Zuul做爲網關的其中一個重要功能,就是實現請求的鑑權。而這個動做咱們每每是經過Zuul提供的過濾器來實現的。
<1>過濾器的生命週期
請求到達首先會通過pre類型過濾器,然後到達routing類型,進行路由,請求就到達真正的服務提供者,執行請求,返回結果後,會到達post過濾器。然後返回響應。
異常流程:
整個過程當中,pre或者routing過濾器出現異常,都會直接進入error過濾器,再error處理完畢後,會將請求交給POST過濾器,最後返回給用戶。
若是是error過濾器本身出現異常,最終也會進入POST過濾器,然後返回。
若是是POST過濾器出現異常,會跳轉到error過濾器,可是與pre和routing不一樣的時,請求不會再到達POST過濾器了。
<2>使用場景
異常處理:通常會在error類型和post類型過濾器中結合來處理。
服務調用時長統計:pre和post結合使用。
<3>自定義過濾器
模擬一個登陸的校驗。基本邏輯:若是請求中有access-token參數,則認爲請求有效,放行。
1 @Component 2 public class LoginFilter extends ZuulFilter{ 3 @Override 4 public String filterType() { 5 // 登陸校驗,確定是在前置攔截 6 return "pre"; 7 } 8 9 @Override 10 public int filterOrder() { 11 // 順序設置爲1 12 return 1; 13 } 14 15 @Override 16 public boolean shouldFilter() { 17 // 返回true,表明過濾器生效。 18 return true; 19 } 20 21 @Override 22 public Object run() throws ZuulException { 23 // 登陸校驗邏輯。 24 // 1)獲取Zuul提供的請求上下文對象 25 RequestContext ctx = RequestContext.getCurrentContext(); 26 // 2) 從上下文中獲取request對象 27 HttpServletRequest req = ctx.getRequest(); 28 // 3) 從請求中獲取token 29 String token = req.getParameter("access-token"); 30 // 4) 判斷 31 if(token == null || "".equals(token.trim())){ 32 // 沒有token,登陸校驗失敗,攔截 33 ctx.setSendZuulResponse(false); 34 // 返回401狀態碼。也能夠考慮重定向到登陸頁。 35 ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); 36 } 37 // 校驗經過,能夠考慮把用戶信息放入上下文,繼續向後執行 38 return null; 39 } 40 }
6.負載均衡和熔斷
1 zuul: 2 retryable: true 3 ribbon: 4 ConnectTimeout: 250 # 鏈接超時時間(ms) 5 ReadTimeout: 2000 # 通訊超時時間(ms) 6 OkToRetryOnAllOperations: true # 是否對全部操做重試 7 MaxAutoRetriesNextServer: 2 # 同一服務不一樣實例的重試次數 8 MaxAutoRetries: 1 # 同一實例的重試次數 9 hystrix: 10 command: 11 default: 12 execution: 13 isolation: 14 thread: 15 timeoutInMillisecond: 6000 # 熔斷超時時長:6000ms