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

一、單點系統架構

傳統項目架構

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

優勢:適合於我的或者小團隊開發,不適合大團隊開發。nginx

分佈式項目架構

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

優勢:程序員

  1. 把模塊拆分,使用接口通訊,下降模塊之間的耦合度。
  2. 把項目拆分紅若干個子項目,不一樣的團隊負責不一樣的子項目。
  3. 增長功能時只須要再增長一個子項目,調用其它系統的接口就能夠。
  4. 能夠靈活的進行分佈式部署。

缺點:github

  1. 系統之間交互須要使用遠程通訊,接口開發增長工做量。
  2. 各個模塊有一些通用的業務邏輯沒法共用。

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

 

什麼是項目集羣

多臺服務器部署相同應用構成一個集羣算法

做用:經過負載均衡設備共同對外提供服務spring

RPC遠程調用

RPC 的全稱是 Remote Procedure Call 是一種進程間通訊方式。
它容許程序調用另外一個地址空間(一般是共享網絡的另外一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即不管是調用本地接口/服務的仍是遠程的接口/服務,本質上編寫的調用代碼基本相同。
好比兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數或者方法,因爲不在一個內存空間,不能直接調用,這時候須要經過就能夠應用RPC框架的實現來解決 數據庫

restful、soap、rpc

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

基於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框架無縫集成。

 

二、面向於服務架構

什麼是SOA

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

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

做用:簡化維護,下降總體風險,伸縮靈活

 

三、微服務架構

什麼是微服務架構

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

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

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

 

SOA架構與微服務架構區別

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

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

 

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

 

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

啓動兩個會員服務工程,端口號分別爲876二、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();

}

 

 

 

 

 

五、服務消費者(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);

    }

}

六、路由網關(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參數能夠正常訪問請求

 

七、斷路器(Hystrix)

服務雪崩應對策略

針對形成服務雪崩的不一樣緣由, 可使用不一樣的應對策略。

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

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

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

服務雪崩解決辦法

一、服務雪崩的緣由

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

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

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

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

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

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

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

三、熔斷設計

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

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

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

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

四、隔離設計

隔離的方式通常使用兩種

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

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

五、超時機制設計

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

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

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

什麼是熔斷機制

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

爲何須要Hystrix

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

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

分佈式系統中常常會出現某個基礎服務不可用形成整個系統不可用的狀況,這種現象被稱爲服務雪崩效應. 爲了應對服務雪崩,一種常見的作法是手動服務降級。而Hystrix的出現,給咱們提供了另外一種選擇。

Hystrix做用

一、斷路器機制

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

二、Fallback

Fallback至關因而降級操做. 對於查詢操做, 咱們能夠實現一個fallback方法, 當請求後端服務出現異常的時候, 可使用fallback方法返回的值. fallback方法的返回值通常是設置的默認值或者來自緩存.

三、源隔離

在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

八、分佈式配置中心

什麼是配置中心

在分佈式系統中,因爲服務數量巨多,爲了方便服務配置文件統一管理,實時更新,因此須要分佈式配置中心組件。在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

相關文章
相關標籤/搜索