SpringCloudalibaba环境准备
一、基本组件
作用 | 组件 |
---|
服务注册与发现、分布式配置中心 | Alibaba-nacos | 服务熔断,降级与限流 | Alibaba-sentinel | 分布式事务 | Alibaba-seata | 远程调用 | SpringCloud-openfeign | 负载均衡 | SpringCloud-loadbalancer | 网关 | SpringCloud-Gateway | 链路追踪 | SpringCloud-zipkin |
二、版本
1、基本环境版本
组件 | 版本 |
---|
jdk | 1.8 | spring-boot | 2.6.3 | spring-cloud | 2021.0.0 | spring-cloud-alibaba | 2021.1 |
2、服务端组件版本
组件 | 版本 |
---|
nacos | 2.0.3 | sentinel | 1.8.1 | seata | 1.3.0 | zipkin | 2.23.16 |
3、其他组件版本
各类客户端组件已经在SpringCloud、SpringCloudAlibaba父POM中定义好了版本,在子模块POM不需要再次指定版本。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
三、服务端组使用说明
1. Nacos 2.0.3
官方文档:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
1. 简介
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
nacos与eureka区别:
模块 | Nacos | Eureka | 说明 |
---|
注册中心 | 是 | 是 | 服务治理基本功能,负责服务中心化注册 | 配置中心 | 是 | 否 | Eureka需要配合Config实现配置中心,且不提供管理界面 | 动态刷新 | 是 | 否 | Eureka需要配合MQ实现配置动态刷新,Nacos采用Netty保持TCP长连接实时推送 | 可用区AZ | 是 | 是 | 对服务集群划分不同区域,实现区域隔离,并提供容灾自动切换 | 分组 | 是 | 否 | Nacos可用根据业务和环境进行分组管理 | 元数据 | 是 | 是 | 提供服务标签数据,例如环境或服务标识 | 权重 | 是 | 否 | Nacos默认提供权重设置功能,调整承载流量压力 | 健康检查 | 是 | 是 | Nacos支持由客户端或服务端发起的健康检查,Eureka是由客户端发起心跳 | 负载均衡 | 是 | 是 | 均提供负载均衡策略,Eureka采用Ribion | 管理界面 | 是 | 否 | Nacos支持对服务在线管理,Eureka只是预览服务状态 |
2. 下载
下载地址:https://github.com/alibaba/nacos/releases
3. 启动
进入nacos的bin目录
cd /nacos/bin
Linux/Unix/Mac
启动命令(standalone代表着单机模式运行,非集群模式):
sh startup.sh -m standalone
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
bash startup.sh -m standalone
Windows
启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
访问:localhost:8848/nacos 登录名和密码默认都是: nacos
集群启动配置
-
打开mysql创建nacos数据库 -
将conf目录下的nacos-mysql.sql导入nacos数据库中 -
将conf目录下的cluster.conf.example拷贝重命名为cluster.conf -
配置集群的**真实**IP地址及端口(3个及以上) 192.168.0.100:8848
192.168.0.100:8850
192.168.0.100:8855
-
修改conf目录下的applicaiton.prooperty配置文件(端口,数据库连接) ### Default web server port:
server.port=8848
#*************** Network Related Configurations ***************#
### If prefer hostname over ip for Nacos server addresses in cluster.conf:
# nacos.inetutils.prefer-hostname-over-ip=false
### Specify local server's IP:
# nacos.inetutils.ip-address=
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123456
-
拷贝3份nacos,修改每一份的conf/application.property的server.port 为刚刚在cluster.conf配置的端口 -
分别进入3个nacos的bin目录中启动(不加 -m cluster 默认也是集群方式启动) sh startup.sh -m cluster
-
打开nacos控制台,查看集群管理。看到其他的服务说明集群配置成功。
4. 关闭服务器
Linux/Unix/Mac
sh shutdown.sh
Windows
shutdown.cmd
或者双击shutdown.cmd运行文件。
5. 客户端使用
1. 服务注册
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 编写配置文件 bootstrap.yml
spring:
application:
name: userServer
cloud:
nacos:
discovery:
server-addr: localhost:8848
3. 主启动类开启服务发现 @EnableDiscoveryClient
package top.scsoul.userserviceserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class UserServiceServerApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceServerApplication.class, args);
}
}
2. 分布式配置
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
- 编写配置文件 bootstrap.yml
spring:
application:
name: userServer
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yml
3. 编写配置文件applicaiton.yml
server:
port: 8001
spring:
application:
name: userServer
abc: application
3. 配置类开启配置动态刷新 @RefreshScope
package top.scsoul.userserviceserver.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import top.scsoul.common.model.dao.User;
import top.scsoul.userserviceserver.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RefreshScope
@RestController
@RequestMapping("/api/user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Value("${abc}")
private String a;
@RequestMapping("a")
public String a() {
return a;
}
}
- 打开nacos 新建配置文件 userServer.yml 写入abc: nacos
- 访问接口查看是否修改成功
3. 客户端注册失败怎么办?
删除data目录
2. sentinel 1.8.1
官方文档:https://github.com/alibaba/Sentinel
1. 简介
Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
sentinel和hystrix区别:
2. 下载
下载地址:https://github.com/alibaba/Sentinel/releases
3. 启动
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
访问:localhost:8080 登录名和密码默认都是: sentinel
4. 客户端使用
- 导包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 编写配置文件 bootstrap.yml
server:
port: 9001
spring:
application:
name: payServer
cloud:
sentinel:
transport:
dashboard: localhost:8080
3. 添加sentinel资源 @SentinelResource
package top.scsoul.payserviceserver.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.scsoul.common.model.dao.User;
import top.scsoul.payserviceserver.feign.UserService;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
public class PayController {
private static final Logger log = LoggerFactory.getLogger(PayController.class);
@Resource
UserService userService;
@GlobalTransactional
@SentinelResource(value = "test", defaultFallback = "defaultFallback")
@RequestMapping("userPay")
public Map<String, Object> userPay(String id, BigDecimal money) {
User user = userService.selectOne(id);
log.info("user:{}", user);
return new HashMap<String, Object>() {{
put("status", 200);
put("id", id);
put("money", money);
put("user", user);
}};
}
public Map<String, Object> defaultFallback() {
return new HashMap<String, Object>() {{
put("status", 500);
put("err", "太拥挤了 ~ 请稍后重试 ");
}};
}
}
4. 登录sentinel监控面板即可看到当前服务,在簇点链路中对相应的url可视化配置服务限流,服务降级等
注意:
- 由于sentinel是懒加载机制,微服务启动后,需要先手动调用一下接口,才会在sentinel面板显示当前微服务。或者在配置文件中关闭懒加载eager: true
- 直接在控制台配置的服务限流,服务降级等信息都是基于内存的,微服务重启后都会失效。Sentinel提供了多种持久化的方案,可以集成redis,mysql,nacos等。请参照博客https://blog.csdn.net/zhangcongyi420/article/details/109412042
3. seata 1.3.0
官方文档:https://seata.io/zh-cn/
1. 简介
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务;
Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案;
四种事务模式中,XA模式正在开发中…,其他事务模式已经实现;
目前使用的流行度情况是:AT > TCC > Saga;
seata的1+3模式 :1个全局的xid,三个角色:
-
TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚; -
TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务; -
RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交互以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚;
其中TC为单独部署的 Server 服务端,TM和RM为嵌入到应用中的 Client 客户端;
借助某某谷老师的比喻:
TC:培训讲师
TM:班主任
RM:学生
-
班主任TM向培训讲师TC申请开课生成唯一的课程号xid -
课程号xid在每一个学生RM中传播开 -
每一个学生RM加入到课程号为xid的课程中,与培训讲师互动 -
班主任TM向培训讲师TC沟通上课情况
- 班主任TM:这堂课怎么样呀?
- 培训讲师TC:不行,有几个学生没有完成课堂作业。
- 班主任TM:好吧,一场失败的课程。
或者
- 班主任TM:这堂课怎么样呀?
- 培训讲师TC:不错,学生全部完成课堂作业。
- 班主任TM:好的,完美结课。
2. 下载
下载地址:https://github.com/seata/seata/releases
3. 配置
进入seata/conf目录
主要关注以下两个配置文件
- file.conf 负责配置存储模式、服务等信息
- registry.conf 负责配置服务注册中心,服务配置中心
这里以配置单机seata服务为例:
1. 将seata注册到nacos
打开registry.conf,找到registry下的type,将其改为nacos,并配置nacos的基本信息
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = "public"
cluster = "default"
username = "nacos"
password = "nacos"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = 0
password = ""
cluster = "default"
timeout = 0
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "public"
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
2. 配置事物组
由于我们在register.conf的config下的type为file,即采用文件形式的配置。
打开file.conf文件,在下方添加:
service {
#transaction service group mapping
vgroupMapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
4. 启动
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
5. 客户端使用
- 导包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
-
编写配置文件application.yml 注意:tx-service-group 必须要与在file.conf中配置的vgroupMapping一致
server:
port: 9001
spring:
application:
name: payServer
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
seata:
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
namespace: public
group: SEATA_GROUP
3. 在需要控制分布式事务的方法上添加 @GlobalTransactional
4. 在每一个分布式数据库添加undo_log表
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
-
启动服务后,稍等片刻控制台会打印TM、RM的注册日志 -
调用接口控制台有事物执行的日志打印。
4. Gateway
SpringCloud Gateway需要自己搭建服务器,在项目搭建中有代码示例
官方网站:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
1. 简介
Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul。网关通常在项目中为了简化
前端的调用逻辑,同时也简化内部服务之间互相调用的复杂度;具体作用就是转发服务,接收并转发所有内外
部的客户端调用;其他常见的功能还有权限认证,限流控制等等。
Zuul 对比 Gateway:
Zuul1是Netflix在2013年开源的网关组件,大规模的应用在Netflix的生产环境中,经受了实践考验。它可以与Eureka、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断等功能。Zuul1的核心是一系列过滤器,过滤器简单易于扩展,已经有一些三方库如spring-cloud-zuul-ratelimit 等提供了过滤器支持。
Zuul1基于Servlet构建,使用的是阻塞的IO,引入了线程池来处理请求。每个请求都需要独立的线程来处理,从线程池中取出一个工作线程执行,下游微服务返回响应之前这个工作线程一直是阻塞的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJFevuDe-1654054026792)(https://bbsmax.ikafan.com/static/L3Byb3h5L2h0dHBzL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20veWl6aGlzaGkveWl6aGlzaGkuZ2l0aHViLmlvL21hc3Rlci9pbWFnZXMvenV1bDEtbXVsdGl0aHJlYWRlZC1zeXN0ZW0tYXJjaGl0ZWN0dXJlLnBuZw==.jpg)]
Spring Cloud Gateway 是Spring Cloud的一个全新的API网关项目,目的是为了替换掉Zuul1。Gateway可以与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断等功能,并且Gateway还内置了限流过滤器,实现了限流的功能。
Gateway基于Spring 5、Spring boot 2和Reactor构建,使用Netty作为运行时环境,比较完美的支持异步非阻塞编程。Netty使用非阻塞的IO,线程处理模型建立在主从Reactors多线程模型上。其中Boss Group轮询到新连接后与Client建立连接,生成NioSocketChannel,将channel绑定到Worker;Worker Group轮询并处理Read、Write事件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jhkSQlAA-1654054026793)(https://bbsmax.ikafan.com/static/L3Byb3h5L2h0dHBzL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20veWl6aGlzaGkveWl6aGlzaGkuZ2l0aHViLmlvL21hc3Rlci9pbWFnZXMvbmV0dHktc2ltcGxlLXRocmVhZC1tb2RlbC5wbmc=.jpg)]
对比项 | Zuul1.x | Gateway |
---|
实现 | 基于Servlet2.x构建,使用阻塞的API。 | 基于Spring 5、Project Reactor、Spring Boot 2,使用非阻塞式的API。 | 长连接 | 不支持 | 支持 | 不适用场景 | 后端服务响应慢或者高并发场景下,因为线程数量是固定(有限)的,线程容易被耗尽,导致新请求被拒绝。 | 中小流量的项目,使用Zuul1.x更合适。 | 限流 | 无 | 内置限流过滤器 | 上手难度 | 同步编程,上手简单 | 门槛较高,上手难度中等 | 技术栈沉淀 | Zuul1开源近9年,经受考验,稳定成熟。 | / |
5. zipkin
1. 简介
Zipkin是 Twitter 的一个开源项目,基于 Google Dapper实现。可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的 REST API 接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源。除了面向开发的API接口之外,它也提供了方便的 UI 组件帮助我们直观的搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户请求的处理时间等。
上图展示了Zipkin的基础架构,主要由4个核心组件构成: Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为Zipkin内部处理的Span格式,以支持后续的存储、分析、展示等功能。
Storage:存储组件,它主要对处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中,我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到 数据库或es 中。
RESTful API:API组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等。
Web UI:UI组件,基于API组件实现的上层应用。通过UI组件用户可以方便而有直观地查询和分析跟踪信息。
zipkin相关概念 Trace、Span、annotations注释
2. 下载
https://zipkin.io/pages/quickstart.html
3. 启动
- 内存版
java -jar zipkin-server-2.23.16-exec.jar
- mysql版
创建zipkin数据库,导入sql文件:
CREATE TABLE IF NOT EXISTS zipkin_spans (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL,
`id` BIGINT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`remote_service_name` VARCHAR(255),
`parent_id` BIGINT,
`debug` BIT(1),
`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query',
PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
CREATE TABLE IF NOT EXISTS zipkin_annotations (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
`day` DATE NOT NULL,
`parent` VARCHAR(255) NOT NULL,
`child` VARCHAR(255) NOT NULL,
`call_count` BIGINT,
`error_count` BIGINT,
PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
java -jar zipkin-server-2.23.16-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=localhost --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=123456
4. 客户端使用
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>3.1.1</version>
</dependency>
- 编写配置文件application.yml
spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://127.0.0.1:9411
discovery-client-enabled: false
sender:
type: web
日志格式化:
[traceId: %X{traceId:-},spanId: %X{spanId:-}]
四、项目搭建
请确保你的电脑上已经成功开启了nacos,sentinel,seata服务
1. 父项目搭建
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>top.scsoul</groupId>
<artifactId>cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>userServiceServer</module>
<module>payServiceServer</module>
<module>common</module>
<module>gateway</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.6.3</spring-boot.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>top.scsoul</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. 添加common模块
- pojo类
- utils工具包
- config配置文件
- 公共POM依赖(避免循环依赖)
3. 添加服务提供者模块
1. 导入依赖
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">
<parent>
<artifactId>cloud</artifactId>
<groupId>top.scsoul</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>userServiceServer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>top.scsoul</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
</project>
2. 编写配置文件
bootstrap.yml
spring:
application:
name: userServer
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
application.yml
server:
port: 8001
spring:
application:
name: userServer
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
abc: application
3. 开启服务发现
配置@EnableDiscoveryClient
package top.scsoul.userserviceserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class UserServiceServerApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceServerApplication.class, args);
}
}
4. 编写接口
这里省略userService调用数据库的代码
package top.scsoul.userserviceserver.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import top.scsoul.common.model.dao.User;
import top.scsoul.userserviceserver.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RefreshScope
@RestController
@RequestMapping("/api/user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Resource
private UserService userService;
@RequestMapping("selectOne")
public User selectOne(Integer id, HttpServletRequest request) {
log.info("port:{}", request.getServerPort());
return this.userService.queryById(id);
}
@Value("${abc}")
private String a;
@RequestMapping("a")
public String a() {
return a;
}
}
5. 测试服务发现
打开nacos控制台:localhost:8848/nacos 登录查看服务列表中是否包含userServer,如果服务存在就说明服务注册成功。
6. 测试配置中心
打开nacos控制台:localhost:8848/nacos 登录添加配置文件,dataId为:userServer.yml内容为:
abc: nacos
访问localhost:8001/api/user/a
若返回nacos则说明获取配置中心的配置动态刷新成功!且服务控制台会打印相应的日志。
dataId 不能随意乱填写,其规范应该为
a
p
p
l
i
c
a
i
t
o
n
N
a
m
e
?
{applicaitonName}-
applicaitonName?{profiles.active}.${file-extension}
并且需要在微服务的bootstrap.yml中指定服务名称,当前启用的配置文件,配置文件文件后缀名
例如:
- userServer-dev.yml
- userServer-prod.yml
- userServer.yml
spring:
application:
name: userServer
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
此时没有指定环境,则会从配置中心拉取userServer.yml配置文件。
4 添加服务消费者模块
1. 导入依赖
<?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>cloud</artifactId>
<groupId>top.scsoul</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>payServiceServer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>top.scsoul</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
</project>
2. 编写配置文件
bootstrap.yml
spring:
application:
name: payServer
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
application.yml
server:
port: 9001
spring:
application:
name: payServer
cloud:
sentinel:
transport:
dashboard: localhost:8080
alibaba:
seata:
tx-service-group: my_test_tx_group
main:
allow-circular-references: true
allow-bean-definition-overriding: true
seata:
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
namespace: public
group: SEATA_GROUP
feign:
sentinel:
enabled: true
3. 开启远程调用、服务发现
@EnableFeignClients
@EnableDiscoveryClient
package top.scsoul.payserviceserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RefreshScope
@EnableFeignClients
@EnableDiscoveryClient
@RestController
@SpringBootApplication
public class PayServiceServerApplication {
@RequestMapping("/")
public String home() {
return "PayServiceServerApplication";
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(PayServiceServerApplication.class, args);
}
}
4. 编写feign远程调用
package top.scsoul.payserviceserver.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import top.scsoul.common.model.dao.User;
@FeignClient(value = "userServer", fallback = UserServiceFallback.class)
public interface UserService {
@GetMapping("/api/user/selectOne")
public User selectOne(@RequestParam(name = "id") String id);
}
package top.scsoul.payserviceserver.feign;
import org.springframework.stereotype.Component;
import top.scsoul.common.model.dao.User;
@Component
public class UserServiceFallback implements UserService {
@Override
public User selectOne(String id) {
User user = new User();
System.out.println("服务熔断");
return user;
}
}
5. 编写控制器
package top.scsoul.payserviceserver.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.scsoul.common.model.dao.User;
import top.scsoul.payserviceserver.feign.UserService;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
public class PayController {
private static final Logger log = LoggerFactory.getLogger(PayController.class);
@Resource
UserService userService;
@GlobalTransactional
@SentinelResource(value = "test", defaultFallback = "defaultFallback")
@RequestMapping("userPay")
public Map<String, Object> userPay(String id, BigDecimal money) {
User user = userService.selectOne(id);
log.info("user:{}", user);
return new HashMap<String, Object>() {{
put("status", 200);
put("id", id);
put("money", money);
put("user", user);
}};
}
public Map<String, Object> defaultFallback() {
return new HashMap<String, Object>() {{
put("status", 500);
put("err", "太拥挤了 ~ 请稍后重试 ");
}};
}
}
6. 测试服务远程调用与负载均衡
- 确保服务提供者注册进nacos,并且由两个健康实例。
- 确保服务消费者注册进nacos
- 多次访问:localhost:9001/api/pay/userPay?id=1&money=100
- 查看服务提供者控制台,是否两个实例都有sql日志打印。有的话说明远程调用,负载均衡配置成功。
7. 测试服务熔断
- 在前面测试的基础上,关闭一台服务提供者。
- 多次访问:localhost:9001/api/pay/userPay?id=1&money=100
- 由于一台服务器依然能够正常工作,所以一次正常,另一次会走feign的fallback兜底方法,服务消费者控制台会打印服务熔断。返回的是一个空user。
8. 测试服务降级
- 打开sentinel控制台
- 找到当前服务(payServer)
- 点击簇点链路
- 选择 /api/pay/userPay 添加流控规则,降级规则
- 多次访问:localhost:9001/api/pay/userPay?id=1&money=100
- 流控会走sentinel配置的兜底方法
5. 添加网关模块
1. 导入依赖
<?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>cloud</artifactId>
<groupId>top.scsoul</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>
2. 编写配置文件
bootsrap.yml
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
applicaiton.yml
server:
port: 80
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: payServer
uri: lb://payServer
predicates:
- Path=/p/**
filters:
- StripPrefix=1
discovery:
locator:
enabled: false
3. 开启服务发现
package top.scsoul.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class GateWayapplication {
public static void main(String[] args) {
SpringApplication.run(GateWayapplication.class, args);
}
}
此时就可以通过网关转发请求了!
例如访问 localhost/p/api/pay/userPay?id=1,将会被转发到payServer服务下,localhost:9001/api/pay/userPay?id=1或者是通过负载均衡到其他的payServer集群服务。
|