Seata 整合学习完成

This commit is contained in:
YoVinchen 2023-08-18 23:43:35 +08:00
parent 2ee2961731
commit 401f58e46f
26 changed files with 178 additions and 110 deletions

View File

@ -21,7 +21,7 @@
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>

View File

@ -1,6 +1,7 @@
package com.test;
import com.apple.eawt.Application;
import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -11,6 +12,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author yovinchen
* @Create 2023/8/14 16:23
*/
@EnableAutoDataSourceProxy
@SpringBootApplication
public class BookApplication {
public static void main(String[] args) {

View File

@ -26,4 +26,15 @@ public class BookController {
System.out.println("调用图书服务");
return service.getBookById(bid);
}
@RequestMapping("/book/remain/{bid}")
public int bookRemain(@PathVariable("bid") int uid){
return service.getRemain(uid);
}
@RequestMapping("/book/borrow/{bid}")
public boolean bookBorrow(@PathVariable("bid") int uid){
int remain = service.getRemain(uid);
return service.setRemain(uid, remain - 1);
}
}

View File

@ -3,6 +3,7 @@ package com.test.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.test.entity.Book;
import org.apache.ibatis.annotations.Update;
/**
* ClassName: BookMapper
@ -16,4 +17,10 @@ public interface BookMapper {
@Select("select * from DB_BOOK where bid = #{bid}")
Book getBookById(int bid);
@Select("select count from DB_BOOK where bid = #{bid}")
int getRemain(int bid);
@Update("update DB_BOOK set count = #{count} where bid = #{bid}")
int setRemain(int bid, int count);
}

View File

@ -11,4 +11,10 @@ import com.test.entity.Book;
*/
public interface BookService {
Book getBookById(int bid);
boolean setRemain(int uid, int i);
int getRemain(int uid);
}

View File

@ -24,4 +24,14 @@ public class BookServiceImpl implements BookService {
public Book getBookById(int bid) {
return mapper.getBookById(bid);
}
@Override
public boolean setRemain(int bid, int count) {
return mapper.setRemain(bid, count) > 0;
}
@Override
public int getRemain(int bid) {
return mapper.getRemain(bid);
}
}

View File

@ -14,8 +14,20 @@ spring:
discovery:
# 配置Nacos注册中心地址
server-addr: localhost:8848
namespace: dd668135-0bfe-489f-ab2b-24aefb21d156
sentinel:
transport:
# 添加监控页面地址即可
dashboard: localhost:8858
seata:
# 注册
registry:
# 使用Nacos
type: nacos
nacos:
# 使用Seata的命名空间这样才能正确找到Seata服务由于组使用的是SEATA_GROUP配置默认值就是就不用配了
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos
# 配置
config:
type: nacos
nacos:
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos

View File

@ -20,7 +20,7 @@
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,5 +1,6 @@
package com.test;
import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@ -12,6 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
* @Create 2023/8/14 16:25
*/
@EnableFeignClients
@EnableAutoDataSourceProxy
@SpringBootApplication
public class BorrowApplication {
public static void main(String[] args) {

View File

@ -1,14 +1,10 @@
package com.test.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.test.entity.User;
import com.test.entity.UserBorrowDetail;
import com.test.service.BorrowService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@ -28,58 +24,20 @@ public class BorrowController {
@Resource
BorrowService service;
@RequestMapping("/borrow1/{uid}")
UserBorrowDetail findUserBorrows1(@PathVariable("uid") int uid) {
@RequestMapping("/borrow/{uid}")
UserBorrowDetail findUserBorrows(@PathVariable("uid") int uid){
return service.getUserBorrowDetailByUid(uid);
}
@RequestMapping("/borrow2/{uid}")
@SentinelResource(value = "findUserBorrows2", blockHandler = "test")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid) throws InterruptedException {
throw new RuntimeException();
}
@RequestMapping("/borrow/take/{uid}/{bid}")
JSONObject borrow(@PathVariable("uid") int uid,
@PathVariable("bid") int bid){
service.doBorrow(uid, bid);
UserBorrowDetail test(int uid, BlockException e) {
// 输出降级异常
System.out.println(e.getClass());
return new UserBorrowDetail(new User(), Collections.emptyList());
}
@RequestMapping("/blocked")
JSONObject blocked() {
JSONObject object = new JSONObject();
object.put("code", 403);
object.put("success", false);
object.put("massage", "您的请求频率过快,请稍后再试");
object.put("code", "200");
object.put("success", true);
object.put("message", "借阅成功!");
return object;
}
// @RequestMapping("/test")
// @SentinelResource(value = "test", fallback = "except", //fallback指定出现异常时的替代方案
// blockHandler = "blocked",
//// 特别注意这种方式会在没有配置blockHandler的情况下将Sentinel机制内也就是限流的异常的异常也一并处理了如果配置了blockHandler那么在出现限流时依然只会执行blockHandler指定的替代方案因为限流是在方法执行之前进行的
// exceptionsToIgnore = IOException.class)
// //忽略那些异常也就是说这些异常出现时不使用替代方案
// String test() {
// throw new RuntimeException("HelloWorld");
// }
//
// //替代方法必须和原方法返回值和参数一致最后可以添加一个Throwable作为参数接受异常
// String except(Throwable t) {
// return t.getMessage();
// }
@RequestMapping("/test")
@SentinelResource("test")
//注意这里需要添加@SentinelResource才可以用户资源名称就使用这里定义的资源名称
String findUserBorrows2(@RequestParam(value = "a", required = false) String a, @RequestParam(value = "b", required = false) String b, @RequestParam(value = "c", required = false) String c) {
return "请求成功a = " + a + ", b = " + b + ", c = " + c;
}
//模拟慢调用
@RequestMapping("/slowCall/{uid}")
String slowCall(@PathVariable("uid") int uid) throws InterruptedException {
//模拟慢调用
Thread.sleep(1000);
return "Hello World";
}
}

View File

@ -1,6 +1,7 @@
package com.test.mapper;
import com.test.entity.Borrow;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@ -23,4 +24,7 @@ public interface BorrowMapper {
@Select("select * from DB_BORROW where bid = #{bid} and uid = #{uid}")
Borrow getBorrow(int uid, int bid);
@Insert("insert into db_borrow(uid, bid) values (#{uid},#{bid})")
int addBorrow(int uid, int bid);
}

View File

@ -13,4 +13,5 @@ import com.test.entity.UserBorrowDetail;
public interface BorrowService {
UserBorrowDetail getUserBorrowDetailByUid(int uid);
boolean doBorrow(int uid, int bid);
}

View File

@ -17,4 +17,10 @@ public interface BookClient {
@RequestMapping("/book/{bid}")
Book getBookById(@PathVariable("bid") int bid);
@RequestMapping("/book/borrow/{bid}")
boolean bookBorrow(@PathVariable("bid") int bid);
@RequestMapping("/book/remain/{bid}")
int bookRemain(@PathVariable("bid") int bid);
}

View File

@ -17,4 +17,10 @@ public interface UserClient {
@RequestMapping("/user/{uid}")
User getUserById(@PathVariable("uid") int uid);
@RequestMapping("/user/borrow/{uid}")
boolean userBorrow(@PathVariable("uid") int uid);
@RequestMapping("/user/remain/{uid}")
int userRemain(@PathVariable("uid") int uid);
}

View File

@ -1,21 +0,0 @@
package com.test.service.client;
import com.test.entity.User;
import org.springframework.stereotype.Component;
/**
* ClassName: UserClientImpl
* Package: com.test.service.client
*
* @author yovinchen
* @Create 2023/8/17 22:25
*/
@Component
public class UserClientImpl implements UserClient{
@Override
public User getUserById(int uid) {
User user = new User();
user.setName("我是替代方案");
return user;
}
}

View File

@ -1,8 +1,5 @@
package com.test.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.test.entity.Book;
import com.test.entity.Borrow;
import com.test.entity.User;
@ -11,6 +8,7 @@ import com.test.mapper.BorrowMapper;
import com.test.service.BorrowService;
import com.test.service.client.BookClient;
import com.test.service.client.UserClient;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -38,7 +36,6 @@ public class BorrowServiceImpl implements BorrowService {
BookClient bookClient;
@Override
@SentinelResource(value = "details", blockHandler = "blocked")
public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
List<Borrow> borrow = mapper.getBorrowsByUid(uid);
User user = userClient.getUserById(uid);
@ -49,8 +46,26 @@ public class BorrowServiceImpl implements BorrowService {
return new UserBorrowDetail(user, bookList);
}
//替代方案注意参数和返回值需要保持一致并且参数最后还需要额外添加一个BlockException
public UserBorrowDetail blocked(int uid, BlockException e) {
return new UserBorrowDetail(null, Collections.emptyList());
@Override
@GlobalTransactional
public boolean doBorrow(int uid, int bid) {
//1. 判断图书和用户是否都支持借阅
if(bookClient.bookRemain(bid) < 1)
throw new RuntimeException("图书数量不足");
if(userClient.userRemain(uid) < 1)
throw new RuntimeException("用户借阅量不足");
//2. 首先将图书的数量-1
if(!bookClient.bookBorrow(bid))
throw new RuntimeException("在借阅图书时出现错误!");
//3. 添加借阅信息
if(mapper.getBorrow(uid, bid) != null)
throw new RuntimeException("此书籍已经被此用户借阅了!");
if(mapper.addBorrow(uid, bid) <= 0)
throw new RuntimeException("在录入借阅信息时出现错误!");
//4. 用户可借阅-1
if(!userClient.userBorrow(uid))
throw new RuntimeException("在借阅时出现错误!");
//完成
return true;
}
}

View File

@ -17,17 +17,23 @@ spring:
# 将ephemeral修改为false表示非临时实例用于持续监控
ephemeral: false
cluster-name: Chengdu
namespace: dd668135-0bfe-489f-ab2b-24aefb21d156
loadbalancer:
nacos:
enabled: true
sentinel:
transport:
# 添加监控页面地址即可
dashboard: localhost:8858
# 关闭Context收敛这样被监控方法可以进行不同链路的单独控制
web-context-unify: false
block-page: /blocked
feign:
circuitbreaker:
enabled: true
seata:
# 注册
registry:
# 使用Nacos
type: nacos
nacos:
# 使用Seata的命名空间这样才能正确找到Seata服务由于组使用的是SEATA_GROUP配置默认值就是就不用配了
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos
# 配置
config:
type: nacos
nacos:
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos

View File

@ -14,4 +14,5 @@ public class Book {
int bid;
String title;
String des;
int count;
}

View File

@ -14,18 +14,13 @@ public class User {
int uid;
String name;
String sex;
int book_count;
/**
* ClassName: Book
* Package: com.test.entity
*
* @author yovinchen
* @Create 2023/8/14 17:01
*/
@Data
public static class Book {
int bid;
String title;
String desc;
int book_count;
}
}

View File

@ -20,7 +20,7 @@
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,5 +1,6 @@
package com.test;
import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -11,6 +12,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @Create 2023/8/14 16:26
*/
@SpringBootApplication
@EnableAutoDataSourceProxy
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);

View File

@ -27,4 +27,15 @@ public class UserController {
System.out.println("调用用户服务");
return service.getUserById(uid);
}
@RequestMapping("/user/remain/{uid}")
public int userRemain(@PathVariable("uid") int uid) {
return service.getRemain(uid);
}
@RequestMapping("/user/borrow/{uid}")
public boolean userBorrow(@PathVariable("uid") int uid) {
int remain = service.getRemain(uid);
return service.setRemain(uid, remain - 1);
}
}

View File

@ -3,6 +3,7 @@ package com.test.mapper;
import com.test.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
/**
* ClassName: UserMapper
@ -15,4 +16,10 @@ import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("select * from DB_USER where uid = #{uid}")
User getUserById(int uid);
@Select("select book_count from DB_USER where uid = #{uid}")
int getUserBookRemain(int uid);
@Update("update DB_USER set book_count = #{count} where uid = #{uid}")
int updateBookCount(int uid, int count);
}

View File

@ -11,4 +11,8 @@ import com.test.entity.User;
*/
public interface UserService {
User getUserById(int uid);
int getRemain(int uid);
boolean setRemain(int uid, int count);
}

View File

@ -24,4 +24,15 @@ public class UserServiceImpl implements UserService {
public User getUserById(int uid) {
return mapper.getUserById(uid);
}
@Override
public int getRemain(int uid) {
return mapper.getUserBookRemain(uid);
}
@Override
public boolean setRemain(int uid, int count) {
return mapper.updateBookCount(uid, count) > 0;
}
}

View File

@ -4,8 +4,20 @@ spring:
discovery:
# 配置Nacos注册中心地址
server-addr: localhost:8848
namespace: dd668135-0bfe-489f-ab2b-24aefb21d156
sentinel:
transport:
# 添加监控页面地址即可
dashboard: localhost:8858
seata:
# 注册
registry:
# 使用Nacos
type: nacos
nacos:
# 使用Seata的命名空间这样才能正确找到Seata服务由于组使用的是SEATA_GROUP配置默认值就是就不用配了
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos
# 配置
config:
type: nacos
nacos:
namespace: c0495138-b5b3-440d-8f1c-dc8cc9ba5fbc
username: nacos
password: nacos