• 1 微服务概述

  • 2.项目搭建

    • 2.1 建立父项目
    • 2.2 服务提供者-支付模块
    • 2.3 服务消费者-订单模块
    • 2.4 抽取公用模块-common
  • 3.注册中心

    • 3.1 eureka注册中心

      • 3.1.1 建立eureka server
      • 3.1.2 支付服务注册到eureka server
      • 3.1.3 搭建eureka server集群
      • 3.1.4 多实例注册到eureka server
    • 3.2 zookeeper注册中心
      • 3.2.1 centos7搭建zk
      • 3.2.2 服务注册
      • 3.2.3 服务调用
    • 3.3 consul注册中心
      • 3.3.1 consul概述及环境搭建
      • 3.3.2 服务注册
      • 3.3.3 服务消费
      • 3.3.4 三个注册中心异同点
  • 4.ribbon-负载均衡

    • 4.1 概述
    • 4.2 getForObject和getForEntity区别
    • 4.3 替换负载规则
    • 4.4 手写负载算法
  • 5.Feign/OpenFeign-服务调用

    • 5.1 概述
    • 5.2 服务调用
    • 5.3 超时控制
    • 5.4 日志增强
  • 6.断路器-Hystrix

    • 6.1 概述
    • 6.2 高延迟高并发场景模拟
    • 6.3 消费者高延迟场景
    • 6.4 降级
    • 6.5 熔断
    • 6.6 限流
    • 6.7 仪表盘
    • 6.8 仪表盘监控服务
  • 7.网关Gateway

    • 7.1 概述
    • 7.2 三大核心概念
    • 7.3 入门配置
    • 7.4 编码方式实现路由转发
    • 7.5 通过微服务名动态路由
    • 7.6 断言-Predicate
    • 7.7 过滤器-Filter
  • 8.配置中心-Spring Cloud Config

    • 8.1 概述
    • 8.2 服务端获取配置信息
    • 8.3 客户端获取配置信息
    • 8.4 配置实时刷新
  • 9.消息总线-Spring Cloud Bus

    • 9.1 概述
    • 9.2 rabbitmq环境搭建
    • 9.3 Bus动态刷新全局广播配置
    • 9.4 Bus动态刷新定点通知

1 微服务概述

概念

单一服务分为一组小的服务,每个服务运行中独立线程中,服务之间相互协调

维度

库存\订单\支付...

spring cloud是微服务架构的一站式解决方案
eureka已停更,注册发现推荐nacos

spring cloud技术栈

注册发现 eureka
负载调用 ribbon
熔断降级 hystrix
网关 zuul
配置 spring cloud config

版本

本教程使用spring boot 2.2.2和spring cloud H版SR1
java 8+
maven 3.5+

组件停更及替换

被动修复bugs\不再合并请求\不发布新版本
eureka停更->zk | consul(不推荐) | nacos(完美替换)
ribbon进入维护状态 -> loadbalancer
feign停更 -> openfeign(推荐)
hystrix停更 -> resilience4j(国外) | sentinel(国内推荐)
zuul -> gateway
config -> apolo(携程) | nacos(推荐)
bus -> nacos

参考资料

官方文档

2.项目搭建

2.1 建立父项目

新建maven项目scdemo

a.字符集

setting->editor->file encoding
全部选择utf-8

b.注解

settings->build->compiler->annotation processer
enable annotation proccessing

c.编译版本

settings->build->compiler->java compiler
target bytecode version: 8

d.隐藏文件

settings->editor->file types
ignore files and folders: 添加.iml;.idea;

e.依赖

pom.xml

...<packaging>pom</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>12</maven.compiler.source><maven.compiler.target>12</maven.compiler.target><junit.version>4.12</junit.version><lombok.version>1.18.10</lombok.version><log4j.version>1.2.17</log4j.version><mysql.version>8.0.18</mysql.version><druid.version>1.1.16</druid.version><mybatis.spring.boot.version>2.1.1</mybatis.spring.boot.version></properties><dependencyManagement><dependencies><dependency><groupId>org.apache.maven.plugins</groupId><artifactId>maven-project-info-reports-plugin</artifactId><version>3.0.0</version></dependency><!--spring boot 2.2.2--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.2.2.RELEASE</version><type>pom</type><scope>import</scope></dependency><!--spring cloud Hoxton.SR1--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR1</version><type>pom</type><scope>import</scope></dependency><!--spring cloud 阿里巴巴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.0.RELEASE</version><type>pom</type><scope>import</scope></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version><scope>runtime</scope></dependency><!-- druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.spring.boot.version}</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency></dependencies></dependencyManagement>
</project>

dependencyManagement只声明,不引入实现

跳过maven单元测试

右侧maven projects点击闪电图标

2.2 服务提供者-支付模块

新建模块 scdemo-payment

a.依赖

pom.xml

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.18</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

b.配置

resources/application.yml

server:port: 8001spring:application:name: cloud-payment-servicedatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/springcloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: rootmybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.gaoliang.springcloud.payment.entity

c.启动类 com.gaoliang.springcloud.payment.PaymentApplication

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

d.建表语句

CREATE TABLE `payment`(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`serial` VARCHAR(20) DEFAULT '',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

e.实体类

订单实体类
com.gaoliang.springcloud.payment.entity.Payment

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {private Long id;private String serial;
}

统一结果类
com.gaoliang.springcloud.payment.entity.CommonResult

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> implements Serializable {private Integer code;private String msg;private T data;public CommonResult(Integer code, String msg) {this(code, msg, null);}public CommonResult(ResultEnum resultEnum, T data) {this(resultEnum.getCode(), resultEnum.getMsg(), data);}public static CommonResult success() {return new CommonResult(ResultEnum.SUCCESS, null);}public static<T> CommonResult success(T data) {return new CommonResult(ResultEnum.SUCCESS, data);}public static<T> CommonResult success(String msg, T data) {return new CommonResult(ResultEnum.SUCCESS.getCode(), msg, data);}public static CommonResult fail() {return new CommonResult(ResultEnum.FAIL, null);}
}

结果枚举
com.gaoliang.springcloud.payment.entity.ResultEnum

@Getter
@AllArgsConstructor
public enum ResultEnum {SUCCESS(200, "操作成功"),FAIL(-1, "操作失败");private int code;private String msg;
}

f.mapper

<?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="com.gaoliang.springcloud.payment.dao.PaymentDao"><insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">insert into payment(serial) values(#{serial})</insert><resultMap id="BaseResultMap" type="com.gaoliang.springcloud.payment.entity.Payment"><id column="id" property="id" jdbcType="BIGINT" /><id column="serial" property="serial" jdbcType="VARCHAR" /></resultMap><select id="getById" parameterType="Long" resultMap="BaseResultMap">select * from payment where id=#{id}</select>
</mapper>

g.dao

com.gaoliang.springcloud.payment.dao.PaymentDao

@Mapper
public interface PaymentDao {int create(Payment payment);Payment getById(@Param("id") Long id);
}

h.service

com.gaoliang.springcloud.payment.service.PaymentService

public interface PaymentService {int create(Payment payment);Payment getById(Long id);
}

i.service impl

com.gaoliang.springcloud.payment.service.impl.PaymentServiceImpl

@Service
public class PaymentServiceImpl implements PaymentService {@Resourceprivate PaymentDao paymentDao;@Overridepublic int create(Payment payment) {return paymentDao.create(payment);}@Overridepublic Payment getById(Long id) {return paymentDao.getById(id);}
}

j.controller

com.gaoliang.springcloud.payment.controller.PaymentController

@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@PostMapping(value="/payment/service")public CommonResult create(@RequestBody Payment payment) {int result = paymentService.create(payment);return result > 0 ? CommonResult.success(result) : CommonResult.fail();}@GetMapping(value="/payment/get/{id}")public CommonResult getById(@PathVariable Long id) {Payment payment = paymentService.getById(id);return payment != null ? CommonResult.success(payment) : CommonResult.fail();}
}

k.启动

启动项目,浏览器访问:
http://localhost:8001/payment/get/1

l.开启热部署

i.父模块pom添加插件

    <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin></plugins></build>

ii.子模块添加依赖

        <!--热部署插件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency>

iii.设置
settings->build->compiler
勾选Display...和Build Project...
iv.ctrl + alt + shift + /,点击1.Registry...
勾选compiler.automake.allow.when.app.running
v.生产环境要关闭热部署

2.3 服务消费者-订单模块

a.新建模块

scdemo-order
pom复制scdmo-payment的pom,修改artifactId

b.配置

配置端口及数据源
虽然该模块目前没有用到数据源,但不配置会报错...

c.拷贝scdemo-payment的实体类

d.配置类

com.gaoliang.springcloud.order.config.ApplicationContextConfig

@Configuration
public class ApplicationContextConfig {@Beanpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}

e.controller

com.gaoliang.springcloud.order.controller.OrderController

@RestController
@Slf4j
public class OrderController {public static final String PAYMENT_URL = "http://localhost:8001";@Resourceprivate RestTemplate restTemplate;@PostMapping("/consumer/payment/create")public CommonResult create(@RequestBody Payment payment) {// 使用restTemplate调用服务提供者接口return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);}@GetMapping("/consumer/payment/get/{id}")public CommonResult getPayment(@PathVariable Long id) {return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);}}

f.启动,测试

使用idea的run dashboard启动两个模块
使用postman测试
url: http://localhost/consumer/payment/create
headers:
Content-Type: application/json
body: raw

{"id": 1,"serial": "111"
}

2.4 抽取公用模块-common

a.新建模块scdemo-common

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.1.0</version></dependency></dependencies>

b.copy实体类包到该模块下
c.其他两个模块依赖该模块

        <dependency><groupId>cn.dfun</groupId><artifactId>scdemo-common</artifactId><version>${project.version}</version></dependency>

d.修改mybatis实体类前缀
scdemo-payment模块application.yml

mybatis:
...type-aliases-package: com.gaoliang.springcloud.common.entity

3.注册中心

3.1 eureka注册中心

服务注册

注册中心配多个避免单点故障
服务与注册中心维持心跳

3.1.1 建立eureka server

a.新建模块

scdemo-eureka-server
依赖

<dependencies><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-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency></dependencies><build><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build>

b.配置

server:port: 7001eureka:instance:# eureka服务端实例名称hostname: localhostclient:# 不向注册中心注册自己register-with-eureka: false# 不去检索服务fetch-registry: false# 服务查询地址service-url:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

c.启动类

com.gaoliang.springcloud.eurekaserver.EurekaServerApplication

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

3.1.2 支付服务注册到eureka server

a.依赖

scdemo-payment pom添加

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

b.配置

添加

eureka:client:service-url:defaultZone: http://localhost:7001/eurekaregister-with-eureka: truefetch-registry: false

c.注解

启动类添加@EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class PaymentApplication {
...

浏览器访问:
http://localhost:7001/
可以看到scdemo-payment服务

d 订单服务注册到eureka server

同支付服务类似
配置

    register-with-eureka: falsefetch-registry: true

3.1.3 搭建eureka server集群

避免注册中心单点故障
原理是多个节点相互注册

a.配置

# 无参数启动
spring:application:name: eureka-serverserver:port: 7001eureka:client:service-url:defaultZone: http://localhost:7001/eureka/---
# 启动参数 --spring.profiles.active=server1
spring:profiles: server1
server:port: 7001
eureka:client:service-url:defaultZone: http://localhost:7002/eureka/
---
# 启动参数 --spring.profiles.active=server2
spring:profiles: server2
server:port: 7002
eureka:client:service-url:defaultZone: http://localhost:7001/eureka/
---

b.启动

Run->Edit Configurations...
分别配置Program argumenents
--spring.profiles.active=server1
--spring.profiles.active=server2

c.服务注册到eureka server集群

修改payment和order的配置

#      defaultZone: http://localhost:7001/eurekadefaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

3.1.4 多实例注册到eureka server

a.多个端口启动

payment服务
VMOptions: -Dserver.port=8002
VMOptions: -Dserver.port=8001

b.@LoadBalanced注解

order模块restTemplate添加@LoadBalanced注解

    @Bean@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}

c.payment模块controller注入端口号并打印

    @Value("${server.port}")private String serverPort;...@GetMapping(value="/payment/get/{id}")public CommonResult getById(@PathVariable Long id) {Payment payment = paymentService.getById(id);String msg = "端口号: " + serverPort;return payment != null ? CommonResult.success(msg, payment) : CommonResult.fail();}

d.order模块修改controller payment服务地址

public static final String PAYMENT_URL = "http://SCDEMO-PAYMENT";

多次访问:
http://localhost/consumer/payment/get/1
可见访问了不同的端口号,默认轮询

e.修改服务在注册中心的显示

eureka:instance:# 服务名:ip:端口instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}

f.actuator微服务信息

修改PaymentController
注入discoveryClient

    @Resourceprivate DiscoveryClient discoveryClient;

打印相关信息

@GetMapping(value="/payment/discovery")public Object discovery() {// 获取服务清单列表List<String> services = discoveryClient.getServices();for(String element: services) {log.info("############element:" + element);}// 获取实例信息List<ServiceInstance> instances = discoveryClient.getInstances("scdemo-payment");for(ServiceInstance instance : instances) {// 服务名 主机名 端口 urilog.info("##########" + instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());}return this.discoveryClient;}

g.eureka保护机制

eureka控制台红字

EMERGENCY! EUREKA MAY BE...

当服务不可用时,eureka不会立即将服务剔除
有可能微服务健康,但eureka server网络不通
设计哲学是宁可保留错误的微服务,也不盲目清理

i.禁用自我保护
eureka server

eureka:server:# 禁用自我保护enable-self-preservation: false# 2秒踢除eviction-interval-timer-in-ms: 2000

eureka client

eureka:instance:# Eureka客户端向服务端发送心跳的时间间隔,默认是30秒lease-renewal-interval-in-seconds: 1# Eureka服务端在收到最后一次心跳后等待时间上限,默认是90秒lease-expiration-duration-in-seconds: 2

3.2 zookeeper注册中心

3.2.1 centos7搭建zk

a.zk环境搭建

** 注意:
a.zk使用低版本会报错
b.要下载*bin.tar.gz版本

# 解压
tar -zxvf apache-zookeeper-3.5.8-bin.tar.gz -C /usr/local/
cd /usr/local
mv apache-zookeeper-3.5.8-bin/ zookeeper
cd zookeeper
# 创建数据文件目录
mkdir data# 配置
cp conf/zoo_sample.cfg conf/zoo.cfg
vi conf/zoo.cfg
# 修改dataDir
dataDir=/usr/local/zookeeper/data# 启动
./bin/zkServer.sh start
# 查看启动状态
./bin/zkServer.sh status

3.2.2 服务注册

payment模块

a.依赖

pom添加

        <!--SpringBoot整合Zookeeper客户端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zookeeper-discovery</artifactId></dependency>

b.配置

server:port: 8004spring:profiles: zk
...cloud:zookeeper:connect-string: 192.168.68.101:2181

c.启动类注解

@EnableDiscoveryClient
public class PaymentApplication {

启动
--spring.profiles.active=zk

d.验证

bin/zkCli.sh
ls /
# 显示服务目录
ls /services
# 显示服务注册信息
get /services/scdemo-payment/b603f451-7d50-42f0-b22d-9ef5bb3588fa

浏览器访问:
http://localhost:8004/payment/get/1
补充说明:
zk节点分临时和持久
关闭8004服务后一段时间,zk节点被删除,是临时节点

3.2.3 服务调用

order模块

a.依赖/配置/注解

参考payment模块

b.配置类

RestTemplate与Euraka配置方式相同
注意:
需要加@LoadBalanced注解,否则会报异常

c.启动

--spring.profiles.active=zk
浏览器访问:
http://localhost/consumer/payment/get/1

3.3 consul注册中心

3.3.1 consul概述及环境搭建

a.概述

go语言实现,服务发现和配置管理,支持多数据中心
官网: https://www.consul.io/docs
中文文档: https://www.springcloud.cc/spring-cloud-consul.html

b.环境搭建

下载: https://releases.hashicorp.com/consul/1.6.1/
consul_1.6.1_windows_amd64.zip
下载后直接解压
目录加入到环境变量

# 验证
consul --version
# 启动
consul agent -dev

浏览器访问: http://localhost:8500/

3.3.2 服务注册

payment模块

依赖

注掉zk依赖,添加consul依赖

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

配置 application-consul.yml

server:port: 8006spring:profiles: consulapplication:name: scdemo-payment
...# consul注册中心配置cloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}
...

启动

--spring.profiles.active=consul
访问控制台,可以看到注册的服务
测试调用:
http://localhost:8006/payment/get/1

3.3.3 服务消费

依赖\配置参考3.3.2,配置类和controller和zk部分一样

3.3.4 三个注册中心异同点

eureka是AP,主要保证高可用
consul和zk是CP,主要保证数据一致
分区容错性P必须保证
场景:
双十一主要保证可用,使用AP,允许部分节点返回旧值
CP数据同步失败时拒绝请求

4.ribbon-负载均衡

项目代码恢复到eureka注册中心(集群)

4.1 概述

客户端负载均衡组件,已进入维护模式
未来使用loadbalancer替换
ribbon是进程内LB,注册中心获取服务列表缓存到本地
nginx是集中式LB
算法: 轮询\随机\根据访问时间加权
新版eureka-client集成了ribbon,不需单独引入

4.2 getForObject和getForEntity区别

getForObject和getForEntity区别:
Object基本可以理解为json,Entity包含了响应头\状态码\响应体等,推荐使用Object

order模块controller添加方法

    @GetMapping("/consumer/payment/getForEntity/{id}")public CommonResult getPaymentEntity(@PathVariable Long id) {ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);return entity.getStatusCode().is2xxSuccessful() ? entity.getBody() : CommonResult.fail();}

4.3 替换负载规则

自带算法七种:
轮询\随机\根据时间加权\过滤故障节点等
ribbon配置不能放到@Component组件能扫描到的当前包和子包下,不然会被所有ribbon客户端共享

配置类

order模块
com.gaoliang.springcloud.myrule.MyRule

@Configuration
public class MyRule {@Beanpublic IRule iRule() {// 随机return new RandomRule();}
}

启动类注解

** 注意这里name要配置服务提供者的名称

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="scdemo-payment", configuration = MyRule.class)
public class OrderApplication {
...

4.4 手写负载算法

恢复默认轮询算法,注掉@RibbonClient注解
轮询算法:
接口第几次请求数%集群总数=实际调用服务器位置下标
服务重启后rest接口计数从1开始

order模块注掉restTemplate配置类的@LoadBalancer注解
定义接口
com.gaoliang.springcloud.order.lb.LoadBalancer

public interface LoadBalancer {ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

实现类
com.gaoliang.springcloud.order.lb.MyLB
** 注意Component注解

@Component
public class MyLB implements LoadBalancer{private AtomicInteger atomicInteger = new AtomicInteger(0);public final int getAndIncrement() {int current;int next;do {current = this.atomicInteger.get();// 整数越界next = current >= Integer.MAX_VALUE ? 0 : current + 1;} while(!this.atomicInteger.compareAndSet(current, next));System.out.println("***next: " + next);return next;}@Overridepublic ServiceInstance instances(List<ServiceInstance> serviceInstances) {// 当前计数对集群数取余int index = getAndIncrement() % serviceInstances.size();return serviceInstances.get(index);}
}

OrderController添加loadBalancer和discoveryClient注入,并添加实现接口

    @Resourceprivate LoadBalancer loadBalancer;@Resourceprivate DiscoveryClient discoveryClient;...@GetMapping("/consumer/payment/lb")public String getPaymentLB() {List<ServiceInstance> instances = discoveryClient.getInstances("scdemo-payment");if(instances == null || instances.size() <= 0) {return null;}ServiceInstance serviceInstance = loadBalancer.instances(instances);URI uri = serviceInstance.getUri();return restTemplate.getForObject(uri + "/payment/lb", String.class);}

PaymentController添加相关接口

    @GetMapping(value="/payment/lb")public String lb() {return this.serverPort;}

启动,测试

5.Feign/OpenFeign-服务调用

5.1 概述

feign是一个声明式WebService客户端
feign已停止维护,推荐open feign
使用方法是定义一个服务接口然后在上面添加注解,支持可插拔式的编码器和解码器,spring cloud对feign进行了封装,使其支持spring mvc标准注解和HttpMessageConverter,Feign可与Ribbon结合

5.2 服务调用

新建模块: scdemo-order-feign
可从scdemo-order拷贝,删除冗余代码,注意修改pom中的artifactId和配置中的应用名,并将子模块添加到父模块pom

依赖

在原有基础上添加

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

配置文件无修改,删除配置文件类(RestTemplate配置)

启动类注解

添加@EnableFeignClients

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderFeignApplication {
...

定义feign接口

com.gaoliang.springcloud.order.service.PaymentFeignService
注意@Component注解和@PathVariable("id")

@Component
@FeignClient(value="scdemo-payment")
public interface PaymentFeignService {@GetMapping(value="/payment/get/{id}")CommonResult getById(@PathVariable("id") Long id);
}

controller

@RestController
@Slf4j
public class OrderController {@Resourceprivate PaymentFeignService paymentFeignService;@GetMapping("/consumer/payment/get/{id}")public CommonResult getPayment(@PathVariable Long id) {return paymentFeignService.getById(id);}
}

启动,测试可见自带ribbon负载均衡

5.3 超时控制

提供者和消费者要约定好超时时间

a.模拟超时异常

payment模块controller添加接口

    @GetMapping(value="/payment/timeout")public String timeout() {try {// 睡眠3秒,模拟超时TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}return serverPort;}

order模块feign接口添加

    @GetMapping(value="/payment/timeout")String timeout();

order模块controller添加

    @GetMapping(value="/consumer/payment/timeout")public String timeout() {return paymentFeignService.timeout();}

启动,访问: http://localhost/consumer/payment/timeout
报错:

feign.RetryableException: Read timed out executing GET http://scdemo-payment/payment/timeout
...

b.超时时间设置

order模块application.yml添加

ribbon:# 建立连接所用的时间ReadTimeout: 5000# 建立连接后服务读取到可用资源所用的时间ConnectTimeout: 5000

再次访问a中接口,报错解除

5.4 日志增强

日志级别

NONE 默认,不显示日志
BASIC 仅记录请求方法\url\状态码及执行时间
HEADERS BASIC+请求和响应的头信息
FULL HEADERS+请求和响应的正文及元数据

配置类
com.gaoliang.springcloud.order.config.FeignConfig

@Configuration
public class FeignConfig {@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}

配置

logging:level:# feign日志级别及监控的端口com.gaoliang.springcloud.order.service.PaymentFeignService: debug

访问: http://localhost/consumer/payment/get/1
可见控制台打印出了详细日志

6.断路器-Hystrix

6.1 概述

处理延迟和容错
停更进维,但设计优秀,重要,推荐resilience4j(头条在用其他大厂很少)和sentinel
服务链路复杂,单个服务不可用或响应时间长造成服务雪崩
hystrix类似保险丝,通过故障监控,返回备选响应,避免雪崩
hystrix提供服务降级\熔断和接近实时的监控

降级

对方系统不可用,给友好提示
程序异常\超时\服务熔断\线程池|信号量打满等情况

熔断

达到最大访问号,拒绝访问,调用服务降级给友好提示
降级->熔断->恢复

限流

秒杀高并发等,严禁拥挤,排队处理

6.2 提供者高延迟高并发场景模拟

a.新建scdemo-payment-hystrix模块

可从scdemo-payment模块copy
依赖在scdemo-payment基础上添加

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

b.service

简单起见只写实现类
com.gaoliang.springcloud.payment.service.PaymentService
paymentInfoOK为立即返回的接口
paymentInfoTimeout模拟返回时间较长的方法

    public String paymentInfoOK(Integer id) {return "线程池: " + Thread.currentThread().getName() + " paymentInfoOK, id: " + id;}public String paymentInfoTimeout(Integer id) {int timeNumber = 3;try {TimeUnit.SECONDS.sleep(timeNumber);} catch (InterruptedException e) {e.printStackTrace();}return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeout, id: " + id + ", 耗时: " + timeNumber;}

c.controller

@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfoOK(@PathVariable Integer id) {String result = paymentService.paymentInfoOK(id);log.info("*****result: " + result);return result;}@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfoTimeout(@PathVariable Integer id) {String result = paymentService.paymentInfoTimeout(id);log.info("*****result: " + result);return result;}
}

d.启动项目,压测

启动jmeter
i.Test Plan右键->Add->Threads->Thread Group
Number of Threads: 200
Loop Count: 100
ii.HTTP Request右键->Add->Sampler->HTTP Request
Server Name or IP: localhost
Port Number: 8001
Path: /payment/hystrix/timeout/1
点击工具栏绿色三角按钮启动
iii.访问: http://localhost:8001/payment/hystrix/ok/1
可见timeout接口高延迟请求堆积导致系统资源占用,其他接口响应也变慢

6.3 消费者高延迟场景

a.新建模块scdemo-order-hystrix

在scdemo-order-feign基础上拷贝,修改pom artifactId,配置文件应用名,父模块引入该子模块
** 注意:
如果未修改artifactId就mvn import会报错,恢复比较麻烦,建议删除idea工程文件后重新导入maven项目,并重新进行项目的各项配置
这里使用eureka server单实例

  client:service-url:defaultZone: http://localhost:7001/eureka
#      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

添加依赖

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

b.feign接口

@Component
@FeignClient(value="scdemo-payment-hystrix")
public interface PaymentHystrixService {@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfoOK(@PathVariable("id") Integer id);@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfoTimeout(@PathVariable("id") Integer id);
}

c.controller

@RestController
@Slf4j
public class OrderHystrixController {@Resourceprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")public String paymentInfoOK(@PathVariable("id") Integer id) {String result = paymentHystrixService.paymentInfoOK(id);return result;}@GetMapping("/consumer/payment/hystrix/timeout/{id}")public String paymentInfoTimeout(@PathVariable("id") Integer id) {String result = paymentHystrixService.paymentInfoTimeout(id);return result;}}

d.压测提供者接口导致消费者接口高延迟

参考6.2.d使用jmeter对提供者/hystrix/timeout接口压测,访问消费者接口:
http://localhost/consumer/payment/hystrix/ok/1
可见明显延迟甚至报错

6.4 降级

超时不等待,出错友好提示
解决:
i.提供者超时,消费者不能一直卡死等待,须服务降级;
ii.提供者down机,消费者不能一直等待,须服务降级;
提供者服务ok,消费者故障或者有自我要求(自己等待时间<提供者),自处理降级

a.提供者降级

service超时方法添加@HystrixCommand注解,并实现handler方法

    @HystrixCommand(fallbackMethod = "paymentInfoTimeoutHandler", commandProperties = {// 超时时间@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000")})public String paymentInfoTimeout(Integer id) {
...public String paymentInfoTimeoutHandler(Integer id) {// 友好提示系统繁忙等return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeoutHandler, id: " + id;}

启动类添加@EnableCircuitBreaker注解

...
@EnableCircuitBreaker
public class PaymentHystrixApplication {
...

消费者调用:http://localhost/consumer/payment/hystrix/timeout/1
可见触发降级,方法抛异常和超时都会降级

b.消费者降级

降级处理可以加在提供着,也可加在消费者
一般加在消费端
对hystrix注解修改热部署不生效建议重启服务
消费者controller添加@HystrixCommand,并实现handler方法

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")@HystrixCommand(fallbackMethod = "paymentInfoTimeoutHandler", commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")})public String paymentInfoTimeout(@PathVariable("id") Integer id) {
...public String paymentInfoTimeoutHandler(Integer id) {return "消费者降级: 线程池: " + Thread.currentThread().getName() + " paymentInfoTimeoutHandler, id: " + id;}

启动类添加@EnableCircuitBreaker 或者 @EnableHystrix

...
@EnableCircuitBreaker
public class OrderHystrixApplication {
...

访问:
http://localhost/consumer/payment/hystrix/timeout/1

由于消费者超时时间设为1.5秒,小于提供者的超时时间,所以直接触发消费者降级.

c.降级方法膨胀-通用降级方法

如果,每个方法都配置降级方法会导致方法膨胀,可配置一个全局通用的默认降级方法
消费者controller加@DefaultProperties,并实现通用降级方法

...
@DefaultProperties(defaultFallback = "paymentInfoGlobalHandler")
public class OrderHystrixController {
...public String paymentInfoGlobalHandler() {return "Global异常处理...";}

d.降级方法与业务逻辑耦合-feignclient fallback

消费者添加配置配置

# feign开启降级处理
feign:hystrix:enabled: true

继承feign接口,实现fallback类
com.gaoliang.springcloud.order.service.PaymentHystrixService

@Component
@FeignClient(value="scdemo-payment-hystrix", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfoOK(@PathVariable("id") Integer id);@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfoTimeout(@PathVariable("id") Integer id);
}

@FeignClient指定fallback属性

@FeignClient(value="scdemo-payment-hystrix", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
...

验证,关闭提供者,然后访问消费者接口:
http://localhost/consumer/payment/hystrix/ok/1
可见直接进入了fallback类的方法

6.5 熔断

当链路某个微服务出错或者响应时间长,进行服务降级,熔断该服务的调用
服务可用后自动恢复
熔断源于Martin Fowler,相关资料(慢):
https://martinfowler.com/bliki/CircuitBreaker.html
半开状态尝试请求响应,如果可用则恢复

a.提供者添加service方法配置@HystrixCommand属性,并实现降级方法

com.gaoliang.springcloud.payment.service.PaymentService

@Service
public class PaymentService {
.../***  服务熔断*/@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", commandProperties = {// 是否开启断路器@HystrixProperty(name="circuitBreaker.enabled", value="true"),// 请求次数@HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"),// 时间窗口期@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="10000"),// 失败率达到多少后跳闸@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="60")// 以上参数表示10秒内失败超过6次跳闸})public String paymentCircuitBreaker(@PathVariable("id") Integer id) {if(id < 0) {throw new RuntimeException("*****id不能为负");}// hutool,国产工具类包,https://www.hutool.cn/docs/#/String serialNumber = IdUtil.simpleUUID();return Thread.currentThread().getName() + "\t" + "调用成功, 流水号: " + serialNumber;}public String paymentCircuitBreakerFallback(@PathVariable("id") Integer id) {return "paymentCircuitBreakerFallback: id不能负数,请稍后再试, id: " + id;}}

b.提供者controller

    /*** 服务熔断*/@GetMapping("/payment/circuit/{id}")public String paymentCircuitBreaker(@PathVariable Integer id) {String result = paymentService.paymentCircuitBreaker(id);return result;}

测试,访问:
http://localhost:8001/payment/circuit/1
id为整数,服务可用
当短时间连续访问
http://localhost:8001/payment/circuit/-1
触发熔断,再次访问:
http://localhost:8001/payment/circuit/1
发现服务不可用被降级处理
经过一段时间后服务自动恢复

c.其他参数说明:

请求总数阈值,默认20,即请求时间内,必须满足总数阈值才会熔断
默认阈值10秒超过20个请求
默认失败率10秒超过50%的请求失败
一段时间(默认5秒)后,断路器是半开状态,会让一个请求进行转发,如果成功断路器会关闭

6.6 限流

略,参见Sentinel章节

6.7 仪表盘

提供准实时监控,报表形式
新建模块scdemo-payment-hystrix-dashboard
添加依赖

    <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

主要是以下依赖

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

配置

server:port: 9001spring:application:name: scdemo-payment-hystrix-dashboard

启动类注解

@SpringBootApplication
@EnableHystrixDashboard
public class PaymentHystrixDashboardApplication {
...

http://localhost:9001/hystrix

6.8 仪表盘监控服务

对scdemo-payment-hystrix模块进行修改
需要保证服务依赖了
spring-boot-starter-actuator
消费者启动类注入Bean

...
public class PaymentHystrixApplication {
main方法.../*** Hystrix升级的坑,此配置不必深究*/@Beanpublic ServletRegistrationBean getServlet() {HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);registrationBean.setLoadOnStartup(1);registrationBean.addUrlMappings("/hystrix.stream");registrationBean.setName("HystrixMetricsStreamServlet");return registrationBean;}
}

hystrix仪表盘填入url和名称
http://localhost:8001/hystrix.stream
Title随便填
访问payment /payment/circuit/接口(发起多次失败请求),并实时查看仪表盘状态
仪表盘:
7色一圈一线

7.网关Gateway

7.1 概述

zuul过时,zuul2跳票,弃用
gateway,新一代网关,spring社区自研
基于spring boot2.0\webflux\project reactor
网关可以做反向代理\鉴权\监控等
webflux基于netty(Reactor模式)

gateway特性

动态路由\断言\过滤器\支持websocket
非阻塞io(zuul1为阻塞io)

7.2 三大核心概念

路由-Route

构建网关的基本模块
由ID\目标uri\一系列断言和过滤器组成,如果断言为true则匹配该路由

断言-Predicte

参考java8的java.util.function.Predicte
匹配HTTP请求所有内容(请求头和参数),请求与断言相匹配则进行路由

过滤-Filter

在请求被路由之前或者之后进行处理

7.3 入门配置

新建模块,scdemo-gateway
依赖
** 注意不能依赖web和acurator

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</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-test</artifactId><scope>test</scope></dependency><!--热部署插件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency></dependencies>

配置

server:port: 9527
spring:application:name: scdemo-gatewaycloud:gateway:routes:# 路由id,没有固定规则但要求唯一,建议配合服务名- id: payment_route# 匹配后提供服务的路由地址uri: http://localhost:8001predicates:# 断言,路径相匹配的进行路由- Path=/payment/get/**- id: payment_route2uri: http://localhost:8001predicates:- Path=/payment/lb/**eureka:instance:hostname: scdemo-gatewayclient:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://localhost:7001/eureka

启动相关项目,访问:
http://localhost:9527/payment/get/1
http://localhost:9527/payment/lb
可见通过网关路由到了微服务

7.4 编码方式实现路由转发

com.gaoliang.springcloud.gateway.config.GatewayConfig

@Configuration
public class GatewayConfig {@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();// 映射地址转发routes.route("path_route_dfun", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();return routes.build();}
}

浏览: http://localhost:9527/guonei
可见路由被转发到了相应地址

7.5 通过微服务名动态路由

问题: 路由写死无法实现负载均衡
scdemo-payment模块启动两个实例8001和8002
VM Options: -Dserver.port=8002

修改网关配置

i 开启动态路由
ii 替换uri

spring:application:name: scdemo-gatewaycloud:gateway:discovery:locator:# 开启从注册中心动态创建路由功能,根据微服务名路由enabled: trueroutes:# 路由id,没有固定规则但要求唯一,建议配合服务名- id: payment_route# 匹配后提供服务的路由地址uri: lb://scdemo-payment
#          uri: http://localhost:8001predicates:# 断言,路径相匹配的进行路由- Path=/payment/get/**- id: payment_route2uri: lb://scdemo-payment
#          uri: http://localhost:8001predicates:- Path=/payment/lb/**

验证:
多次访问:
http://localhost:9527/payment/lb
可见端口切换

7.6 断言-Predicate

配置

          predicates:- Path=/payment/lb/**# 在该时间之后访问才有效 Before和Between的使用类似
#            - After=2020-07-27T18:25:25.078+08:00[Asia/Shanghai]# 需要携带此cookie才能访问
#            - Cookie=username,vincent# 请求头要有X-Request-Id且属性值为整数的正则表达式- Header=X-Request-Id, \d+# 此外还有Host\Method\Path等

获取上述时间格式的测试类

public class T2 {public static void main(String[] args) {ZonedDateTime zbj = ZonedDateTime.now();System.out.println(zbj);}
}

配置文件predicates注释逐行放开,使用curl测试

# 测试cookie
curl http://localhost:9527/payment/lb --cookie "username=vincent"
# 测试header
curl http://localhost:9527/payment/lb -H "X-Request-Id:123"

7.7 过滤器-Filter

官方提供了一系列的GatewayFilter

生命周期: Pre\Post
类型: 单一\全局

Filter作用:
全局日志记录\统一网关鉴权等

自定义过滤器

com.gaoliang.springcloud.gateway.filter.MyLogGatewayFilter
实现GlobalFilter, Ordered接口

@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {/*** 过滤方法*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("come in MyLogGatewayFilter: " + new Date());String uname = exchange.getRequest().getQueryParams().getFirst("uname");if(uname == null) {log.info("******非法用户*******");// 不被接受状态码exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);return exchange.getResponse().setComplete();}return chain.filter(exchange);}/*** 加载过滤器顺序,数值越小优先级越高*/@Overridepublic int getOrder() {return 0;}
}

浏览器访问:
http://localhost:9527/payment/lb?uname=vincent
可以正常访问,去掉uname参数无法访问

8.配置中心-Spring Cloud Config

8.1 概述

cloud config\cloud bus没有停更进维,但渐渐被nacos渠道
nacos,注册中心\配置\bus->稳了

问题

多模块引用同一配置(如数据库配置)以及多环境配置,修改繁琐,重复配置
spring cloud config提供中心化的外部配置
服务端是独立微服务应用,为客户端提供配置信息,以及加密\解密等
客户端启动时从配置中心加载配置,默认通过git存储(有助于版本控制,可通过git客户端方便的管理)

config功能

集中管理配置
多环境配置,动态更新
运行期间动态调整
REST接口暴露配置信息

8.2 服务端获取配置信息

a.新建仓库

github新建仓库,springcloud-config

# 克隆
git clone https://github.com/Object520/springcloud-config.git

b.仓库添加三个配置文件

config-dev.yml

config:info: master branch,springcloud-config/config-dev.yml version=1

config-test.yml

config:info: master branch,springcloud-config/config-test.yml version=1

config-prod.yml

config:info: master branch,springcloud-config/config-prod.yml version=1

c.新建模块

scdemo-config-server模块
依赖

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></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><scope>runtime</scope><optional>true</optional></dependency></dependencies>

配置

server:port: 3344spring:application:name: scdemo-config-servercloud:config:server:git:uri: https://gitee.com/iacs/scdemo-config.gitusername: wu_peizhen@126.compassword: ww458141458141search-paths:- scdemo-config# 读取分支label: master# 服务注册到Eureka
eureka:client:service-url:defaultZone: http://localhost:7001/eureka

启动类
com.gaoliang.springcloud.configserver.ConfigServerApplication

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

启动eureka和config模块,访问:

格式: /{label}/{application}-{profile}.yml

http://localhost:3344/master/config-dev.yml

省略分支,默认master

http://localhost:3344/config-dev.yml
可见配置内容
访问:
http://localhost:3344/config/dev/master
可返回json串

8.3 客户端获取配置信息

新建模块

scdemo-config-client
依赖与server相同,只需把spring-cloud-config-server修改为

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

配置

bootstrap.yml是系统级的,优先级更高

server:port: 3355spring:application:name: scdemo-config-client# config客户端配置cloud:config:# 分支名称label: master# 配置文件名称name: config# 读取后缀名称profile: dev# 配置中心地址uri: http://localhost:3344# 服务注册到Eureka
eureka:client:service-url:defaultZone: http://localhost:7001/eureka

c.启动类

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

新建Controller

com.gaoliang.springcloud.controller.ConfigClientController.java
@RestController
@RefreshScope
public class ConfigClientController {// 因为config仓库以rest形式暴露,所以所有客户端都可以通过config服务端访问到github上对应的文件信息@Value("${config.info}")private String configInfo;@Value("${server.port}")private String serverPort;@GetMapping("/configInfo")public String getConfigInfo() {return "serverPort: " + serverPort + "\t\n\n configInfo" + configInfo;}
}

启动项目,访问: http://localhost:3355/configInfo
可以获取到配置信息
修改bootstrap.yml的分支名称和profile即可获取其他分支其他环境的配置

8.4 配置实时刷新

问题1:

修改git上配置文件的内容,服务端获取的配置实时更新但客户端接口获取的配置没有实时更新
客户端重启后可以获取,但生产环境重启繁琐

curl -X POST "http://localhost:3355/actuator/refresh"

客户端配置实时更新

i. 确保依赖了spring-boot-starter-actuator
ii. 配置

# 暴露监控端点
management:endpoints:web:exposure:include: "*"

iii. controller添加@RefreshScope注解

@RefreshScope
public class ConfigClientController {
...

此时配置仍无法实时刷新,需要发送post请求刷新
iv.post请求刷新配置

curl -X POST "http://localhost:3355/actuator/refresh"

问题2:

如果有多台客户端,需要写多个脚本刷新多个客户端配置,繁琐;
使用消息总线解决.

9.消息总线-Spring Cloud Bus

9.1 概述

可实现配置广播刷新,并对个别实例差异化处理
支持两种消息代理: rabbitmq和kafka
使用消息代理构建共用消息主题,消息被所有实例监听和消费

基本原理

实例监听mq中同一个topic,一个服务刷新数据时,把信息放入topic中,其他实例得到通知更新自身配置

9.2 rabbitmq环境搭建

centos7安装rabbitmq(自备环境)

9.3 Bus动态刷新全局广播配置

两种设计思想

i.消息总线触发一个客户端refresh,从而刷新所有客户端配置
ii.消息总线触发一个服务端refresh,从而刷新所有客户端

选型

i打破了微服务唯一职责性,破坏各节点对等性
i具有局限性,如微服务迁移时,网络地址发生变化,要做到自动刷新,就会增加更多的修改
因此选择ii

项目配置

i. scdemo-config-server模块
添加依赖

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

添加配置
** 注意:
rabbitmq是spring的子项,否则项目不会报错,但执行刷新请求会报错,项目会连接localhost:5672的mq地址

spring:# rabbitmq相关配置rabbitmq.:host: 192.168.68.101port: 5672username: guestpassword: guest# rabbitmq相关配置,暴露bus刷新配置端点
management:endpoints:web:exposure:include: "bus-refresh"

ii. scdemo-config-client模块
添加依赖

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

添加配置

spring:# rabbitmq相关配置rabbitmq.:host: 192.168.68.101port: 5672username: guestpassword: guest

scdemo-config-client启动3355和3366两个实例
此时修改git配置,config-server实时更新,两个客户端实例不更新
iii.执行脚本

curl -X POST "http://localhost:3355/actuator/refresh"

可见两个客户端配置同时被刷新
rabbitmq管控台可以看到springCloudBus exchange

9.4 Bus动态刷新定点通知

# 通过路径指定实例: scdemo-config-client:3355
curl -X POST "http://localhost:3344/actuator/bus-refresh/scdemo-config-client:3355"

附: 异常

Failed to configure a DataSource: 'url' attribute...

1.mvn install后问题解决
2.项目依赖了mybatis的druid的starter但没有配置数据库,去掉依赖后正常启动

Caused by: org.eclipse.jgit.api.errors.TransportException: https://gitee.com/iacs/scdemo-config.git: Authentication is required but no CredentialsProvider has been registered

Spring Cloud入门上相关推荐

  1. Spring Cloud 入门 之 Config 篇(六)

    一.前言 随着业务的扩展,为了方便开发和维护项目,我们通常会将大项目拆分成多个小项目做成微服务,每个微服务都会有各自配置文件,管理和修改文件起来也会变得繁琐.而且,当我们需要修改正在运行的项目的配置时 ...

  2. Spring Cloud 入门 之 Zuul 篇(五)

    一.前言 随着业务的扩展,微服务会不对增加,相应的其对外开放的 API 接口也势必增多,这不利于前端的调用以及不同场景下数据的返回,因此,我们通常都需要设计一个 API 网关作为一个统一的 API 入 ...

  3. Spring Cloud 入门 之 Hystrix 篇(四)

    一.前言 在微服务应用中,服务存在一定的依赖关系,如果某个目标服务调用慢或者有大量超时造成服务不可用,间接导致其他的依赖服务不可用,最严重的可能会阻塞整条依赖链,最终导致业务系统崩溃(又称雪崩效应). ...

  4. Spring Cloud 入门 之 Feign 篇(三)

    一.前言 在上一篇文章<Spring Cloud 入门 之 Ribbon 篇(二)> 中介绍了 Ribbon 使用负载均衡调用微服务,但存在一个问题:消费端每个请求方法中都需要拼接请求服务 ...

  5. Spring Cloud 入门 之 Ribbon 篇(二)

    一.前言 上一篇<Spring Cloud 入门 之 Eureka 篇(一)> 介绍了微服务的搭建,服务注册与发现.但在文章中留了一个小尾巴--如何正确使用 Eureka 进行服务发现并调 ...

  6. Spring Cloud 入门 之 Eureka 篇(一)

    一.前言 Spring Cloud 是一系列框架的有序集合.它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据 ...

  7. Spring Cloud入门教程(二):客户端负载均衡(Ribbon)

    对于大型应用系统负载均衡(LB:Load Balancing)是首要被解决一个问题.在微服务之前LB方案主要是集中式负载均衡方案,在服务消费者和服务提供者之间又一个独立的LB,LB通常是专门的硬件,如 ...

  8. Spring Cloud入门-Admin服务监控中心(Hoxton版本)

    文章目录 Spring Cloud入门系列汇总 摘要 Spring Boot Admin 简介 创建admin-server模块 创建admin-client模块 监控信息演示 结合注册中心使用 修改 ...

  9. Spring Cloud入门-Sentinel实现服务限流、熔断与降级(Hoxton版本)

    文章目录 Spring Cloud入门系列汇总 摘要 Sentinel简介 安装Sentinel控制台 创建sentinel-service模块 限流功能 创建RateLimitController类 ...

最新文章

  1. day042前端之HTML
  2. spm oracle cloud,oracle11g新特点——SQLPlanManagement(SPM)-Oracle
  3. 《Java编程思想》10.6 匿名内部类奇怪的代码
  4. B1007 素数对猜想
  5. MyEclipse运行时自动保存
  6. [多线程] Thread
  7. 双非本科上岸北大,复试成绩专业第一!
  8. 大班音乐机器人反思_大班音乐教案:机器人教案及教学反思
  9. AI产品开发的核心原则:以研究为核心驱动
  10. 【Python古诗词鉴赏小程序】千古绝唱,精选中国最美古诗句,经典咏流传~
  11. Origin2021学习版申请与安装
  12. 2021华为秋招算法工程师面试经历(实习过)
  13. 计算机网络实验|DNS 域名服务协议
  14. 财务会计之借贷记账法的【科目方向】和【科目余额方向】分析
  15. java xml特殊字符_mybatis xml中特殊字符处理及特殊符号
  16. 负载均衡器之F5和Nginx
  17. QQ好友不在线也可发送自定义表情(转)
  18. 支付宝支付(详细版)
  19. LeetCode刷题记 --- pta 7- 4
  20. 基于云服务器 B/S模式 JavaWeb RFID 图书借阅管理系统

热门文章

  1. pulltorefresh刷新
  2. 企业经济性裁减人员规定——劳动合同法小全书
  3. 测试手机信号格数软件,手机信号格数显示测试是怎么测试的啊?帮忙指导一下吧...
  4. 2021-06-23对深度学习模型进行更小,更快,更好的综述——微软研究院
  5. 深度揭秘亚马逊无货源运营思路,掌握运营技巧
  6. 咨询入门教程之二--ERP项目咨询顾问的五大能力
  7. 办公室局域网打印机共享设置
  8. VK1621/VK1622/VK1623/VK1625有什么稳定的LCD液晶显示驱动芯片支持多种封装,提供专业工程服务
  9. 【转】与BT下载相关的概念
  10. 如何开发微信公众号以及如何运营微信公众号