零、微服务注册中心、配置中心
0.1 组件选择
02、SpringCloud组件.pdf
注册中心:SpringCloud Alibaba Nacos(服务发现/注册)
配置中心:SpringCloud Alibaba Nacos(动态配置管理)
负载均衡:SpringCloud Ribbon
声明式HTTP客户端:SpringCloud Feign ——调用远程服务
负载均衡:SpringCloud Ribbon —— feign中已经整合,无需显示引用
服务容错:SpringCloud Alibaba Sentinel ——限流、降级、熔断
API网关:SpringCloud Gateway ——webflux 编程模式
调用链路监控:SpringCloud Sleuth
分布式事务:SpringCloud Alibaba Seata ——原Fescar,即分布式事务解决方案
依赖&组件版本对应关系
SpringBoot、SpringCloud、SpringCloud Alibaba及各组件版本关系
父pom引入对应版本的依赖
<properties>
<cloud.alibaba.version>2.2.7.RELEASE</cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
0.2 Nacos作为注册中心
0.2.0 Nacos启动
前面SpringCloud Alibaba Nacos服务注册和配置中心笔记中有详细的记载。我这里下载的是nacos2.0.3 windows版(与上面的版本对应)
解压,bin目录下打开cmd 输入:startup.cmd -m standalone
(通过单机模式启动)
注:2.0版本默认通过集群模式启动,视频中的1.x版本默认通过单机模式启动,1.x版本直接双击startup.cmd即可运行单机版cos
然后访问:http://192.168.109.1:8848/nacos/#/login 默认账号密码均为nacos
0.2.1 common模块中引入nacos-discovery依赖
因为其他微服务都需要注册到Nacos中,根据依赖的传递性,直接在common中引入nacos依赖即可。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
0.2.1 gulimall-product注册到nacos
yml:
# 配置数据源
spring:
datasource:
username: root
password: 10086
url: jdbc:mysql://192.168.190.135:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
# 这里开始是配置nacos
application:
name: gulimall-product # 注册到nacos中的服务名
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos注册中心的地址,这里因为是本机所以使用localhost
# 配置mybatis-plus
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml # 配置sql映射目录
global-config:
db-config:
id-type: auto # 配置主键自增
server:
port: 10000
主启动类别忘加上@EnableDiscoveryClient注解。
启动gulimall-product测试(启动前nacos一定要先启动):
gulimall-product成功注册进nacos
其他服务使用同样的方式配置即可
0.3 引入(Open)Feign远程服务调用
例子:会员想要从优惠券服务中获取当前会员领取到的所有优惠券。
会员服务会从nacos寻找优惠券服务,然后远程调用优惠券服务获得信息。
(Member的service层—->调用—->Coupon的controller层)
远程调用其他服务步骤:
- 引入open-feign
- 调用方Service编写一个接口,告诉SpringCloud这个接口需要调用远程服务(一般会放在feign这个包下),会使用到@FeignClient注解
- 声明接口的每一个方法都需要指明调用哪个远程服务的哪个请求
- Controller层定义一个与Service层对应的方法
主启动类开启远程调用功能 @EnableFeignClient
0.3.0 common引入Feign依赖
我们之前已经在父pom中引入了openfeign的依赖,所以这里不再需要引入
0.3.1 Coupon与Member微服务分别添加Feign调用
1. CouponController定义接口
首先我们需要在被调用的微服务的Controller层定义一个接口供调用方调用(这里为了方便起见只在controller层进行了响应的修改,实际上需要在service层与dao层都实现相应方法)
@RestController
@RequestMapping("gulimallcoupon/coupon")
public class CouponController {
@RequestMapping("/member/list")
public R memberCoupons() {
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满100减10");
return R.ok().put("coupons", couponEntity);
}
}
2. Member Service层定义远程调用接口
首先需要在Service层上增加@FeignClient(value = “微服务名称”)注解指明需要调用哪个微服务
- 定义方法接口,并声明要调用的微服务接口的rest地址
- 这里定义方法接口的方法签名一定要和微服务提供者(Coupon)的controller中调用的方法签名(最好直接复制controller里面的方法)一致。
- 如果需要传递参数,那么
@RequestParam
和@RequestBody
@PathVariable
等不能省略,必需加。
当前接口方法的含义:当前服务调用该方法,会到服务注册中心找到 gulimall-coupon
服务,去调用该服务的/gulimallcoupon/coupon/member/list
请求对应的方法。
package com.atguigu.gulimall.gulimallmember.feign;
import com.atguigu.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author MrLinxi
* @Description 远程调用gulimall-coupon微服务的/gulimallcoupon/coupon/member/list请求方法
* @create 2021-12-11-14:45
*/
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
@RequestMapping("/gulimallcoupon/coupon/member/list")
public R memberCoupons();
}
3. Member Controller层定义对应Service层方法
@RestController
@RequestMapping("gulimallmember/member")
public class MemberController {
@Autowired
private CouponFeignService couponFeignService;
/**
* 远程调用Coupon微服务,获取当前Member的优惠券信息
*/
@RequestMapping("/coupons")
public R feignTest() {
MemberEntity memberEntity = new MemberEntity();
memberEntity.setNickname("张三");
R memberCoupons = couponFeignService.memberCoupons();
return R.ok().put("member", memberEntity).put("coupons", memberCoupons.get("coupons"));
}
}
4. Member主启动类开启远程调用
主启动类加上@EnableFeignClient(basePackages = “xxx.xxx.xxx”)注解
注:@EnableFeignClient注解默认会扫描注解所在包的当前包以及子包下的@FeignCilent,如果需要扫描其他包下的FeignClient,则需要单独使用basePackages指定包的位置。这里我们的@FeignClient在主启动类所在包的子包下,所以理论上可以不用指明包路径。(之前SpringCloud教程中,也没有指定包路径,就是因为这个原因)
package com.atguigu.gulimall.gulimallmember;
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.openfeign.EnableFeignClients;
@MapperScan("com.atguigu.gulimall.gulimallmember.dao")
@SpringBootApplication
@EnableDiscoveryClient
//@EnableFeignClients(basePackages = "com.atguigu.gulimall.gulimallmember.feign")
@EnableFeignClients
public class GulimallMemberApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallMemberApplication.class, args);
}
}
0.3.2 测试
分别启动gulimall-coupon与gulimall-member服务。访问http://localhost:8000/gulimallmember/member/coupons
我们停掉gulimall-coupon服务,再次访问,调用失败
0.4 Nacos作为配置中心
参考:官方介绍
0.4.0 之前获取配置文件信息的方式
我们创建application.properties
配置文件,添加如下内容(随便写)
coupon.user.name=zhangsan
coupon.user.age=30
在com.atguigu.gulimall.gulimallcoupon.controller.CouponController中,增加以下内容
@Value("${coupon.user.name}") //获取配置文件中coupon.user.name的值
private String name;
@Value("${coupon.user.age}") //获取配置文件中coupon.user.age
private Integer age;
@RequestMapping("/configtest")
public R nacosConfigTest() {
return R.ok().put("name", name).put("age", age);
}
启动访问http://localhost:7000/gulimallcoupon/coupon/configtest,发现获取到了application.properties配置文件中的值
这样做存在一个问题,如果服务启动之后需要修改配置文件,那么就需要频繁的重新部署微服务,针对这个问题,我们使用nacos作为配置中心,直接从nacos上获取相应配置
0.4.1 common模块引入nacos-config依赖
<!--naocs config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
注:springboot 2.4及以上版本需要添加spring-cloud-starter-bootstrap依赖
0.4.2 使用bootstrap.yml配置Nacos Config的元数据
这里以gulimall-coupon为例
首先,这里需要新建一个bootstrap.yml配置文件。原因:Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。
全局的放在:bootstrap.yml 自己的放在:application.yml
之前在SpringCloud中详细介绍过nacos作为配置中心的使用方法:Nacos作为服务配置中心演示
bootstrap.yml
spring:
application:
name: gulimall-coupon
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml # 默认properties,视频中没有配置这个,nacos不支持yml
# namespace: 命名空间的ID # 默认public
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
application.yml
application.yml中可以通过配置spring.profiles.active来指定不同环境(dev/pro/test),实现切换不同环境下的配置。这里视频中没有配置这个。如果不写就会拼接成:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
spring:
profiles:
active: dev # 表示开发环境
0.4.3 业务类controller新建测试接口
com.atguigu.gulimall.gulimallcoupon.controller.CouponController
通过Spring Cloud原生注解@RefreshScope 实现配置动态更新
@RestController
@RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
@RequestMapping("gulimallcoupon/coupon")
public class CouponController {
@Value("${coupon.user.name}") //获取配置文件中coupon.user.name的值
private String name;
@Value("${coupon.user.age}") //获取配置文件中coupon.user.age
private Integer age;
/**
* nacos 作为配置中心测试
* @return
*/
@RequestMapping("/configtest")
public R nacosConfigTest() {
return R.ok().put("name", name).put("age", age);
}
}
0.4.4 测试从nacos配置中心获取配置
Nacos中的DataId匹配规则
理论:
- Nacos中的dataid的组成格式与SpringBoot配置文件中的匹配规则
官网:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
注意nacos只识别yaml,不支持yml。
Nacos中新建配置
在配置中心新建一个配置:配置管理->配置列表->右上角有个”+”号
测试
然后我们修改一下nacos上的配置,不需要重新启动微服务即可获取最新配置。
小总结:如何使用Nacos作为配种中心统一管理配置
- 引入依赖:
spring-cloud-starter-alibaba-nacos-config
,springboot 2.4及以上版本需要添加spring-cloud-starter-bootstrap
依赖 - 创建bootstrap.yml配置文件(properties也行):指明服务名、配置中心地址、file-extension、namespace等
- 在nacos中按照DataId匹配规则新建配置文件
- 通过@Value(“${配置项名称}”)注解获取某个配置的值,@RefreshScope直接实现动态更新
- 可以利用命名空间来做环境隔离(dev、test、prod)。
- 每一个微服务之间互相隔离配置,为每一个微服务创建一个namespace,只加载自己命名空间下的所有配置
注意:需要在bootstrap.yml中通过spring.cloud.nacos.config.namespace来指定命名空间(写命名空间的ID,一串类似于UUID)
0.5.2 配置集
一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
0.5.3 配置集ID
Nacos 中的某个配置集的 ID,配置集 ID 是组织划分配置的维度之一,Data ID 通常用于组织划分系统的配置集,一个系统或者应用可以包含多个配置集,一个系统应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识,Data ID 通常采用类 Java 包 如 ( com.taobao.tc.refund.log.level ) 的命名规则保证全局唯一性,此命名规则非强制。可以理解为配置文件名,其符合的匹配规则为:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
0.5.4 配置分组
Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或
Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个
配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置
分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和
MQ_topic 配置。
在bootstrap.yml中通过spring.cloud.nacos.config.group
来指定配置分组
0.5.5 小结
每个微服务创建自己的命名空间,使用配置分组区分开环境(dev、test、prod)
然后我们现在让coupon微服务读取,gulimall-coupon命名空间下的dev分组的配置文件:
读取成功:
0.6 加载多配置集
我们可以将微服务的配置文件中配置的datasource、mybatis都放在nacos中。
注意:bootstrap.yml不能放在nacos中,因为微服务启动的时候需要知道配置中心到底在哪。
将application.yml中的配置均注释掉。
然后在bootstrap.yml中配置需要从nacos中读取的配置文件
spring:
application:
name: gulimall-coupon
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml # 默认properties
group: dev
namespace: fcce9d4a-268e-4a4d-81ac-cf90890d9d94 # 默认public
# 新版本的ext-config被弃用了,使用extension-configs
# 加载nacos中的数据源配置文件
extension-configs[0].data-id: datasource.yaml
extension-configs[0].group: dev
extension-configs[0].refresh: true # 开启动态刷新配置文件
# 加载nacos中的mybatis配置文件
extension-configs[1].data-id: mybatis.yaml
extension-configs[1].group: dev
extension-configs[1].refresh: true # 开启动态刷新配置文件
# 加载nacos中的nacos注册中心以及微服务端口配置文件
extension-configs[2].data-id: other.yaml
extension-configs[2].group: dev
extension-configs[2].refresh: true # 开启动态刷新配置文件
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
启动coupon微服务,可以看到读取了nacos中我们指定的几个配置文件。
测试,访问:http://localhost:7000/gulimallcoupon/coupon/configtest
读取到的是spring.cloud.nacos.config.group指定的dev下的配置文件。
来测试一下数据源有没有配置对,访问:http://localhost:7000/gulimallcoupon/coupon/list
成功连接上了数据库,说明数据源配置成功
0.6.1 总结
- 微服务任何配置信息,任何配置文件都可以放在配置中心中(bootstrap.yml不能放在nacos)
- 只需要在bootstrap.yml中说明加载配置中心中的哪些配置文件即可(bootstrap的作用是告诉微服务去哪里找配置中心读取配置文件)
- 使用@Value,@ConfigurationProperties注解都可以获取配置文件中的值,优先使用配置中心中有的值
一、Gateway作为API网关
官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
SpringCloud Gateway笔记:https://www.yuque.com/mrlinxi/pxvr4g/einthl#m04Ko
网关作为流量的入口,常用功能包括路由转发、权限校验、限流控制等。而 springcloud gateway作为 SpringCloud 官方推出的第二代网关框架,取代了 Zuul 网关。1.1 Gateway的基本概念
API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
- 破坏了服务无状态特点:为了保证对外服务的安全性,我们需要实现对服务访问的权限控制,而开放服务的权限控制机制将会贯穿并污染整个开放服务的业务逻辑,这会带来的最直接问题是,破坏了服务集群中REST API无状态的特点。
- 无法直接复用既有接口:当我们需要对一个即有的集群内访问接口,实现外部服务访问时,我们不得不通过在原有接口上增加校验逻辑,或增加一个代理调用来实现权限控制,无法直接复用原有的接口。
以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性,典型的架构图如图所示:
三大核心概念:
路由(Route):路由是构建网关的基本模块,它由ID,目标URI(Uniform Resource Identifier,统一资源标识符),一系列的断言和过滤器组成,如果断言为true则匹配该路由。
断言(Predicate):开发人员可以匹配Http请求中的所有内容(例如请求头或者请求参数),如果请求参数与断言相匹配则进行路由。
过滤(Filter):指的是Spring框架中的GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
一句话:请求到达网关时,网关先断言请求是否符合某个路由规则,如果符合,那么将请求路由到指定的地方(中间需要一系列的Filter进行过滤)
1.2 创建网关子模块gulimall-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>gulimall</artifactId>
<groupId>com.atguigu.gulimall</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<description>API网关</description>
<version>0.0.1-SNAPSHOT</version>
<artifactId>gulimall-gateway</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
主启动
package com.atguigu.gulimall.gulimallgateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
// 这里用不到数据库,所以排除掉数据源的自动配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class GulimallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallGatewayApplication.class, args);
}
}
配置文件
① application.yml
用于设定注册中心和微服务的端口号
spring:
application:
name: gulimall-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos注册中心的地址
server:
port: 88
② bootstrap.yml
用于指定配置中心,我们先在nacos上创建一个gateway微服务的namespace
spring:
application:
name: gulimall-gateway
cloud:
nacos:
config:
server-addr: localhost:8848 #设置配置中心的地址
file-extension: yaml
namespace: 998783f3-e980-48e5-bba6-6cfc01e748a2
设定路由规则&测试
我们想设定一个路由规则,请求参数url等于baidu的时候转发到百度官网,url=qq的时候转发到qq官网
spring:
application:
name: gulimall-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos注册中心的地址
gateway:
routes:
#Query A 参数有A就行,Query B,C 参数B的值为C即可 C可以是正则表达式
#实现针对于“http://localhost:88/hello?url=baidu”,转发到“https://www.baidu.com/hello”,
#针对于“http://localhost:88/hello?url=qq”的请求,转发到“https://www.qq.com/hello”
- id: baidu_router
uri: https://www.baidu.com # 要转发的地址
predicates:
- Query=url,baidu
- id: qq_router
uri: https://www.qq.com
predicates:
- Query=url,qq
server:
port: 88
建议
其实这里不大需要引入common依赖,直接引入nacos依赖和gateway即可
<dependencies>
<!--GATEWAY 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--naocs 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--naocs config 服务配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>