SpringCloud微服務基礎 Eureka、Feign、Ribbon、Zuul、Hystrix、配置中心的基礎使用


1、單點系統架構

傳統項目架構

傳統項目分為三層架構,將業務邏輯層、數據庫訪問層、控制層放入在一個項目中。

優點:適合於個人或者小團隊開發,不適合大團隊開發。

分布式項目架構

根據業務需求進行拆分成N個子系統,多個子系統相互協作才能完成業務流程子系統之間通訊使用RPC遠程通訊技術。

優點:

  1. 把模塊拆分,使用接口通信,降低模塊之間的耦合度。
  2. 把項目拆分成若干個子項目,不同的團隊負責不同的子項目。
  3. 增加功能時只需要再增加一個子項目,調用其它系統的接口就可以。
  4. 可以靈活的進行分布式部署。

缺點:

  1. 系統之間交互需要使用遠程通信,接口開發增加工作量。
  2. 各個模塊有一些通用的業務邏輯無法共用。

為了解決上面分布式架構的缺點,我們引入了soa架構,SOA:Service Oriented Architecture面向服務的架構。也就是把工程拆分成服務層、表現層兩個工程。服務層中包含業務邏輯,只需要對外提供服務即可。表現層只需要處理和頁面的交互,業務邏輯都是調用服務層的服務來實現。

 

什么是項目集群

多台服務器部署相同應用構成一個集群

作用:通過負載均衡設備共同對外提供服務

RPC遠程調用

RPC 的全稱是 Remote Procedure Call 是一種進程間通信方式。
它允許程序調用另一個地址空間(通常是共享網絡的另一台機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即無論是調用本地接口/服務的還是遠程的接口/服務,本質上編寫的調用代碼基本相同。
比如兩台服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數或者方法,由於不在一個內存空間,不能直接調用,這時候需要通過就可以應用RPC框架的實現來解決

restful、soap、rpc

(1)restful是一種架構設計風格,提供了設計原則和約束條件,而不是架構。而滿足這些約束條件和原則的應用程序或設計就是 RESTful架構或服務。
(2)soap象訪問協議是一種數據交換協議規范,
是一種輕量的、簡單的、基於XML的協議的規范。SOAP協議和HTTP協議一樣,都是底層的通信協議,只是請求包的格式不同而已,SOAP包是XML格式的。
soap

基於xml並封裝成了符合http協議,因此,它符合任何路由器、 防火牆或代理服務器的要求。
soap可以使用任何語言來完成,只要發送正確的soap請求即可,基於soap的服務可以在任何平台無需修改即可正常使用。
(3)RPC就是從一台機器(客戶端)上通過參數傳遞的方式調用另一台機器(服務器)上的一個函數或方法(可以統稱為服務)並得到返回的結果。
RPC 會隱藏底層的通訊細節(不需要直接處理Socket通訊或Http通訊)
RPC 是一個請求響應模型。客戶端發起請求,服務器返回響應(類似於Http的工作方式)
RPC 在使用形式上像調用本地函數(或方法)一樣去調用遠程的函數(或方法)。

rpc遠程調用框架

幾種比較典型的RPC的實現和調用框架。 
(1)RMI實現,利用java.rmi包實現,基於Java遠程方法協議(Java Remote Method Protocol) 
和java的原生序列化。 
(2)Hessian,是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 基於HTTP協議,采用二進制編解碼。 
(3)thrift是一種可伸縮的跨語言服務的軟件框架。thrift允許你定義一個描述文件,描述數據類型和服務接口。依據該文件,編譯器方便地生成RPC客戶端和服務器通信代碼。

(4)SpringCloud 為開發人員提供了快速構建分布式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件總線、全局鎖、決策競選、分布式會話等等。

(4) Dubbo是阿里巴巴公司開源的一個高性能優秀的服務框架,使得應用可通過高性能的 RPC 實現服務的輸出和輸入功能,可以和 Spring框架無縫集成。

 

2、面向於服務架構

什么是SOA

業務系統分解為多個組件,讓每個組件都獨立提供離散,自治,可復用的服務能力

通過服務的組合和編排來實現上層的業務流程

作用:簡化維護,降低整體風險,伸縮靈活

 

3、微服務架構

什么是微服務架構

架構設計概念,各服務間隔離(分布式也是隔離),自治(分布式依賴整體組合)其它特性(單一職責,邊界,異步通信,獨立部署)是分布式概念的跟嚴格執行

SOA到微服務架構的演進過程

作用:各服務可獨立應用,組合服務也可系統應用(巨石應用[monolith]的簡化實現策略-平台思想)

 

SOA架構與微服務架構區別

SOA架構主要針對企業級、采用ESB服務(ESB企業服務總線),非常重,需要序列化和反序列化,采用XML格式傳輸。

微服務架構主要互聯網公司,輕量級、小巧,獨立運行,基於Http+Rest+JSON格式傳輸。

 

ESB也可以說是傳統中間件技術與XML、Web服務等技術相互結合的產物。

 

4、SpringCloud

SpringCloud 為開發人員提供了快速構建分布式系統的一些工具,包括配置管理、服務發現、斷路器、路由、負載均衡、微代理、事件總線、全局鎖、決策競選、分布式會話等等。它運行環境簡單,可以在開發人員的電腦上跑。另外說明spring cloud是基於Springboot的,所以需要開發中對Springboot有一定的了解,如果不了解的話可以看螞蟻課堂SpringBoot課程。

服務提供者與消費關系

服務提供者:提供服務被人調用

消費者:調用被人服務

服務的注冊與發現(Eureka )

在這里,我們需要用的的組件上Spring Cloud Netflix的Eureka ,eureka是一個服務注冊和發現模塊。

什么是Eureka

官方的介紹在這里Eureka wiki。Eureka是Netflix開源的一個RESTful服務,主要用於服務的注冊發現。Eureka由兩個組件組成:Eureka服務器和Eureka客戶端。Eureka服務器用作服務注冊服務器。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、作為輪詢負載均衡器,並提供服務的故障切換支持。Netflix在其生產環境中使用的是另外的客戶端,它提供基於流量、資源利用率以及出錯狀態的加權負載均衡。
在我看來,Eureka的吸引力來源於以下幾點:

開源:大家可以對實現一探究竟,甚至修改源碼。

可靠:經過Netflix多年的生產環境考驗,使用應該比較靠譜省心

功能齊全:不但提供了完整的注冊發現服務,還有Ribbon等可以配合使用的服務。

基於Java:對於Java程序員來說,使用起來,心里比較有底。

spring cloud可以使用Spring Cloud, 與Eureka進行了很好的集成,使用起來非常方便。

創建server-eureka 項目

Maven依賴

<?xml version="1.0" encoding="UTF-8"?>

<modelVersion>4.0.0</modelVersion>

 

<groupId>com.wenlong</groupId>

<artifactId>server-eureka</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>jar</packaging>

 

<name>server-eureka</name>

<description>Demo project for Spring Boot</description>

 

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.5.17.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>Edgware.SR5</spring-cloud.version>

</properties>

 

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-eureka-server</artifactId>

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

</plugins>

</build>

</project>

配置application.yml

server:

port: 8888

eureka:

instance:

hostname: localhost

client:

registerWithEureka: false

fetchRegistry: false

serviceUrl:

defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

啟動EurekaServer

@SpringBootApplication

@EnableEurekaServer

public class App {

    public static void main(String[] args) {

        SpringApplication.run(App.class args);

    }

}

打開eureka server 界面的

http://localhost:8761 ,界面如下:

No application available 沒有服務被發現 ……^_^ 
因為沒有注冊服務當然不可能有服務被發現了。

服務提供者

創建一個服務提供者 會員服務工程 (eurekaMember),提供會員查詢服務信息

創建項目service-member

Maven依賴

<parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.5.17.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>

    </properties>

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-eureka</artifactId>

        </dependency>

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

            <scope>test</scope>

        </dependency>

    </dependencies>

    <dependencyManagement>

        <dependencies>

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>Dalston.RC1</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>

        </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.yml配置

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8888/eureka/

server:

port: 8762

spring:

application:

name: service-member

服務接口

@RestController

public class MemberController {

    @RequestMapping("/getUserList")

    public List<String> getUserList() {

        List<String> listUser = new ArrayList<String>();

        listUser.add("zhangsan");

        listUser.add("lisi");

        listUser.add("yushengjun");

        return listUser;

    }

}

 

發布服務

通過注解@EnableEurekaClient 表明自己是一個eurekaclient.

@SpringBootApplication

@EnableEurekaClient

public class AppMember {

    public static void main(String[] args) {

        SpringApplication.run(AppMember.class args);

    }

}

演示效果

需要指明spring.application.name,這個很重要,這在以后的服務與服務之間相互調用一般都是根據這個name 。 

啟動工程,打開127.0.0.1:8888 ,即eureka server 的網址:

你會發現一個服務已經注冊在服務中了,服務名為SERVICE-MEMBER ,端口為7862

這時打開 http://127.0.0.1:8762/getUserList ,你會在瀏覽器上看到 :

["zhangsan","lisi","yushengjun"]

服務消費者

創建項目sercice-order

Maven依賴

<parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.5.17.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>

    </properties>

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-eureka</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-ribbon</artifactId>

        </dependency>

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

            <scope>test</scope>

        </dependency>

    </dependencies>

    <dependencyManagement>

        <dependencies>

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>Dalston.RC1</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>

        </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.yml配置

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8888/eureka/

server:

port: 8764

spring:

application:

name: service-order

編寫service,調用service-member

@SuppressWarnings("unchecked")

@Service

public class MemberService {

    @Autowired

    RestTemplate restTemplate;

 

    public List<String> getOrderByUserList() {

        return restTemplate.getForObject("http://service-member/getUserList" List.class);

    }    

}

演示效果

@EnableEurekaClient

@SpringBootApplication

public class AppOrder {

    public static void main(String[] args) {

        SpringApplication.run(AppOrder.class args);

    }

}

  • 也可以在啟動類中使用@EnableDiscoveryClient注解替代@EnableEurekaClient。

 

 

 

 

 

 

Ribbon實現負載均衡

什么是ribbon

ribbon是一個負載均衡客戶端 類似nginx反向代理,可以很好的控制htt和tcp的一些行為。

開啟ribbon

啟動兩個會員服務工程,端口號分別為8762、8763,訂單服務 使用負載均衡策略輪訓到會員服務接口。

Service-order項目引入依賴:

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-ribbon</artifactId>

</dependency>

添加配置文件,如下:

* @LoadBalanced注解表明這個restRemplate開啟負載均衡的功能

@Bean

@LoadBalanced

RestTemplate restTemplate(){

return new RestTemplate();

}

 

 

 

 

 

5、服務消費者(Feign使用)

什么是Feign

Feign是一個聲明式的偽Http客戶端,它使得寫Http客戶端變得更簡單。使用Feign,只需要創建一個接口並注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的編碼器和解碼器。Feign默認集成了Ribbon,並和Eureka結合,默認實現了負載均衡的效果。

  • Feign 采用的是基於接口的注解
  • Feign 整合了ribbon
  • 默認實現了負載均衡的效果

創建service-order-feign工程

Maven依賴

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-eureka</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-ribbon</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-feign</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    </dependencies>

application.yml配置

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8888/eureka/

server:

port: 8765

spring:

application:

name: service-order-feign

 

編寫service,調用service-member

@FeignClient("service-member")

public interface MemberFeign {

    @RequestMapping("/getUserList")

    public List<String> getOrderByUserList();

}

@FeignClient 需要調用服務名稱

@RequestMapping服務請求名稱

演示效果

@SpringBootApplication

@EnableEurekaClient

@EnableFeignClients

public class OrderFeignApp {

    public static void main(String[] args) {

        SpringApplication.run(OrderFeignApp.class args);

    }

}

6、路由網關(zuul)

什么是網關

Zuul的主要功能是路由轉發和過濾器。路由功能是微服務的一部分,比如/api/user轉發到到user服務,/api/shop轉發到到shop服務。zuul默認和Ribbon結合實現了負載均衡的功能, 類似於nginx轉發。

創建工程service-zuul

<parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.5.17.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>

    </properties>

 

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-eureka</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-zuul</artifactId>

        </dependency>

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

            <scope>test</scope>

        </dependency>

    </dependencies>

 

    <dependencyManagement>

        <dependencies>

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>Dalston.RC1</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>

        </plugins>

    </build>

application.yml配置

eureka:

client:

serviceUrl:

defaultZone: http://localhost:8888/eureka/

server:

port: 8769

spring:

application:

name: service-zuul

zuul:

routes:

api-a:

path: /api-member/**

service-id: service-member

api-b:

path: /api-order/**

service-id: service-order

發送請求http://127.0.0.1:8769/api-member/getMemberAll

轉發到http://127.0.0.1:8762/getMemberAll

開啟網關: 啟動器加注解@EnableZuulProxy

服務過濾

@Component

public class MyFilter extends ZuulFilter {

 

    private static Logger log = LoggerFactory.getLogger(MyFilter.class);

 

    @Override

    public String filterType() {

        return "pre";

    }

 

    @Override

    public int filterOrder() {

        return 0;

    }

 

    public boolean shouldFilter() {

        return true;

    }

 

    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();

        HttpServletRequest request = ctx.getRequest();

        log.info(String.format("%s >>> %s" request.getMethod() request.getRequestURL().toString()));

        Object accessToken = request.getParameter("token");

        if (accessToken != null) {

            return null;

        }

        log.warn("token is empty");

        ctx.setSendZuulResponse(false);

        ctx.setResponseStatusCode(401);

        try {

            ctx.getResponse().getWriter().write("token is empty");

        } catch (Exception e) {

        }

        return null;

 

    }

}

如果請求參數中沒有傳入token參數 直接返回報錯信息

傳入token參數可以正常訪問請求

 

7、斷路器(Hystrix)

服務雪崩應對策略

針對造成服務雪崩的不同原因, 可以使用不同的應對策略。

  1. 流量控制
  2. 改進緩存模式
  3. 服務自動擴容
  4. 服務調用者降級服務

流量控制 的具體措施包括:

  • 網關限流
  • 用戶交互限流
  • 關閉重試

服務雪崩解決辦法

1、服務雪崩的原因

(1)某幾個機器故障:例如機器的硬驅動引起的錯誤,或者一些特定的機器上出現一些的bug(如,內存中斷或者死鎖)。

(2)服務器負載發生變化:某些時候服務會因為用戶行為造成請求無法及時處理從而導致雪崩,例如阿里的雙十一活動,若沒有提前增加機器預估流量則會造服務器壓力會驟然增大二掛掉。

(3)人為因素:比如代碼中的路徑在某個時候出現bug

2、解決或緩解服務雪崩的方案

(1)熔斷模式:這種模式主要是參考電路熔斷,如果一條線路電壓過高,保險絲會熔斷,防止火災。放到我們的系統中,如果某個目標服務調用慢或者有大量超時,此時,熔斷該服務的調用,對於后續調用請求,不在繼續調用目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復調用。

(2)隔離模式:這種模式就像對系統請求按類型划分成一個個小島的一樣,當某個小島被火少光了,不會影響到其他的小島。例如可以對不同類型的請求使用線程池來資源隔離,每種類型的請求互不影響,如果一種類型的請求線程資源耗盡,則對后續的該類型請求直接返回,不再調用后續資源。這種模式使用場景非常多,例如將一個服務拆開,對於重要的服務使用單獨服務器來部署,再或者公司最近推廣的多中心。

(3)限流模式:上述的熔斷模式和隔離模式都屬於出錯后的容錯處理機制,而限流模式則可以稱為預防模式。限流模式主要是提前對各個類型的請求設置最高的QPS閾值,若高於設置的閾值則對該請求直接返回,不再調用后續資源。這種模式不能解決服務依賴的問題,只能解決系統整體資源分配問題,因為沒有被限流的請求依然有可能造成雪崩效應。

3、熔斷設計

在熔斷的設計主要參考了hystrix的做法。其中最重要的是三個模塊:熔斷請求判斷算法、熔斷恢復機制、熔斷報警

(1)熔斷請求判斷機制算法:使用無鎖循環隊列計數,每個熔斷器默認維護10個bucket,每1秒一個bucket,每個blucket記錄請求的成功、失敗、超時、拒絕的狀態,默認錯誤超過50%且10秒內超過20個請求進行中斷攔截。

(2)熔斷恢復:對於被熔斷的請求,每隔5s允許部分請求通過,若請求都是健康的(RT<250ms)則對請求健康恢復。

(3)熔斷報警:對於熔斷的請求打日志,異常請求超過某些設定則報警

4、隔離設計

隔離的方式一般使用兩種

(1)線程池隔離模式:使用一個線程池來存儲當前的請求,線程池對請求作處理,設置任務返回處理超時時間,堆積的請求堆積入線程池隊列。這種方式需要為每個依賴的服務申請線程池,有一定的資源消耗,好處是可以應對突發流量(流量洪峰來臨時,處理不完可將數據存儲到線程池隊里慢慢處理)

(2)信號量隔離模式:使用一個原子計數器(或信號量)來記錄當前有多少個線程在運行,請求來先判斷計數器的數值,若超過設置的最大線程個數則丟棄改類型的新請求,若不超過則執行計數操作請求來計數器+1,請求返回計數器-1。這種方式是嚴格的控制線程且立即返回模式,無法應對突發流量(流量洪峰來臨時,處理的線程超過數量,其他的請求會直接返回,不繼續去請求依賴的服務)

5、超時機制設計

超時分兩種,一種是請求的等待超時,一種是請求運行超時。

等待超時:在任務入隊列時設置任務入隊列時間,並判斷隊頭的任務入隊列時間是否大於超時時間,超過則丟棄任務。

運行超時:直接可使用線程池提供的get方法

什么是熔斷機制

熔斷機制,就是下游服務出現問題后,為保證整個系統正常運行下去,而提供一種降級服務的機制,通過返回緩存數據或者既定數據,避免出現系統整體雪崩效應。在springcloud中,該功能可通過配置的方式加入到項目中。

為什么需要Hystrix

在微服務架構中,我們將業務拆分成一個個的服務,服務與服務之間可以相互調用(RPC)。為了保證其高可用,單個服務又必須集群部署。由於網絡原因或者自身的原因,服務並不能保證服務的100%可用,如果單個服務出現問題,調用這個服務就會出現網絡延遲,此時若有大量的網絡涌入,會形成任務累計,導致服務癱瘓,甚至導致服務"雪崩"。為了解決這個問題,就出現斷路器模型。

Hystrix 是一個幫助解決分布式系統交互時超時處理和容錯的類庫, 它同樣擁有保護系統的能力。

分布式系統中經常會出現某個基礎服務不可用造成整個系統不可用的情況,這種現象被稱為服務雪崩效應. 為了應對服務雪崩,一種常見的做法是手動服務降級。而Hystrix的出現,給我們提供了另一種選擇。

Hystrix作用

1、斷路器機制

斷路器很好理解, 當Hystrix Command請求后端服務失敗數量超過一定比例(默認50%), 斷路器會切換到開路狀態(Open). 這時所有請求會直接失敗而不會發送到后端服務. 斷路器保持在開路狀態一段時間后(默認5秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(CLOSED), 否則重新切換到開路狀態(OPEN). Hystrix的斷路器就像我們家庭電路中的保險絲, 一旦后端服務不可用, 斷路器會直接切斷請求鏈, 避免發送大量無效請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力.

2、Fallback

Fallback相當於是降級操作. 對於查詢操作, 我們可以實現一個fallback方法, 當請求后端服務出現異常的時候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設置的默認值或者來自緩存.

3、源隔離

在Hystrix中, 主要通過線程池來實現資源隔離. 通常在使用的時候我們會根據調用的遠程服務划分出多個線程池. 例如調用產品服務的Command放入A線程池, 調用賬戶服務的Command放入B線程池. 這樣做的主要優點是運行環境被隔離開了. 這樣就算調用服務的代碼存在bug或者由於其他原因導致自己所在線程池被耗盡時, 不會對系統的其他服務造成影響. 但是帶來的代價就是維護多個線程池會對系統帶來額外的性能開銷. 如果是對性能有嚴格要求而且確信自己調用服務的客戶端代碼不會出問題的話, 可以使用Hystrix的信號模式(Semaphores)來隔離資源.

什么是服務的降級

所有的RPC技術里面服務降級是一個最為重要的話題,所謂的降級指的是當服務的提供方不可使用的時候,程序不會出現異常,而會出現本地的操作調

項目搭建

service-order工程新增Maven依賴

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-hystrix</artifactId>

</dependency>

 

 

 

 

 

 

 

 

RestTemplate方式使用斷路器

Rest請求方式接口改造,發生錯誤執行orderError方法

@HystrixCommand(fallbackMethod = "orderError")

public List<String> getOrderUserAll() {

return restTemplate.getForObject("http://service-member/getMemberAll" List.class);

}

public List<String> orderError() {

    List<String> listUser = new ArrayList<String>();

    listUser.add("not orderUser list");

    return listUser;

}

啟動方式

@EnableEurekaClient

@EnableHystrix

@SpringBootApplication

public class OrderApp {

    public static void main(String[] args) {

        SpringApplication.run(OrderApp.class args);

    }

    @Bean

    @LoadBalanced

    RestTemplate restTemplate() {

        return new RestTemplate();

    }

}

Feign使用斷路器

@FeignClient(value = "service-member", fallback = OrderServiceFeignFallBack.class)

public interface OrderServiceFeign {

@RequestMapping("/list")

List<String> getMembers();

}

// 使用內部類

class OrderServiceFeignFallBack implements OrderServiceFeign {

@Override

public List<String> getMembers() {

List<String> list = new ArrayList();

list.add("Feign fallback");

return list;

}

}

application.yml配置:

# feign設置開啟hystrix

feign:

hystrix:

enabled: true

# 設置hystix超時時間,默認一秒

hystrix:

command:

default:

execution:

isolation:

thread:

timeoutInMilliseconds: 5000

8、分布式配置中心

什么是配置中心

在分布式系統中,由於服務數量巨多,為了方便服務配置文件統一管理,實時更新,所以需要分布式配置中心組件。在Spring Cloud中,有分布式配置中心組件spring cloud config ,它支持配置服務放在配置服務的內存中(即本地),也支持放在遠程Git倉庫中。在spring cloud config 組件中,分兩個角色,一是config-server,二是config-client。

創建git地址,上傳配置文件

文件:

內容:

創建config-server項目

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.5.17.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>

    </properties>

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-config-server</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    <dependencyManagement>

        <dependencies>

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>Camden.SR6</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>

        </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.yml 配置文件

spring:

application:

name: server-config

cloud:

config:

server:

git:

uri: git@github.com:WenLongUP/spring-cloud.git

default-label: master

search-paths: config

server:

port: 8889

啟動config-server項目

@SpringBootApplication

@EnableConfigServer

public class ConfigServerApplication {

    public static void main(String[] args) {

        SpringApplication.run(ConfigServerApplication.class args);

    }

}

 

 

 

 

 

創建config-client項目

    <dependencies>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-config</artifactId>

        </dependency>

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

            <scope>test</scope>

        </dependency>

    </dependencies>

bootstrap.yml

spring:

application:

name: server-config-client

cloud:

config:

uri: http://localhost:8889/

label: master

name: config

profile: dev

server:

port: 8899

 

spring.cloud.config.label 指明遠程倉庫的分支

spring.cloud.config.uri= http://localhost:8888/ 指明配置服務中心的網址。

spring.cloud.config.name 配置文件名

spring.cloud.config.profile

dev開發環境配置文件

test測試環境

pro正式環境

此處的name和profile是和配置文件名相對應的,詳細請參考匹配規則

匹配規則:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

@SpringBootApplication

@RestController

public class ConfigClientApplication {

    public static void main(String[] args) {

        SpringApplication.run(ConfigClientApplication.class args);

    }

    @Value("${name}")

    String name;

    @RequestMapping(value = "/getUserName")

    public String getUserName () {

        return name;

    }

}

 

 源碼地址: https://github.com/wenlongup/spring-cloud

Java學習筆記: https://gitee.com/wenlongup/JavaStudyNotes

Java大數據技術分享群: 684474011


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM