先說說什麼是響應式
響應式編程或反應式編程(英語:Reactive programming)是一種面向數據流和變化傳播的編程範式,直白的說就是:將變化的值經過數據流進行傳播。
WebFlux又是什麼呢
WebFlux 模塊的名稱是 spring-webflux,名稱中的 Flux 來源於 Reactor 中的類 Flux。Spring webflux 有一個全新的非堵塞的函數式 Reactive Web 框架,能夠用來構建異步的、非堵塞的、事件驅動的服務,在伸縮性方面表現很是好。
spring-webflux 模塊。該模塊包含對響應式 HTTP 和 WebSocket 客戶端的支持,以及對 REST,HTML 和 WebSocket 交互等程序的支持。通常來講,Spring MVC 用於同步處理,Spring Webflux 用於異步處理。
Spring Boot Webflux 有兩種編程模型實現,一種相似 Spring MVC 註解方式,另外一種是基於 Reactor 的響應式方式。
實踐走起
我在網找了下發現如今支持的DAL包有:
spring-boot-starter-data-redis-reactive、spring-boot-starter-data-mongodb-reactive
也許還有別的,我本意是想要spring-boot-starter-data-mysql-reactive,然而並木有。那就說下上面2個包的實踐把。
spring-boot-starter-data-redis-reactive
用到的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.flying-cattle</groupId>
<artifactId>mybatis-dsc-generator</artifactId>
<version>${mybatis-dsc-generator.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
YMl配置
server:
port: 8080
spring:
application:
name: webFlux-test
redis:
host: 127.0.0.1
port: 6379
password: pwd2020
timeout: 5000
lettuce:
pool:
max-active: 200
max-idle: 20
min-idle: 5
max-wait: 1000
整合redis-reactive
雖然包是starter,可是仍是要有本身的配置才能用否則報錯以下:
Description:
Field redisTemplate in com.flying.cattle.wf.service.impl.RedisServiceImpl required a bean of type 'org.springframework.data.redis.core.ReactiveRedisTemplate' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.data.redis.core.ReactiveRedisTemplate' in your configuration.
看了下官方文檔須要加上以下:
@Bean
public ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
ReactiveRedisTemplate<String, String> reactiveRedisTemplate = new ReactiveRedisTemplate<>(factory,RedisSerializationContext.string());
return reactiveRedisTemplate;
}
發現了麼是ReactiveRedisTemplate<String, String> 感受就不很友好了,原本我是想聲明成ReactiveRedisTemplate<String, Serializable>,搞古了一下子木有搞定。有那個大佬有好的方案,望指點哈
Service代碼:
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private ReactiveRedisTemplate<String, String> redisTemplate;
@Override
public Mono<String> getById(String key) {
// TODO Auto-generated method stub
ReactiveValueOperations<String, String> operations = redisTemplate.opsForValue();
return operations.get(key);
}
@Override
public Mono<String> addUser(String key,User user) {
// TODO Auto-generated method stub
ReactiveValueOperations<String, String> operations = redisTemplate.opsForValue();
return operations.getAndSet(key, JSON.toJSONString(user));
}
@Override
public Mono<Boolean> deleteById(String key) {
// TODO Auto-generated method stub
ReactiveValueOperations<String, String> operations = redisTemplate.opsForValue();
return operations.delete(key);
}
@Override
public Mono<String> updateById(String key,User user) {
// TODO Auto-generated method stub
ReactiveValueOperations<String, String> operations = redisTemplate.opsForValue();
return operations.getAndSet(key, JSON.toJSONString(user));
}
@Override
public Flux<String> findAll(String key) {
// TODO Auto-generated method stub
ReactiveListOperations<String, String> operations = redisTemplate.opsForList();
return operations.range(key, 0, -1);
}
@Override
public Mono<Long> addlist(String key,List<String> list) {
// TODO Auto-generated method stub
ReactiveListOperations<www.wanhaoptdL.com String, String> operations = redisTemplate.opsForList();
return operations.leftPushAll(key, list);
}
@Override
public Flux<String> findUsers(String key) {
ReactiveValueOperations<String, String> operations = redisTemplate.opsForValue();
return redisTemplate.keys(key).flatMap(keyId ->operations.get(keyId));
}
}
Controller代碼
@RestController
@RequestMapping("/user")
public class UserController {
public final static String USER_KEY="user";
@Autowired
private RedisService redisService;
@Autowired
private RedisGenerateId redisGenerateId;
@GetMapping("/getId")
public Long getUserId(){
return redisGenerateId.generate(USER_KEY);
}
public String getKey(www.wanhaoyld.com Long id) {
return USER_KEY+"_"+id;
}
@GetMapping("/getById/{id}")
public Mono<String> getUserById(@PathVariable("id")Long id){
return redisService.getById(getKey(id));
}
@GetMapping("/add")
public Mono<String> add(User user){
user = new User();
user.setAccount("admin1");
user.setPassword("123123");
user.setNickname("admin");
user.setEmail("505237@qq.com");
user.setPhone(www.chaoyuL.com"13666275002");
user.setSex(true);
String bd="1990-01-01";
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
try {
user.setBirthday(fmt.parse(bd));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace(www.mnaycm.cn);
}
user.setProvince("四川省");
user.setCity("成都市");
user.setCounty("高新區");
user.setAddress("天 府大道XXd段XX號");
user.setState("1");
// 以上是模擬數據
ValidationResult vr=ValidationUtils.validateEntity(user);
if (!vr.isHasErrors()) {
user.setId(getUserId());
System.out.println(JSON.toJSONString(user));
return redisService.addUser(getKey(user.getId()),user);
}else {
return Mono.just(vr.getFirstErrors());
}
}
@GetMapping("/addlist")
public Mono<Long> addlist(){
List<String> list=new ArrayList<String>();
User user = new User();
user.setAccount("admin1");
user.setPassword("123123");
user.setNickname("admin");
user.setEmail("505237@qq.com");
user.setPhone("13666275002");
user.setSex(true);
user.setBirthday(new Date());
user.setProvince("四川省");
user.setCity("成都市");
user.setCounty("高新區");
user.setAddress("天 府大道XXd段XX號");
user.setState("1");
//添加第一條數據
Long id=redisGenerateId.generate("user");
user.setId(id);
list.add(JSON.toJSONString(user));
//添加第二條數據
id=redisGenerateId.generate(www.xgjrfwsc.cn"user");
user.setId(id);
list.add(JSON.toJSONString(user));
//添加第三條數據
id=redisGenerateId.generate("user");
user.setId(id);
list.add(JSON.toJSONString(user));
return redisService.addlist("list", list);
}
/**
* 這個就是流響應式的接口了,是一個一個的返回數據的,異步返回
* delayElements(Duration.ofSeconds(2))這個是不要的,只是方便看效果
* redis 直接就是一個一個返回,不須要produces,不知道爲何...還木有深究。
*/
@GetMapping(value="/findAll",produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<String> findAll(){
return redisService.findAll("list").delayElements(Duration.ofSeconds(2));
}
@GetMapping("/getUsers")
public Flux<String> findUsers() {
// TODO Auto-generated method stub
return redisService.findUsers(USER_KEY+"_"+"*").delayElements(Duration.ofSeconds(2));
}
}
一個是差list數據類型,一個是匹配key查詢的,都是一個一個返回的,實際開發中去掉.delayElements(Duration.ofSeconds(2))就好
整合mongodb-reactive
須要的包,只須要在redis的基礎上下面的jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
MongoDB就很人性化了,感受就很友好。並且是真的starter包,配置好數據庫鏈接,就不須要其餘配置了,直接可用
DAO
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import com.flying.cattle.wf.entity.User;
public interface UserRepository extends ReactiveMongoRepository<User, Long>{
}
SERVICE(接口層我就不貼代碼了)
@Service
public class MongoServiceImpl implements MongoService {
@Autowired
private UserRepository userRepository;
@Override
public Mono<User> getById(Long id) {
// TODO Auto-generated method stub
return userRepository.findById(id);
}
@Override
public Mono<User> addUser(User user) {
// TODO Auto-generated method stub
return userRepository.save(user);
}
@Override
public Mono<Boolean> deleteById(Long id) {
// TODO Auto-generated method stub
userRepository.deleteById(id);
return Mono.create(userMonoSink -> userMonoSink.success());
}
@Override
public Mono<User> updateById(User user) {
// TODO Auto-generated method stub
return userRepository.save(user);
}
@Override
public Flux<User> findAllUser() {
// TODO Auto-generated method stub
return userRepository.findAll();
}
}
CONTROLLER
@RestController
@RequestMapping("/usermg")
public class UserMongoController {
public final static String USER_KEY="user";
@Autowired
private RedisGenerateId redisGenerateId;
@Autowired
private MongoService mongoService;
@GetMapping("/getId")
public Long getUserId(){
return redisGenerateId.generate(USER_KEY);
}
@GetMapping("/add")
public Mono<User> add(User user) {
user = new User();
user.setAccount("admin1");
user.setPassword("123123");
user.setNickname("admin");
user.setEmail("505237@qq.com");
user.setPhone("13666275002");
user.setSex(true);
String bd = "1990-01-01";
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
try {
user.setBirthday(fmt.parse(bd));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
user.setProvince("四川省");
user.setCity("成都市");
user.setCounty("高新區");
user.setAddress("天 府大道XXd段XX號");
user.setState("1");
// 以上是模擬數據
ValidationResult vr = ValidationUtils.validateEntity(user);
if (!vr.isHasErrors()) {
user.setId(getUserId());
System.out.println(JSON.toJSONString(user));
return mongoService.addUser(user);
} else {
System.err.println(vr.getFirstErrors());
}
return null;
}
/**
* 注意這裏produces = MediaType.APPLICATION_STREAM_JSON_VALUE
* 若是不是application/stream+json則調用端沒法滾動獲得結果,將一直阻塞等待數據流結束或超時。
*/
@GetMapping(value="/findAll",produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<User> findAll(){
return mongoService.findAllUser().delayElements(Duration.ofSeconds(1));
}
}
代碼就這些,你們要體驗這個框架,建議仍是用MongoDB把,畢竟redis主要是作緩存的。
給你們看下數據結構圖mysql