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

springcloud必知功能使用教程的更多相关文章

  1. 2020年SpringCloud 必知的18道面试题

    今天跟大家分享下SpringCloud常见面试题的知识. 1.什么是Spring Cloud? Spring cloud流应用程序启动器是基于Spring Boot的Spring集成应用程序,提供与外 ...

  2. Visual Studio (VS IDE) 你必须知道的功能和技巧 - 【.Net必知系列】

    前言 本文主要阐述一些Visual Studio开发下需要知道的少部分且比较实用的功能,也是很多人忽略的部分.一些不常用而且冷门的功能不在本文范围,当然本文的尾巴[.Net必知系列]纯属意淫,如有雷同 ...

  3. Android程序员必知必会的网络通信传输层协议——UDP和TCP

    1.点评 互联网发展至今已经高度发达,而对于互联网应用(尤其即时通讯技术这一块)的开发者来说,网络编程是基础中的基础,只有更好地理解相关基础知识,对于应用层的开发才能做到游刃有余. 对于Android ...

  4. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  5. MySQL必知必会1-20章读书笔记

    MySQL备忘 目录 目录 使用MySQL 检索数据 排序检索数据 过滤数据 数据过滤 用通配符进行过滤 用正则表达式进行搜索 创建计算字段 使用数据处理函数 数值处理函数 汇总数据 分组数据 使用子 ...

  6. 《MySQL 必知必会》读书总结

    这是 <MySQL 必知必会> 的读书总结.也是自己整理的常用操作的参考手册. 使用 MySQL 连接到 MySQL shell>mysql -u root -p Enter pas ...

  7. 《SQL必知必会》学习笔记(一)

    这两天看了<SQL必知必会>第四版这本书,并照着书上做了不少实验,也对以前的概念有得新的认识,也发现以前自己有得地方理解错了.我采用的数据库是SQL Server2012.数据库中有一张比 ...

  8. SQL 必知必会

    本文介绍基本的 SQL 语句,包括查询.过滤.排序.分组.联结.视图.插入数据.创建操纵表等.入门系列,不足颇多,望诸君指点. 注意本文某些例子只能在特定的DBMS中实现(有的已标明,有的未标明),不 ...

  9. c++程序员必知的几个库

    c++程序员必知的几个库 1.C++各大有名库的介绍——C++标准库 2.C++各大有名库的介绍——准标准库Boost 3.C++各大有名库的介绍——GUI 4.C++各大有名库的介绍——网络通信 5 ...

随机推荐

  1. 解决python 保存json到文件时 中文显示16进制编码的问题

    python 2.7 import codecs import json with codecs.open('Options.json', 'w', encoding='utf-8') as f: j ...

  2. [MyBatis]org.apache.ibatis.binding.BindingException的避免

    我遇到的org.apache.ibatis.binding.BindingException问题是因为Mapper.java中接口和SQL的参数多于一个,Mybatis不知道如何一一对应,解决方法是加 ...

  3. MySQL ALTER命令-修改数据表名或者修改数据表字段

    需要修改数据表名或者修改数据表字段时,就需要使用到MySQL ALTER命令. 删除,添加或修改表字段 如下命令使用了 ALTER 命令及 DROP 子句来删除表的 i 字段: ALTER TABLE ...

  4. 数据分析 - Excel 综合实例 - 杜邦分析法

    项目背景 杜邦分析法 利用 杜邦分析法完成对一份数据的动态分析 流程图如下 项目源数据 左侧为竖向的数据管理, 右侧为横向的数据管理 横向的数据管理在数据透视的时候会很不方便, 并不推荐 常用叫法左边 ...

  5. zookeeper-3.5.5安装报错:找不到或无法加载主类 org.apache.zookeeper.server.quorum.QuorumPeerMain

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/jiangxiulilinux/artic ...

  6. python定义接口继承类invalid syntax解决办法

    class s_all(metaclass=abc.ABCMeta): #python2.7用此方法定义接口继承 # __metaclass__ = abc.ABCMeta @abc.abstract ...

  7. 第一个smarty例子--分页显示数据

    模板页index.tpl:   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...

  8. mongodb 报错 not authorized on admin to execute command【 version 3.2.18 】

    mongodb version 3.2.18 测试问题: 分析: 从报错内容上看是权限不够,但不明了为什么,因为已经使用的超级用户权限: { "_id" : "admin ...

  9. 005-log-slf4j

    一.概述 SLF4J = Simple Logging Facade for Java.     author: Ceki Gülcü     SLF4J,即简单日志门面(Simple Logging ...

  10. 【python画图】

    1.热力图 import numpy as np import numpy.random import matplotlib.pyplot as plt # Generate some test da ...