喵喵商城项目_分布式组件初探

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。

Spring Cloud Alibaba

简介

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。

目前 Spring Cloud Alibaba 提供了如下功能:

  1. 服务限流降级:支持 WebServletWebFlux, OpenFeignRestTemplateDubbo 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  2. 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  3. 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  4. Rpc服务:扩展 Spring Cloud 客户端 RestTemplate OpenFeign,支持调用 Dubbo RPC 服务
  5. 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  6. 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
  7. 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  8. 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Workerschedulerx-client)上执行。
  9. 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

搭配环境

项目中会使用到spring cloud alibaba的相关依赖,因此需要在common项目中导入以进行依赖管理

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Nacos注册中心

Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 在此项目中将作为我们的注册中心和配置中心。

1. 引入Nacos Discovery依赖

common项目的pom.xml文件进行修改,引入Nacos Discovery Starter依赖

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

需要注意的是,高版本的Nacos默认需要添加集群,而我们并未配置集群,因此我们在bin目录下使用如下命令启动Nacos server

1
2
cd nacos-server-1.4.3\nacos\bin
startup.cmd -m standalone

在微服务common中的application.yml配置文件中配置Nacos Server地址和微服务名称

1
2
3
4
5
6
spring:
cloud:
nacos:
discovery: 127.0.0.1:8848
application:
name: meowmall-xxxxx

之后在每一个微服务的application中添加注解@EnableDiscoveryClient使得服务启动后注册中心能监测到服务上线。

image-20220209103949048

Feign远程调用

现有场景如下:若想要获取通过服务member中的会员模块领取到该会员所有coupon服务中的优惠卷,则需要跨服务调用。

场景中member服务会先去注册中心寻找coupon服务,注册中心Nacos Register调用一台优惠卷服务器,提供给该会员服务,然后允许远程调用。

feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。给远程服务发送的是轻量化的HTTP请求。

会员服务想调用其他服务,首先要添加OpenFeign依赖,通过该依赖,则获取了远程调用其他服务的能力。

1. 在pom.xml文件中导入以下依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2. 在coupon服务中修改以下内容

之后将使用member微服务来掉用该controller

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;

@RequestMapping("/member/list")
public R membercoupons(){ //全系统的所有返回都返回R
// 应该去数据库查用户对于的优惠券,简化为构造了一个优惠券给其返回
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满100减10");//优惠券的名字
return R.ok().put("coupons",Arrays.asList(couponEntity));
}
}

3. 在memberapplication类上加注解@EnableFeignClients

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
@EnableDiscoveryClient
// 该注解告诉Spring该packages中是需要远程调用的接口
@EnableFeignClients(basePackages = "com.heavytiger.meowmall.member.feign")
public class MeowmallMemberApplication {

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

}

4. 在membercom.heavytiger.meowmall.member.feign包下新建FeignService接口

1
2
3
4
5
6
7
@Service
@FeignClient("meowmall-coupon")
// 表示向注册中心请求"meowmall-coupon"服务
public interface CouponFeignService {
@RequestMapping("/coupon/coupon/member/list")
public R memberCoupons();
}

5. 在member服务中编写测试请求,尝试获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping("member/member")
public class MemberController {
@Autowired
private MemberService memberService;

@Autowired
private CouponFeignService couponFeignService;

@RequestMapping("/coupons")
public R test() {
MemberEntity memberEntity = new MemberEntity();
memberEntity.setNickname("张三");
// 假设张三去数据库查了后返回了张三的优惠券信息
R mc = couponFeignService.memberCoupons();

// 打印会员和优惠券信息
return R.ok().put("member", memberEntity).put("coupons", mc.get("coupons"));
}
}

6. 重新部署membercoupon服务

可以看到,服务部署成功,在访问http://localhost:8000/member/member/coupons能够获取到张三的打折卷信息,远程调用成功!

image-20220209150708841

错误解决:No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?

添加spring-cloud-starter-loadbalancer后解决

common服务中修改pom.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--引入Nacos Register-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入spring-cloud-starter-loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>

Nacos配置中心

我们在使用SpringBoot时,经常会在properties或者其他文件中配置一个全局值,该服务需要用到该值时可以使用@Value注解获取,但是这样做的坏处是,若在服务器上线环境中需要修改中间的某个值,需要将服务下线,待修改完成才能再部署上线,且若存在集群还需要多次重复修改文件,这样会很繁琐。因此Nacos配置中心解决了这个问题,允许在项目运行中动态地修改配置。

1. 在common项目中引入依赖

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2. 在coupons项目中创建/src/main/resources/bootstrap.properties,这个文件是springboot里规定的,他优先级别比application.properties

1
2
spring.application.name=meowmall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

3. 在CouponController类中编写如下代码,获取application.properties中的配置数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;

//从application.properties中获取
@Value("${coupon.user.name}")
private String name;
//不要写user.xxx,这是环境里的变量
@Value("${coupon.user.age}")
private Integer age;

@RequestMapping("/test")
public R test(){
return R.ok().put("name",name).put("age",age);
}
}

4. 浏览器去nacos server里的配置列表,点击号,data ID:meowmall-coupon.properties,配置如下内容

1
2
coupon.user.name="heavytiger"
coupon.user.age=20

image-20220209165218476

5. 点击发布后。重启coupon,访问http://localhost:7000/coupon/coupon/test

可以看到数据此时已经获取到了

image-20220209170231474

6. 再次修改,再次获取并没有发生动态变化,原因是没有加注解

需要在couponcontroller上加@RefreshScope注解实时刷新作用域

此时可以发现数据在修改后动态变化了,并且nacos配置中心中的内容要优先于本地的配置内容

image-20220209180231981

image-20220209180251210

可以看到控制台已经提示键值对被更新,此时获取到的数据发生改变,优先级高于本地配置

image-20220209180637033

Nacos配置中心进阶

命名空间

命名空间可以用作配置隔离,一般一个微服务,使用一个命名空间,默认是public命名空间,也可以新增命名空间,例如dev(开发配置空间)test(测试配置空间)prod(上线配置空间)等。properties在每个空间中都可以使用相同的key配置一份不同的值。同样,也可以为每个微服务配置一个命名空间,使得微服务之间互相隔离。

image-20220209185432911

image-20220209185514568

使用克隆配置将public命名空间中的数据导入新的dev命名空间

1
2
3
# 在dev命名空间中将配置修改为如下所示:
coupon.user.name=dev_heavytiger
coupon.user.age=66

此时开启服务获取到的仍是默认的命名空间,在bootstrap.properties中进行如下配置,将dev命名空间修改为被选择的空间

1
2
3
4
spring.application.name=meowmall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 填写dev的命名空间ID,将选择使用该命名空间
spring.cloud.nacos.config.namespace=7f753f44-7858-43a3-8c6a-6d552adf21eb

可以看到,此时访问获得的数据已经变成了dev空间下设置的数据。

image-20220209190405618

配置分组

默认所有的配置集都属于DEFAULT_GROUP。自己可以创建分组,比如双十一618双十二

这样做可以在同一个命名空间下有同名的不属于同一分组的配置集。

例如命名空间dev中的分组11_11配置如图所示

image-20220209203620748

修改bootstrap.properties为如下所示:

1
2
3
4
spring.application.name=meowmall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=7f753f44-7858-43a3-8c6a-6d552adf21eb
spring.cloud.nacos.config.group=11_11

重新运行,继续访问得到如下结果,可以看到此时为分组11_11中的数据:

image-20220209203843479

同时加载多个配置集

为了简化配置,不至于太过繁琐,可以同时加载多个配置集

datasource.yml

1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.103:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root

mybatis.yml

1
2
3
4
5
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto

other.yml

1
2
3
4
5
6
7
8
9
spring:
application:
name: gulimall-coupon
cloud:
nacos:
discovery:
server-addr: 192.168.11.1:8848
server:
port: 7000

bootstrap.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring.application.name=meowmall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=7f753f44-7858-43a3-8c6a-6d552adf21eb
# 配置文件所在的组
spring.cloud.nacos.config.group=dev

spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true

spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=dev
spring.cloud.nacos.config.ext-config[1].refresh=true

spring.cloud.nacos.config.ext-config[2].data-id=other.yml
spring.cloud.nacos.config.ext-config[2].group=dev
spring.cloud.nacos.config.ext-config[2].refresh=true

这样在启动时,可以同时加载多个配置集的配置,使得Nacos中的配置文件被简化

Spring Cloud Gateway网关

网关可以动态地管理服务,假设Product服务有100个服务器,不可能强制配置死,比如让前端每次都访问7000端口调用coupon服务,若出现服务下线,还需要进行动态修改,因此需要设置网关进行动态地管理,能从Nacos服务注册中心实时地感知服务上线或下线。

此外网关可以用于路由转发,权限校验,流量控制等功能。SpringCloud gateway就具有这些功能,且性能非常好。

网关有三大核心概念:

  1. Route: 发一个请求给网关,网关要将请求路由到指定的服务。路由有id,目的地uri,断言的集合,匹配了断言就能到达指定位置
  2. Predicate: 类似于java中的assert断言,匹配请求中的任何信息,包括请求头,请求体等
  3. Filter: 过滤器请求和响应都可以被修改。 客户端发请求给服务端,中间存在网关。将请求先交给映射器,如果能匹配处理就直接交给handler处理,然后交给一系列filter,处理成匹配的格式后就交给指定的服务,最后再返回结果给客户端。

网关架构图如图所示:

image-20220209210555011

网关的使用:

1. 创建新的项目meowmall-gateway,在pom.xml中导入相关的依赖

1
2
3
4
5
<dependency>
<groupId>com.heavytiger.meowmall</groupId>
<artifactId>meowmall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

2. 开启注册服务发现功能@EnableDiscoveryClient

1
2
3
4
5
6
7
8
9
10
// 需要排除数据源自动配置,否则之后会因为没有配置数据源报错
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class MeowmallGatewayApplication {

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

}

3. 配置application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
spring:
application:
name: meowmall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: leetcode_route
uri: https://leetcode-cn.com/
predicates:
- Query=url,leetcode

- id: blog_route
uri: https://heavytiger.github.io/
predicates:
- Query=url,blog
server:
port: 88

在此配置中,出现url匹配到leetcode则跳转到leetcode的官网,匹配到blog则跳转到本人的博客

启动后,发现跳转后地址栏路径不发生变化,因此其他的资源通过该路径均无法被请求到,只有html页面

image-20220209215219003

image-20220209215311519

参考资料

[1] Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目_哔哩哔哩_bilibili

[2] 从前慢-谷粒商城篇章1_unique_perfect的博客-CSDN博客_从前慢 谷粒商城


-------------本文到此结束 感谢您的阅读-------------
谢谢你请我喝肥宅快乐水(๑>ڡ<) ☆☆☆