Spring Cloud 快速入門

文章參考於 史上最簡單的 SpringCloud 教程 | 終章

Spring Cloud 是一個微服務框架,與 Spring Boot 結合,開發簡單。將一個大工程項目,分紅多個小 web 服務工程,能夠分別獨立擴展,又能夠共同合做。java

環境

  1. spring 官網的 sts 3.9.2,就是有spring 相關插件的eclipse;
  2. apache maven 3.5.4,配置阿里雲鏡像
  3. jdk1.8
  4. Spring Cloud Finchley版本
  5. Spring Boot 2.0.3

Spring Cloud 組件

服務註冊中心、服務、斷路器、配置中心git

服務的註冊與發現 - Eureka

使用 Eureka 來實現服務註冊中心、服務提供者和服務消費者。web

服務註冊中心

一個服務註冊中心,全部的服務都在註冊中心註冊,負載均衡也是經過在註冊中心註冊的服務來使用必定策略來實現。spring

1.新建一個 Spring Boot 工程,用來管理服務,elipse右鍵 -> new -> Spring Starter Project :sql

clipboard.png

點擊 next ,選擇 Cloud Discovery 下的 Eureka Server 組件apache

clipboard.png

生成的 pom 文件:api

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.beigege.cloud</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>eureka-server</name>
    <description>spring cloud學習</description>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </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>

2.使用 @EnableEurekaServer 來講明項目是一個 Eureka:瀏覽器

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

3.修改配置文件,配置文件,能夠是 application.properties 或 application.yml,這裏改後綴,使用 yml,兩種格式內容都是同樣的 只是格式不一樣 application.yml:緩存

server:
  port: 8761 #服務端口

spring:
  application:
    name: eurka-server #服務應用名稱

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否將本身註冊到Eureka Server,默認爲true
    fetchRegistry: false #是否從Eureka Server獲取註冊信息,默認爲true
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #服務註冊的 URL

默認 eureka server 也是一個 eureka client,registerWithEureka: false 和 fetchRegistry: false 來代表項目是一個 Eureka Server。app

4.運行 EurekaServerApplication 類,啓動項目,訪問項目 http://localhost:8761/

clipboard.png

服務提供者

1.新建項目名爲 eureka-client 的 Spring Boot 項目,跟 Eureka Server 新建同樣,只是在選擇 Spring Boot 組件的時候,不選 Eureka Server ,選擇 Eureka Client,這裏爲了測試,再添加一個 Web 下的 Web 組件:

clipboard.png

2.修改配置,application.yml:

server:
  port: 8762
  
spring:
  application:
    name: service-hi #服務之間的調用都是根據這個 name
    
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3.添加 @EnableEurekaClient 註解,並添加一個測試接口 /hi:

@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaClientApplication {

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

    @Value("${server.port}")
    String port;

    @RequestMapping("/hi")
    public String home(@RequestParam(value = "name", defaultValue = "beigege") String name) {
        return "hi " + name + " ,i am from port:" + port;
    }
}

4.啓動 Eureka Server 和 Eureka Client 項目,訪問http://localhost:8761 ,即eureka server 的網址:

clipboard.png

服務消費者 - Ribbon | Feign

Spring Cloud 兩種調用服務的方式,ribbon + restTemplate,和 feign。

Ribbon

ribbon 在客戶端實現了負載均衡。

啓動 Eureka Server 和 Eureka Client,修改 Eureka Client 端口,再啓動一個 Eureka Client 實例,至關於一個小的集羣,訪問localhost:8761

clipboard.png

1.新建一個 spring boot 工程,名稱爲 service-ribbon,建立過程和 eureka-client 同樣,組件多選一個 Cloud Routing 下的 Ribbon,建立完成以後的 pom 文件依賴:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

application.yml 配置:

server:
  port: 8764
  
spring:
  application:
    name: service-ribbon
    
eureka: 
  client: 
    service-url: 
      defaultZone: http://localhost:8761/eureka/

ServiceRibbonApplication 加上 @EnableEurekaClient 註解

2.向 Spring 注入一個 bean: restTemplate,並添加 @LoadBalanced 註解,代表這個 restTemplate 開啓負載均衡功能:

@SpringBootApplication
@EnableEurekaClient
public class ServiceRibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceRibbonApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

3.寫一個接口用來調用以前的 service-hi 服務的 /hi 接口:
新建 service 層,名叫 HelloService,調用 /hi 接口,這裏用服務名 SERVICE-HI 代替具體的 URL:

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    public String hiService(String name) {
        return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
    }

}

新建 controller 層,調用 service 層:

@RestController
public class HelloControler {

    @Autowired
    HelloService helloService;
    @RequestMapping(value = "/hi")
    public String hi(@RequestParam String name){
        return helloService.hiService(name);
    }
}

4.啓動 service-ribbon 工程,屢次訪問該工程的 /hi 接口,即 localhost:8764/hi?name=test:

hi test ,i am from port:8762
hi test ,i am from port:8763

上面交替顯示,說明實現了負載均衡,ribbon 會在客戶端發送請求時作一個負載均衡。

Feign

整合了 Ribbon,具備負載均衡的能力,整合了Hystrix,具備熔斷的能力.

1.建立一個 spring boot工程,建立過程和 eureka-client 同樣,多添加一個組件 Cloud Routing 下的 Feign,項目名叫 service-feign,建立完後的 pom 文件依賴:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

application.yml 配置文件:

server:
  port: 8765

spring:
  application:
    name: service-feign

eureka:
  client:
    serviceUrl: 
      defaultZone: http://localhost:8761/eureka/

SericeFeignApplication 上添加 @EnableEurekaClient 和 @EnableFeignClients 註解:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SericeFeignApplication {

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

2.寫一個 SchedualServiceHi 接口,經過@ FeignClient(「服務名」),來指定調用哪一個服務,@GetMapping("接口名"),來向接口發送 Get 請求,@RequestParam 是請求參數:

@FeignClient(value = "service-hi")
public interface SchedualServiceHi {

    @GetMapping("/hi")
    String sayHiFromClientOne(@RequestParam(value = "name") String name);
    
}

寫一個 controller 層測試 SchedualServiceHi 接口:

@RestController
public class HiController {
    
    @Autowired
    SchedualServiceHi schedualServiceHi;

    @GetMapping(value = "/hi")
    public String sayHi(@RequestParam String name) {
        return schedualServiceHi.sayHiFromClientOne( name );
    }

}

3.啓動 service-feign,訪問localhost:8765/hi?name=test 測試,下面交替顯示,說明實現了負載均衡:

hi test ,i am from port:8762
hi test ,i am from port:8763

斷路器 - Hystrix

鏈式調用服務,其中一個服務宕機,其餘服務可能會跟着異常,發生雪崩,當對一個服務的調用失敗次數到達必定閾值,斷路器會打開,執行服務調用失敗時的處理,避免連鎖故障,上面已經啓動:eureka-server、兩個 eureka-client 實例、service-ribbon 和 service-feign,下面繼續修改,分別在 service-ribbon 和 service-feign 上實現斷路器。

service-ribbon + Hystrix

1.在 pom 文件中加入 Hystrix 依賴:

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

2.在程序的啓動類 ServiceRibbonApplication 加 @EnableHystrix 註解開啓Hystrix:

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class ServiceRibbonApplication {

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

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

3.添加熔斷方法,改造 HelloService 類,在 hiService 方法上面添加 @HystrixCommand 註解,fallbackMethod 是熔斷方法,當服務不可用時會執行,該方法,即 hiError 方法:

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "hiError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
    }

    public String hiError(String name) {
        return "hi,"+name+",sorry,error!";
    }

}

重啓 service-ribbon,訪問 localhost:8764/hi?name=test

hi test ,i am from port:8763

關閉兩個 eureka-client 實例,在訪問 localhost:8764/hi?name=test,瀏覽器顯示:

hi,test,sorry,error!

當服務不可用時,斷路器會迅速執行熔斷方法

service-feign + Hystrix

1.Feign 自帶斷路器,在D版本的Spring Cloud以後,它沒有默認打開。須要在配置文件中配置打開:

feign: 
  hystrix: 
    enabled: true

2.在請求接口指定熔斷實現類,fallback 指定熔斷實現類:

@FeignClient(value ="service-hi",fallback = SchedualServiceHiHystric.class)
public interface SchedualServiceHi {
    
    @GetMapping("/hi")
    String sayHiFromClientOne(@RequestParam(value = "name") String name);

}

SchedualServiceHiHystric 類實現 SchedualServiceHi 接口:

@Component
public class SchedualServiceHiHystric implements SchedualServiceHi{

    @Override
    public String sayHiFromClientOne(String name) {
        return "sorry "+name;
    }

}

3.訪問測試,重啓 service-feign,訪問 localhost:8765/hi?name=test:

sorry test

打開 eureka-client,再次訪問 localhost:8765/hi?name=test:

hi test ,i am from port:8762

證實斷路器起到做用,注意瀏覽器緩存。

路由網關 - Zuul

1.建立一個 spring boot 工程,名稱爲 service-zuul,建立過程和 eureka-client 同樣,組件要多添加一個 Cloud Routing 下的 Zuul,pom 文件的依賴以下:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2.在入口 application 中添加 @EnableZuulProxy 註解,開啓路由:

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ServiceZuulApplication {

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

application.yml 配置:

server:
  port: 8769
 
spring:
  application:
    name: service-zuul
    
eureka:
  client:
    serviceUrl: 
      defaultZone: http://localhost:8761/eureka/

添加路由

修改 application.yml 添加路由配置:

server:
  port: 8769
 
spring:
  application:
    name: service-zuul
    
eureka:
  client:
    serviceUrl: 
      defaultZone: http://localhost:8761/eureka/
      
zuul:
  routes:
    api-a: 
      path: /api-a/**
      service-id: service-ribbon
    api-b:
      path: /api-b/**
      service-id: service-feign

8769 端口的全部 api-a 請求會轉發到 service-ribbon 工程,api-b 到 service-feign

啓動 service-zuul,訪問 localhost:8769/api-a/hi?name=test,瀏覽器顯示:

hi test ,i am from port:8762

訪問 localhost:8769/api-b/hi?name=test,瀏覽器顯示:

hi test ,i am from port:8762

說明路由成功。

過濾器

新建自定義過濾器,繼承 ZuulFilter:

@Component
public class MyFilter extends ZuulFilter {

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

    /*
     * filterType:返回一個字符串表明過濾器的類型,在zuul中定義了四種不一樣生命週期的過濾器類型,具體以下:
     * pre:路由以前
     * routing:路由之時 
     * post: 路由以後 
     * error:發送錯誤調用 
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /*
     * filterOrder:過濾的順序
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /*
     * shouldFilter:這裏能夠寫邏輯判斷,是否要過濾,本文true,永遠過濾。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /*
     * run:過濾器的具體邏輯。能夠很複雜,包括查sql,nosql去判斷該請求到底有沒有權限訪問,
     * 這裏是判斷請求有沒有 token 參數
     */
    @Override
    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) {
            log.warn("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

            return null;
        }
        log.info("ok");
        return null;
    }
}

重啓 service-zuul 訪問 localhost:8769/api-a/hi?name=test,瀏覽器顯示

token is empty

加上 token 參數:localhost:8769/api-a/hi?name=test&token=666

hi test ,i am from port:8762

分佈式配置中心

遠程資源庫

1.這裏在 碼雲 註冊一個帳號,用來建立存儲配置的資源庫,在碼雲上建立一個名叫 SpringCloudConfig 項目:

clipboard.png

2.新建一個配置文件 config-client-dev.properties,也能夠是 yml 配置文件,內容爲 test = version 1,而後提交:

clipboard.png

clipboard.png

2.默認是 master 分支,新建一個分支,名字叫 aa:

clipboard.png

clipboard.png

修改 aa 分支中的 config-client-dev.properties 配置文件的 test 屬性爲 version 2:

clipboard.png

config-server

1.新建 spring boot 項目,組件選擇 Cloud Config 中的 Config Server,pom 中的依賴:

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

在入口程序添加 @EnableConfigServer 註解:

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {

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

application.yml 配置文件,若是是公開的不須要填寫用戶名密碼:

server:
  port: 8888
  
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/candy-boy/SpringCloudConfig  #配置git倉庫地址,後面的.git無關緊要 
          username:  #訪問git倉庫的用戶名,碼雲登錄用戶名
          password:  #訪問git倉庫的用戶密碼,碼雲登錄密碼

2.訪問測試,啓動程序,訪問配置文件,能夠以下訪問:

  • {application}/{profile}[/{label}]

    如 localhost:8888/config-client/dev,即訪問 config-client-dev.properties,其中 {application} 就是 最後一道橫線前面的 config-client,{profile} 是最後一道橫線後面到點,即 dev,{label} 指的是資源庫的分支,不填則爲默認分支,剛建立的資源庫,默認分支是 master,訪問結果以下:

clipboard.png

其餘訪問方式如:

clipboard.png

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

若是是下面這樣,多是用戶名或密碼錯誤:

clipboard.png

3.測試其餘分支,訪問 aa 分支下的 config-client-dev.properties,localhost:8888/config-client/dev/aa

clipboard.png

config-client

待續....

相關文章
相關標籤/搜索