Spring Cloud(2)主要組件應用實例


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進行了很好的集成,使用起來非常方便。

 

實現服務注冊

創建EureKaserver 項目

Maven依賴

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.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>
        <!--eureka server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
        <!-- spring boot test -->
        <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 

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

因為沒有注冊服務當然不可能有服務被發現了。

 

實現案例訂單服務調用會員服務查詢用戶信息

服務提供者

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

創建項目service-member

Maven依賴

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.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");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,端口為8762

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

["zhangsan","lisi"]

 

服務消費者

創建項目sercice-order

Maven依賴

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.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); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }

在工程的啟動類中,通過@EnableDiscoveryClient向服務中心注冊;並且向程序的ioc注入一個bean: restTemplate;並通過@LoadBalanced注解表明這個restRemplate開啟負載均衡的功能。

 

使用ribbon實現負載均衡

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

 

 

什么是ribbon

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

修改會員服務工程代碼區分端口項目

    @Value("${server.port}") private String serverPort; @RequestMapping("/getUserList") public List<String> getUserList() { List<String> listUser = new ArrayList<String>(); listUser.add("zhangsan"); listUser.add("lisi"); listUser.add("端口號:"+serverPort); return listUser; }

開啟ribbon

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

 

服務消費者(Feign)

什么是Feign

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

簡而言之:

  • Feign 采用的是基於接口的注解
  • Feign 整合了ribbon

創建service-order-feign工程

Maven依賴

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.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.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>

    <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: 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轉發。

搭建SpringCloud網關

創建工程service-zuul

Maven依賴

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.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>

    <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: 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參數 直接返回報錯信息

 

 

斷路器(Hystrix)

為什么需要 Hystrix?

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

什么是服務雪崩

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

服務雪崩應對策略

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

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

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

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

 

Hystrix作用

服務降級

什么是服務降級

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

service-order工程新增Maven依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

Rest方式使用斷路器

Rest請求方式接口改造 @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(); } }

@HystrixCommand 作用:服務發生錯誤,回調方法。

@EnableHystrix 啟動斷路器

 

Fegin使用斷路器

改造service-order-feign工程

@FeignClient(value="service-member",fallback=MemberFeignService.class) public interface MemberFeign { @RequestMapping("/getMemberAll") public List<String> getOrderByUserList(); } @Component public class MemberFeignService implements MemberFeign { public List<String> getOrderByUserList() { List<String> listUser = new ArrayList<String>(); listUser.add("not orderUser list"); return listUser; } }

配置文件新增

 

feign: hystrix: enabled: true

 

 

分布式配置中心

什么是配置中心

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

 

 


免責聲明!

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



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