springcloud必知功能使用教程

springcloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分佈式系統基礎設施的開發,如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啓動和部署。Spring Cloud並沒有重複製造輪子,它只是將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過Spring Boot風格進行再封裝屏蔽掉了複雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分佈式系統開發工具包。現在我們一一體驗一下這些組件的功能作用。首先從服務提供者和消費者開始。

Rest項目演練

  • microcloud-api 模塊,作爲公共的信息導入配置模塊;

  • microcloud-provider-product:作爲服務提供者;

  • microcloud-consumer:作爲微服務調用的客戶端使用;

新建一個maven父項目:microcloud

其中pom文件如下

<?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>hdk</groupId>
    <artifactId>springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>microcloudapi</module>
        <module>microcloudproviderproduct</module>
        <module>microcloudconsumer</module>
    </modules>
    <properties>
        <jdk.version>1.8</jdk.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencyManagement>
    <dependencies>
    <dependency> <!-- 進行SpringCloud依賴包的導入處理 -->
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Finchley.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    <dependency> <!-- SpringCloud離不開SpringBoot,所以必須要配置此依賴包 -->
    <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.1.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
    </dependencyManagement>
    <build>
        <finalName>microcloud</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${jdk.version}</source><!-- 源代碼使用的開發版本 -->
                    <target>${jdk.version}</target><!-- 需要生成的目標class文件的編譯版本 -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

microcloud-api

【microcloud-api】模塊,建立一個公共模板,這模塊的主要功能是提供公共處理的工具類,實體,接口等。

pom文件如下:

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-api</artifactId>
    <version>1.0.0</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

由於實體對象不管是服務提供放還是消費者都需要用到,實體對象先創建到api模塊中,創建一個Product實體
package hdk.vo;
import java.io.Serializable;
public class Product implements Serializable {

    private Long productId;
    private String productName;
    private String productDesc;

    public String getProductDesc() {
        return productDesc;
    }
    public void setProductDesc(String productDesc) {
        this.productDesc = productDesc;
    }
    public String getProductName() {
        return productName;
    }
    public void setProductName(String productName) {
        this.productName = productName;
    }
    public Long getProductId() {
        return productId;
    }
    public void setProductId(Long productId) {
        this.productId = productId;
    }
    @Override
    public String toString() {
        return "Product{" +
                "productId=" + productId +
                ", productName='" + productName + '\'' +
                ", productDesc='" + productDesc + '\'' +
                '}';
    }
}

由於實體對象不管是服務提供放還是消費者都需要用到,實體對象先創建到api模塊中,創建一個Product實體

package cn.hdk.vo;
import java.io.Serializable;
public class Product implements Serializable {

    private Long productId;
    private String productName;
    private String productDesc;

    public String getProductDesc() {
        return productDesc;
    }
    public void setProductDesc(String productDesc) {
        this.productDesc = productDesc;
    }
    public String getProductName() {
        return productName;
    }
    public void setProductName(String productName) {
        this.productName = productName;
    }
    public Long getProductId() {
        return productId;
    }
    public void setProductId(Long productId) {
        this.productId = productId;
    }
    @Override
    public String toString() {
        return "Product{" +
                "productId=" + productId +
                ", productName='" + productName + '\'' +
                ", productDesc='" + productDesc + '\'' +
                '}';
    }
}

服務提供方

創建一個Product Rest提供者的項目模塊,這個模塊對應的數據庫腳本如下

CREATE DATABASE springcloud CHARACTER SET UTF8 ;
USE springcloud ;
CREATE TABLE product (
prodcutId BIGINT AUTO_INCREMENT ,
productName VARCHAR(50) ,
 productDesc VARCHAR(50) ,
CONSTRAINT pk_prodcut_id PRIMARY KEY(prodcutId)
) ;

INSERT INTO product(productName,productDesc) VALUES ('電子鎖骨',database()) ;
INSERT INTO product(productName,productDesc) VALUES ('Springboot',database()) ;
INSERT INTO product(productName,productDesc) VALUES ('水錶',database()) ;
INSERT INTO product(productName,productDesc) VALUES ('門禁',database()) ;
INSERT INTO product(productName,productDesc) VALUES ('攝像頭',database()) ;

【microcloud-provider-product】模塊繼續使用mybaits對數據庫進行操作,pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.0.0</version>

    <artifactId>microcloud-provider-product</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-provider-product】創建一個ProductMapper對數據庫的操作接口,這個接口方法特別簡單

package hdk.mapper;
import hdk.vo.Product;
import java.util.List;
public interface ProductMapper {
    boolean create(Product product);
    public Product findById(Long id);
    public List<Product> findAll();
}

【microcloud-provider-product】新增修改application.yml文件,追加對mybatis以及數據庫的支持

server:
 port: 8080
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.cj.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: 111111% # 數據庫連接密碼
logging:
  level:
hdk.mapper: debug

【microcloud-provider-product】創建修改src/main/resources/mapping/ProductMapper.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="hdk.mapper.ProductMapper">
    <select id="findById" resultType="cn.hdk.vo.Product" parameterType="long">
        select productId,productName,productDesc from product WHERE productId=#{id} ;
    </select>
    <select id="findAll" resultType="cn.hdk.vo.Product">
        SELECT productId,productName,productDesc from product;
    </select>
    <insert id="create" parameterType="cn.hdk.vo.Product">
        INSERT INTO product(productName,productDesc) VALUES (#{productName},database()) ;
    </insert>
</mapper>

【microcloud-provider-product】建立IProductService接口,並創建相關實現類

package hdk.service;
import hdk.vo.Product;
import java.util.List;
public interface IProductService {
    Product get(long id);
    boolean add(Product product);
    List<Product> list();
}


package hdk.service.impl;

import hdk.mapper.ProductMapper;
import hdk.service.IProductService;
import hdk.vo.Product;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class ProductServiceImpl implements IProductService {

    @Resource
    private ProductMapper productMapper;

    @Override
    public Product get(long id) {
        return productMapper.findById(id);
    }

    @Override
    public boolean add(Product product) {
        return productMapper.create(product);
    }

    @Override
    public List<Product> list() {
        return productMapper.findAll();
    }
}

【microcloud-provider-product】 定義主程序類,並定義好mapper掃描包

package hdk;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("hdk.mapper")
public class ProductApp{
    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class,args);
    }
}

【microcloud-provider-product】編寫單元測試

package hdk;
import hdk.service.IProductService;
import hdk.vo.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest(classes = ProductApp.class)
@RunWith(SpringRunner.class)
public class ProductServiceTest {
    @Resource
    private IProductService iProductService;
    @Test
    public void testGet() {
        System.out.println(iProductService.get(1));
    }
    @Test
    public void testAdd() {
        Product dept = new Product() ;
        dept.setProductName("lison-" + System.currentTimeMillis());
        System.out.println(iProductService.add(dept));
    }
    @Test
    public void testList() {
        System.out.println(iProductService.list());
    }
}

【microcloud-provider-product】建立ProductController建立一個Rest服務類

package hdk.controller;
import hdk.service.IProductService;
import hdk.vo.Product;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
@RestController
@RequestMapping("/prodcut")
public class ProductController {

    @Resource
    private IProductService iProductService;

    @RequestMapping(value="/get/{id}")
    public Object get(@PathVariable("id") long id) {
        return this.iProductService.get(id) ;
    }
    @RequestMapping(value="/add")
    public Object add(@RequestBody Product product) {
        return this.iProductService.add(product) ;
    }
    @RequestMapping(value="/list")
    public Object list() {
        return this.iProductService.list() ;
    }
}

瀏覽器訪問:

調用get請求:localhost:8080/product/get/1

調用list請求:localhost:8080/product/list

服務消費方

創建一個maven新模塊:【microcloud-consumer】這個模塊作爲服務的消費方,調用前面的product服務

【microcloud-consumer】修改pom文件,pom文件內容如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>microcloud-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-consumer】修改application.yml配置文件

server:

port: 80

【microcloud-consumer】創建Rest配置類,在這需要調用Rest服務,一般需要用到RestTemplate類對象

package hdk.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestConfig {
    @Bean
    public RestTemplate restTemplate() {
        return  new RestTemplate();
    }
}

【microcloud-consumer】新建一個controller,負責使用RestTemplate調用遠程的product服務

package hdk.controller;

import hdk.vo.Product;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {
    public static final String PRODUCT_GET_URL = "http://localhost:8080/prodcut/get/";
    public static final String PRODUCT_LIST_URL="http://localhost:8080/prodcut/list/";
    public static final String PRODUCT_ADD_URL = "http://localhost:8080/prodcut/add/";
    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        Product product = restTemplate.getForObject(PRODUCT_GET_URL + id, Product.class);
        return  product;
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        List<Product> list = restTemplate.getForObject(PRODUCT_LIST_URL, List.class);
        return  list;
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        Boolean result = restTemplate.postForObject(PRODUCT_ADD_URL, product, Boolean.class);
        return  result;
    }
}

【microcloud-consumer】編寫啓動類

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }
}

調用測試

新增:http://localhost/consumer/product/add?productName=lison

列表查詢:http://localhost/consumer/product/list

獲得單個數據:http://localhost/consumer/product/get?id=1

SpringSecurity

前面使用了RestTemplate進行遠程接口調用,但要注意,這些Rest服務最終都可能暴露在公網的,任何人都可能調用,如果你的Rest服務屬於一些私密信息,這樣會導致信息的泄露。

如果想進行安全方面的處理,首先要在服務的提供方上進行處理。

【microcloud-provider-product】修改pom文件,追加 SpringSecurity 相關依賴信息

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

【microcloud-provider-product】修改application.yml配置文件,進行安全的用戶名配置

spring: 
 security:
   user:
     name: admin  # 認證用戶名
     password: hdk  # 認證密碼
     roles:
      - USER # 授權角色

在項目中訪問rest接口,localhost:8080/product/list,這個時候會要求先輸入用戶名以及密碼才能允許訪問

服務消費方處理

【microcloud-consumer】 修改RestConfig配置類,在裏面添加 HttpHeaders 的配置信息

package cn.hdk.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Base64;

@Configuration
public class RestConfig {

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

    @Bean
    public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置
        HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息
        String auth = "admin:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    }
}

【microcloud-consumer】 修改ConsumerProductController,在進行服務端調用的時候加上這個頭信息

package hdk.controller;


import hdk.vo.Product;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {
    public static final String PRODUCT_GET_URL = "http://localhost:8080/prodcut/get/";
    public static final String PRODUCT_LIST_URL="http://localhost:8080/prodcut/list/";
    public static final String PRODUCT_ADD_URL = "http://localhost:8080/prodcut/add/";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private HttpHeaders httpHeaders;

    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        Product product = restTemplate.exchange(PRODUCT_GET_URL + id,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), Product.class).getBody();
        return  product;
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        List<Product> list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), List.class).getBody();
        return  list;
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        Boolean result = restTemplate.exchange(PRODUCT_ADD_URL, HttpMethod.POST,new HttpEntity<Object>(product,httpHeaders), Boolean.class).getBody();
        return  result;
    }
}

調用測試

新增:http://localhost/consumer/product/add?productName=lison

列表查詢:http://localhost/consumer/product/list

獲得單個數據:http://localhost/consumer/product/get?id=1

microcloud-security模塊

現在服務提供方只有一個Product服務,但真實的項目開發中必然有多個服務提供方,絕大多數情況下,這些服務都會用到安全驗證,而且密碼也會一樣,如果每個服務都單獨維護,每次密碼變動改動都會很大,所以應該單獨建立一個安全驗證的模塊

創建一個microcloud-security模塊,修改其pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.0.0</version>

    <artifactId>microcloud-security</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

【springcloud】修改父工程pom文件,把相應的版本依賴加到裏面

<dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-security</artifactId>
            <version>1.0.0</version>
        </dependency>

【microcloud-security】建立一個統一的安全配置類,這個類負責用戶以及密碼相關的配置

package hdk.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(AuthenticationManagerBuilder auth)
            throws Exception {
                auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("root").password(new BCryptPasswordEncoder().encode("hdk")).roles("USER").
                        and().withUser("admin").password(new BCryptPasswordEncoder().encode("hdk")).roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {


        http.httpBasic().and().authorizeRequests().anyRequest()
                .fullyAuthenticated();
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

【microcloud-provider-product】修改pom文件,刪除spring-boot-starter-security的依賴信息,並加入自己定義的microcloud-security依賴

 <!--<dependency>-->
            <!--<groupId>org.springframework.boot</groupId>-->
            <!--<artifactId>spring-boot-starter-security</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-security</artifactId>
        </dependency>

【microcloud-provider-product】修改application.yml,刪除與安全相關的配置項。

# security:
#   user:
#     roles:
#      - USER # 授權角色
#     name:  root
#     password:  hdk

調用測試

新增:http://localhost/consumer/product/add?productName=lison

列表查詢:http://localhost/consumer/product/list

獲得單個數據:http://localhost/consumer/product/get?id=1

Eureka服務註冊與發現

Eureka 服務端

新建一個microcloud-eureka模塊,這模塊做的事情非常簡單,既啓動Eureka的服務端,pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-eureka</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-eureka】修改application.yml文件,在裏面配置eureka相關信息

server:
 port: 7001
eureka:
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱

【microcloud-eureka】新增Eureka啓動類,增加Eureka服務端註解

package hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp.class,args);
    }
}

在瀏覽器上執行

http://localhost:7001/

服務提供方註冊到Eureka

【microcloud-provider-product】修改pom文件,增加eureka客戶端相關信息

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

【microcloud-provider-product】修改application.yml配置文件,在者個文件中定義要註冊的eureka服務的地址

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
      

【microcloud-provider-product】修改啓動類,在這個類上增加eureka客戶端的註解信息

package hdk;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("cn.hdk.mapper")
@EnableEurekaClient
public class ProductApp{
    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class,args);
    }
}

啓動後發現Application的名字是UNKNOWN,爲此應該爲這單獨取一個名字
【microcloud-provider-product】修改application.yml配置文件,爲這個微服務起一個名字

spring:
 application:
   name: microcloud-provider-product

【microcloud-provider-product】修改application.yml配置文件,追加主機名稱的顯示:

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    instance-id: microcloud-provider-product
    

【microcloud-provider-product】修改application.yml配置文件

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    instance-id: microcloud-provider-product
    prefer-ip-address: true #顯示IP
    

【microcloud-provider-product】如果想看狀態信息需要增加actuator模塊,這一塊的內容已經在講springboot的時候講過,修改pom文件,增加

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

【microcloud-provider-product】修改application.yml文件,追加info相關配置

info:
  app.name: microcloud-provider-product
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

注意:由於在yml文件中使用了$,這個時候啓動是會報錯的,因此還需要一個maven-resources-plugin插件的支持

【microcloud】在父工程增加插件,修改pom文件

<build>
        <finalName>microcloud</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <delimiters>
                        <delimiter>$</delimiter>
                    </delimiters>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${jdk.version}</source><!-- 源代碼使用的開發版本 -->
                    <target>${jdk.version}</target><!-- 需要生成的目標class文件的編譯版本 -->
                </configuration>
            </plugin>
        </plugins>
    </build>

啓動後:

另外在關閉【microcloud-provider-product】項目後,刷新eureka發現項目還在,隔一段時間後會發現

這其實就是觸發了安全模式

【microcloud-eureka】設置服務的清理間隔時間,修改application.yml文件

server:
 port: 7001
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱

【microcloud-provider-product】修改application.yml配置

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    instance-id: microcloud-provider-product
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)

由於所有的服務都註冊到了 Eureka 之中

這樣如果配置了“lease-expiration-duration-in-seconds”此選項,

表示距離上一次發送心跳之後等待下一次發送心跳的間隔時間,如果超過了此間隔時間,則認爲該微服務已經宕機了。

【microcloud-provider-product】對於註冊到 Eureka 上的服務,可以通過發現服務來獲取一些服務信息,修改ProductController,增加一個方法

package hdk.controller;


import cn.hdk.service.IProductService;
import cn.hdk.vo.Product;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;

@RestController
@RequestMapping("/prodcut")
public class ProductController {

    @Resource
    private IProductService iProductService;

    @Resource
    private DiscoveryClient client ; // 進行Eureka的發現服務

    @RequestMapping(value="/get/{id}")
    public Object get(@PathVariable("id") long id) {
        return this.iProductService.get(id) ;
    }
    @RequestMapping(value="/add")
    public Object add(@RequestBody Product product) {
        return this.iProductService.add(product) ;
    }
    @RequestMapping(value="/list")
    public Object list() {
        return this.iProductService.list() ;
    }


    @RequestMapping("/discover")
    public Object discover() { // 直接返回發現服務信息
        return this.client ;
    }
}

【microcloud-provider-product】修改ProductApp, 在主程序中啓用發現服務項

package hdk;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("cn.hdk.mapper")
@EnableEurekaClient
@EnableDiscoveryClient
public class ProductApp{
    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class,args);
    }
}

訪問:localhost:8080/prodcut/discover

Eureka 安全機制

一般情況下Eureka 和服務的提供註冊者都會在一個內網環境中,但免不了在某些項目中需要讓其他外網的服務註冊到Eureka,這個時候就有必要讓Eureka增加一套安全認證機制了,讓所有服務提供者通過安全認證後才能註冊進來

【microcloud-eureka】修改pom文件,引入SpringSecurity的依賴包

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

【microcloud-eureka】 修改application.yml文件,增加用戶、密碼驗證

server:
 port: 7001
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@localhost:7001/eureka
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱

spring:
  security:
    user:
      name: admin
      password: hdk

【microcloud-provider-product】修改application.yml文件,增加驗證信息

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@localhost:7001/eureka
  instance:
    instance-id: microcloud-provider-product
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)

【microcloud-eureka】新增配置類EurekaSecurityConfig,重寫configure方法,把csrf劫持關閉

package hdk;


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class EurekaSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        super.configure(http);
    }
}

HA 高可用

現在的Eureka還是單節點的情況,如果Eureka出現了錯誤,將會導致整個集羣無法繼續使用,這個時候就需要考慮Eureka的高可用了。

現在需要3個eureka ,每個eureka都需要配置hostname,所有先修改hosts文件內容如下

127.0.0.1 eureka1

127.0.0.1 eureka2

127.0.0.1 eureka3

【microcloud-eureka】爲了方便操作,講microcloud-eureka項目複製兩份,分別複製爲【microcloud-eureka2】、 【microcloud-eureka2】

【microcloud-eureka】修改application.yml配置文件,修改端口以及註冊位置

server:
 port: 7001
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        #defaultZone: http://admin:hdk@localhost:7001/eureka
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka1 # 定義 Eureka 實例所在的主機名稱

spring:
  security:
    user:
      name: admin
      password: hdk

【microcloud-eureka2】修改application.yml配置文件

server:
 port: 7002
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        #defaultZone: http://admin:hdk@localhost:7001/eureka
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka2 # 定義 Eureka 實例所在的主機名稱

spring:
  security:
    user:
      name: admin
      password: hdk

【microcloud-eureka3】修改application.yml配置文件

server:
 port: 7003
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        #defaultZone: http://admin:hdk@localhost:7001/eureka
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka3 # 定義 Eureka 實例所在的主機名稱

spring:
  security:
    user:
      name: admin
      password: hdk

啓動eureka,eureka2,eureka3,進入服務的後臺查看副本

登陸http://localhost:7001/

【microcloud-provider-product】修改application.yml配置文件,配置多臺enreka的註冊

server:
 port: 8080
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.cj.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: root1234% # 數據庫連接密碼
 application:
   name: microcloud-provider-product
# security:
#   user:
#     roles:
#      - USER # 授權角色
#     name:  root
#     password:  hdk

logging:
  level:
    cn.hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      #defaultZone: http://admin:hdk@localhost:7001/eureka
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance:
    instance-id: microcloud-provider-product
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)

info:
  app.name: microcloud-provider-product
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

打包發佈

在項目中,需要講Eureka發佈到具體服務器上進行執行,打包部署其實和springboot裏面講的大同小異和properties文件稍微有點不同,對於properties文件,不同的環境會有不同的配置文件比如application-dev.properties,application-test.properties,application-pro.properties等
但如果是yml文件,所有的的配置都再同一個yml文件中

【microcloud-eureka】修改application.yml文件

spring:
  profiles:
    active:
      - dev-7001

---
server:
 port: 7001
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka1 # 定義 Eureka 實例所在的主機名稱
spring:
  profiles: dev-7001
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-eureka

---
server:
 port: 7002
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka2 # 定義 Eureka 實例所在的主機名稱
spring:
  profiles: dev-7002
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-eureka2

---
server:
 port: 7003
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance: # eureak實例定義
    hostname: eureka3 # 定義 Eureka 實例所在的主機名稱
spring:
  profiles: dev-7003
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-eureka3

【microcloud-eureka】添加一個打包插件,修改pom文件

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-eureka</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>eureka-server</finalName>
        <plugins>
            <plugin> <!-- 該插件的主要功能是進行項目的打包發佈處理 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration> <!-- 設置程序執行的主類 -->
                    <mainClass>cn.hdk.EurekaApp</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

【microcloud-eureka】 在pom文件所在目錄

mvn clean install package

接下來就可以在項目的編譯目錄發現

eureka-server.jar 文件

採用默認的方式執行 eureka-server.jar那麼此時將運行在 7001 端口上:java -jar eureka-server.jar

運行其它的兩個 profile 配置:

· 運行“dev-7002”profile:java -jar eureka-server.jar --spring.profiles.active=dev-7002;

· 運行“dev-7003”profile:java -jar eureka-server.jar --spring.profiles.active=dev-7003

Ribbon負載均衡

現在服務提供方已經可以通過Eureka進行註冊了,但對於服務的消費者,目前並沒有處理,對於服務的消費方,也應該連接上eureka,進行服務的獲取,這個時候就應該使用Ribbon這個組件了

ribbon對應的pom文件如下

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

Ribbon基本使用

【microcloud-consumer】 修改pom文件,增加eureka的支持

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-consumer】 修改RestConfig配置類,在獲取RestTemplate對象的時候加入Ribbon的配置信息

package cn.hdk.config;


import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Base64;

@Configuration
public class RestConfig {

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

    @Bean
    public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置
        HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息
        String auth = "root:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    }

}

【microcloud-consumer】 修改RestConfig配置類,在獲取RestTemplate對象的時候加入Ribbon的配置信息```

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka

【microcloud-consumer】修改項目啓動類,增加Eureka客戶端的配置註解

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }
}

【microcloud-consumer】 修改ConsumerProductController控制器

現在在eureka中註冊的服務名稱都是大寫字母:

MICROCLOUD-PROVIDER-PRODUCT

package hdk.controller;


import hdk.vo.Product;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

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

    public static final String PRODUCT_GET_URL = "http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/get/";
    public static final String PRODUCT_LIST_URL="http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/list/";
    public static final String PRODUCT_ADD_URL = "http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/add/";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private HttpHeaders httpHeaders;

    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        Product product = restTemplate.exchange(PRODUCT_GET_URL + id,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), Product.class).getBody();
        return  product;
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        List<Product> list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), List.class).getBody();
        return  list;
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        Boolean result = restTemplate.exchange(PRODUCT_ADD_URL, HttpMethod.POST,new HttpEntity<Object>(product,httpHeaders), Boolean.class).getBody();
        return  result;
    }


}

訪問地址:http://localhost/consumer/product/list

這個時候Ribbon與Eureka已經整合成功

Ribbon負載均衡的實現

通過上面的代碼發現我們用到了一個註解@LoadBalanced,根據這名字大概就能知道Ribbon是可以實現負載均衡的

【microcloud-provider-product】 複製兩份

分別爲【microcloud-provider-product2】與【microcloud-provider-product3】

【springcloud數據庫】複製兩份

分別爲【springcloud2數據庫】【springcloud3數據庫】 裏面分別執行spingcloud數據庫的腳本

【microcloud-provider-product2】修改application.yml文件如下

server:
 port: 8081
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.cj.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud2?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: root1234% # 數據庫連接密碼
 application:
   name: microcloud-provider-product
# security:
#   user:
#     roles:
#      - USER # 授權角色
#     name:  root
#     password:  hdk

logging:
  level:
    hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      #defaultZone: http://admin:hdk@localhost:7001/eureka
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance:
    instance-id: microcloud-provider-product2
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)



info:
  app.name: microcloud-provider-product2
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

【microcloud-provider-product3】修改application.yml文件如下

server:
 port: 8082
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.cj.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud3?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: root1234% # 數據庫連接密碼
 application:
   name: microcloud-provider-product
# security:
#   user:
#     roles:
#      - USER # 授權角色
#     name:  root
#     password:  hdk

logging:
  level:
    cn.hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      #defaultZone: http://admin:hdk@localhost:7001/eureka
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance:
    instance-id: microcloud-provider-product3
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)



info:
  app.name: microcloud-provider-product3
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

分別啓動3個服務提供方,訪問

http://localhost:8080/product/get/1

http://localhost:8081/product/get/1

http://localhost:8082/product/get/1

確認3個服務是能正確提供訪問的

【microcloud-consumer】啓動
訪問:http://localhost/consumer/product/list

自定義Ribbon路由

前面已經使用Ribbon實現了路由,通過測試,也不難發現默認Ribbon使用的路由策略是輪詢,可以看下源代碼BaseLoadBalancer

全局路由配置

這種負載均衡的策略其實也是可以由用戶來修改的,如果想要去修改,可以使用自定義的LoadBalance

【microcloud-consumer】 修改RestConfig

package hdk.config;


import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Base64;

@Configuration
public class RestConfig {

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

    @Bean
    public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置
        HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息
        String auth = "root:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    }

    @Bean
    public IRule ribbonRule() { // 其中IRule就是所有規則的標準
        return new com.netflix.loadbalancer.RandomRule(); // 隨機的訪問策略
    }

}

這個時候重啓測試發現,默認的路由規則已經變成了隨機

單獨設置某個Ribbon的路由

有時候,某個消費者可能需要訪問多個多個服務提供方,而希望每個服務提供方提供的路由規則並不相同,這個時候就不能讓Spring掃描到IRULE,需要通過@RibbonClient 來指定服務於配置的關係
【microcloud-consumer】 修改RestConfig,刪除IRULE

package cn.hdk.config;


import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Base64;

@Configuration
public class RestConfig {

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

    @Bean
    public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置
        HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息
        String auth = "root:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    }

//    @Bean
//    public IRule ribbonRule() { // 其中IRule就是所有規則的標準
//        return new com.netflix.loadbalancer.RandomRule(); // 隨機的訪問策略
//    }

}

【microcloud-consumer】新增一個路由規則的配置類,注意這個類不應該放到SpringCloud掃描不到的位置,否則又回變成全局的IRULE,所以這個時候應該單獨使用一個新的包,着個包和啓動並不在同一個包下

package hdk.config;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;

public class RibbonConfig {
    @Bean
    public IRule ribbonRule() { // 其中IRule就是所有規則的標準
        return new com.netflix.loadbalancer.RandomRule(); // 隨機的訪問策略
    }
}

【microcloud-consumer】 修改啓動類,使用@RibbonClient指定配置類

package hdk;
import hdkconfig.RibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name ="MICROCLOUD-PROVIDER-PRODUCT" ,configuration = RibbonConfig.class)
public class ConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }
}

這裏的name 只服務的名稱,如果需要有多個服務提供方,這個時候可以使用@RibbonClients進行配置

服務提供方的信息獲取

在服務的消費方,也是可以獲取到服務提供方的具體信息

【microcloud-consumer】修改ConsumerProductController

package cn.hdk.controller;


import cn.hdk.vo.Product;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

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

    public static final String PRODUCT_GET_URL = "http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/get/";
    public static final String PRODUCT_LIST_URL="http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/list/";
    public static final String PRODUCT_ADD_URL = "http://MICROCLOUD-PROVIDER-PRODUCT/prodcut/add/";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private HttpHeaders httpHeaders;

    @Resource
    private LoadBalancerClient loadBalancerClient;

    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        Product product = restTemplate.exchange(PRODUCT_GET_URL + id,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), Product.class).getBody();
        return  product;
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        ServiceInstance serviceInstance = this.loadBalancerClient.choose("MICROCLOUD-PROVIDER-PRODUCT") ;
        System.out.println(
                "【*** ServiceInstance ***】host = " + serviceInstance.getHost()
                        + "、port = " + serviceInstance.getPort()
                        + "、serviceId = " + serviceInstance.getServiceId());
        List<Product> list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), List.class).getBody();
        return  list;
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        Boolean result = restTemplate.exchange(PRODUCT_ADD_URL, HttpMethod.POST,new HttpEntity<Object>(product,httpHeaders), Boolean.class).getBody();
        return  result;
    }

}

脫離Eureka使用Ribbon

之前所用Ribbon都是從Eureka中獲取服務並通過@LoadBalanced來實現負載均衡的,其實Ribbon也可以脫離Eureka來使用

複製【microcloud-consumer】 成一個新的模塊【microcloud-consumer-ribbon】

【microcloud-consumer-ribbon】 修改pom文件,刪除eureka的依賴添加ribbon的依賴

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-consumer-ribbon</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</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-ribbon</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-consumer-ribbon】 修改application.yml配置文件

server:
 port: 80

ribbon:
eureka:
 enabled: false

MICROCLOUD-PROVIDER-PRODUCT:
ribbon:
  listOfServers: http://localhost:8080,http://localhost:8081,http://localhost:8082

【microcloud-consumer-ribbon】 修改 RestConfig,刪除@LoadBalanced註解

package hdk.config;


import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Base64;

@Configuration
public class RestConfig {

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

    @Bean
    public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置
        HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息
        String auth = "root:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    }


}

【microcloud-consumer-ribbon】修改ConsumerProductController,修改服務的調用URI

package hdk.controller;


import cn.hdk.vo.Product;
import cn.xiangxue.config.RibbonConfig;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;

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

    public static final String PRODUCT_TOPIC = "MICROCLOUD-PROVIDER-PRODUCT";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private HttpHeaders httpHeaders;

    @Resource
    private LoadBalancerClient loadBalancerClient;


    @RequestMapping("/product/list")
    public  Object listProduct() {
        ServiceInstance serviceInstance = this.loadBalancerClient.choose(PRODUCT_TOPIC) ;
        System.out.println(
                "【*** ServiceInstance ***】host = " + serviceInstance.getHost()
                        + "、port = " + serviceInstance.getPort()
                        + "、serviceId = " + serviceInstance.getServiceId());

        URI uri = URI.create(String.format("http://%s:%s/prodcut/list/" ,
                serviceInstance.getHost(), serviceInstance.getPort()));

        List<Product> list = restTemplate.exchange(uri,HttpMethod.GET,new HttpEntity<Object>(httpHeaders), List.class).getBody();
        return  list;
    }
}

​ 【microcloud-consumer-ribbon】啓動

訪問:http://localhost/consumer/product/list

Feign接口服務

前面已經學習了Ribbon,從Eureka獲取服務的實例在通過RestTemplate調用,並轉換成需要的對象

List list = restTemplate.exchange(PRODUCT_LIST_URL,HttpMethod.GET,new HttpEntity(httpHeaders), List.class).getBody();

可以發現所有的數據調用和轉換都是由用戶直接來完成的,我們可能不想直接訪問Rest接口,如果轉換回來的直接是對象而不需要直接使用RestTemplate進行轉換就好了,這個時候就需要使用Feign了

Feign基本使用

複製【microcloud-consumer】 成一個新的模塊【microcloud-consumer-feign】

【microcloud-consumer-feign】修改pom文件,增加對feign的支持

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-consumer-feign</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</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>
    </dependencies>

</project>

【microcloud-service】,新建立一個microcloud-service模塊,這個模塊專門定義客戶端的調用接口

【microcloud-service】,修改pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-service</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-service】如果要通過Feign進行遠程調用,依然需要安全服務提供方的認證問題,不過在feign裏面已經集成了這塊功能

package hdk.feign;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfig {
    @Bean
    public BasicAuthRequestInterceptor getBasicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("admin", "hdk");
    }
}

【microcloud-service】 新建一個IProductClientService接口

package cn.hdk.service;

import cn.hdk.feign.FeignClientConfig;
import cn.hdk.vo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;
@FeignClient(name = "MICROCLOUD-PROVIDER-PRODUCT",configuration = FeignClientConfig.class)
public interface IProductClientService {
    @RequestMapping("/product/get/{id}")
    public Product getProduct(@PathVariable("id")long id);

    @RequestMapping("/product/list")
    public  List<Product> listProduct() ;

    @RequestMapping("/product/add")
    public boolean addPorduct(Product product) ;

}

【microcloud-consumer-feign】 修改pom文件,引入microcloud-service 包

 <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-service</artifactId>
        </dependency>

【microcloud-consumer-feign】 由於microcloud-service裏面已經做了安全驗證,並且後面並不直接使用RestTemplate ,刪除RestConfig.java類

【microcloud-consumer-feign】 修改ConsumerProductController,這個時候直接使用microcloud-service定義的服務就可以了

package hdk.controller;


import hdk.service.IProductClientService;
import cn.hdk.vo.Product;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {
    @Resource
    private IProductClientService iProductClientService;

    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        return  iProductClientService.getProduct(id);
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        return iProductClientService.listProduct();
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        return  iProductClientService.addPorduct(product);
    }
    
}

【microcloud-consumer-feign】修改程序主類

package hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients("hdk.service")
public class ConsumerFeignApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerFeignApp.class,args);
    }
}

啓動測試:

http://localhost/consumer/product/list

可以做個測試,看下是否真的如此

【microcloud-consumer-feign】修改程序主類

package hdk;

import cn.xiangxue.config.RibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients("cn.hdk.service")
@RibbonClient(name ="MICROCLOUD-PROVIDER-PRODUCT" ,configuration = RibbonConfig.class)
public class ConsumerFeignApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerFeignApp.class,args);
    }
}

啓動測試:

http://localhost/consumer/product/list

可以發現,現在的路由規則以及變成了隨機訪問

其他配置

數據壓縮

前面我們已經知道Feign之中最核心的作用就是將Rest服務的信息轉化爲接口,這其中還有其他的一些地方應該要考慮,比如:數據的壓縮
Rest協議更多的傳輸的是文本,JSON或者XML,如果用戶發送的請求很大,這個時候有必要對數據進行壓縮處理,好在feign本身就提供了壓縮的支持
雖然Feign支持壓縮,但默認是不開啓的
再看下FeignClientEncodingProperties,可以根據這裏面的屬性進行相關壓縮的配置

【microcloud-consumer-feign】 修改application.yml配置文件

feign:
 compression:
  request:
    enabled: true
    mime-types: # 可以被壓縮的類型
     - text/xml
     - application/xml
     - application/json
    min-request-size: 2048 # 超過2048的字節進行壓縮

日誌配置

在構建@FeignClient註解修飾的服務客戶端時,會爲一個客戶端都創建一個feign.Logger實例,可以利用日誌來分析Feign的請求細節,不過默認

【microcloud-consumer-feign】 修改 application.yml配置文件,增加日誌信息

logging:
 level:
  cn.hdk.service: DEBUG

【microcloud-service】修改FeignClientConfig,開啓日誌輸出

package hdk.feign;


import feign.Logger;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignClientConfig {

    @Bean
    public Logger.Level getFeignLoggerLevel() {
        return feign.Logger.Level.FULL ;
    }

    @Bean
    public BasicAuthRequestInterceptor getBasicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("admin", "hdk");
    }
}

小結

  • 當使用 Feign 要通過接口的方法訪問 Rest 服務的時候會根據設置的服務類型發出請求,這個請求是發送給 Eureka

  • 隨後由於配置了授權處理,所以繼續發送授權信息(“Authorization”)

其實在外面使用RestTemplate的時候也是這麼做的,可以對應日誌的加密內容和直接訪問其實是一樣的。

  • 在進行服務調用的時候 Feign 融合了 Ribbon 技術,所以也支持有負載均衡的處理

Feign = RestTempate + HttpHeader + Ribbon + Eureka 綜合體,使用feign大大增加了代碼的靈活程度

Hystrix 熔斷機制

在分佈式環境下,微服務之間不可避免的發生互相調用的情況,但是沒有一個系統是能保證自身絕對正確的,在服務的調用過程中,很可能面臨服務失敗的問題,因此需要一個公共組件能夠在服務通過網絡請求訪問其他微服務時,能對服務失效情況下有很強的容錯能力,對微服務提供保護和監控。

Hystrix是netflix的一個開源項目,他能夠在依賴服務失效的情況下,通過隔離系統依賴的方式,防止服務的級聯失敗(服務的雪崩)

對於服務的熔斷機制,其實需要考慮兩種情況

    1. 服務提供方存活,但調用接口報錯
    1. 服務提供方本身就出問題了

服務提供方報錯

其實這種情況類似於異常捕獲機制,當出現異常,返回一個通用的接口報文

【microcloud-provider-product】 複製一份成爲【microcloud-provider-product-hystrix】

【microcloud-provider-product-hystrix】修改pom文件,增加 Hystrix依賴

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

【microcloud-provider-product-hystrix】 修改ProductController

package hdk.controller;

import hdk.service.IProductService;
import hdk.vo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;

@RestController
@RequestMapping("/product")
public class ProductController {

    @Resource
    private IProductService iProductService;

    @Resource
    private DiscoveryClient client ; // 進行Eureka的發現服務

    @RequestMapping(value="/get/{id}")
    @HystrixCommand(fallbackMethod = "getFallback")
    public Object get(@PathVariable("id") long id) {
        Product product = this.iProductService.get(id);
        if(product == null) {
            throw new RuntimeException("該產品已下架!") ;
        }
        return  product;
    }

    public Object  getFallback(@PathVariable("id") long id){
        Product product = new Product();
        product.setProductName("HystrixName");
        product.setProductDesc("HystrixDesc");
        product.setProductId(0L);
        return product;
    }


    @RequestMapping(value="/add")
    public Object add(@RequestBody Product product) {
        return this.iProductService.add(product) ;
    }
    @RequestMapping(value="/list")
    public Object list() {
        return this.iProductService.list() ;
    }


    @RequestMapping("/discover")
    public Object discover() { // 直接返回發現服務信息
        return this.client ;
    }
}

一旦 get()方法上拋出了錯誤的信息,那麼就認爲該服務有問題
會默認使用“@HystrixCommand”註解之中配置好的fallbackMethod 調用類中的指定方法,返回相應數據

【microcloud-provider-product-hystrix】修改啓動類,增加對熔斷的支持

package hdk;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("cn.hdk.mapper")
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ProductHystrixApp {
    public static void main(String[] args) {
        SpringApplication.run(ProductHystrixApp.class,args);
    }
}

測試:localhost:8080/product/get/100 訪問

服務器失連

在某些情況下,服務提供方並沒有失效,但可能由於網絡原因,服務的消費方並不能調用到服務接口,在這種情況下,直接在服務的提供方提供熔斷機制依然還是不夠的,這方面的處理需要在服務的消費方進行服務的回退(服務的降級)處理
服務的熔斷:熔斷指的是當服務的提供方不可使用的時候,程序不會出現異常,而會出現本地的操作調用,服務的熔斷是在服務消費方實現的,在斷網情況下服務提供方的任何處理都是沒有意義的。

【microcloud-service】新增一個IProductClientService的失敗調用(降級處理)

package hdk.service.fallback;


import hdk.service.IProductClientService;
import hdk.vo.Product;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class IProductClientServiceFallbackFactory implements FallbackFactory<IProductClientService> {
    @Override
    public IProductClientService create(Throwable throwable) {
        return  new IProductClientService() {
            @Override
            public Product getProduct(long id) {
                Product product = new Product();
                product.setProductId(999999L);
                product.setProductName("feign-hystrixName");
                product.setProductDesc("feign-hystrixDesc");
                return  product;
            }

            @Override
            public List<Product> listProduct() {
                return null;
            }

            @Override
            public boolean addPorduct(Product product) {
                return false;
            }
        };
    }
}

【microcloud-service】 修改IProductClientService,增加fallback配置

package hdk.service;


import hdk.feign.FeignClientConfig;
import hdk.service.fallback.IProductClientServiceFallbackFactory;
import cn.hdk.vo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "MICROCLOUD-PROVIDER-PRODUCT",configuration = FeignClientConfig.class,
fallbackFactory = IProductClientServiceFallbackFactory.class)
public interface IProductClientService {
    @RequestMapping("/product/get/{id}")
    public Product getProduct(@PathVariable("id")long id);

    @RequestMapping("/product/list")
    public  List<Product> listProduct() ;

    @RequestMapping("/product/add")
    public boolean addPorduct(Product product) ;

}

【microcloud-consumer-feign】 複製一份成爲【microcloud-consumer-hystrix】模塊

【microcloud-consumer-hystrix】 修改application.yml配置文件,啓用hystrix配置

feign:
 hystrix:
    enabled: true
 compression:
  request:
    enabled: true
    mime-types: # 可以被壓縮的類型
     - text/xml
     - application/xml
     - application/json
    min-request-size: 2048 # 超過2048的字節進行壓縮

啓動,服務提供者

訪問:http://localhost/consumer/product/get?id=1,能正常訪問

關閉,服務提供者

訪問:http://localhost/consumer/product/get?id=1,也能正常訪問

HystrixDashboard

在hystrix裏面提供一個Dashboard(儀表盤)的功能,他是一種監控的功能,可以利用它來進行整體服務的監控

新建一個模塊【microcloud-consumer-hystrix-dashboard】
【microcloud-consumer-hystrix-dashboard】pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-consumer-hystrix-dashboard</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

    </dependencies>
</project>

【microcloud-provider-product-hystrix】 pom文件確保裏面有健康檢查模塊

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

【microcloud-consumer-hystrix-dashboard】 修改application.yml配置文件

server:
  port: 9001
  

【microcloud-consumer-hystrix-dashboard】 創建一個啓動類

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApp {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApp.class,args);
    }
}

啓動運行:http://localhost:9001/hystrix

【microcloud-provider-product-hystrix】 修改applcation.yml文件

management:
  endpoints:
    web:
      exposure:
        include: '*'

【microcloud-provider-product-hystrix】啓動
訪問:localhost:8080/actuator/hystrix.stream

http://localhost:9001/hystrix 填寫信息如下
http://admin:hdk@localhost:8080/actuator/hystrix.stream

這個時候對localhost:8080的訪問都可以被監控到

Turbine

HystrixDashboard 前面已經知道了,它的主要功能是可以對某一項微服務進行監控,但真實情況下,不可能只對某一個服務進行監控,更多的是對很多服務進行一個整體的監控,這個時候就需要使用到turbine來完成了。

爲了演示監控多個服務模塊,這個時候新建一個模塊【microcloud-provider-user-hystrix】,爲簡單起見,這個模塊並不連接數據庫,也不做安全控制。

【microcloud-provider-user-hystrix】pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-provider-user-hystrix</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</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-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-api】新增一個VO類:Users

package cn.hdk.vo;
import java.io.Serializable;

public class Users implements Serializable {
    private String name;
    private int age;
    private String sex;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}

【microcloud-provider-user-hystrix】 新建一個UserController


package hdk.controller;

import hdk.vo.Users;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class UserController {
    @RequestMapping("/get/{name}")
    @HystrixCommand
    public  Object get(@PathVariable("name")String name) {
        Users users = new Users();
        users.setName(name);
        users.setAge(18);
        users.setSex("F");
        return users;
    }
}

【microcloud-provider-user-hystrix】新增啓動類

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaClient
public class UsersApp {
    public static void main(String[] args) {
        SpringApplication.run(UsersApp.class,args);
    }
}

【microcloud-provider-user-hystrix】修改application.yml配置文件

server:
 port: 8090

spring:
 application:
   name: microcloud-provider-users


logging:
  level:
    hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance:
    instance-id: microcloud-provider-users
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)



info:
  app.name: microcloud-provider-users
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

management:
  endpoints:
    web:
      exposure:
        include: '*'
        

啓動後:
訪問地址:http://localhost:8090/users/get/hdk
hystrix監控地址:http://localhost:8090/actuator/hystrix.stream

前面準備工作完成後,如果想要實現 turbine 的配置,準備一個turbine模塊

新增【microcloud-consumer-turbine】模塊,pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-consumer-turbine</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-consumer-turbine】修改application.yml配置文件

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableTurbine
public class TurbineApp {
    public static void main(String[] args) {
        SpringApplication.run(TurbineApp.class,args);
    }
}

turbine監控地址:

啓動Dashboard: http://localhost:9001/hystrix
在Dashboard裏面填上 turbine監控地址

這時還會報錯。

【microcloud-security】如果現在需要turbine進行加密服務的訪問,那麼只能折衷處理,讓訪問/actuator/hystrix.stream與/turbine.stream這兩個地址的時候不需要用戶密碼驗證

【microcloud-security】 修改WebSecurityConfiguration

package cn.hdk.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(AuthenticationManagerBuilder auth)
            throws Exception {
                auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("root").password(new BCryptPasswordEncoder().encode("hdk")).roles("USER").
                        and().withUser("admin").password(new BCryptPasswordEncoder().encode("hdk")).roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().authorizeRequests().anyRequest()
                .fullyAuthenticated();
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/actuator/hystrix.stream","/turbine.stream") ;
    }
}

​ turbine監控地址:http://localhost:9101/turbine.stream

啓動Dashboard: http://localhost:9001/hystrix

在Dashboard裏面填上 turbine監控地址

刷新:

http://localhost:8080/prodcut/get/1

http://localhost:8090/users/get/1

Zuul路由

前面所有的微服務都是通過Eureka找到的,但是在很多開發中爲了規範微服務的使用,提供有一個處理控制器Zuul

Zuul其實是一個API網關,類似於設計模式裏面的Facade門面模式,他的存在就像是整個微服務的門面,所有的外部客戶端訪問都需要經過它來進行調度與過濾

基本使用

新建立一個模塊【microcloud-zuul-gateway】
【microcloud-zuul-gateway】 的pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-zuul-gateway</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</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-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-zuul-gateway】修改application.yml文件

server:
  port: 9501

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
    register-with-eureka: false

spring:
  application:
    name:  microcloud-zuul-gateway

【microcloud-zuul-gateway】 創建啓動類

package hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.web.bind.annotation.RequestMapping;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class,args);
    }
}

Zuul配置路由

前面以及簡單的使用了zuul,但你會發現訪問地址還必須知道程序的名稱,如果不知道這個名稱是無法訪問的,但如果讓用戶知道了這名稱,那麼使用zuul就是去它的實際意義的,我們可以通過名稱直接調用

既然是使用代理,那麼代理的功能就是不能讓用戶看到真實的操作,屏蔽真實的調用地址,這個時候就需要自己增加zuul的路由規則配置了。

【microcloud-zuul-gateway】修改application.yml配置文件,增加路由配置

zuul:
  routes:
    microcloud-provider-users:  /users-proxy/**

這個時候就可以通過/users-proxy 來訪問microcloud-provider-users服務

http://localhost:9501/users-proxy/users/get/1

但是還會發現,雖然現在以及開啓了路由訪問的支持,但依然通過應用程序的名稱還是能訪問
http://localhost:9501/microcloud-provider-users/users/get/1

【microcloud-zuul-gateway】修改application.yml文件,忽略掉用戶服務的名稱

zuul:
  routes:
    microcloud-provider-users:  /users-proxy/**
  ignored-services:
    microcloud-provider-users
    

做完後,就可以進行代理的安全使用,但真實情況下,一般會有很多微服務,如果完全按照上面的配置方式會非常的麻煩,所有最加到的做法是可以採用一個通配符“*”的模式來統一完成。

【microcloud-zuul-gateway】修改application.yml文件

zuul:
  routes:
    microcloud-provider-users:  /users-proxy/**
  ignored-services:
    "*"

除開上面這一種訪問模式以外,在zuul中還有另外一種配置方式

【microcloud-zuul-gateway】修改application.yml文件

zuul:
  routes:
    users.path: /users-proxy/**
    users.serviceId: microcloud-provider-users
  ignored-services:
    "*"

其中在配置文件中出現的users其實是一個邏輯名稱,這個名稱主要作用是將path與serviceId綁定在一起

【microcloud-zuul-gateway】如果說不想通過eureka進行訪問,對於zuul來說也是可以實現的,但是在真實的開發環境中,基本不會使用

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: microcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
  ignored-services:
    "*"

訪問:http://localhost:9501/users2-proxy/users/get/1

【microcloud-zuul-gateway】 設置公共前綴

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: microcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
  ignored-services:
    "*"
  prefix: /hdk-api

一旦設置了公共前綴,所以的訪問路徑都要在前面加上前綴

http://localhost:9501/hdk-api/users-proxy/users/get/1

http://localhost:9501/hdk-api/users2-proxy/users/get/1

zuul 過濾訪問

其實zuul的功能本質上就是一個代理操作,類似於nginx,但是在真實的使用中,所有的微服務一點都有增加的認證信息,那麼就必須在其訪問之前追加認證的頭部操作,這樣的功能需要通過zuul的過去操作完成。

【microcloud-zuul-gateway】 修改application.yml配置,增加產品微服務

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: microcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
    product:
      path: /product-proxy/**
      serviceId: microcloud-provider-product
  ignored-services:
    "*"
  prefix: /hdk-api
  

這樣直接訪問:http://localhost:9501/hdk-api/product-proxy/prodcut/get/1
這樣是訪問不到的

【microcloud-zuul-gateway】追加過濾處理


package hdk.filter;


import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import java.nio.charset.Charset;
import java.util.Base64;

public class AuthorizedRequestFilter extends ZuulFilter{
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext() ; // 獲取當前請求的上下文
        String auth = "admin:hdk"; // 認證的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理
        String authHeader = "Basic " + new String(encodedAuth);
        currentContext.addZuulRequestHeader("Authorization", authHeader);
        return null;
    }
}

其中filterType爲過濾的類型
在進行Zuul過濾的時候可以設置其過濾執行的位置,那麼此時有如下幾種類型:
pre:在請求發出之前執行過濾,如果要進行訪問,肯定在請求前設置頭信息
route:在進行路由請求的時候被調用;
post:在路由之後發送請求信息的時候被調用;
error:出現錯誤之後進行調用

【microcloud-zuul-gateway】建立一個配置程序類

package hdk.config;

import cn.hdk.filter.AuthorizedRequestFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class ZuulConfig {
    @Bean
    public AuthorizedRequestFilter getAuthorizedRequestFilter() {
        return new AuthorizedRequestFilter() ;
    }
}

這個時候訪問:
http://localhost:9501/hdk-api/product-proxy/prodcut/get/1
http://localhost:9501/hdk-api/users-proxy/users/get/1

這兩個服務都能正常訪問了。

Zuul安全訪問

作爲所有接口的統一門面,zuul也是可以進行加密訪問的
【microcloud-zuul-gateway】修改pom文件,增加安全訪問模塊

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

【microcloud-zuul-gateway】修改application.yml配置文件,增加用戶配置

spring:
  application:
    name:  microcloud-zuul-gateway
  security:
    user:
      name: admin
      password: hdk
      

再訪問http://localhost:9501/hdk-api/users-proxy/users/get/1
這個時候就需要輸入用戶名密碼了

Feign訪問Zuul

前面學習feign的時候確實已經知道,他其實是去eureka中獲取服務地址的,如果想使用feign來訪問zuul,首先就應該讓zuul註冊到eureka中

【microcloud-zuul-gateway】 修改application.yml文件

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@eureka1:7001/eureka,http://admin:hdk@eureka2:7002/eureka,http://admin:hdk@eureka3:7003/eureka
  instance:
    instance-id: microcloud-zuul-gateway
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
    

【microcloud-service】現在所有的服務要通過zuul的代理進行訪問,新增接口

package .hdk.service;

import hdk.feign.FeignClientConfig;
import hdk.service.fallback.IProductClientServiceFallbackFactory;
import hdk.service.fallback.IZUUlClientServiceallbackFactory;
import hdk.vo.Product;
import hdk.vo.Users;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "MICROCLOUD-ZUUL-GATEWAY",configuration = FeignClientConfig.class,
        fallbackFactory = IZUUlClientServiceallbackFactory.class)
public interface IZUUlClientService {

    @RequestMapping("/hdk-api/product-proxy/product/get/{id}")
    public Product getProduct(@PathVariable("id")long id);

    @RequestMapping("/hdk-api/product-proxy/product/list")
    public List<Product> listProduct() ;

    @RequestMapping("/hdk-api/product-proxy/product/add")
    public boolean addPorduct(Product product) ;

    @RequestMapping("/hdk-api/users-proxy/users/get/{name}")
    public Users getUsers(@PathVariable("name")String name);
}

新增IZUUlClientServiceallbackFactory,在Zuul由於出現網絡問題失去聯繫後進行容錯處理

package.hdk.service.fallback;


import hdk.service.IProductClientService;
import hdk.service.IZUUlClientService;
import hdk.vo.Product;
import hdk.vo.Users;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class IZUUlClientServiceallbackFactory implements FallbackFactory<IZUUlClientService> {
    @Override
    public IZUUlClientService create(Throwable throwable) {
        return  new IZUUlClientService() {
            @Override
            public Product getProduct(long id) {
                Product product = new Product();
                product.setProductId(999999L);
                product.setProductName("feign-zuulName");
                product.setProductDesc("feign-zuulDesc");
                return  product;
            }

            @Override
            public List<Product> listProduct() {
                return null;
            }

            @Override
            public boolean addPorduct(Product product) {
                return false;
            }

            @Override
            public Users getUsers(String name) {
                Users user = new Users();
                user.setSex("F");
                user.setAge(17);
                user.setName("zuul-fllback:"+name);
                return user;
            }
        };
    }
}

【microcloud-consumer-hystrix】 修改ConsumerProductController,增加一個新的方法,訪問接口

package hdk.controller;


import hdk.service.IProductClientService;
import hdk.service.IZUUlClientService;
import hdk.vo.Product;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

    @Resource
    private IProductClientService iProductClientService;

    @Resource
    private IZUUlClientService izuUlClientService;


    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        return  iProductClientService.getProduct(id);
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        return iProductClientService.listProduct();
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        return  iProductClientService.addPorduct(product);
    }

    @RequestMapping("/product/getProductAndUser")
    public Object getProductAndUser(long id) {
        Map<String,Object> result = new HashMap();
        result.put("product",izuUlClientService.getProduct(id));
        result.put("user",izuUlClientService.getUsers(id+""));
        return  result;
    }
}

依次啓動eureka,user服務,product服務,zuul服務,customerhystrix服務

在地址欄輸入:

http://localhost/consumer/product/getProductAndUser?id=1

關閉zuul服務

http://localhost/consumer/product/getProductAndUser?id=1

發現服務降級已經開啓

Zuul熔斷

zuul是一個代理服務,但如果被代理的服務突然斷了,這個時候zuul上面會有出錯信息,例如,停止product服務

現在服務的調用方已經做了處理,不會出現這樣的錯誤信息,但一般來說,對於zuul本身代理方,也應該進行zuul的降級處理

修改【microcloud-zuul-gateway】建立fallback回退處理類

package hdk.fallback;


import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ProviderFallback implements   FallbackProvider  {
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders() ;
                headers.set("Content-Type", "text/html; charset=UTF-8");
                return headers;
            }

            @Override
            public InputStream getBody() throws IOException {
                // 響應體
                return new ByteArrayInputStream("產品微服務不可用,請稍後再試。".getBytes());
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.BAD_REQUEST.getReasonPhrase();
            }

            @Override
            public void close() {

            }
        };
    }
}

訪問:http://localhost:9501/hdk-api/product-proxy/prodcut/get/1

getRoute:方法可以返回服務的ID,比如‘microcloud-provider-product’,如果需要匹配全部適應 “*”

SpringCloudConfig分佈式配置中心

雖然springcloud使用springboot進行開發,節省了大量的配置文件,但每個服務依然有自己的application.yml配置文件,而且每個服務一般都有負載均衡,所以,這麼依賴對於配置文件的統一管理就非常有必要了

左邊這一塊我們很熟悉,最開始有個eureka,它通過配置文件application.yml啓動,在這個配置文件裏面會指定端口,實例名,註冊地址等

對於服務提供商來說,它也需要把相關信息寫到application.yml文件中,比如數據庫配置,端口,項目名稱等,其中最重要的就就是要指定eureka的具體位置

這是前面反覆說過的,但現在是基於cloudConfig的配置中心,最開始啓動eureka的時候,eureka的具體配置就不是寫死在eureka的application.yml文件中了,這個時候也會有application.yml(bootstrap.yml)配置文件,只是這裏的配置指定的時候config的配置中心,在eureka啓動的時候讀取【配置中心】的配置,並啓動

對於服務提供商也是同樣的道理,以產品服務爲例,它的application.yml文件也不在指定具體的配置,真實需要訪問的數據庫,端口等信息也是在啓動的時候直接從【配置中心】讀取。

所以說config配置中心在其中佔據非常重要的位置,但config裏面的配置從哪來呢?其實是從git服務器裏面來的,開發者需要把相關的配置上傳到git服務器,這裏的git服務器可以自己搭建,也可以直接用github,後面項目爲了方便就直接使用github了。

配置中心搭建

準備好git服務器之後,接下來就要準備配置中心了

【microcloud-config】 新建一個配置中心的服務提供模塊,pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-config</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-security</artifactId>
        </dependency>
    </dependencies>

</project>

引入springcloudserver的同時,這分佈式配置中心也不是誰都能訪問的,所以增加了安全驗證模塊。
應該還記得這時候的用戶名密碼爲:admin/hdk

【microcloud-config】 新增application.yml配置文件,增加git連接配置信息

server:
  port: 7101

spring:
  application:
    name: microcloud-config
  cloud:
    config:
      server:
        git:
          uri: https://github.com/huangdongkui/microconfig.git
          

【microcloud-config】 新增啓動類

package cn.hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigApp {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApp.class,args);
    }
}

這些工作準備完成後,爲了測試【microcloud-config】是能正確運行的,還需要上傳個配置文件到github
【GITHUB】 上傳一個application.yml

spring:
   profiles:
      active:
      - dev
---
spring:
  profiles: dev
  application:
      name: microconfig-test-dev
---
spring:
 profiles: default
 application:
      name: microconfig-test-default
      

準備好配置文件後啓動【microcloud-config】

NO 訪問形式 訪問路徑
1 /{application}-{profile}.yml http://localhost:7101/application-dev.ymlhttp://localhost:7101/application-default.ymlhttp://localhost:7101/application-beta.yml 不存在
2 /{application}/{profile}[/{label}] http://localhost:7101/application/dev/masterhttp://localhost:7101/application/default/master
3 /{label}/{application}-{profile}.yml http://localhost:7101/master/application-default.ymlhttp://localhost:7101/master/application-dev.yml

簡單的客戶端

現在已經成功的搭建好了配置中心,但這個時候如果只通過url的訪問形式其實沒什麼太多的意義,最終還是需要把github相關信息加載到客戶端上進行訪問

新增加一個【microcloud-config-client】模塊,這模塊講讀取github裏面的信息,也不做其他的事情,只是顯示一下。

新建【microcloud-config-client】,pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-config-client</artifactId>
    <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>
    </dependencies>
</project>

【github】上傳一個新的配置文件microcloud-config-client.yml,後面就通過程序來讀取這個配置

​```
spring:
   profiles:
      active:
        - dev
---
server:
  port: 8201

spring:
  profiles: dev
  application:
      name: microconfig-test-client

eureka:
 client:
  service-url:
    defaultZone: http://admin:hdk@localhost:7001/eureka
---
server:
  port: 8102

spring:
  profiles: beta
  application:
      name: microconfig-test-client

eureka:
 client:
  service-url:
    defaultZone: http://admin:hdk@localhost:7001/eureka

【microcloud-config-client】新建bootstrap.yml文件,這文件讀取配置中心的配置

spring:
 cloud:
  config:
    name: microcloud-config-client # 定義要讀取的資源文件的名稱
    profile: dev # 定義profile的 名稱
    label: master # 定義配置文件所在的分支
    uri: http://localhost:7101 # SpringCloudConfig的服務地址
    username: admin # 連接的用戶名
    password: hdk # 連接的密碼

可能有些人奇怪,爲什麼不直接把相關信息寫道application.yml文件之中,其實這是一種規範

  • “application.yml”:對應的是用戶級的資源配置項;

  • “bootstrap.yml”:對應的是系統級的資源配置,其優先級更高

【microcloud-config-client】新建application.yml文件,這文件只是簡單的配置一個應用名稱

spring:
 application:
  name: microcloud-config-client # 編寫應用的名稱

【microcloud-config-client】新建一個controller,這個controller顯示從服務器下載到的配置文件

package hdk.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.serviceUrl.defaultZone}")
    private String eurekaServers; 
    @RequestMapping("/config")
    public String getConfig() {
        return "ApplicationName = " + this.applicationName + "、EurekaServers = "
                + this.eurekaServers;
    }
}

【microcloud-config-client】 新建一個啓動類

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

啓動配置中心,訪問,確認配置通過url是能正常訪問的。

http://localhost:7101/microcloud-config-client-beta.yml

啓動【microcloud-config-client】發現tomcat啓動了,佔用的端口就是dev的8201,訪問

http://localhost:8201/config

這個時候簡單的客戶端已經搭建完成。

Eureka與服務提供商讀取配置

有了上面這些基礎,接下來就可以完成這個圖的功能了,這裏依然簡化一下,只考慮product產品服務與eureka

eureka與product服務的配置信息要求去配置中心獲取,所以在正式部署項目之前,先準備兩個配置,上傳到github之中

microcloud-config-eureka-client.yml,這個是eureka的配置文件,這裏就沒有考慮eureka的高可用了

spring:
  profiles:
    active:
      - dev
---
server:
 port: 7001
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@localhost:7001/eureka
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱
spring:
  profiles: dev
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-config-eureka-client
---
server:
 port: 7002
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@localhost:7002/eureka
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱
spring:
  profiles: beta
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-config-eureka-client

microcloud-config-product-client.yml,這個事對於產品服務這個服務提供商提供者的配置文件

spring:
  profiles:
    active:
      - dev
---
server:
 port: 8080
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: root1234% # 數據庫連接密碼
 application:
   name: microcloud-config-product-client
 profiles: dev

logging:
  level:
    cn.hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@localhost:7001/eureka
  instance:
    instance-id: microcloud-config-product-client
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)

info:
  app.name: microcloud-provider-product
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

management:
  endpoints:
    web:
      exposure:
        include: '*'
        
        
---
server:
 port: 8081
mybatis:
 mapper-locations: # 所有的mapper映射文件
 - classpath:mapping/*.xml
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
   driver-class-name: com.mysql.jdbc.Driver # 配置MySQL的驅動程序類
   url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8 # 數據庫連接地址
   username: root # 數據庫用戶名
   password: root1234% # 數據庫連接密碼
 application:
   name: microcloud-config-product-client
 profiles: beta

logging:
  level:
    cn.hdk.mapper: debug

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://admin:hdk@localhost:7002/eureka
  instance:
    instance-id: microcloud-config-product-client
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5  # 如果現在超過了5秒的間隔(默認是90秒)

info:
  app.name: microcloud-config-product-client
  company.name: hdk
  build.artifactId: $project.artifactId$
  build.modelVersion: $project.modelVersion$

management:
  endpoints:
    web:
      exposure:
        include: '*'

有了這兩個配置文件,接下來就可以搭建eureka服務和product服務了

eureka配置

複製【microcloud-eureka】一份,修改成爲【microcloud-config-eureka-client】,

【microcloud-config-eureka-client】 修改pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-config-eureka-client</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </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-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-config-eureka-client】創建bootstrap.yml文件,讀取配置中心eureka的配置

spring:
  cloud:
     config:
       uri:  http://localhost:7101
       name: microcloud-config-eureka-client
       profile:  beta
       label:  master
       username: admin
       password: hdk

【microcloud-config-eureka-client】 修改application.yml,刪除不需要的配置
spring:
  application:
    name: microcloud-config-eureka-client
    

由於使用的是beta,它裏面指定的eureka的端口是7002
重啓後訪問:localhost:7002

product服務配置

複製【microcloud-provider-product】項目爲【microcloud-config-product-client】

【microcloud-config-product-client】pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.0.0</version>


    <artifactId>microcloud-config-product-client</artifactId>
    <dependencies>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <!--<dependency>-->
            <!--<groupId>org.springframework.boot</groupId>-->
            <!--<artifactId>spring-boot-starter-security</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-security</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-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

    </dependencies>

</project>

【microcloud-config-product-client】新增bootstrap.yml 文件,配置如下

spring:
  cloud:
    config:
      uri:  http://localhost:7101
      name: microcloud-config-product-client
      profile:  beta
      label:  master
      username: admin
      password: hdk
      

【microcloud-config-product-client】 修改application.yml文件

spring:
 application:
    name: microcloud-config-product-client
    

啓動product服務,訪問eureka,現在產品服務已經添加上去了。

Config配置中心高可用

現在不管是erueka還是服務提供者都是基於SpringCloudConfig獲取配置文件的,這個時候配置中心就至關重要了,但在真實的項目環境中,難免SpringCloudConfig會出現各種問題,這個時候就需要考慮config的高可用機制了。

其實解決方式也很簡單,把SpringCloudConfig註冊到Eureka就搞定了,這個時候用戶訪問的時候不是直接從配置中心獲取配置,而是通過eureka中獲取配置中心的地址,再從配置中心獲取具體服務的參數就行。

複製【microcloud-eureka】一份,修改成爲【microcloud-ha-config-eureka】,這個eureka不註冊具體的業務服務,只是負責config配置中心的負載均衡使用

【microcloud-ha-config-eureka】pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>microcloud-ha-config-eureka</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </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-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>
    
</project>

【microcloud-ha-config-eureka】 修改application.yml文件

server:
 port: 7301
eureka:
  server:
    eviction-interval-timer-in-ms: 1000   #設置清理的間隔時間,而後這個時間使用的是毫秒單位(默認是60秒)
    enable-self-preservation: false #設置爲false表示關閉保護模式
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
        defaultZone: http://admin:hdk@localhost:7301/eureka
  instance: # eureak實例定義
    hostname: localhost # 定義 Eureka 實例所在的主機名稱
spring:
  security:
    user:
      name: admin
      password: hdk
  application:
    name: microcloud-ha-config-eureka

啓動類如下

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class HaConfigEurekaApp {
    public static void main(String[] args) {
        SpringApplication.run(HaConfigEurekaApp.class,args);
    }
}

【microcloud-config】再複製兩份,總共3個配置中心,分別爲【microcloud-config2】【microcloud-config3】

【microcloud-config】【microcloud-config2】【microcloud-config3】 修改pom文件

增加eureka的支持

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

【microcloud-config】【microcloud-config2】【microcloud-config3】修改application.yml文件,增加eureka的註冊地址

server:
  port: 7101

spring:
  application:
    name: microcloud-config
  cloud:
    config:
      server:
        git:
          uri: https://github.com/hdkeud/microconfig.git

eureka:
 client:
  service-url:
    defaultZone: http://admin:hdk@localhost:7301/eureka
 instance:
  prefer-ip-address: true # 在地址欄上使用IP地址進行顯示
  instance-id: microcloud-config1

啓動eureka並啓動三個配置中心後

【microcloud-config-client】 修改pom文件,增加eureka的支持

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>microcloud-config-client</artifactId>
    <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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-config-client】 修改bootstrap.yml文件,增加eureka相關配置

spring:
 cloud:
  config:
    name: microcloud-config-client # 定義要讀取的資源文件的名稱
    profile: dev # 定義profile的 名稱
    label: master # 定義配置文件所在的分支
    #uri: http://localhost:7101 # SpringCloudConfig的服務地址
    username: admin # 連接的用戶名
password: hdk # 連接的密碼
discovery:
      enabled: true   # 通過配置中心加載配置文件
      service-id: MICROCLOUD-CONFIG   # 在eureka之中註冊的服務ID
    
eureka: 
 client: 
  service-url:
    defaultZone: http://admin:hdk@localhost:7301/eureka

訪問:http://localhost:8201/config

自動刷新

在整個SpringCloudConfig設計之中,我們已經實現了配置的統一管理,但其實還有一個問題,就是自動刷新。

嘗試修改一下【github】 microcloud-config-client.yml文件

spring:
   profiles:
      active:
        - dev
---
server:
  port: 8201

spring:
  profiles: dev
  application:
      name: microconfig-test-client2

eureka:
 client:
  service-url:
    defaultZone: http://admin:hdk@localhost:7001/eureka
---
server:
  port: 8102

spring:
  profiles: beta
  application:
      name: microconfig-test-client2

eureka:
 client:
  service-url:
    defaultZone: http://admin:hdk@localhost:7001/eureka

這裏的修改非常簡單,只是修改了下應用名稱,提交後

訪問:http://localhost:8201/config

發現配置並沒有修改,一直要重啓【microcloud-config-client】後纔會發現配置已經修改成功,其實這對大多數應用沒有什麼問題,如果你定時要關注這個小問題也是有辦法處理的,在springcloud裏面可以藉助消息總線SpringCloudBus解決這問題。

springcloudbus是基於SpringCloudStream的,SpringCloudStream的作用其實也是一種適配器模式的體現,消息中間件由很多,比如activemq,rabbitmq ,kafka,不同的消息中間件都會由使用上的差異,而SpringCloudStream就是爲了屏蔽各種消息中間件的差異而存在的.

與之前的架構不一樣的地方在於增加了消息總線,消息總線連接了config配置中心和各個配置中心的消費方,當配置提交到github的時候,可以藉助/bus/refresh刷新,config配置中心再將變更的消息通知到其他的客戶端

【github】 修改配置microcloud-config-client.yml

spring:
   profiles:
      active:
        - dev
---
server:
  port: 8201

spring:
  profiles: dev
  application:
      name: microconfig-test-client2

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
  register-with-eureka: false

info:
  app.name: microcloud-config-client-dev
  company.name: hdk
---
server:
  port: 8102

spring:
  profiles: beta
  application:
      name: microconfig-test-client2

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
  register-with-eureka: false
  
info:
  app.name: microcloud-config-client-dev
  company.name: hdk

啓動rabbitmq

登陸查看 http://localhost:15672

準備bus配置中心

新建立一個模塊【microcloud-config-bus】,這模塊是配置中心的升級版,作用也是配置中心。

【microcloud-config-bus】 修改pom文件

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-config-bus</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</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-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>

【microcloud-config-bus】 修改application.yml文件,配置上git目錄,一樣連接上eureka,要和消息中間件通訊,所以RabbitMQ的連接信息也配置上

server:
  port: 7201
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/hdkeud/microconfig.git
    bus:
      trace:
        enabled: true
  rabbitmq:
    host: localhost
    port: 5672    # RabbitMQ的監聽端口
    username: hdk  # 用戶名
    password: 5428325 # 密碼
  application:
    name: microcloud-config-bus

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
 instance:
    prefer-ip-address: true # 在地址欄上使用IP地址進行顯示
    instance-id: microcloud-config-bus
management:
  endpoints:
    web:
      exposure:
        include: "*"

新增啓動類

package hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigBusApp {
    public static void main(String[] args) {
        SpringApplication.run(ConfigBusApp.class,args);
    }
}

先啓動eureka,再啓動ConfigBusApp 之後訪問:

http://localhost:7301/

發現新的註冊中心已經註冊上去了。

準備新的客戶端

新建立microcloud-config-bus-client模塊,這模塊是註冊中心的客戶端,從註冊中心獲取數據,職責和【microcloud-config-client】一樣,可用基於他拷貝修改,只是增加bus相關的功能。

【microcloud-config-bus-client】 修改pom文件

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-config-bus-client</artifactId>
    <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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>


</project>

【microcloud-config-bus-client】bootstrap.yml 增加rabbitmq相關信息,另外的配置和前面一樣,需要從eureka找到註冊中心,也需要找具體配置文件信息

spring:
  cloud:
    config:
      name: microcloud-config-client # 定義要讀取的資源文件的名稱
      profile: dev # 定義profile的 名稱
      label: master # 定義配置文件所在的分支
      #uri: http://localhost:7101 # SpringCloudConfig的服務地址
      username: admin # 連接的用戶名
      password: hdk # 連接的密碼
      discovery:
        enabled: true
        service-id: MICROCLOUD-CONFIG-BUS

  rabbitmq:
    host: localhost
    port: 5672    # RabbitMQ的監聽端口
    username: hdk  # 用戶名
    password: 5428325 # 密碼

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
  register-with-eureka: false

修改application.yml文件

spring:

 application:

  name: microcloud-config-client # 編寫應用的名稱

【microcloud-config-bus-client】 建立一個配置文件的映射類,這類是爲了演示使用,裏面的屬性和github的屬性一一對應,同時增加@RefreshScope,代表這個類是可用基於rabbitmq自動刷新的

package cn.hdk.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@Component
@RefreshScope
public class InfoConfig {
    @Value("${info.app.name}")
    private String appName ;
    @Value("${info.company.name}")
    private String companyName ;

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @Override
    public String toString() {
        return "InfoConfig{" +
                "appName='" + appName + '\'' +
                ", companyName='" + companyName + '\'' +
                '}';
    }
}

【microcloud-config-bus-client】修改ConfigClientController

package cn.hdk.controller;

import cn.hdk.config.InfoConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController

public class ConfigClientController {

    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.serviceUrl.defaultZone}")
    private String eurekaServers;

    @Resource
    private InfoConfig infoConfig;

    @RequestMapping("/config")
    public String getConfig() {
        return "ApplicationName = " + this.applicationName + "、EurekaServers = "
                + this.eurekaServers+"、infos = " +infoConfig.toString();
    }
}

新增啓動類

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ConfigClientBusApp {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientBusApp.class,args);
    }
}

啓動後訪問

http://localhost:8201/config,這時候已經能獲得配置中心的數據

測試自動刷新

其實這裏的自動刷新只能說是半自動的。
【github】microcloud-config-client.yml,隨便修改裏面的內容,提交

spring:
   profiles:
      active:
        - dev
---
server:
  port: 8201

spring:
  profiles: dev
  application:
      name: microconfig-test-client2

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
  register-with-eureka: false

info:
  app.name: microcloud-config-client-dev
  company.name: hdk
---
server:
  port: 8102

spring:
  profiles: beta
  application:
      name: microconfig-test-client2

eureka:
 client:
  serviceUrl:
    defaultZone: http://admin:hdk@localhost:7301/eureka
  register-with-eureka: false
  
info:
  app.name: microcloud-config-client-beta
  company.name: hdkbeta
  

【microcloud-config-bus-client】 刷新客戶端
http://localhost:8201/config
這個時候信息並沒自動刷新,數據還是以前的,這是以爲對應消息中間件來說,還需要給他發個消息,代表數據已經更新了。

【microcloud-config-bus】使用postman發生一條post刷新的指令
http://localhost:7201/actuator/bus-refresh

訪問http://localhost:15672,發現消息隊列裏面已經有消息傳遞了。

【microcloud-config-bus-client】 刷新客戶端

http://localhost:8201/config 發現數據已經跟新

SpringCloudStream 消息驅動

SpringCloudStream看名字就知道他和消息隊列相關,但它又不是消息隊列,準確來說它類似於硬件裏面的驅動程序,也就是前面說的適配器模式的體現
在系統開發裏面難免用到消息隊列,但各個的消息隊列又有所區別,SpringCloudStream的作用就是屏蔽各種消息隊列的區別,對消息隊列的API進行進一步的抽象,使得在springcloud裏面能更加方便的集成各種消息系統

首先來看SpringCloudStream的組成

不管是生產者還是消費者,並不會直接和消息中間件打交道,在springcloudstream中抽象了已成binder(綁定層),有了這一層,使用者並不關心具體的消息中間件配置了,由訪問層真正的和消息隊列進行通信

創建消息生產者

【microcloud-stream-provider】創建一個新的模塊,這模塊負責生產一個消息

【microcloud-stream-provider】 pom文件如下,映入springcloudstream的相關組件

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-stream-provider</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

【microcloud-stream-provider】 修改application.yml文件

server:
 port: 8401

spring:
  cloud:
    stream:
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          output: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
  application:
    name: microcloud-stream-provider
    

【microcloud-stream-provider】 定義一個消息發送接口

package hdk.service;
import cn.hdk.vo.Product;
public interface IMessageProvider {
     void send(Product product);
}

【microcloud-stream-provider】 定義接口的實現類

package hdk.service.impl;


import hdk.service.IMessageProvider;
import hdk.vo.Product;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;

import javax.annotation.Resource;

@EnableBinding(Source.class)
public class MessageProviderImpl implements IMessageProvider{
    @Resource
    private MessageChannel output;  // 消息的發送管道

    @Override
    public void send(Product product) {
        output.send(MessageBuilder.withPayload(product).build());
    }
}

【microcloud-stream-provider】 定義啓動類主程序

package hdk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

【microcloud-stream-provider】 編寫測試類

package hdk.test;
import hdk.StreamProviderApp;
import hdk.service.IMessageProvider;
import hdk.vo.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;

@SpringBootTest(classes = StreamProviderApp.class)
@RunWith(SpringRunner.class)
public class TestMessageProvider {

    @Resource
    private IMessageProvider messageProvider;

    @Test
    public void testSend() {
        Product product = new Product();
        product.setProductId(1L);
        product.setProductName("messageName");
        product.setProductDesc("desc");
        messageProvider.send(product);
    }
}

運行測試方法
RabbitMq:http://localhost:15672/

創建消息消費者

【microcloud-stream-consumer】 新建模塊

【microcloud-stream-consumer】修改pom文件如下

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-stream-consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>hdk</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
    </dependencies>
</project>

【microcloud-stream-consumer】 修改application.yml配置文件

server:
  port: 8402

spring:
  cloud:
    stream:
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          input: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
  application:
    name: microcloud-stream-consumer
    

【microcloud-stream-consumer】 定義一個消息的監聽

package hdk.listener;

import hdk.vo.Product;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(Sink.class)
public class MessageListener {
    @StreamListener(Sink.INPUT)
    public void input(Message<Product> message) {
        System.err.println("【*** 消息接收 ***】" + message.getPayload());
    }
}

【microcloud-stream-consumer】 創建啓動類

package hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

啓動後,發現rabbitmq裏面已經有了一個消費者

【microcloud-stream-provider】 運行測試類

【microcloud-stream-consumer】 已經收到消息

【rabbitmq】在消息隊列中,也檢測到了這匿名隊列,可以發現Exchange默認的類型就是TOPIC,RoutingKey我們沒有自定,默認的就是#,會給所有的消費者發消息

自定義消息通道

【micocloud-api】修改pom文件,增加stream的支持

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
    </dependencies>
     <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
    </dependencies>

【micocloud-api】 增加管道接口

package hdk.channel;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;

public interface DefaultProcess {
    public static final String OUTPUT = "hdk_output"; // 輸出通道名稱
    public static final String INPUT = "hdk_input"; // 輸入通道名稱
    @Input(DefaultProcess.INPUT)
    public SubscribableChannel input();
    @Output(DefaultProcess.OUTPUT)
    public MessageChannel output();
}

【microcloud-stream-provider】 修改application.yml配置文件

server:
 port: 8401

spring:
  cloud:
    stream:
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          hdk_output: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
  application:
    name: microcloud-stream-provider
    

【microcloud-stream-consumer】 修改application.yml文件

server:
  port: 8402

spring:
  cloud:
    stream:
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          hdk_input: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
  application:
    name: microcloud-stream-consumer
    

【microcloud-stream-provider】 修改MessageProviderImpl發送實現類

package hdk.service.impl;
import hdk.channel.DefaultProcess;
import hdk.service.IMessageProvider;
import hdk.vo.Product;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;

import javax.annotation.Resource;

@EnableBinding(DefaultProcess.class)
public class MessageProviderImpl implements IMessageProvider{
    @Resource
    @Qualifier("hdk_output")
    private MessageChannel output;  // 消息的發送管道

    @Override
    public void send(Product product) {
        output.send(MessageBuilder.withPayload(product).build());
    }
}

【microcloud-stream-consumer】 修改MessageListener

package hdk.listener;
import hdk.channel.DefaultProcess;
import hdk.vo.Product;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(DefaultProcess.class)
public class MessageListener {
    @StreamListener(DefaultProcess.INPUT)
    public void input(Message<Product> message) {
        System.err.println("【*** 消息接收 ***】" + message.getPayload());
    }
}

完成.

分組(隊列)

Group(分組)其實就是對應rabbitmq裏面得隊列,在前面的案例中,我們並沒有指定group,產生的就是一個匿名隊列。

如果啓動了多個【microcloud-stream-consumer】接收者,但並沒有指定group,,那麼將會產生多個匿名的消息隊列,導致多個接收者都會收到同一個消息,也就是說一個消息被重複消費了,這在某些業務場景來說是並不運行的。

這個時候就需要用到group分組了,對於不想重複消費個某消息的各個消費者必須屬於同一個組。

【microcloud-stream-consumer】 修改application.yml

server:
  port: 8403

spring:
  cloud:
    stream:
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          hdk_input: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
            group:  hdk_group
  application:
    name: microcloud-stream-consumer

這個時候服務生產者【microcloud-stream-provider】發送的任何消息都只會被同一個group的某一個消費者處理了。

RoutingKey

RoutingKey其實是RabbitMq的概念,在RabbitMq裏面其實有好幾種Exchange,在SpringCloudStream裏面默認就是使用的最通用的Topic

如果沒有配置RoutingKey,它使用的RoutingKey其實就是#,既類似於fanout的廣播類型
其實也可以使用RoutingKey來實現類似於direct類型,既然直連

【microcloud-stream-consumer】修改application.yml配置文件

server:
  port: 8402

spring:
  cloud:
    stream:
       rabbit:
        bindings:
          hdk_input:
            consumer:
              bindingRoutingKey: hdkKey # 設置一個RoutingKey信息
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          hdk_input: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
            group:  hdk_group
  application:
    name: microcloud-stream-consumer

【microcloud-stream-provider】定義 RoutingKey 的表達式配置:

server:
 port: 8401

spring:
  cloud:
    stream:
      rabbit:
         bindings:
          hdk_output:
              producer:
                routingKeyExpression: '''hdkKey'''
       binders: # 在此處配置要綁定的rabbitmq的服務信息;
          defaultRabbit: # 表示定義的名稱,用於於binding整合
            type: rabbit # 消息組件類型
            environment: # 設置rabbitmq的相關的環境配置
              spring:
                rabbitmq:
                   addresses: localhost
                   port: 5672
                   username: hdk
                   password: 123456
                   virtual-host: /
       bindings: # 服務的整合處理
          hdk_output: # 這個名字是一個通道的名稱,在分析具體源代碼的時候會進行說明
            destination: hdkExchange # 表示要使用的Exchange名稱定義
            content-type: application/json # 設置消息類型,本次爲對象json,如果是文本則設置“text/plain”
            binder: defaultRabbit # 設置要綁定的消息服務的具體設置
  application:
    name: microcloud-stream-provider

SpringCloudSleuth 鏈路跟蹤

在微服務的架構下,系統由大量服務組成,每個服務可能是由不同的團隊開發,開發使用不同的語言,部署在幾千臺服務器上,並且橫跨多個不同的數據中心,一次請求絕大多數情況會涉及多個服務,在系統發生故障的時候,想要快速定位和解決問題,就需要跟蹤服務請求序列

SpringCloudSleuth使用的核心組件是Twitter推出的Zipkin監控組件,zipkin就是一個分佈式的跟蹤系統,用戶收集服務的數據

基本使用

跟蹤服務

【microcloud-sleuth】 新建立模塊,這個模塊用戶跟蹤用戶的請求,把請求的鏈路進行展示

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>hdk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microcloud-sleuth</artifactId>

   <dependencies>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-sleuth</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-zipkin</artifactId>
       </dependency>
       <dependency>
          <groupId>io.zipkin.java</groupId>
           <artifactId>zipkin-server</artifactId>
           <version>2.9.3</version>
       </dependency>
       <dependency>
           <groupId>io.zipkin.java</groupId>
           <artifactId>zipkin-autoconfigure-ui</artifactId>
           <version>2.9.3</version>
       </dependency>
   </dependencies>
</project>

【microcloud-sleuth】修改application.yml文件

server:
  port: 8601
spring:
  application:
    name: microcloud-zipkin-server


management:
  metrics:
    web:
      server:
        auto-time-requests: false
        

【microcloud-sleuth】新建啓動類

package .hdk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.internal.EnableZipkinServer;

@SpringBootApplication
@EnableZipkinServer
public class SleuthApp {
    public static void main(String[] args) {
        SpringApplication.run(SleuthApp.class,args);
    }
}

客戶端配置

爲了演示鏈路追蹤,需要啓動一些列服務

1.【microcloud-eureka】,啓動這個服務的目的是讓product,users兩服務註冊到其中,後面需要zuul的調用

2.修改一下服務的pom文件,增加sleuth的支持

【microcloud-provider-user-hystrix】

【microcloud-provider-product-hystrix】

【microcloud-zuul-gateway】

【microcloud-consumer-hystrix】

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

3.修改一下服務的application.yml文件,增加zipkin的配置

【microcloud-provider-user-hystrix】

【microcloud-provider-product-hystrix】

【microcloud-zuul-gateway】

【microcloud-consumer-hystrix】

spring:
  zipkin:
    base-url: http://localhost:8601 #所有的數據提交到此服務之中
    sender:
      type: web       #提交的類型是web 服務
  sleuth:
    sampler:
      probability: 1.0 # 定義抽樣比率,默認爲0.1
  1. 依次啓動

【microcloud-eureka】

【microcloud-provider-user-hystrix】

【microcloud-provider-product-hystrix】

【microcloud-zuul-gateway】

【microcloud-consumer-hystrix】

訪問

http://localhost/consumer/product/getProductAndUser?id=1

訪問跟蹤服務:

http://localhost:8601/zipkin/dependency/

數據持久化

現在以及成功實現了一個 SpringCloudSleuth 的基本操作,但會發現,如果重新啓動【microcloud-sleuth】服務,所有的鏈路跟蹤數據都會丟失,那麼這些數據應該存儲到數據庫裏面的。
但又有另外一個問題,如果請併發量特別大,對於mysql來說可能會承受不了這麼大的併發,爲了解決這個問題,可以使用消息隊列緩衝處理,最後才從mq中把數據存到mysql中

源碼

https://download.csdn.net/download/wolf12/11544808

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章