SpringCloud基礎入門

撇腳攻城獅發表於2020-10-07

提示:文章寫完後,目錄可以自動生成,如何生成可參考右邊的幫助文件


前言

隨著專案的越來越大,每次啟動編譯時間將會越來越長,這對於開發、測試、部署帶來極大的影響,大大降低了效率。這時,關於微服務的模式就興起了。


一、SpringCloud概念

1. 是什麼

Spring cloud是一個基於Spring Boot實現的服務治理工具包,用於微服務架構中管理和協調服務的。微服務是一種架構風格,一個大型複雜軟體應用由一個或多個微服務組成。系統中的各個微服務可被技術選型,獨立開發,獨立部署,獨立運維,各個微服務之間是鬆耦合的。每個微服務僅關注於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表著一個小的業務能力。

2. 組成部分

服務註冊發現(註冊中心)——Netflix Eureka : 幫我們管理服務的通訊地址(ip,埠)
客戶端負載均衡——Netflix Ribbon\Feign : 解決服務的呼叫的
斷路器——Netflix Hystrix :解決微服務故障的,保護微服務的
服務閘道器——Netflix Zuul :統一訪問入口,微服務的大門(安保部門)
分散式配置——Spring Cloud Config :統一管理微服務的配置

二、搭建微服務場景

1.搭建環境

搭建一個maven專案,配置pom檔案

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </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>
        <springboot.version>2.0.5.RELEASE</springboot.version>
    </properties>

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

2. 服務提供者

新建子模組,服務提供者,maven專案,編寫介面。此處偷懶將業務程式碼直接寫到controller,應該是寫到service層的。

@RestController
@RequestMapping("/provider")
public class UserController {

    //    @Autowired
   //    private IUserService userService;
    @GetMapping("/user/{id}") //user/1
    public User getUser(@PathVariable("id") Long id) {

        // 正常應該呼叫service獲取使用者,現在模擬一下
        return new User(id, "zs");
    }
}

3. 服務消費者

同理建立子模組,服務消費者。這裡用到了框架自帶的RestTemplate傳送請求。需要先在入口類注入。

@Configuration // <beans></beans>
public class CfgBean {

    @Bean //<bean class="org.springframework.web.client.RestTemplate"></bean>
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}



@RestController
@RequestMapping("/consumer")
public class UserController {

    //多個方法呼叫只需改一處就ok
    public static  final String URL_PREFIX = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id")Long id){
        //呼叫遠端服務 http請求
        String url = URL_PREFIX+"/provider/user/"+id;
        return restTemplate.getForObject(url,User.class );
    }
}

4. 測試

分別啟動,通過呼叫消費者得到提供者的返回值。

三、Eureka註冊中心

上面我們是在消費者中寫死了提供者的URL,這肯定在實際中是不可取的,所以我們可以呼叫Eureka將服務提供者統一管理。

1. 搭建Eureka

建立一個普通maven專案

pom

<!--springboot支援-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

<!--Eureka服務端支援-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

yml配置

server:
  port: 7001
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否要註冊到eureka
    fetchRegistry: false #表示是否從Eureka Server獲取註冊資訊
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機配置

入口類

@SpringBootApplication
@EnableEurekaServer //標識是eureka服務端
public class EnrekaServerApplication_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EnrekaServerApplication_7001.class);
    }
}

2. 服務提供者註冊到Eureka

新增依賴

 <!--eureka客戶端支援 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

yml

eureka:
  client:
    service-url:
     defaultZone: http://localhost:7001/eureka #告訴服務提供者要把服務註冊到哪兒
 instance:
    prefer-ip-address: true # 當呼叫getHostname獲取例項的hostname時,返回ip而不是host名稱
    ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找

入口類

@SpringBootApplication
@EnableEurekaClient //表示是eureka的客戶端
public class UserProviderApplication_8001 {
    public static void main(String[] args) {
        SpringApplication.run(UserProviderApplication_8001.class);
    }
}

3. 服務消費者呼叫

新增依賴

 <!--eureka客戶端支援 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

yml

eureka:
  client:
    service-url:
     defaultZone: http://localhost:7001/eureka #告訴服務提供者要把服務註冊到哪兒
 instance:
    prefer-ip-address: true # 當呼叫getHostname獲取例項的hostname時,返回ip而不是host名稱
    ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找

入口類

@SpringBootApplication
@EnableEurekaClient //表示是eureka的客戶端
public class UserProviderApplication_8001 {
    public static void main(String[] args) {
        SpringApplication.run(UserProviderApplication_8001.class);
    }
}

服務呼叫

 @Autowired
private DiscoveryClient discoveryClient;// Eureka客戶端,可以獲取到服務例項資訊

// String baseUrl = "http://localhost:8081/user/";
        // 根據服務名稱,獲取服務例項
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 因為只有一個UserService,因此我們直接get(0)獲取
        ServiceInstance instance = instances.get(0);
        // 獲取ip和埠資訊
        String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
       this.restTemplate.getForObject(baseUrl + id, User.class)

4. 叢集

  • Eureka叢集
    修改yml,選擇不同的啟用可達到叢集的目的
eureka:
  instance:
    prefer-ip-address: true
  client:
    serviceUrl:
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka
spring:
  profiles:
    active: eureka1
---
#eureka1第一個Eureka
spring:
  profiles: eureka1
  application:
    name: eureka1
server:
  port: 1010  #埠
eureka:
  instance:
    hostname: eureka1  #主機
    instance-id: eureka1:1010
---
#eureka1第一個Eureka
spring:
  profiles: eureka2
  application:
      name: eureka2
server:
  port: 1011  #埠
eureka:
  instance:
    hostname: eureka2  #主機
    instance-id: eureka2:1011
---
#eureka3第一個Eureka
spring:
  profiles: eureka3
  application:
      name: eureka3
server:
  port: 1012  #埠
eureka:
  instance:
    hostname: eureka3  #主機
    instance-id: eureka3:1012
  • 服務提供者叢集
    同理修改yml配置

四、Ribbon負載均衡

為了提供併發量,有時同一個服務提供者可以部署多個(商品服務)。這個客戶端在呼叫時要根據一定的負責均衡策略完成負載呼叫。

1. 是什麼

簡單的說,Ribbon是一個客戶端負載均衡器,我們可以在配置檔案中列出load Balancer後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連線等)去連線這些機器,我們也很容易使用Ribbon實現自定義的負載均衡演算法。

2. 負載均衡測試

依賴

   <!--客戶端負載均衡實現 ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

消費者入口類修改

    @Bean
    @LoadBalanced //開啟負載均衡
    public RestTemplate getRestTemplate(){
        return  new RestTemplate();
    }

服務消費

@RestController
@RequestMapping("/consumer")
public class UserController {

    //多個方法呼叫只需改一處就ok
    //public static  final String URL_PREFIX = "http://localhost:8001";
    public static  final String URL_PREFIX = "http://USER-PROVIDER"; //通過服務名從註冊中心獲取服務列表,通過負載均衡呼叫

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id")Long id){
        //呼叫遠端服務 http請求
        String url = URL_PREFIX+"/provider/user/"+id;
        return restTemplate.getForObject(url,User.class );
    }
}

測試
會出現輪詢的效果

2. 負載均衡策略

部分

  • RoundRobinRule(預設):簡單輪詢服務列表來選擇伺服器。
  • WeightedResponseTimeRule:為每一個伺服器賦予一個權重值。伺服器響應時間越長,這個伺服器的權重就越小。這個規則會隨機選擇伺服器,這個權重值會影響伺服器的選擇。
  • RandomRule:隨機選擇一個可用的伺服器。

在這裡插入圖片描述

五、Feign負載均衡

前面的可以發現當我們通過RestTemplate呼叫其它服務的API時,所需要的引數須在請求的URL中進行拼接,如果引數少的話或許我們還可以忍受,一旦有多個引數的話,這時拼接請求字串就會效率低下,並且顯得好傻。

Feign是一個宣告式的Web Service客戶端,它的目的就是讓Web
Service呼叫更加簡單。Feign提供了HTTP請求的模板,通過編寫簡單的介面和插入註解,就可以定義好HTTP請求的引數、格式、地址等資訊。而Feign則會完全代理HTTP請求,我們只需要像呼叫方法一樣呼叫它就可以完成服務請求及相關處理。Feign整合了Ribbon和Hystrix(關於Hystrix我們後面再講),可以讓我們不再需要顯式地使用這兩個元件。

總起來說,Feign具有如下特性:
可插拔的註解支援,包括Feign註解和JAX-RS註解;
支援可插拔的HTTP編碼器和解碼器;
支援Hystrix和它的Fallback;
支援Ribbon的負載均衡;
支援HTTP請求和響應的壓縮。

1. 搭建

依賴,將ribbon修改為feign

    <!--feign的支援-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

yml

server:
  port: 9003
eureka:
  client:
    registerWithEureka: false #不註冊到Eureka,不在註冊中心顯示
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka

呼叫服務的名字,要和服務提供者裡面訪問地址和引數等保持一致。

//呼叫服務名字
@FeignClient(value = "USER-PROVIDER")
@RequestMapping("/provider")
public interface UserCilent {
    @RequestMapping("/user/{id}") //user/1
     User getUser(@PathVariable("id") Long id);
}

入口類

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "上面服務介面包") 
public class UserConsumerFeign9002Application {

	public static void main(String[] args) {
		SpringApplication.run(UserConsumerFeign9002Application.class, args);
	}
}

消費程式碼

@RestController
@RequestMapping("/consumer")
public class UserController {
    @Autowired
    private UserCilent userCilent;
    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id")Long id){
        System.out.println(userCilent.getClass());
        return userCilent.getUser(id);
    }
}

六、Hystrix斷路器

Hystrix是保證微服務群健壯框架,做了隔離,熔斷,降級等操作.最終達到不會由於某一個服務出問題而導致雪崩現象,讓整體群死掉。

1、簡介

Hystrix是國外知名的視訊網站Netflix所開源的非常流行的高可用架構框架。Hystrix能夠完美的解決分散式系統架構中打造高可用服務面臨的一系列技術難題。

Hystrix “豪豬”,具有自我保護的能力。hystrix 通過如下機制來解決雪崩效應問題。

資源隔離(限流):包括執行緒池隔離和訊號量隔離,限制呼叫分散式服務的資源使用,某一個呼叫的服務出現問題不會影響其他服務呼叫。

熔斷:當失敗率達到閥值自動觸發降級(如因網路故障/超時造成的失敗率高),熔斷器觸發的快速失敗會進行快速恢復。

降級機制:超時降級、資源不足時(執行緒或訊號量)降級,降級後可以配合降級介面返回託底資料。

快取:提供了請求快取、請求合併實現。

2. 實現

服務提供依賴

<!--斷路器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

yml

server:
  port: 4020
spring:
  application:
    name: user-provider
eureka:
  instance:
    prefer-ip-address: true #顯示eureka客戶端真實ip
  client:
    service-url:
     #defaultZone: http://localhost:1010/eureka #告訴服務提供者要把服務註冊到哪
     #eureka叢集環境
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka

入口類
在這裡插入圖片描述
在這裡插入圖片描述

3. Feign實現

Feign自己封裝了Hytrix,不需要再匯入jar
yml

server:
  port: 9003
eureka:
  client:
    registerWithEureka: false #不註冊到Eureka,不在註冊中心顯示
    service-url:
      defaultZone: http://localhost:7001/eureka
feign:
   hystrix:
       enabled: true #開啟熔斷支援
   client:
    config:
      remote-service:           #服務名,填寫default為所有服務
        connectTimeout: 3000
        readTimeout: 3000
hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 3000

降級處理

package cn.itsource.springcloud.client;

import cn.itsource.springcloud.domain.User;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
//UserClient 表示對那個介面進行託底處理
public class UserClientHystrixFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public User getUser(Long id) {
                return new User(id,"出異常,請聯絡管理員!");
            }
        };
    }
}

//呼叫服務名字
@FeignClient(value = "USER-PROVIDER",fallbackFactory = UserClientHystrixFallbackFactory.class)
@RequestMapping("/provider")
public interface UserClient {
    @RequestMapping("/user/{id}")
    User getUser(@PathVariable("id") Long id);

}

七、 Zuul閘道器

在這裡插入圖片描述

1. 搭建

依賴

<!--springboot支援-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</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-zuul</artifactId>
		</dependency>

yml

server:
  port: 2010
spring:
  application:
    name: zuul-gateway
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1011/eureka,http://eureka3:1012/eureka
  instance:
    instance-id: zuul:2010
    prefer-ip-address: false

入口類

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulGatewayApp2010 {

    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApp2010.class,args);
    }
}

路由配置

zuul:
  routes:
    myUser.serviceId: user-provider # 服務名
    myUser.path: /myUser/** # 把myUser打頭的所有請求都轉發給user-provider
  ignored-services: "*" #所有服務都不允許以服務名來訪問
  prefix: "/services" #加一個統一字首

2. 過濾器

Zuul作為閘道器的其中一個重要功能,就是實現請求的鑑權。而這個動作我們往往是通過Zuul提供的過濾器來實現的。

自定義過濾器

@Component
public class LoginFilter extends ZuulFilter{
    @Override
    public String filterType() {
        // 登入校驗,肯定是在前置攔截
        return "pre";
    }

    @Override
    public int filterOrder() {
        // 順序設定為1
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        // 返回true,代表過濾器生效。
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        // 登入校驗邏輯。
        // 1)獲取Zuul提供的請求上下文物件
        RequestContext ctx = RequestContext.getCurrentContext();
        // 2) 從上下文中獲取request物件
        HttpServletRequest req = ctx.getRequest();
        // 3) 從請求中獲取token
        String token = req.getParameter("access-token");
        // 4) 判斷
        if(token == null || "".equals(token.trim())){
            // 沒有token,登入校驗失敗,攔截
            ctx.setSendZuulResponse(false);
            // 返回401狀態碼。也可以考慮重定向到登入頁。
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // 校驗通過,可以考慮把使用者資訊放入上下文,繼續向後執行
        return null;
    }
}

3.負載均衡與熔斷

Zuul中預設就已經整合了Ribbon負載均衡和Hystix熔斷機制。但是所有的超時策略都是走的預設值,比如熔斷超時時間只有1S,很容易就觸發了。因此建議我們手動進行配置:

zuul:
  retryable: true
ribbon:
  ConnectTimeout: 250 # 連線超時時間(ms)
  ReadTimeout: 2000 # 通訊超時時間(ms)
  OkToRetryOnAllOperations: true # 是否對所有操作重試
  MaxAutoRetriesNextServer: 2 # 同一服務不同例項的重試次數
  MaxAutoRetries: 1 # 同一例項的重試次數
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMillisecond: 3000 # 熔斷超時時長:3000ms

八、SpringCloud Config分散式配置中心

1. 是什麼

在分散式系統中,由於服務數量巨多,為了方便服務配置檔案統一管理,實時更新,所以需要分散式配置中心元件。在Spring Cloud中,有分散式配置中心元件spring cloud config ,它支援配置服務放在配置服務的記憶體中(即本地),也支援放在遠端Git倉庫中。在spring cloud config 元件中,分兩個角色,一是config server,二是config client。

2. 和githee整合

2.1 服務端配置

在這裡插入圖片描述

2.2 建立子模組

依賴

<dependencies>
    <!--springboot支援-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <!--eureka客戶端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--配置中心支援-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
</dependencies>

yml

server:
  port: 1299
eureka:
  client:
    service-url:
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka # 叢集

  instance:
    prefer-ip-address: true
spring:
  application:
    name: spring-cloud-config-server
  cloud:
    config:
      server:
        git:
          uri: gitee的uri
          username: xxx
          password: xxx

入口類

@SpringBootApplication
@EnableEurekaClient //加入註冊中心
@EnableConfigServer //啟用配置服務端
public class ConfigServerApplication_1299 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication_1299.class);
    }
}

測試
http://localhost:1299/application-user/test
http://localhost:1299/application-user/dev

總結

簡單的springcloud就差不多入門了,還有更多更深入的等著我們。。。。。。