筆記77 微服務筆記4

SpringCloud基礎概念(二)git

4、負載均衡Robbingithub

假設有多個user-service集羣,那麼訪問的時候到底該訪問哪個呢?spring

1.開啓負載均衡mybatis

在RestTemplate的配置方法上添加@LoadBalanced註解:架構

1  @Bean
2     @LoadBalanced
3     public RestTemplate restTemplate() {
4         return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
5     }

 

2.修改負載均衡規則app

  格式是:{服務名稱}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的實現類。負載均衡

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 # 對當前實例的重試次數

切換次數取決於MaxAutoRetriesNextServer參數的值。

引入spring-retry依賴

<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 }
    • 首先這是一個接口,Feign會經過動態代理,幫咱們生成實現類。這點跟mybatis的mapper很像

    • @FeignClient,聲明這是一個Feign客戶端,相似@Mapper註解。同時經過value屬性指定服務名稱

    • 接口中的定義方法,徹底採用SpringMVC的註解,Feign會根據註解幫咱們生成URL,並訪問獲取結果

3.開啓Fegin功能

  在啓動類上添加註解@EnableFeignClients。

4.Feign中已經集成了負載均衡和熔斷,只需簡單配置便可。

<1>負載均衡

  不須要額外引入依賴,也不須要再註冊RestTemplate對象。直接配置便可:

1 user-service:
2   ribbon:
3     ConnectTimeout: 250 # 鏈接超時時間(ms)
4     ReadTimeout: 1000 # 通訊超時時間(ms)
5     OkToRetryOnAllOperations: true # 是否對全部操做重試
6     MaxAutoRetriesNextServer: 1 # 同一服務不一樣實例的重試次數
7     MaxAutoRetries: 1 # 同一實例的重試次數

<2>熔斷機制

開啓熔斷

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.簡單路由選擇

經過@EnableZuulProxy註解開啓Zuul的功能:

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地址

將符合path 規則的一切請求,都代理到 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>使用場景

    • 請求鑑權:通常放在pre類型,若是發現沒有訪問權限,直接就攔截了

    • 異常處理:通常會在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

 有關微服務的相關代碼請參照:https://github.com/lyj8330328/Load-Balancing

相關文章
相關標籤/搜索