SpringCloud学习笔记
1.学习前言
学习前提
- 熟练使用SpringBoot 微服务快速开发框架
- 了解过Dubbo + Zookeeper 分布式基础
1.2常见的面试题
1. 什么是我服务?
2. 微服务之间是如何独立通讯的?
3. springcloud和dubbo有哪些区别?
4. springboot和springcloud之前有神区别,谈谈对他们的理解。
5. 什么是微服务熔断?什么是微服务降级?
6. 微服务的优缺点分别是什么?说下你在项目开发中遇到的坑。
7. 你所知道的微服务技术栈有哪些?列举一二。
8. Eureka和Zookeeper都可以提供微服务注册与发现的功能,说说两者区别。
2.微服务概述
2.1什么是微服务?
- 微服务(Microservice Architecture)是近几年流行的一种思想架构。
- 就目前而言,业内并没有对微服务有一个统一的定义。
- 但通常而言,微服务是一种架构,或者说是一种风格,他是将单一的应用划分为一组小的服务,每个服务运行在独立的自己的进程内,服务之间互相协作,互相配置,服务之间采用轻量级的通信机制(HTTP)互相沟通,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应用尽量避免统一的集中式的服务管理机制,对具体的一个服务而言,应该根据上下文,选择合适的语言,工具(maven)对其进行构建,可以有一个非常轻量级的集中式管理协调这些服务,可以使用不同的语言来编写服务,可以使用不同的数据存储。
- 再从技术角度分析
- 微服务的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底的去耦合,每一个服务提供单个业务功能,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行独立的启动或者销毁,拥有自己独立的数据库。
2.2微服务与微服务架构
-
微服务
- 强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应用,狭义的看,可以看作是IDEA中的一个个微服务工程,或者Moudel。IDEA 工具里面使用Maven开发的一个个独立的小Moudel,它具体是使用SpringBoot开发的一个小模块,专业的事情交给专业的模块来做,一个模块就做着一件事情。强调的是一个个的个体,每个个体完成一个具体的任务或者功能。
-
微服务架构
- 一种新的架构形式,Martin Fowler 于2014年提出。微服务架构是一种架构模式,它体长将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制(如HTTP)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)对其进行构建。
2.3微服务的优缺点
-
优点
- 单一职责原则
- 每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或者业务需求。
- 开发简单,开发效率高,一个服务可能就是专一的只干一件事
- 微服务能够被小团队单独开发,这个团队只需2-5个开发人员组成
- 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的
- 微服务能使用不同的语言开发
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果,无需通过合作才能体现价值
- 微服务允许利用和融合最新技术
- 微服务只是业务逻辑的代码,不会和HTML,CSS,或其他的界面混合
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一的数据库
-
缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 系统部署依赖问题
- 服务间通信成本问题
- 数据一致性问题
- 系统集成测试问题
- 性能和监控问题
-
微服务的技术栈有哪些?
3.springCloud入门概述
3.1springcloud是什么?
springcloud官网:https://spring.io/
3.2springcloud和springboot的关系
- SpringBoot专注于开发方便的开发单个个体微服务
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务
- SpringBoot可以离开SpringCloud独立使用,开发项目,但SpringCloud离不开SpringBoot,属于依赖关系
- SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架
3.3Dubbo和springcloud技术选型
-
1.分布式+服务治理 Dubbo
目前成熟的互联网架构,应用服务化拆分 + 消息中间件 -
2.Dubbo 和 SpringCloud对比
可以看一下社区活跃度:
https://github.com/dubbo
https://github.com/spring-cloud -
对比结果:
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服务网关 | 无 | Spring Cloud Netfilx Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总栈 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
3.5springcloud能干啥?
- Distributed/versioned configuration 分布式/版本控制配置
- Service registration and discovery 服务注册与发现
- Routing 路由
- Service-to-service calls 服务到服务的调用
- Load balancing 负载均衡配置
- Circuit Breakers 断路器
- Distributed messaging 分布式消息管理
4.springcloud Rest学习环境搭建:服务提供者
4.1介绍
-
我们会使用一个Dept部门模块做一个微服务通用案例Consumer消费者(Client)通过REST调用Provider提供者(Server)提供的服务。
-
回顾Spring,SpringMVC,Mybatis等以往学习的知识。
-
Maven的分包分模块架构复习。
一个简单的Maven模块结构是这样的:- app-parent: 一个父项目(app-parent)聚合了很多子项目(app-util\app-dao\app-web…)
- pom.xml
- app-core
- pom.xml
- app-web
- pom.xml
- …
- app-parent: 一个父项目(app-parent)聚合了很多子项目(app-util\app-dao\app-web…)
一个工程带多个Moudule子模块。
MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)
- microservicecloud-api 【封装的整体entity/接口/公共配置等】
- microservicecloud-consumer-dept-80 【服务提供者】
- microservicecloud-provider-dept-8001 【服务消费者】
4.2springcloud版本选择
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) |
4.3创建父工程
- 新建父工程项目springcloud,切记Packageing是pom模式
- 主要是定义POM文件,将后续各个子模块公用的jar包等统一提取出来,类似一个抽象父类
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.haust</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>springcloud-provider-dept-8001</module>
<module>springcloud-consumer-dept-80</module>
<module>springcloud-eureka-7001</module>
<module>springcloud-eureka-7002</module>
<module>springcloud-eureka-7003</module>
<module>springcloud-provider-dept-8002</module>
<module>springcloud-provider-dept-8003</module>
<module>springcloud-consumer-dept-feign</module>
<module>springcloud-provider-dept-hystrix-8001</module>
<module>springcloud-consumer-hystrix-dashboard</module>
<module>springcloud-zuul-9527</module>
<module>springcloud-config-server-3344</module>
<module>springcloud-config-client-3355</module>
<module>springcloud-config-eureka-7001</module>
<module>springcloud-config-dept-8001</module>
</modules>
<!--打包方式 pom-->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springCloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--SpringBoot 启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--日志测试~-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
父工程为springcloud,其下有多个子mudule,详情参考完整代码了解
springcloud-consumer-dept-80访问springcloud-provider-dept-8001下的controller使用REST方式
如DeptConsumerController.java
/**
* @Auther: csp1999
* @Date: 2020/05/17/22:44
* @Description:
*/
@RestController
public class DeptConsumerController {
/**
* 理解:消费者,不应该有service层~
* RestTemplate .... 供我们直接调用就可以了! 注册到Spring中
* (地址:url, 实体:Map ,Class<T> responseType)
* <p>
* 提供多种便捷访问远程http服务的方法,简单的Restful服务模板~
*/
@Autowired
private RestTemplate restTemplate;
/**
* 服务提供方地址前缀
* <p>
* Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
*/
private static final String REST_URL_PREFIX = "http://localhost:8001";
//private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
/**
* 消费方添加部门信息
* @param dept
* @return
*/
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
// postForObject(服务提供方地址(接口),参数实体,返回类型.class)
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
/**
* 消费方根据id查询部门信息
* @param id
* @return
*/
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
// getForObject(服务提供方地址(接口),返回类型.class)
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
/**
* 消费方查询部门信息列表
* @return
*/
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
使用RestTemplete先需要放入Spring容器中
ConfigBean.java
@Configuration
public class ConfigBean {
//@Configuration -- spring applicationContext.xml
//配置负载均衡实现RestTemplate
// IRule
// RoundRobinRule 轮询
// RandomRule 随机
// AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
// RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
springcloud-provider-dept-8001的dao接口调用springcloud-api模块下的pojo,可使用在springcloud-provider-dept-8001的pom文件导入springcloud-api模块依赖的方式:
<!--我们需要拿到实体类,所以要配置api module-->
<dependency>
<groupId>com.haust</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
springcloud-consumer-dept-80和springcloud-provider-dept-8001的pom.xml和父工程下的依赖基本一样,直接看完整代码即可,不再添加重复笔记。
5.Eureka服务注册中心
5.1什么是EureKa
- Netflix在设计Eureka,遵循的就是api原则。
- Eureka是Netflix的子模块,也是核心模块之一。Eureka是基于REST的服务,用于定位服务,以实现云端中间件层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不是修改服务调用的配置文件,功能类似于Dubbo的注册中心,比如Zookeeper
5.2原理理解
-
Eureka基本的架构
- springcloud封装了Netflix公司开发的eureka模块来实现服务的注册与发现。
- eureka采用了C-S的架构设计,eurekaServer作为服务注册功能的服务器,他是服务注册中心。
- 系统中的其他服务,使用eureka的客户端连接到eurekaserver并维持心跳连接,这样系统维护人员就可以通过eurekaserver来监控系统中各个微服务是否正常运行,springcloud的一些其他模块就可以通过eurekaserver来发现系统中的其他微服务并执行相关逻辑。
-
和Dubbo架构对比。
-
Eureka包含两个组件:EurekaServer和EurekaClient.
-
Eureka server提供服务注册,各个节点启动后,回到EurekaServer中进行注册,这样eureka server中的服务注册表中将会存储所有可用服务节点信息,服务节点的信息可以在界面中直观的看到。
-
Eureka client 是一个Java客户端,用于简化Eureka server的交互,客户端同事也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServcer发送心跳,如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册中心把这个服务节点移除掉。(默认周期为90S)
-
三大角色
- Eureka Server:提供服务的注册与发现
- Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能够找到
- Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务。
5.3构建步骤
- Eureka-server
- pom.xml 文件
<!--导包~-->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<!--导入Eureka Server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- applicaltion.yml
server:
port: 7001
Eureka配置
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false # fetch-registry如果为false,则表示自己是注册中心
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- EurekaServer启动类
@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer服务端的启动类,可以接受别人注册进来
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
- 启动成功后访问http://localhost:7001/得到以下页面
- eureka client
- 导入Eureka依赖
<!--Eureka依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- application中新增Eureka配置
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springCloud-provider-dept #修改eureka上面的默认描述信息
- 主启动类上加上@EnableEurekaClient注解
@SpringBootApplication
@MapperScan(basePackages = {"com.cy.*.dao"})
@EnableEurekaClient //在服务启动后自动注册到eureka服务中
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
- 可以在Eureka注册管理页面中看到SPRINGCLOUD-PROVIDER-DEPT服务已经注册进去了
- 修改Eureka上的默认描述信息
如果停掉SPRINGCLOUD-PROVIDER-DEPT服务,等待30秒后,监控会开启保护机制:
- 配置关于服务加载的监控信息
- pom.xml中添加依赖
<!--actuator完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- application.yml中的配置
# info配置
info:
# 项目的名称
app.name: cy.springCloud
# 公司的名称
company.name: cy.springCloud.com
点击后会跳转到页面
6.负载均衡Ribbon
6.1什么是负载均衡
Ribbon是一个基于Http和tcp的客户端负载均衡工具,基于Netflix Ribbon实现的,它不像spring cloud 服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个Spring Cloud微服务中.包括Fegin提供的声明式服务调用也是基于Ribbon实现的.Ribbon默认提供很多种负载均衡算法,列入轮询,随机等。
6.2负载均衡的实现方式
- @LoadBalanced 注解
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //负载均衡的实现方式
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- ClientHttpRequestInterceptor接口
ClientHttpRequestInterceptor是一个客户端发送请求的拦截器,而restful正是使用http请求方式,所以会被拦截。ClientHttpRequestInterceptor中只有一个intercept()方法。
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
ClientHttpResponse intercept(HttpRequest var1, byte[] var2, ClientHttpRequestExecution var3) throws IOException;
}
- LoadBalancerInterceptor
LoadBalancerInterceptor实现了ClientHttpRequestInterceptor ,所以也实现了intercept()方法。
当我们在客户端发起请求的时候,我们的请求会被拦截。
会获取到一个请求路径,并且通过请求路径会获得一个服务名称,这个服务名称就是我们在eureka中注册的服务名,然后再将服务名称交给loadBalancer对象去执行。
通过代码中得知,loadBalancer对象是RibbonLoadBalancerClient,它实现了LoadBalancerClient接口。
当我们点进execute方法中,会根据我们的serverID拿到loadBalancer对象,这里面包含了注册的服务,然后再通过getServer完成负载均衡筛选。
loadBalancer对象中注册的服务。
6.3负载均衡策略
Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口就是一种规则:
- 通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 配置文件方式:在application.yml文件中,添加新的配置修改规则
6.4负载均衡饥饿加载
Ribbon默认采用是懒加载,即在第一次访问时才会去创建LoadBalanceClient,请求时间会很长,而饥饿加载则会在项目启动时去创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
7.Nacos注册中心
7.1认识Nacos
nacos是阿里巴巴的产品,现在是springcloud中的一个组件,想比eureka功能更加丰富,在国内受欢迎程度较高。
中文官网:https://nacos.io/zh-cn/
在GitHub中下载nacos安装包
1.4.4版本比较稳定,解压得到文件夹目录
在bin目录下,双击startup.cmd启动nacos
启动成功,http://192.168.3.160:8848/nacos/index.html这是nacos的地址
7.2服务注册到nacos中
- 在父工程中引入nacos的管理依赖
<!-- nacos的管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- 注释掉原有服务中的eureka依赖
- 添加nacos的客户端依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 配置application.yml中nacos配置
spring:
application:
name: springCloud-consumer #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务
7.3nacos服务分级模型
- nacos服务分级存储模型
- 一级是服务,例如userserice
- 二级是集群,例如上海南京
- 三级是实例,例如上海机房某台部署了userservice的服务器
- 如何设置实例集群属性
- 修改application.yml文件,添加spring.cloud.discovery.cluster-name属性
spring:
application:
name: springCloud-consumer #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务
cluster-name: SH #集群名称 代指南京
7.4nacos环境隔离-namespace
- 在NACOS 控制台可以创建namespace,用来隔离不同环境。
- 然后填写一个新的命名空间信息
- 保存后会在控制台看到这个命名空间的id
- 修改application.yml,添加namespace:
8.nacos配置管理
8.1新建配置
- 在nacos中添加配置信息:
- 在弹出的表单中填写配置信息
8.2获取nacos中的配置文件
- 配置文件获取步骤
- 引入nacos的配置管理客户端依赖
- 在项目的resource目录下添加一个bootstrap.yml文件,这个就是引导文件,优先级高于application.yml
- 在服务中将pattern.dateformat这个属性注入到controller中
8.3nacos配置热更新
nacos中的配置文件变更后,微服务无需重启就可以感知,不过需要通过下面两种配置实现。
- 方式一:在@value注入的变量所在类上添加注解@RefreshScope
方式二:使用@ConfigurationProperties注解来实现
8.4多环境配置共享
- 配置文件的优先级
- 服务名-profile.yaml > 服务名称.yaml > 本地配置
9.Feign远程调用
Feign 是一个声明式的 REST 客户端,它能让 REST 调用更加简单。Feign 供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。官方地址:https://github.com/OpenFeign/feign
而 Feign 则会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。
9.1定义和使用Feign客户端
- 使用feign的步骤如下:
1. 引入依赖
- 在启动类上假如注解开启feign的功能。
- 编写feign客户端
主要是基于springmvc的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式: get
- 请求路径: /user/{id}
- 请求参数: Long id
- 返回值类型: User
9.2feign自定义配置
feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
- 配置feign日志有两种方式:
- 方式一:配置文件方式
- 全局生效:
- 局部生效
- 全局生效:
- 方式一:配置文件方式
- 方式二:Java代码方式,需要先声明一个bean
- 然后如果配置全局,则需要把它放到@EnableFeignClients这个注解中:
- 如果是局部配置,则把它放到@FeignClient这个注解中:
9.3feign的性能优化
feign底层的客户端实现:
- URLConnection: 默认实现,不支持连接池
- APACHE HttpClient: 支持连接池
- OKHttp: 支持连接池
因此优化feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好使用basic或者none
feign添加httpclient的支持:
引入依赖:
配置连接池:
9.4feign的最佳实践
方式一(继承):给消费者的FeignClient和提供者的Controller定义统一的父接口作为标准
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
9.4抽取feignClient
实现最佳实践方式二的步骤如下:
- 首先创建一个module,命名为feign-api,然后引入feign的starter依赖
- 然后将order-service中编写的UserClient、user、defaultFeignConfiguration都复制到feign-api项目中
- 在order-service中引入feign-api依赖
- 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包。
- 重启测试
当定义的feignClient不在springbootApplication的扫描包范围内,这些feignClient无法使用。有两种方式解决
方式一: 指定feignClient所在包
方式二:指定feignClient字节码
10.网关Gateway
10.1什么是网关
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
10.2网关功能
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
10.3网关的技术实现
在springcloud中网关的实现包括两种
- gateway
- zuul
Zuul是基于servlet的实现,属于阻塞式编程,而springcloudGateway则是基于spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
10.4搭建网关服务
搭建网关服务的步骤:
- 创建新的module,引入springcloudGateway的依赖和nacos的服务发现和依赖:
- 编写路由配置及nacos地址
10.5路由断言工厂
- 我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
- 例如Path=/user/**是按照路径匹配,这个规则是由org.springframework.cloud.handler.predicate.PathRoutePredicateFactory类来处理的
- 像这样的断言工厂springcloudGateway还有十几个
10.6路有过滤器GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
spring提供了31种不同的路由过滤器工厂,例如:
案例:给所有进入userservice的请求添加一个请求头Truth,123456
实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器
10.7全局过滤器GlobalFilter
全局过滤器的作用也是出力一切进入网关的请求和微服务响应,与gatewayFilter的作用一样,区别在于gatewayFilter通过配置定义,处理逻辑是固定的。而globalFilter的逻辑需要自己去写代码实现。
定义方式是实现GlobalFilter接口。
总结:
- 实现GlobalFilter接口
- 添加@Order注解或实现Ordered接口
- 编写处理逻辑
10.8过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前,
- GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定Order值,由我们自己定
- 路由过滤器和DefaultFilter的order是由spring指定,默认是按照声明顺序从1递增
- 当过滤器的order值一样时,会按照DefaultFilter>路由过滤器>GlobalFilter顺序执行。
10.8跨域问题处理
跨域:域名不一致就是跨域,主要包括:
- 域名不同:www.taobao.com和www.taobao.org和www.jd.com和miaosha.jd.com
- 域名相同,端口不同:localhost:8080和localhost:8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截问题
解决方案:cors
网关处理跨域采用同样的CORS方案,并且只需要简单配置即可实现:
11.初识MQ
什么是MQ
MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列,也就是事件驱动架构中的broker。
11.1.rabbitMq快速入门
- 下载镜像
docker pull rabbitmq:3-management - 执行下面的命令来运行MQ容器
11.2.RabbitMq的结构和概念
RabbitMQ中的几个概念:
- channel:操作MQ的工具
- exchange:路由消息到队列(交换机)
- queue:缓存消息
- virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
常见消息模型
MQ的官方文档中给出了5个MQ的Demo示例,对应了几种不同的用法:
- 基本消息队列(BasicQueue)
- 工作消息队列(WorkQueue)
- 发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种:
- Fanout Exchange:广播
- Direct Exchange: 路由
- Topic Exchange: 主题
HelloWorld案例
官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:
- publisher:消息发布者,将消息发送到队列queue
- queue:消息队列,负责接受并缓存消息
- consumer:订阅队列,处理队列中的消息
11.3.SpringAMQP
- 步骤1:引入AMQP依赖
因为publisher和consumer服务都是需要依赖AMQP,因此这里把依赖直接放到父工程mq-demo中:
- 步骤2:在publisher中编写测试方法,向simple.queue发送消息
- 在publisher服务中编写application.yml,添加mq链接信息:
2.在publisher服务中新建一个测试类,编写测试方法:
步骤3:在consumer中编写消费逻辑,监听simple.queue
- zai consumer服务中编写application.yml,添加mq连接信息:
- 在cpmsumer服务中新建一个类,编写消费逻辑: