简介
SpringCloud alibaba Gitub地址:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
Alibaba是什么
spring Cloud for Alibaba,它是由一些阿里巴巴的开源组件和云产品组成的。这个项目的目的是为了让大家所熟知的 Spring 框架,其优秀的设计模式和抽象理念,以给使用阿里巴巴产品的 Java 开发者带来使用 Spring Boot 和 Spring Cloud 的更多便利。
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。该项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
Alibaba能做什么
服务限流降级:默认支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics 监控。
服务注册与发现:适配Spring Cloud服务注册与发现标准,默认集成了 Ribbon的支持。分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:基于Spring Cloud Stream为微服务应用构建消息驱动能力。
阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker (schedulerx-client)上执行。
组件
Sentinel:面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。
Nacos:阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Nacos
简介
为什么叫Nacos
前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。
Nacos是什么
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos: Dynamic Naming and Configuration Service
Nacos就是注册中心+配置中心的组合
等价于 Nacos = Eureka+Config + Bus
安装
1.官网下载Nacos
2.解压安装包,直接运行bin目录下的startup.cmd
3.命令运行成功一哈直接访问http://localhost:8848/nacos
(默认账号密码都是nacos
)
测试环境构建
1.创建maven父级工程
2.创建common公用包
3.创建order订单服务提供者
4.创建web服务消费者
1.创建Maven父级工程
maven简单项目即可
删除src目录
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>com.tuxc</groupId>
<artifactId>SpringCloud-alibaba-Study</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>order-provider</module>
<module>provider-common</module>
<module>web-consumer</module>
</modules>
<packaging>pom</packaging>
<!--父级项目-->
<dependencyManagement>
<dependencies>
<!--springCloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springCloudAlibaba-->
<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>
<!--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>
</dependencies>
</dependencyManagement>
</project>
2.创建common公用包
导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
编写pojo类
@Data
public class Order {
// id
private String id;
// 订单名称
private String name;
// 订单金额
private Integer money;
}
3.创建order订单服务提供者
创建项目导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.tuxc</groupId>
<artifactId>provider-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
编写配置文件
port: 9001
spring:
application:
name: order-provider-9001
编写启动类
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
编写服务:
1.controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
@GetMapping("/{id}")
public Order findByOrder(@PathVariable("id") String id){
return orderService.findByOrder(id);
}
@GetMapping("/findAll")
public List<Order> findAll(){
return orderService.findAll();
}
}
2.service
@Service
public class OrderService {
private HashMap<String, Order> hashMap;
@PostConstruct
public void init(){
hashMap = new HashMap<>();
for (int i = 1; i <= 10; i++) {
Order order = new Order();
order.setId(""+i);
order.setMoney(i+10);
order.setName("订单:"+i);
hashMap.put(""+i, order);
}
}
public Order findByOrder(String id) {
return hashMap.get(id);
}
public List<Order> findAll() {
List<Order> orders = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
orders.add(hashMap.get(""+i));
}
return orders;
}
}
4.创建web服务消费者
创建项目导入依赖,编写配置文件
这几步与之前一样,这里就不再过多说明
编写启动类
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
编写controller
@Slf4j
@RestController
@RequestMapping("/web")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public Order findOrderById(@PathVariable String id){
Order order = restTemplate.getForObject("http://localhost:9001/order/" + id, Order.class);
return order;
}
@GetMapping("/findAll")
public List<Order> findAll(){
List<Order> object = restTemplate.getForObject("http://localhost:9001/order/findAll", List.class);
return object;
}
}
注意事项
由于是maven父子级项目
在运行前,一定要将所有项目都打包编译一次,免得出现依赖找不到等等错误
服务注册中心演示
1.导入nacos服务发现依赖
2.编写配置文件
3.编写主启动类,开启服务注册
4.运行测试
查看官方文档
官方文档
从官方文档中,我们可以得到服务发现相关依赖
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
导入nacos服务发现依赖
服务提供者和服务消费者都需要导入此依赖
编写配置文件
在配置文件中添加以下依赖
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
management:
endpoints:
web:
exposure:
include: '*'
编写主启动类,开启服务注册
@EnableDiscoveryClient
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
运行测试
修改代码完成调用
修改服务消费者,controller层代码
@Slf4j
@RestController
@RequestMapping("/web")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public Order findOrderById(@PathVariable String id){
ServiceInstance instances = discoveryClient.getInstances("order-provider-9001");
String host = instances .getHost();
log.info("获取到地址:"+host);
int port = instances .getPort();
log.info("获取到端口号:"+port);
// Order order = restTemplate.getForObject("http://localhost:9001/order/" + id, Order.class);
Order order = restTemplate.getForObject("http://" + host + ":" + port + "/order/" + id, Order.class);
return order;
}
@GetMapping("/findAll")
public List<Order> findAll(){
List<Order> object = restTemplate.getForObject("http://localhost:9001/order/findAll", List.class);
return object;
}
}
DiscoveryClient是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服务
负载均衡演示
为了方便测试我们复制一下服务提供者,修改下端口号
采用DiscoveryClient完成负载均衡
@Slf4j
@RestController
@RequestMapping("/web")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public Order findOrderById(@PathVariable String id){
List<ServiceInstance> instances = discoveryClient.getInstances("order-provider-9001");
int index = new Random().nextInt(instances.size());
log.info("服务数量:"+index);
ServiceInstance serviceInstance = instances.get(index);
String host = serviceInstance.getHost();
log.info("获取到地址:"+host);
int port = serviceInstance.getPort();
log.info("获取到端口号:"+port);
// Order order = restTemplate.getForObject("http://localhost:9001/order/" + id, Order.class);
Order order = restTemplate.getForObject("http://" + host + ":" + port + "/order/" + id, Order.class);
return order;
}
@GetMapping("/findAll")
public List<Order> findAll(){
List<Order> object = restTemplate.getForObject("http://localhost:9001/order/findAll", List.class);
return object;
}
}
采样Ribbon方式完成负载均衡
使用注解开启Ribbon
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
使用Ribbon直接使用注册的服务名调用接口
/**
* @author tuxuchen
* @date 2021/8/18 16:33
*/
@Slf4j
@RestController
@RequestMapping("/web")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public Order findOrderById(@PathVariable String id){
List<ServiceInstance> instances = discoveryClient.getInstances("order-provider-9001");
int index = new Random().nextInt(instances.size());
log.info("服务数量:"+index);
ServiceInstance serviceInstance = instances.get(index);
String host = "order-provider-9001";
log.info("获取到地址:"+host);
int port = serviceInstance.getPort();
log.info("获取到端口号:"+port);
// Ribbon方式 直接使用服务名
Order order = restTemplate.getForObject("http://" + host + "/order/" + id, Order.class);
return order;
}
@GetMapping("/findAll")
public List<Order> findAll(){
List<Order> object = restTemplate.getForObject("http://localhost:9001/order/findAll", List.class);
return object;
}
}
采样openFegin方式完成负载均衡
1.导入openFegin依赖
2.开启服务
3.编写接口
4.编写Controller
1.导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
2.开启服务
@EnableFeignClients // 开启feign
@EnableDiscoveryClient
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
3.编写接口
@FeignClient("order-provider-9001")
public interface FeignService {
@GetMapping("/order/{id}")
Order findOrderById(@PathVariable("id") @RequestParam("id") String id);
}
4.编写controller
@Autowired
private FeignService feignService;
@GetMapping("/{id}")
public Order findOrderById(@PathVariable String id){
log.info("接收到参数:"+id);
Order order = feignService.findOrderById(id);
log.info("调用结果:"+order.toString());
return order;
}
服务配置中心演示
使用nacos作为配置中心,其实就是将nacos当做一个服务端,将各个微服务看成是客户端,我们将各个微服务的配置文件统一存放在nacos上,然后各个微服务从nacos上拉取配置即可。
Config配置中心入门
导入nacos服务配置依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
在微服务中添加nacos config的配置
注意:不能使用原来的application.yaml作为配置文件,而是新建一个bootstrap.yaml作为配置文件
配置文件优先级(由高到低):
bootstrap.properties -> bootstrap.yaml -> application.properties -> application.yaml
server:
port: 9002
spring:
application:
name: web-consumer-9002
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
profiles:
active: dev
management:
endpoints:
web:
exposure:
include: '*'
在nacos中添加配置
点击配置列表,点击右边+号,新建配置。在新建配置过程中,要注意下面的细节:
1)Data ID不能随便写,要跟配置文件中的对应,对应关系如图所示
2)配置文件格式要跟配置文件的格式对应,且目前仅仅支持YAML和Properties
3)配置内容按照上面选定的格式书写
注释本地的application.yam中的内容, 启动程序进行测试
如果依旧可以成功访问程序,说明我们nacos的配置中心功能已经实现
动态刷新
在入门案例中,我们实现了配置的远程存放,但是此时如果修改了配置,我们的程序是无法读取到的,因此,我们需要开启配置的动态刷新功能。
在远处nacos中添加以下配置
注解方式(推荐)
@RefreshScope /* 只需要在需要动态读取配置的类上添加此注解就可以 */
@RestController
@RequestMapping("/config")
public class ConfigController {
@Value("${tuxc.appName}")
private String appName;
@GetMapping("/tuxc")
public String test(){
return appName;
}
}
硬编码方式
@Autowired
private ConfigurableApplicationContext applicationContext;
@GetMapping( "/nacos-config-test1" )
public String nacosConfingTest1(){
return(applicationContext.getEnvironment().getProperty( "config.appName" ) );
}
配置共享
当配置越来越多的时候,我们就发现有很多配置是重复的,这时候就考虑可不可以将公共配置文件提取出来,然后实现共享呢?当然是可以的。接下来我们就来探讨如何实现这一功能。
配置共享三级目录:
- Data ID
- Group分组
- namespase命名空间
Data ID(常用)
如果想在同一个微服务的不同环境之间实现配置共享,其实很简单。只需要提取一个以spring.application.name 命名的配置文件,然后将其所有环境的公共配置放在里面即可。
1.创建web-consumer-9002.yaml存放公用配置
2.创建web-consumer-9002-test.yaml存放测试配置
3.创建web-consumer-9002-dev.yaml存放开发配置
4.在本地bootstrap.yaml中修改映射
创建web-consumer-9002.yaml存放公用配置
创建web-consumer-9002-test.yaml存放测试配置
创建web-consumer-9002-dev.yaml存放开发配置
在本地bootstrap.yaml中修改映射
Group分组
1.创建两个分组,开发分组和测试分组
2.只需要修改bootstrap.yaml配置文件 指向某一个配置即可
创建两个分组,开发分组和测试分组
修改配置文件
通过新增group配置 即可指向具体的分组
namespace命名空间
1.创建命名空间
2.在配置文件中引入命名空间
创建命名空间
点击切换到对应的命名空间,然后创建相应配置
在配置文件中引入命名空间
1.将命名框架的id复制下来
2.在bootstrap.yaml文件中引入
不同微服务中间共享配置
不同为服务之间实现配置共享的原理类似于文件引入,就是定义一个公共配置,然后在当前配置中引入。
bootstrap.yaml
spring:
application:
name: service-product
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #nacos中心地址
file-extension: yaml # 配置文件格式
shared-dataids: all-service.yaml # 配置要引入的配置
refreshable-dataids: all-service.yaml # 配置要实现动态配置刷新的配置
profiles:
active: dev # 环境标识
集群和持久化配置
Nacos集群官网说明
官方文档地址:官网地址
Nacos支持三种部署模式
- 单机模式 - 用于测试和单机试用。
- 集群模式 - 用于生产环境,确保高可用。
- 多集群模式 - 用于多数据中心场景。
单机模式:
在0.7版本之前,在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:
- 安装数据库,版本要求:5.6.5+
- 初始化mysql数据库,数据库初始化文件:nacos-mysql.sql
- 修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。
# 在最下面增加以下配置
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=nacos_devtest
db.password=youdontknow
数据库脚本在nacos/conf目录下
集群模式:
官网集群架构图:
因此开源的时候推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
什么是vip
虚拟IP=nginx
默认Nacos使用嵌入式数据库实现数据存储, 所以,如果启用多个默认配置的Nacos节点,数据存储是存在一致性问题的,为了解决这个问题,Nacos采样了集中式存储的方式来支持集群化部署,目前只支持Mysql存储
Liunx集群搭建
环境准备
- 64 bit OS Linux/Unix/Mac,推荐使用Linux系统。
- 64 bit JDK 1.8+;
- Maven 3.2.x+;
- 3个或3个以上Nacos节点才能构成集群。
- Liunx版Nacos
参考官网选择,下载安装包的方式:
集群搭建
1.上传安装包到Liunx服务器/opt目录下
2.解压安装包
3.参考单机模式配置MySQL
4.配置集群配置文件
5.修改nacos/bin下面的启动脚本startup.sh,使它能够接受不同的端口启动
6.修改Nginx配置,由她作为负载均衡器
# 解压安装包
tar -xvf nacos-server-1.3.0.tar.gz
# 将解压后的文件夹 拷贝近 mynacos目录下
cp -r nacos /mynacos/
# 进入目录
cd /mynacos/nacos # pwd /mynacos/nacos
# 参考单机模式配置Mysql
配置集群配置文件:
在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。
注意这个ip不能写127.0.0.1,必须是liunx命令hostname -i 能够识别的ip
# ip:port
192.168.111.144:3333
192.168.111.144:4444
192.168.111.144:5555
修改启动脚本:
集群启动,我们希望可以类似其他软件的shell命令,传递不同的端口号启动不同的nacos实例
如命令: ./startup.sh -p 3333
则表示启动端口号3333的nacos实例,和上一步集群配置文件中配置一致
打开startup.sh
修改前:
修改后:
修改前:
修改后:
修改Nginx配置:
在nginx.conf下添加以下配置
upstream cluster{
server 127.0.0.1:3333;
server 127.0.0.1:4444;
server 127.0.0.1:5555;
}
location / {
# root html;
# index index.html index.htm;
proxy_pass http://cluster;
}
到目前位置集群配置就完成了!
启动测试
cd /mynacos/nacos/bin
# 启动
./startup.sh -p 3333
./startup.sh -p 4444
./startup.sh -p 5555
# 统计
ps -ef | grep nacos | grep -v grep | wc -l # 3 表示三个nacos启动成功
# 启动nginx 找到nginx sbin目录
./nginx -c /usr/local/nginx/conf/nginx.conf # 指定配置文件启动
访问nginx的http://+IP+端口号/nacos/
微服务注册进Nacos
spring:
application:
name: web-consumer-9002
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 如果使用nacos集群只需要配置nginx地址 IP+端口号