后端微服务基础架构Spring Cloud
版本关系
版本发布说明-阿里云Spring Cloud Alibaba官网
选择
创建项目
创建父项目
什么都不动,创建即可
1) 删掉没用的文件
保留
2) pom中加入
打包方式 <packaging>pom</packaging>
3) 删掉src
4) pom.xml中删除没用的
5)更改pom.xml中
spring-boot-starter-parent版本为更改为3.3.4,点击右上角的maven标记刷新依赖
删掉依赖
properties更换为
<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-cloud.version>2023.0.3</spring-cloud.version><spring-cloud-alibaba.version>2023.0.3.2</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>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.4</version><relativePath/> <!-- lookup parent from repository --></parent><packaging>pom</packaging><groupId>com.i7i8i9</groupId><artifactId>weifuwu3</artifactId><version>0.0.1-SNAPSHOT</version><name>weifuwu3</name><description>weifuwu3</description><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-cloud.version>2023.0.3</spring-cloud.version><spring-cloud-alibaba.version>2023.0.3.2</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>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
创建最外层服务
1.创建module ---services
就是个普通java module,不用选add sample code
pom中也是加入打包方式
<packaging>pom</packaging>
<?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>weifuwu3</artifactId><groupId>com.i7i8i9</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>services</artifactId><packaging>pom</packaging><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>
删除src
创建微服务
新建module ,主要是确认一定在上面建立的services服务下,不用选add sample code,Parent一定要对,
创建完成后,在父级服务pom中可以看到<modules>
查看maven结构
可以点击齿轮,选中Group Modules
完善项目
导入公共依赖
主要是在最外层services pom.xml中加入,比如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"><parent><artifactId>weifuwu3</artifactId><groupId>com.i7i8i9</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>services</artifactId><packaging>pom</packaging><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!-- 导入nacos--><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></dependencies>
</project>
在各层服务依赖中查看
启动
给每个微服务pom.xml导入Web依赖
在某个微服务上点击右键 generate,选择 edit starters
不行自己导
<?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>weifuwu3</artifactId><groupId>com.i7i8i9</groupId><version>0.0.1-SNAPSHOT</version><relativePath>../../pom.xml</relativePath></parent><modelVersion>4.0.0</modelVersion><artifactId>service-order</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>
新建主程序
src/main/java下新建
com.i7i8i9.auth.AuthMainApplication
package com.i7i8i9.order;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class OrderMainApplication {public static void main(String[] args) {SpringApplication.run(OrderMainApplication.class, args);}
}
编写application.yml
spring.application.name=service-order
server.port=8080
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
Nacos
下载,启动
http://192.168.32.1:8848/nacos/index.html
基本原则
1.Gateway: 前后端之间会经过GateWay, 后端服务之间因为属于同一个工程,一般不经过
GateWay,但也可以经过
服务
1.目录
服务可以都放在服务模块下,但是需要在服务模块的pom.xml体现
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-demo</artifactId><groupId>com.i7i8i9</groupId><version>0.0.1-SNAPSHOT</version></parent><packaging>pom</packaging><modelVersion>4.0.0</modelVersion><modules><module>service-product</module><module>service-order</module><module>seata-account</module><module>seata-business</module><module>seata-order</module><module>seata-storage</module></modules><artifactId>services</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!-- sentinel是流量保护 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><!-- nucos 注册中心 服务发现--><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><!-- openfeign 远程调用--><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>annotationProcessor</scope></dependency><dependency><groupId>com.i7i8i9</groupId><artifactId>model</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies>
</project>
Feign远程调用
指的是需要调用其他服务的服务
1.服务src下建立feign文件夹
2.服务主程序上加注释
@EnableFeignClients(basePackages = "com.atguigu.business.feign")
3.Feign文件夹内建需要调用的抽象类客户端
主要目的是供serviceImp使用
快速的方式是去被调用controller方面里面复制
方法名前面可以去掉public
package com.atguigu.business.feign;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(value="seata-storage") //服务名称nacos里面有
public interface StorageFeignClient {//找到扣库存服务的接口复制其方法名和参数/**** @param commodityCode* @param count* @return*/@GetMapping("/deduct")String deduct(@RequestParam("commodityCode") String commodityCode,@RequestParam("count") Integer count);
}
4.服务实现类
主要是装配Feign客户端,再调用其方法
package com.atguigu.business.service.impl;import com.atguigu.business.feign.OrderFeignClient;
import com.atguigu.business.feign.StorageFeignClient;
import com.atguigu.business.service.BusinessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BusinessServiceImpl implements BusinessService {//第一步就是拿到远程服务客户端@AutowiredStorageFeignClient storageFeignClient;@AutowiredOrderFeignClient orderFeignClient;@Overridepublic void purchase(String userId, String commodityCode, int orderCount) {//TODO 1. 扣减库存storageFeignClient.deduct(commodityCode,orderCount);//TODO 2. 创建订单orderFeignClient.create(userId,commodityCode,orderCount);}
}
GateWay
SpringCloud基础2——Nacos配置、Feign、Gateway-阿里云开发者社区
nacos(七): gateway(单体)-CSDN博客
创建服务
pom.xml引入gateway和loadbalence
<!--网关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--消费者--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId><version>3.1.5</version></dependency>
新建module
加上服务发现@EnableDiscoveryClient
package com.i7i8i9.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
启动
yml
application.yml
spring:active: devapplication:name: gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848username: nacospassword: nacosconfig:server-addr: 127.0.0.1:8848file-extension: propertiesusername: nacospassword: nacosloadbalancer:nacos:enabled: truegateway:discovery:locator:enabled: trueroutes: # 网关路由配置#第一组网关路由配置,针对于服务service-auth- id: serviceAuth # 路由id,自定义,只要唯一即可uri: lb://service-auth # 指的是从nacos中按照名称获取微服务,并遵循负载均衡策略predicates: # 路由断言。也就是判断请求是否符合路由规则的条件。predicates译为谓语、断言- Path=/auth/** # 路径断言。当请求路径满⾜Path指定的规则时,才进⾏路由换发filters:- StripPrefix=1 # 拼接好url之后去掉1层路径也就是p1#http://localhost:10010/auth/token,因为/auth/路径触发了第一个路由规则,路由去掉p1这一项,重新拼接为lb://service-auth/auth去获取访问的结果。:datasource:driver-class-name: ${i7i8i9.datasource.driver-class-name}url: jdbc:mysql://${i7i8i9.datasource.host}:${i7i8i9.datasource.port}/${i7i8i9.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=trueusername: ${i7i8i9.datasource.username}password: ${i7i8i9.datasource.password}
- id: serviceAuth # 路由id,自定义,只要唯一即可
uri: lb://service-auth # 指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates: # 路由断言。也就是判断请求是否符合路由规则的条件。predicates译为谓语、断言
- Path=/auth/** # 路径断言。当请求路径满⾜Path指定的规则时,才进⾏路由换发
filters:
- StripPrefix=1 # 拼接好url之后去掉1层路径也就是p1
#http://localhost:10010/auth/token,因为/auth/路径触发了第一个路由规则,路由去掉p1这一项,重新拼接为lb://service-auth/auth去获取访问的结果。:
假设有个服务实际调用地址为
http://localhost:8085/token
则也可以用以下两种方式访问:
http://localhost:10010/service-auth/token 这种service-auth就是实际上nacos服务名称
http://localhost:10010/auth/token 这是标准的,auth就是上面的断言,就是当出现auth时就去调用nacos 里面的service-auth
404: 注意如果请求404则可能是最后面多了/
AxiosError {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}
application-dev.yml
server:port: 10010 #当前spring boot工程运行时所占用的端口号i7i8i9:datasource:driver-class-name: com.mysql.cj.jdbc.Driverhost: 120.79.150.231 #主机地址port: 3306 #端口号database: basic #当前需要连接的数据库username: admin #用户password: tyuueew333444IIIIOOO88qqq #密码
服务查看
跨域
可以使用nginx
nginx: download
默认80端口
D:\soft\nginx-1.27.5\conf
4
nginx -s reload
npm run build
事务
每个微服务都有对应的数据库
本地事务
主要是单接口测试,自己操作自己的数据库
操作数据库的服务的main主类上
@EnableTransactionManagement
一般ServiceImpl类会加
@Transactional
注意没有引入mybatis是没有这个注解的
全局事务-alibaba seata
本地事务要先配好
1.启动客户端Seata-绿色软件
下载Seata Java Download | Apache Seata
要使用2.1.0
两次解压,找到文件夹seata-server
点击bat文件即可启动
登录 http://localhost:7091/,账号密码都是seata 7091是网页端口
注意服务监听端口不是7091,而是8091
2.为服务总配置引入seata
在最外层服务pom.xml中,ctrl进入
spring-cloud-alibaba-dependencies
在里面搜索
可见seata的版本是2.1.0所以启动seata服务端一定要用2.1.0
复制下面的alibaba的seata,注意不是springboot的
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><version>2023.0.3.2</version></dependency>
在服务最外层service模块下的pom.xml引入
不用写版本号
引入之后在maven依赖中可以看到已经有了
3.服务文件引入
可以配置每个服务的yml用于指定分组
但也可以不在yml配,默认为
default_tx_group,因为如下有默认组
添加file.conf文件,以后也可以放到nacos里面
service {#transaction service group mapping default_tx_group是默认分组vgroupMapping.default_tx_group = "default"#only support when registry.type=file, please don't set multiple addresses#注意不是seara网页端口default.grouplist = "127.0.0.1:8091"#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false
}
4.标注全局事务
@GlobalTransactional
也就是需要配置TM,它是入口
package com.atguigu.business.service.impl;import com.atguigu.business.feign.OrderFeignClient;
import com.atguigu.business.feign.StorageFeignClient;
import com.atguigu.business.service.BusinessService;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BusinessServiceImpl implements BusinessService {//第一步就是拿到远程服务客户端@AutowiredStorageFeignClient storageFeignClient;@AutowiredOrderFeignClient orderFeignClient;
@GlobalTransactional@Overridepublic void purchase(String userId, String commodityCode, int orderCount) {//TODO 1. 扣减库存storageFeignClient.deduct(commodityCode,orderCount);//TODO 2. 创建订单orderFeignClient.create(userId,commodityCode,orderCount);}
}
四种事务模式
默认是AT模式,就是自动模式
XA模式:
使用早期数据库模式,第一阶段提交就没有真正提交, 一直占用数据库,阻塞等待
更改方法
TCC
全手动,适合于夹杂了非数据库事务,也就是广义的事务,比如发邮件,发短信
要额外写接口这三个阶段(准备,提交,回滚)要做什么
比如第一阶段已经发了短信,只能再第三阶段再发一个纠正短信了
Saga
长事务
可能要好多天,比如请假,要经过审批,就不适合于用锁了
基于消息队列,第二个人收到第一个人的消息再做事,某个人失败了就通知所有人失败了
其他人收到消息后使用自定义处理,也就是补偿代码
其它
1.配置输出为null
order.timeout=null;order.auto-confirm=nullorder.db-urlnull
原因
proerties:
#spring.cloud.nacos.discovery.server-addr=localhost:8848
yml
没用discovery
server:port:8900
spring:application:name: service-ordercloud:nacos:server-addr: localhost:8848config:namespace: devconfig:import:- nacos:common.properties?group=order- nacos:db.properties?group=order
日志
给微服务配置远程日志
在每个服务的application.yml中给feign包
logging:level:com.i7i8i9.feign: debug
#注意:远程配置的优先级高于本地server:port:8900 #虽然这里写了8090,但是实际使用端口还是可以在Edit configurattion中指定
spring:profiles:active: uat #第1步: 指定本项目激活环境,该名字需要对应nacos的namespaceinclude: feign #包含yml标识 feign,也就是application-feign.ymlapplication:name: service-ordercloud:nacos:server-addr: localhost:8848config:namespace: ${spring.profiles.active:dev} #获取激活环境名称,找到nacos同名命名空间,如果获取不到环境名称就使用冒号后面的默认值
# :dev是默认值dev的意思,如果找不到这个变量
logging:level:com.i7i8i9.feign: debug---
spring: #on-profile: dev 也就是说dev需要启用的配置,每个环境要导入的不一定一样config:import:- nacos:common.properties?group=order- nacos:db.properties?group=orderactivate:on-profile: dev---
spring: #on-profile: UAT 也就是说dev需要启用的配置,每个环境要导入的不一定一样config:import:- nacos:common.properties?group=order- nacos:db.properties?group=orderactivate:on-profile: uat---
spring: #on-profile: prod 也就是说dev需要启用的配置,每个环境要导入的不一定一样config:import:- nacos:common.properties?group=order- nacos:db.properties?group=orderactivate:on-profile: prod
然后在微服务的配置类里面把Level放到容器中(就是加上@Bean)
package com.i7i8i9.config;import feign.Logger;
import feign.RetryableException;
import feign.Retryer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class OrderConfig {// @BeanRetryer retryer(){return new Retryer.Default();}@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}@LoadBalanced //注解式负载均衡,不要难过其他操作@BeanRestTemplate restTemplate(){return new RestTemplate();}}
注释
idea创建java类时自动配置注解 idea如何设置类的自动注释_mob64ca1415f0ab的技术博客_51CTO博客
前端
Vue3与ts中使用axios vue3加ts_mob64ca14031c97的技术博客_51CTO博客
Debug
1.seata两段事务debug
标注断点
全部服务都以debug模式启动
发送请求
刷新seata
可见因为标记了@GlobalTransactional出现一个全局事务
applicationId就是所属应用: seata-business
transactionName触发全局事务入口 purchase(java.lang.String, java.lang.String, int)
status:beigin
timeout: 1分钟没做完就过期
还可以点击最后面查看用到了哪些全局锁(每个服务修改自己数据前获得的,以保证别的服务不能穿插着改)
放行
报错
指向扣库存,原因是库存分支事务注册失败,超过了上述配置的60s导致
重新来
重新发送请求,再加个断点
放行
就会来到第二个断点
打开最右侧的包括分支事务
就可以查看分支事务,是已经注册完了的库存事务
全局锁也能看到信息
可以看到在数据表storage_tbl有一把全局锁
去storage_db数据库查看
数据此时是98
并且有undo日志
数据复制出来
{"@class":"org.apache.seata.rm.datasource.undo.BranchUndoLog","xid":"192.168.32.1:8091:9628920695037968","branchId":9628920695037969,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"org.apache.seata.rm.datasource.undo.SQLUndoLog","sqlType":"UPDATE","tableName":"storage_tbl","beforeImage":
{"@class":"org.apache.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",
[{"@class":"org.apache.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",
[{"@class":"org.apache.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":100},
{"@class":"org.apache.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":1}]]}]]},"afterImage":
{"@class":"org.apache.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",
[{"@class":"org.apache.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",
[{"@class":"org.apache.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":98},
{"@class":"org.apache.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":1}]]}]]}}]]}
有before镜像
有after镜像
目的就是为了恢复
保存订单处设置断点
因为订单设置了一定会失败,所以可以看到account和storage都undo_log
两个子事务
两个全局锁