首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,576 阅读
2
Mac打印机设置黑白打印
4,886 阅读
3
修改elementUI中el-table树形结构图标
4,865 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,645 阅读
5
intelliJ Idea 2022.2.X破解
4,316 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
MyBatis
WordPress
Nacos
Vue
Spring Cloud Alibaba
MacOS
Mybatis-Plus
Typecho
jQuery
Java Script
asp.net
IntelliJ IDEA
Element UI
MySQL
微信小程序
Sentinel
Laughing
累计撰写
608
篇文章
累计收到
1,421
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
93
篇与
的结果
2021-07-29
Spring Cloud集成Sentinel之使用OpenFein实现服务调用
在Spring Cloud使用OpenFeign调用Nacos服务提供者中,我们介绍了OpenFeign调用Nacos的方式。在Spring Cloud集成Sentinel之@SentinelResource注解使用一文中,我们也介绍了@SentinelResource注解,但是注解的方式。本文我们更进一步,介绍以下OpenFeign如何与Setinel集成。添加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-consumer-order9003</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> </dependencies> </project>修改配置文件server: port: 8888 spring: application: name: nacos-consumer-order cloud: nacos: discovery: server-addr: 192.168.120.180:1111 sentinel: transport: dashboard: 192.168.120.180:9000 port: 8719 management: endpoints: web: exposure: include: "*" logging: level: cc.lisen.springcloud.alibaba.service.NacosPaymentService: debug #设置feign客户端超时时间(OpenFeign默认支持ribbon) nacos-provider-payment: ribbon: #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000 NFLoadBalancerRuleClassName: cc.lisen.springcloud.alibaba.config.CustomerBalancerRule sentinel: enabled: true修改启动类@SpringBootApplication //@EnableDiscoveryClient @EnableFeignClients public class NacosConsumerOrderMain8888 { public static void main(String[] args) { SpringApplication.run(NacosConsumerOrderMain8888.class, args); } }定义Feign接口@FeignClient(value = "nacos-provider-payment", fallback = NacosPaymentServiceFallback.class) public interface NacosPaymentService { @GetMapping(value = "/echo/{string}") String echo(@PathVariable("string") String string); } class NacosPaymentServiceFallback implements NacosPaymentService { @Override public String echo(String string) { return "进入feignClient fallback了"; } }定义测试接口 @GetMapping("/echo/{id}") public String echo(@PathVariable("id") String id) { log.info("******************输出:" + id); return nacosPaymentService.echo(id); }
2021年07月29日
1,083 阅读
0 评论
0 点赞
2021-07-28
Spring Cloud集成Sentinel之@SentinelResource注解使用
通过Spring Cloud集成Sentinel之流量控制及Spring Cloud集成Sentinel之服务熔断降级两篇文章,我们可以发现一个问题,当系统触发限流或者熔断时,系统排除的异常很不友好。现在前后端分离项目,我们后端一般会封装一个公共的实体返回给前端,比如下面代码中的CommonResult,了解到这,我们分别介绍以下@SentinelResource注解中常用的几个参数:value、blockHandler、fallback。测试代码@SentinelResource注解代码@RestController public class RateLimitController { @GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException",fallback = "handleFallback") public CommonResult byResource() { return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用"); } public CommonResult handleFallback(Throwable exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用"); } @GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler",blockHandlerClass = MyHandler.class,blockHandler = "handlerException2") public CommonResult customerBlockHandler() { return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); } }异常代码public class MyHandler { public static CommonResult handlerException1(BlockException exception) { return new CommonResult(4444, "按客户自定义的global handlerException---1"); } public static CommonResult handlerException2(BlockException exception) { return new CommonResult(4444, "按客户自定义的global handlerException---2"); } }valuevalue用于指定资源名称。在Sentinel配置中,我们前面使用的api地址作为的资源名称,其实也可以通过这个value指定资源名称。在Sentinel中,如果以/开头,那么便意味着通过api地址作为资源名,否则系统认为value作为资源名。比如这个方法@GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler",blockHandlerClass = MyHandler.class,blockHandler = "handlerException2") public CommonResult customerBlockHandler() { return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); }在sentinel配置中,指定资源名称/rateLimit/customerBlockHandler或customerBlockHandler是等价的。[alt type="warning"]value必须是唯一的[/alt]只指定fallbackfallback负责业务异常和限流时处理。测试代码 @GetMapping("/fallbackOnly") @SentinelResource(value = "fallbackOnly", fallback = "handlefallbackOnlyException") public CommonResult fallbackOnly() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); }我们触发限流规则,可以看到如下输出,已经是我们自定义的异常类,而不是系统自带的。还有一个fallbackClass注解,如此类似,不再赘诉。只指定blockHandlerblockHandler只负责sentinel控制台配置违规测试代码 @GetMapping("/blockHandlerOnly") @SentinelResource(value = "blockHandlerOnly", blockHandler = "handleblockHandlerException") public CommonResult blockHandlerOnly() { int age = 10/0; return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); } public CommonResult handleblockHandlerException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用"); }我们触发一个熔断规则,可以看到如下输出fallback和blockHandler同时存在fallback负责处理异常,blockHandler负责sentinel控制台配置违规。@GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException", fallback = "handleFallback") public CommonResult byResource() { return new CommonResult(200, "按资源名称限量ok", new Payment(2020L, "SERIAL001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, "handleException\t 服务不可用"); } public CommonResult handleFallback(Throwable exception) { return new CommonResult(444, "handleFallback\t 服务不可用"); }
2021年07月28日
1,169 阅读
0 评论
0 点赞
2021-07-28
Spring Cloud集成Sentinel之热点参数
热点参数何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。Sentinel 利用LRU策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。 @GetMapping("/testHotkey") // @SentinelResource(value = "testHotkey", blockHandler = "deal_testHotkey") @SentinelResource(value = "testHotkey") public String testHotkey(@RequestParam(value = "productId", required = false) String productId, @RequestParam(value = "userId", required = false) String userId) { return "--------------testHotkey"; }如果参数0,1s请求次数超过依次,触发熔断,但是如果参数0的值是p2,那么可以请求2次。自适应限流Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统规则 系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。系统规则支持以下的模式:Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps minRt 估算得出。设定参考值一般是 CPU cores 2.5。CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
2021年07月28日
1,056 阅读
0 评论
0 点赞
2021-07-28
Spring Cloud集成Sentinel之服务熔断降级
Sentinel除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。降级策略通常用以下几种方式来衡量资源是否处于稳定的状态:慢调用比例 (DEGRADE_GRADE_RT):当统计时常内持续进入N个请求同时请求数到达最小请求数,对应时刻的平均响应时间(秒级)均超过比例阈值,那么在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException)。注意 Sentinel 默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms。若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx来配置。异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的在统计时常内请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule中的count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表0% - 100%。异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若timeWindow小于60s,则结束熔断状态后仍可能再进入熔断状态。代码测试慢调用比例测试代码@GetMapping("/testRT") public String testRT() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "-----------testRT"; }在1s内,请求数如果超过5个,平均请求时常超过800ms的比例达到20%,那么在接下来的1min内进行服务熔断。一开始正常访问,但是访问第6次时,达到条件,出发熔断降级。异常比例测试代码@GetMapping("/testExceptionPer") public String testExceptionPer() { int age = 1 / 0; return "-----------testExceptionPer"; }在1s内,请求数如果超过5个,异常的比例达到10%,那么在接下来的1min内进行服务熔断。一开始访问,报以下异常多次访问后,触发熔断降级异常数可以参考异常比例,不再赘述。
2021年07月28日
1,169 阅读
0 评论
0 点赞
2021-07-28
Spring Cloud集成Sentinel之流量控制
在Spring Cloud Alibaba sentinel的安装与配置中,我们介绍了Sentinel的功能以及安装部署方式。在Spring Cloud集成Sentinel之基础功能介绍中,我们介绍了如何在Spring Cloud中集成Sentinel。本文我们在上面的基础上继续深入,介绍一下Sentinel的流量控制功能。Sentinel流量控制介绍流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。FlowSlot会根据预设的规则,结合前面NodeSelectorSlot、ClusterNodeBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName) 的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉BlockException来自定义被限流之后的处理逻辑。同一个资源可以创建多条限流规则。FlowSlot会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:resource:资源名,即限流规则的作用对象count: 限流阈值grade: 限流阈值类型(QPS 或并发线程数)limitApp: 流控针对的调用来源,若为 default 则不区分调用来源strategy: 调用关系限流策略controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)名词解释资源名:唯一名称、默认请求路径,也可以通过@SentinelResource的value属性指定,不可重复。针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)阀值类型/单机阀值: QPS(每秒钟的请求数量):当调用该api的QPS达到阀值的时候,进行限流线程数:当调用该api的线程数达到阀值的时候,进行限流是否集群:不需要集群流控模式:直接:api达到限流条件时,直接限流 关联:当关联的资源达到阀值时,就限流自己 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阀值,就限流)【api级别的针对来源】流控效果:快速失败:直接失败,抛异常 Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阀值/codeFactor,经过预热时长,才达到设置的QPS阀值 排队等待:匀速排队,让请求以均匀的速度通过,阀值类型必须设置为QPS,否则无效代码展示基于QPS的流量控制当QPS超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。对应FlowRule中的 controlBehavior字段。在代码中增加以下服务接口 @GetMapping("/testQPSA") public String testA() { return "-----------testQPSA"; }如何此时我们不配置Sentinel,无论如何访问,都不会报错,流量会直接打到服务器,直到服务器崩溃。此时,我们打开Sentinel,设置流控如下配置的信息代表每秒最多允许一个请求,超过依次的请求直接报错。此时我们快速刷新接口,可以看到以下信息基于并发数的流量控制基于并发数的控制,也就是控制同一时刻能够访问接口的数量。 @GetMapping("/testThreadA") public String testThreadA() { try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "-----------testThreadA"; }为了测试方便,我让程序暂停10S。上图代表我们统一时刻只能有一个线程访问。然后启动用浏览器及curl命令同时访问。可以看到第一个浏览器能正常打开,但是curl命令的无法访问。流控模式:直接以上两个例子都是直接控制,也就是关联自己的资源。流控模式:关联当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。像对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。这次我们新增以下两个服务。 @GetMapping("/testRelationA") public String testRelationA() { log.info(Thread.currentThread().getName() + "\t testRelationA"); return "-----------testRelationA"; } @GetMapping("/testRelationB") public String testRelationB() { log.info(Thread.currentThread().getName() + "\t testRelationB"); return "-----------testRelationB"; }我们模拟:当testRelationB达到阈值时,停止testRelationA服务。这次我们用JMeter进行测试,使用JMeter循环访问testRelationB可以看到testRelationB访问一直正常,但是testRelationA此时已经不能访问了。流控模式:链路只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)这次我们删除所有的流控规则,查看sentinel我们可以看到/testRelationB的资源入口是sentinel_web_servlet_context这次我们设置流控规则如下多次点击http://localhost:8401/testRelationB,可以看到浏览器显示流控效果:快速失败以上代码都是直接快速失败的例子,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。流控效果:Warm Up(预热)Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。我们增加以下代码。测试预测@GetMapping("/testWarmup") public String testWarmup() { return "-----------testWarmup"; }以上表示,一开始系统QPS为1(3/3),10秒之后,系统QPS为3。启用JMeter测试,设置请求100个,系统处理时间为50s,也就说1s发送两个请求。可以看到,一开始,间隔成功一个、失败一个(因为我们JMeter设置的是1s处理两个请求,而Sentinel设置的是1s只能处理一个请求),10秒之后,就一直成功了(因为10s后,阈值为10,也就是说我们1s能处理10个请求,小于我们JMeter测试的一秒两个请求)流控效果:排队等待排队等待(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。 @GetMapping("/testQueue") public String testQueue() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "-----------testQueue"; }一次处理一个请求,超时时间为20S,使用JMeter默认10个请求可以看到每个线程大概耗时都是1s。
2021年07月28日
1,196 阅读
0 评论
0 点赞
2021-07-28
Spring Cloud集成Sentinel之基础功能介绍
Sentinel涉及的知识点比较多,我们这篇文章先看一下基础的,如何在Spring Cloud中引入Sentiel。父工程与前面章节介绍的父工程一样,这里之简单罗列一下依赖<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> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement>添加服务提供者添加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-sentiel-service8401</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </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-sentinel</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件主要是配置Nacos服务器及Sentinel Dashboard的地址。server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: 192.168.120.180:1111 sentinel: transport: dashboard: 192.168.120.180:9000 # 默认8719端口,假如端口被占用,依次+1,直到找到未被占用端口 port: 8719 management: endpoints: web: exposure: include: "*"增加接口@RestController @Slf4j public class FlowLimitController { @GetMapping("/testA") public String testA() { return "-----------testA"; } @GetMapping("/testB") public String testB() { log.info(Thread.currentThread().getName() + "\ttestB"); return "-----------testB"; } @GetMapping("/testD") public String testD() { int age = 10 / 0; log.info(Thread.currentThread().getName() + "\ttestB"); return "-----------testD"; } @GetMapping("/testHotkey") @SentinelResource(value = "testHotkey", blockHandler = "deal_testHotkey") public String testHotkey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { return "--------------testHotkey"; } public String deal_testHotkey(String p1, String p2, BlockException blockException) { return "--------------deal_testHotkey"; } }测试访问http://localhost:8401/testA,然后打开Sentinel Dashboard,可以看到我们当前定义的服务接口。一定要访问依次我们的服务接口,否则在sentinel dashboard中无法看到
2021年07月28日
1,112 阅读
0 评论
1 点赞
2021-07-28
Spring Cloud Alibaba sentinel的安装与配置
sentinel简介Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 具有以下特性:丰富的应用场景:Sentinel承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。sentinel与hystrix区别提到服务的限流、熔断,就不得不提大名鼎鼎的豪猪哥(hystrix)。只是这货目前停更了。随着Spring Cloud 2020发布,hystix组件也被移除了。Sentinel 基本概念资源资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。规则围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。Sentinel 是如何工作的Sentinel 的主要工作机制如下:对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。sentinel安装下载按钮官方下载地址运行官方下载下来的是一个jar包,默认端口是8080执行以下命令运行即可java -jar -Dserver.port=9000 /root/sentinel/sentinel-dashboard-1.8.2.jar如果想后台运行,可以执行以下命令wsl -u root nohup java -jar -Dserver.port=9000 /root/sentinel/sentinel-dashboard-1.8.2.jar >log.out 2>1 &默认用户名、密码都是sentinel
2021年07月28日
1,073 阅读
0 评论
1 点赞
2021-07-27
Spring Cloud使用OpenFeign调用Nacos服务提供者
什么是OpenFeignOpenFeign为微服务架构下服务之间的调用提供了解决方案,OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。Spring Cloud 2020版本已经彻底移除了Netflix相关组件,OpenFeign便是Feign的替换者。本文我们是在Spring Cloud使用Nacos作为服务中心编写服务提供者的基础上进行的,通过OpenFeign调用前面编写的nacos-provider-payment服务。编写服务消费者创建cloudalibaba-consumer-order8888工程,调用我们编写的nacos-provider-payment服务。添加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-consumer-order9003</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> </dependencies> </project>修改配置文件修改application.yml增加Nacos相关配置。server: port: 8888 spring: application: name: nacos-consumer-order cloud: nacos: discovery: server-addr: 192.168.120.180:1111 management: endpoints: web: exposure: include: "*" 修改启动类添加@EnableFeignClients注解,支持使用OpenFeign@SpringBootApplication //@EnableDiscoveryClient @EnableFeignClients public class NacosConsumerOrderMain8888 { public static void main(String[] args) { SpringApplication.run(NacosConsumerOrderMain8888.class, args); } }编写Feign客户端添加@FeignClient注解,value便是我们服务提供者的名称。@FeignClient(value = "nacos-provider-payment") public interface NacosPaymentService { @GetMapping(value = "/echo/{string}") String echo(@PathVariable("string") String string); }测试编写controller进行测试@RestController @Slf4j @RequestMapping("/nacos/consumer") public class NacosConsumerController { @Resource private NacosPaymentService nacosPaymentService; @GetMapping("/echo/{id}") public String echo(@PathVariable("id") String id) throws IllegalAccessException { log.info("******************输出:" + id); return nacosPaymentService.echo(id); } }OpenFeign超时控制OpenFeign调用接口,默认超时时间为1S。我们修改一下nacos-provider-payment服务,让放服务暂停2s,然后查看一下接口调用。 @GetMapping(value = "/echo/{string}") public String echo(@PathVariable("string") String string) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello Nacos Discovery " + string+"\t当前服务端口:"+serverPort; }但是有时候我们业务接口可能需要处理的时间较长,那么我们可以配置接口调用的超时时间。我们可以在配置文件中增加以下内容#设置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000我们再次测试接口,可以发现接口可以正常调用。OpenFeign日志打印功能增加日志bean@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }修改配置文件YML文件里配置需要开启日志的Feign客户端logging: level: cc.lisen.springcloud.alibaba.service.NacosPaymentService: debug测试再次调用接口,可以看到输出的日志负载均衡如果我们同时启动了9001、9002端口,也就是说我们针对服务提供者做了集群,那么我们多次调用服务提供者时,会发现系统会依次输出9001、9002端口。也就是说,OpenFeign自带了负债均衡功能,默认采用的时轮询算法。我们修改一下默认的负债均衡算法,设置的简单一点,如果我们的请求包含name=zhangsan参数,就调用到9001端口,否则就调用9002端口。实现IRule接口public class CustomerBalancerRule implements IRule { private ILoadBalancer balancer = new BaseLoadBalancer(); @Override public Server choose(Object object) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String name = request.getParameter("name"); List<Server> servers = balancer.getAllServers(); if ("zhangsan".equals(name)) { if (servers.stream().anyMatch(e -> e.getPort() == 9001)) { return servers.stream().filter(e -> e.getPort() == 9001).collect(Collectors.toList()).get(0); } return servers.get(0); } return servers.get(1); } @Override public void setLoadBalancer(ILoadBalancer iLoadBalancer) { this.balancer = iLoadBalancer; } @Override public ILoadBalancer getLoadBalancer() { return this.balancer; } }配置服务提供者,调用负载均衡算法#设置feign客户端超时时间(OpenFeign默认支持ribbon) nacos-provider-payment: ribbon: #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000 NFLoadBalancerRuleClassName: cc.lisen.springcloud.alibaba.config.CustomerBalancerRule
2021年07月27日
1,142 阅读
0 评论
0 点赞
2021-07-27
Spring Cloud使用Nacos作为服务中心编写服务提供者
在Nacos集群配置中,我们介绍了Nacos的安装以及集群配置,本文我们介绍一下在Spring Cloud中如何使用Nacos作为服务注册中心。编写父工程父工程只提供基本的依赖关系,一定要注意版本,否则会出现很多乱七八糟的错误。父工程依赖如下<?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>cc.lisen</groupId> <artifactId>Cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>cloud-provider-payment8001</module> <module>cloud-consumer-order80</module> <module>cloud-api-common</module> <module>cloud-provider-payment8004</module> <module>cloud-consumerzk-order80</module> <module>cloud-consumer-feign-order80</module> <module>cloud-provider-hystrix-payment8001</module> <module>cloud-consumer-feign-hystrix-order80</module> <module>cloud-consumer-hystrix-dashboard9001</module> <module>cloud-gateway-gateway9527</module> <module>cloud-config-center-3344</module> <module>cloud-config-client-3355</module> <module>cloud-config-client-3366</module> <module>cloud-stream-rabbitmq-provider8801</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloudalibaba-provider-payment9001</module> <module>cloudalibaba-provider-payment9002</module> <module>cloudalibaba-consumer-order8888</module> <module>cloudalibaba-config-nacos-client3377</module> <module>cloudalibaba-sentinel-service8401</module> <module>seata-order-service2001</module> <module>seata-storage-service2002</module> <module>seata-account-service2003</module> </modules> <!-- 统一管理jar包版本--> <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> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>en,fr</locales> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> </plugin> </plugins> </reporting> </project> 请忽略其他的项目模块。编写服务提供者编写一个简单的服务提供者,输出一个字符串。添加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-provider-payment9002</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>修改配置文件server: port: 9001 spring: application: name: nacos-provider-payment cloud: nacos: discovery: server-addr: 192.168.120.180:8848 management: endpoints: web: exposure: include: "*"修改启动类@SpringBootApplication public class PaymentProviderMain9001 { public static void main(String[] args){ SpringApplication.run(PaymentProviderMain9001.class,args); } }编写服务接口@RestController @Slf4j public class NacosController { @Value("${server.port}") private String serverPort; @GetMapping(value = "/echo/{string}") public String echo(@PathVariable String string) { return "Hello Nacos Discovery " + string+"\t当前服务端口:"+serverPort; } }启动服务,我们可以查看一下Nacos是否有对应的服务。集群一般情况下,为了系统的健壮性,我们都会进行集群。我这里为了方便调试,复制了一个cloudalibaba-provider-payment9001工程,只是将端口号改成9002,然后,我们启动9002端口项目,再次查看Nacos可以看到当前有两个实例了。集群的关键时服务名称需要一致,也就是我们配置文件中的spring.application.name必须保持一致。
2021年07月27日
1,023 阅读
0 评论
0 点赞
2021-07-26
Seata基础教程下-Spring Cloud Alibaba使用Seata
在seata基础教程上-seata 1.4.2的安装及基于Nacos的配置中,我们基于Nacos完成了Seata的安装及配置,这篇文章我们介绍下在Spring Cloud Alibaba中的使用。环境准备学习本文之前,你应该准备好以下环境。Nacos 2.0.2版本并运行Seata Server 1.4.2版本,配置好Nacos并运行前置知识本文通过Spring Cloud Alibaba,使用Nacos作为服务中心,通过OpenFeign实现服务之间的调用,所以你必须具备以下基础知识:在Spring Cloud中使用Nacos作为服务注册、发现中心的配置及使用。在Spring Cloud中通过OpenFeign实现服务之间的调用。MyBatis的使用。场景描述本文参考尚硅谷阳哥的视频教程,使用最新的Nacos(2.0.2)及Seata(1.4.2),演示用户下单的业务场景,基本流程如下其实业务很简单,用户下单,扣减商品库存表数据然后减去账户余额,如果不是微服务架构,我们一般使用一个系统,基本上一个@Transactional注解就解决了。基于微服务就不同了,我们提供三个微服务,分别为订单服务、库存服务、账户服务,三个服务对应三个数据库,分别为seata_order、seata_storage、seata_account,现在不同业务分布在不同的数据库中,那么我们在进行数据回滚就不太好处理了。Seata的分布式交易解决方案有了Seata就简单了,我们只需要使用一个@GlobalTransactional注解在业务方法上就可以了。Spring Cloud使用Seata创建数据库创建三个数据库,分别为seata_order、seata_storage、seata_account。订单微服务表结构create table t_order ( id bigint auto_increment comment '主键', user_id bigint null comment '用户Id', product_id bigint null, count int null, money decimal(11) null, status int(1) null, constraint t_order_id_uindex unique (id) ); alter table t_order add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8;账户微服务表结构create table t_account ( id bigint auto_increment, user_id bigint null, total decimal null, used decimal null, residue decimal null, constraint t_account_id_uindex unique (id) ); alter table t_account add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_account.t_account (id, user_id, total, used, residue) VALUES (1, 1, 1000, 0, 1000);库存微服务表结构create table t_storage ( id bigint auto_increment, product_id bigint null, total int null, used int null, residue int null comment '剩余库存', constraint t_storage_id_uindex unique (id) ); alter table t_storage add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_storage.t_storage (id, product_id, total, used, residue) VALUES (2, 1, 100, 0, 100);创建工程工程结构,如下图,请忽略其他模块。[alt type="warning"]一定要注意依赖里面的版本号,不然会出现各种错误。[/alt]创建父工程父工程用于维护依赖管理,没有具体代码。增加依赖<?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>cc.lisen</groupId> <artifactId>Cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>cloud-provider-payment8001</module> <module>cloud-consumer-order80</module> <module>cloud-api-common</module> <module>cloud-provider-payment8004</module> <module>cloud-consumerzk-order80</module> <module>cloud-consumer-feign-order80</module> <module>cloud-provider-hystrix-payment8001</module> <module>cloud-consumer-feign-hystrix-order80</module> <module>cloud-consumer-hystrix-dashboard9001</module> <module>cloud-gateway-gateway9527</module> <module>cloud-config-center-3344</module> <module>cloud-config-client-3355</module> <module>cloud-config-client-3366</module> <module>cloud-stream-rabbitmq-provider8801</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloudalibaba-provider-payment9001</module> <module>cloudalibaba-provider-payment9002</module> <module>cloudalibaba-consumer-order80</module> <module>cloudalibaba-config-nacos-client3377</module> <module>cloudalibaba-sentinel-service8401</module> <module>seata-order-service2001</module> <module>seata-storage-service2002</module> <module>seata-account-service2003</module> </modules> <!-- 统一管理jar包版本--> <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> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>en,fr</locales> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> </plugin> </plugins> </reporting> </project> 创建通用模块通用模块这里维护了一个CommonResult用于返回Json对象。增加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-api-common</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> </dependencies> </project>增加实体package cc.lisen.springcloud.entites; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-24 11:25 */ @Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { private Integer code; private String message; private T data; public CommonResult(Integer code, String message) { this(code, message, null); } }创建订单微服务模块订单模块维护订单微服务,通过Feign调用库存及账户微服务。增加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-order-service2001</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件配置文件一定要注意,tx-service-group一定要与我们Seata服务端的配置保持一致。server: port: 2001 spring: application: name: seata-order-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Order { /** * 主键 */ private Long id; /** * 用户Id */ private Long userId; private Long productId; private Integer count; private BigDecimal money; private Integer status; }增加Mapperpublic interface OrderDao { int create(Order record); int update(@Param("userId") Long userId, @Param("status") Integer status); 数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.OrderDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Order"> <!--@Table t_order--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="count" jdbcType="INTEGER" property="count"/> <result column="money" jdbcType="DECIMAL" property="money"/> <result column="status" jdbcType="INTEGER" property="status"/> </resultMap> <sql id="Base_Column_List"> <!--@mbg.generated--> id, user_id, product_id, `count`, money, `status` </sql> <insert id="create" parameterType="cc.lisen.springcloud.alibaba.seata.domain.Order"> <!--@mbg.generated--> insert into t_order (user_id, product_id, `count`, money, `status`) values (#{userId,jdbcType=BIGINT}, #{productId,jdbcType=BIGINT}, #{count,jdbcType=INTEGER}, #{money,jdbcType=DECIMAL}, #{status,jdbcType=INTEGER}) </insert> <update id="update"> update t_order set status = 1 where user_id = #{userId} and status = #{status} </update> </mapper>增加服务接口增加账户微服务Feign调用接口@FeignClient(value = "seata-account-service") public interface IAccountService { @PostMapping("/account/decrease") public CommonResult accountDecrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money); }增加库存微服务Feign调用接口@FeignClient(value = "seata-storage-service") public interface IStorageService { @PostMapping("/storage/decrease") public CommonResult storageDecrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count); } 增加订单接口public interface IOrderService { int create(Order record); }增加订单服务实现@Service @Slf4j public class OrderServiceImpl implements IOrderService { @Resource private OrderDao orderDao; @Resource private IStorageService storageService; @Resource private IAccountService accountService; @Override @GlobalTransactional(name = "fsp-create-order") // @Transactional public int create(Order order) { log.info("--------->开始新建订单"); orderDao.create(order); log.info("--------->订单微服务开始调用库存,做扣减Count"); storageService.storageDecrease(order.getProductId(), order.getCount()); log.info("--------->订单微服务开始调用库存,做扣减End"); log.info("--------->订单微服务开始调用账户,做扣减Money"); accountService.accountDecrease(order.getUserId(), order.getMoney()); log.info("--------->订单微服务开始调用账户,做扣减End"); log.info("--------->修改订单状态开始"); orderDao.update(order.getUserId(), 0); log.info("--------->修改订单状态End"); return 1; } }增加接口controller@RestController @Slf4j @RequestMapping("/order") public class OrderController { @Resource private IOrderService orderService; @PostMapping("/create") public CommonResult create(Order order) { orderService.create(order); return new CommonResult(200, "订单创建成功"); } }增加DataSource,让Seata代理数据库@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) public class SeataOrderServiceMain2001 { public static void main(String[] args) { SpringApplication.run(SeataOrderServiceMain2001.class, args); } }创建库存微服务模块库存微服务用于提供扣减库存的微服务接口。增加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-storage-service2002</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2002 spring: application: name: seata-storage-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_storage?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Storage { private Long id; private Long productId; private Integer total; private Integer used; private Integer residue; }增加Mapperpublic interface StorageDao { int decrease(@Param("productId") Long productId, @Param("count") Integer count); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.StorageDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Storage"> <!--@Table t_storage--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="total" jdbcType="INTEGER" property="total"/> <result column="used" jdbcType="INTEGER" property="used"/> <result column="residue" jdbcType="INTEGER" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, product_id, total, used, residue </sql> <update id="decrease"> update t_storage set used = used + #{count}, residue = residue - #{count} where product_id = #{productId} </update> </mapper>增加服务接口public interface IStorageService { int decrease(Long productId, Integer count); } 增加服务实现@Service @Slf4j public class StorageServiceImpl implements IStorageService { @Resource private StorageDao storageDao; @Override // @Transactional public int decrease(Long productId, Integer count) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); return storageDao.decrease(productId, count); } }增加接口controller@RestController @RequestMapping("/storage") @Slf4j public class StorageController { @Resource private IStorageService storageService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) { storageService.decrease(productId, count); return new CommonResult(200, "扣减库存成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) public class SeataStorageServiceMain2002 { public static void main(String[] args) { SpringApplication.run(SeataStorageServiceMain2002.class, args); } }创建账户微服务模块账户微服务用于提供扣减账户金额的微服务接口。增加依赖<?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>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-account-service2003</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-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2003 spring: application: name: seata-account-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Account { private Long id; private Long userId; private Long total; private Long used; private Long residue; }增加Mapperpublic interface AccountDao { int decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.AccountDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Account"> <!--@Table t_account--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="total" jdbcType="DECIMAL" property="total"/> <result column="used" jdbcType="DECIMAL" property="used"/> <result column="residue" jdbcType="DECIMAL" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, user_id, total, used, residue </sql> <update id="decrease"> update t_account set used = used + #{money}, residue=residue - #{money} where user_id = #{userId} </update> </mapper>增加服务接口public interface IAccountService { int decrease( Long userId, BigDecimal money); }增加服务实现这里注意以下,我们模拟了报错信息@Service @Slf4j public class AccountServiceImpl implements IAccountService { @Resource private AccountDao accountDao; @Override // @Transactional public int decrease(Long userId, BigDecimal money) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); //模拟报错 int age = 10 / 0; return accountDao.decrease(userId, money); } }增加接口controller@RestController @Slf4j @RequestMapping("/account") public class AccountController { @Resource private IAccountService accountService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) { accountService.decrease(userId, money); return new CommonResult(200, "账户扣减成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class SeataAccountServiceMain2003 { public static void main(String[] args) { SpringApplication.run(SeataAccountServiceMain2003.class, args); } }测试通过postman测试可以看到系统抛出了异常,然后我们查看以下数据库,可以看到数据没有发生任何变化。
2021年07月26日
1,060 阅读
0 评论
1 点赞
2021-07-26
Seata基础教程上-seata 1.4.2的安装及基于Nacos的配置
先吐槽一下,seata的文档真的是乱的一批。我们这里只介绍安装及基本使用过程。1.seata介绍Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。具体原理可以参考官网http://seata.io/zh-cn/docs/overview/what-is-seata.html。[alt type="success"]一定要注意版本问题,包括安装的Nacos版本、Seata版本、Spring Cloud版本、Spring Boot版本等等。[/alt]2.前置条件2.1安装JDK并配置环境变量安装JDK1.8及以上版本,并正确配置环境变量。2.2maven我这里用的是3.6.3,并配置阿里云仓库。2.3Git由于需要将Seata Server的配置文件导入Nacos,所以需要安装Git2.4安装NacosNacos 2.0.2版本,关于Nacos的安装,我们稍后会出一个专门的文章进行介绍。[tag type="warning"]温馨提示[/tag]所以学习本篇文章之前,您至少应该改知道Nacos的安装以及在Spring Cloud中如何利用Nacos进行服务的注册与发现。Nacos默认端口为8848,由于我这里配置了集群,所以前端端口使用的是1111,大家在下面配置文件注意以下。3.seata安装我这里使用的seata是1.4.2(截止到目前最新的版本)。[btn href="http://seata.io/zh-cn/blog/download.html" type="default"]Seata下载地址[/btn]3.1.修改配置文件3.1.1.修改file.conf进入Seata的conf目录,找到file.conf,如下图修改以下内容## transaction log store, only used in seata-server store { ## store mode: file、db、redis mode = "db" #我们用数据库存储,改成db ## rsa decryption public key publicKey = "" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" # 改成MySql driverClassName = "com.mysql.jdbc.Driver" ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param url = "jdbc:mysql://ip:3306/数据库名称?rewriteBatchedStatements=true" user = "数据库用户" password = "数据库密码" minConn = 5 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { ## redis mode: single、sentinel mode = "single" ## single mode property single { host = "127.0.0.1" port = "6379" } ## sentinel mode property sentinel { masterName = "" ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381" sentinelHosts = "" } password = "" database = "0" minConn = 1 maxConn = 10 maxTotal = 100 queryLimit = 100 } } 3.1.2.修改registry.conf进入Seata的conf目录,找到registry.conf,如下图我们这里用Nacos,所以主要是配置Nacos的信息,配置内容包括两部分,registry服务,将type从file改成nacos,将seata服务配置进nacos;config将type从file改成nacos,这样不需要每个项目都放file.conf修改内容如下:registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" #改成Nacos nacos { application = "seata-server" serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 group = "SEATA_GROUP" #Nacos分组 namespace = "" username = "nacos" #Nacos用户名 password = "nacos" #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" aclToken = "" } 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 = "nacos" #改成Nacos nacos { serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 namespace = "" group = "SEATA_GROUP" #Nacos分组 username = "nacos" #Nacos用户名 password = "nacos" #Nacos密码 # dataId = "" } consul { serverAddr = "127.0.0.1:8500" aclToken = "" } apollo { appId = "seata-server" ## apolloConfigService will cover apolloMeta apolloMeta = "http://192.168.1.204:8801" apolloConfigService = "http://192.168.1.204:8080" namespace = "application" apolloAccesskeySecret = "" cluster = "seata" } zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" nodePath = "/seata/seata.properties" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } } 3.1.3.导入Seata相应的配置项到Nacos的配置中心(非注册中心)由于我这里注册中心跟配置中心用的一个,所以我其实导入的就是一个Nacos。3.1.3.1.创建导入脚本在conf文件夹下,创建nacos-config.sh,并将以下内容粘贴进去#!/usr/bin/env bash # Copyright 1999-2019 Seata.io Group. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at、 # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. while getopts ":h:p:g:t:u:w:" opt do case $opt in h) host=$OPTARG ;; p) port=$OPTARG ;; g) group=$OPTARG ;; t) tenant=$OPTARG ;; u) username=$OPTARG ;; w) password=$OPTARG ;; ?) echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1 ;; esac done urlencode() { for ((i=0; i < ${#1}; i++)) do char="${1:$i:1}" case $char in [a-zA-Z0-9.~_-]) printf $char ;; *) printf '%%%02X' "'$char" ;; esac done } if [[ -z ${host} ]]; then host=localhost fi if [[ -z ${port} ]]; then port=8848 fi if [[ -z ${group} ]]; then group="SEATA_GROUP" fi if [[ -z ${tenant} ]]; then tenant="" fi if [[ -z ${username} ]]; then username="" fi if [[ -z ${password} ]]; then password="" fi nacosAddr=$host:$port contentType="content-type:application/json;charset=UTF-8" echo "set nacosAddr=$nacosAddr" echo "set group=$group" failCount=0 tempLog=$(mktemp -u) function addConfig() { curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [[ -z $(cat "${tempLog}") ]]; then echo " Please check the cluster status. " exit 1 fi if [[ $(cat "${tempLog}") =~ "true" ]]; then echo "Set $1=$2 successfully " else echo "Set $1=$2 failure " (( failCount++ )) fi } count=0 for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do (( count++ )) key=${line%%=*} value=${line#*=} addConfig "${key}" "${value}" done echo "=========================================================================" echo " Complete initialization parameters, total-count:$count , failure-count:$failCount " echo "=========================================================================" if [[ ${failCount} -eq 0 ]]; then echo " Init nacos config finished, please start seata-server. " else echo " init nacos config fail. " fi3.1.3.2.创建配置文件在conf的上级目录,创建config.txt并粘贴以下内容transport.type=TCP transport.server=NIO transport.heartbeat=true transport.enableClientBatchSendRequest=true transport.threadFactory.bossThreadPrefix=NettyBoss transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler transport.threadFactory.shareBossWorker=false transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector transport.threadFactory.clientSelectorThreadSize=1 transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread transport.threadFactory.bossThreadSize=1 transport.threadFactory.workerThreadSize=default transport.shutdown.wait=3 service.vgroupMapping.my_tx_group=default #自定义,一定注意与spring cloud配置文件保持一致 service.default.grouplist=127.0.0.1:8091 #seata服务 service.enableDegrade=false service.disableGlobalTransaction=false client.rm.asyncCommitBufferLimit=10000 client.rm.lock.retryInterval=10 client.rm.lock.retryTimes=30 client.rm.lock.retryPolicyBranchRollbackOnConflict=true client.rm.reportRetryCount=5 client.rm.tableMetaCheckEnable=false client.rm.tableMetaCheckerInterval=60000 client.rm.sqlParserType=druid client.rm.reportSuccessEnable=false client.rm.sagaBranchRegisterEnable=false client.rm.tccActionInterceptorOrder=-2147482648 client.tm.commitRetryCount=5 client.tm.rollbackRetryCount=5 client.tm.defaultGlobalTransactionTimeout=60000 client.tm.degradeCheck=false client.tm.degradeCheckAllowTimes=10 client.tm.degradeCheckPeriod=2000 client.tm.interceptorOrder=-2147482648 store.mode=db store.lock.mode=db store.session.mode=db store.publicKey= store.file.dir=file_store/data store.file.maxBranchSessionSize=16384 store.file.maxGlobalSessionSize=512 store.file.fileWriteBufferCacheSize=16384 store.file.flushDiskMode=async store.file.sessionReloadReadSize=100 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://数据库IP:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user=数据库用户名 store.db.password=数据库密码 store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000 store.redis.mode=single store.redis.single.host=127.0.0.1 store.redis.single.port=6379 store.redis.sentinel.masterName= store.redis.sentinel.sentinelHosts= store.redis.maxConn=10 store.redis.minConn=1 store.redis.maxTotal=100 store.redis.database=0 store.redis.password= store.redis.queryLimit=100 server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTimeoutUnlockEnable=false server.distributedLockExpireTime=10000 client.undo.dataValidation=true client.undo.logSerialization=jackson client.undo.onlyCareUpdateColumns=true server.undo.logSaveDays=7 server.undo.logDeletePeriod=86400000 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k log.exceptionRate=100 transport.serialization=seata transport.compressor=none metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898上图一定要注意配置数据库信息及service.vgroupMapping.my_tx_group=default,这个后续在spring cloud项目的配置文件中会用到。3.1.3.3.导入配置文件打开git bash,进入到conf目录中,执行以下命令sh [nacos-config.sh文件路径] -h [nacos-ip地址] -p 8848 -g SEATA_GROUP[导入的组] -u [用户名] -w [密码] 执行完成后,查看我们Nacos配置中心中,配置文件是否已存在。3.1.4.创建相关的数据库(在回滚或提交前会将日志保存在数据库中,成功后会删除)3.1.3.1.创建seata数据库,并预置数据这个数据库就是我们上面配置文件中使用到的数据库,脚本如下-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTS `global_table` ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;3.1.4.2.创建undo_log表注意:在业务数据库中创建undo_log,与上面的脚本不在一个数据库中。比如我有三个微服务,三个数据库,那么就需要在三个业务数据库中创建undo_log表-- 在业务数据库中创建undo_log(与上面的脚本不在一个数据库中) CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(128) 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';4.运行seata运行Nacos这里不多介绍。运行seata进入seata的bin目录,执行./seata-server.bat,运行成功后,查看Nacos的服务列表能看到我们配置的seata即代表成功。
2021年07月26日
1,243 阅读
0 评论
0 点赞
2021-07-22
Spring Data Jpa Listener事件的扩展之自定义EntityListener
随着DDD的设计模式逐渐被大家认可和热捧。JPA通过Listener这种机制可以很好地实现事件分离、状体分离。假如,订单的状态变化可能对我们来说比较重要,我们需要定一个类去监听订单状态变更,通知相应的逻辑代码各自去干各自的活。新增一个UserAuditListener类,在相应的操作上添加Callbacks注解@Slf4j public class UserAuditListener { @PostPersist private void postPersist(SysUser sysUser) { recordLog(sysUser, OperateType.CREATE); } @PostRemove private void postRemove(SysUser sysUser) { recordLog(sysUser, OperateType.REMOVE); } @PostUpdate private void postUpdate(SysUser sysUser) { recordLog(sysUser, OperateType.UPDATE); } @PostLoad public void postLoad(SysUser sysUser) { recordLog(sysUser, OperateType.LOAD); } /** * 记录审计日志 * * @param sysUser 用户实体 * @param operateType 操作类型 */ private void recordLog(SysUser sysUser, OperateType operateType) { log.info("{}执行了{}操作", sysUser, operateType.getType()); } } enum OperateType { CREATE("创建"), UPDATE("更新"), REMOVE("删除"), LOAD("查询"); private final String type; OperateType(String type) { this.type = type; } public String getType() { return this.type; } }修改实体,增加@EntityListeners注解@Entity @Table(name = "sys_user") @Data @Slf4j @EntityListeners({UserAuditListener.class}) public class SysUser extends AbstractAuditable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long userId; @JoinColumn(name = "dept_id", referencedColumnName = "dept_id") @ManyToOne(cascade = CascadeType.ALL) private SysDept sysDept; private String userName; private String nickName; private String userType; private String email; @Column(name = "phonenumber") private String phoneNumber; private String sex; private String avatar; @JsonIgnore private String password; @Enumerated(EnumType.STRING) private Status status; private String delFlag; private String loginIp; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp loginDate; private String remark; private Long recycleCompanyId; private Long medicalInstitutionId; private Long ethnicity; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")}) private Set<SysRole> sysRoles; }测试这样,我们在增删改查方法调用时,就会执行我们对应的callback注解的方法比如我们插入用户@GetMapping("/save") public Long save(){ SysUser sysUser = new SysUser(); sysUser.setNickName("测试123321"); sysUser.setUserName("测试123321"); sysUser.setSex("0"); sysUserRepository.save(sysUser); return sysUser.getUserId(); }保存后,会自动调用postPersist方法
2021年07月22日
1,350 阅读
0 评论
1 点赞
1
2
3
4
...
8