您的位置:首页 > 编程语言 > Java开发

spring的webflux初探

2018-01-31 21:34 197 查看

spring的webflux初探

不久前, spring进行了较大的改动, 主要目的是为了增加对响应式编程的支持.

spring 默认是采用了reactor项目作为响应式编程(reactive programming)的支持, 我也以此作为基础来谈.

reactor项目地址: https://github.com/reactor/reactor

为什么要reactor

总的来说, reactor也是一个用于编写异步代码的库, 众所周知, 对于同步程序来说, 有IO耗时长之类的开销. 所以人们不断的推崇使用异步的方式来编写一些代码, 而java也提供了编写异步程序的方法给开发者, 那么我们为什么需要reactor. 就我短时间的使用体验来说, reactor使我们编写异步代码变得更加简单快捷, 让某项工作更加简单或让其更有效率, 我觉得就是一个库应该解决的问题, 显然reactor做到了, 在使用了reactor后, 你就再也不用写callback那种又臭又长的面条代码了, 代码的可读性与可维护性大大加强了. 相比于future, reactor又提供了更多功能齐全的操作, 编程复杂的也大大降低

好了, 我们并不是来介绍reactor的, 更多有关reactor的资料以及它与jvm其他异步方式的对比请参考reactor文档: http://projectreactor.io/docs/core/release/reference

webflux实例

webflux与webmvc的类比

webmvcwebflux
controllerhandler
request mappingrouter
* pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

<groupId>cn.edu.ncu</groupId>
<artifactId>reactive-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>reactive-demo</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId&g
4000
t;org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webflux -->
<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</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mindrot/jbcrypt -->
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

</project>


编写handler

先写个hello world handler练练手

package cn.edu.ncu.reactivedemo.handlers;

@Service
public class HelloWorldHandler {

public Mono<ServerResponse> helloWorld(ServerRequest request){
return ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("hello world"));
}
}


注册路由

将写好的handler注册到路由上

package cn.edu.ncu.reactivedemo;

@Configuration
public class Router {
@Autowired private HelloWorldHandler helloWorldHandler;
@Autowired private UserHandler userHandler;

@Bean
public RouterFunction<?> routerFunction(){
return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld);
}
}


启动类

默认采用netty作为reactor的底层启动

package cn.edu.ncu.reactivedemo;

@SpringBootApplication
public class ReactiveDemoApplication {

public static void main(String[] args) {
SpringApplication.run(ReactiveDemoApplication.class, args);
}
}


访问http://127.0.0.1:8080/hello

返回hello world表示成功

使用数据库

暂时支持reactive编程的数据库只有MongoDB, redis, Cassandra, Couchbase

我们直接采用redis作为测试, 做一个简陋的注册登录的接口就行了

* 配置redis

package cn.edu.ncu.reactivedemo.config;

@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory factory;
@Bean
public ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory){
return new ReactiveRedisTemplate<String, String>(connectionFactory, RedisSerializationContext.string());
}
@Bean
public Rea
d9b7
ctiveRedisConnection connection(ReactiveRedisConnectionFactory connectionFactory){
return connectionFactory.getReactiveConnection();
}

public @PreDestroy void flushDb(){
factory.getConnection().flushDb();
}
}


测试redis接口

package cn.edu.ncu.reactivedemo;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ReactiveDemoApplication.class)
public class RedisTests {
@Autowired
private ReactiveRedisConnection connection;
@Test
public void testRedis(){
connection
.stringCommands().set(ByteBuffer.wrap("h".getBytes()), ByteBuffer.wrap("w".getBytes()))
.subscribe(System.out::println);
}
}


编写userHandler

package cn.edu.ncu.reactivedemo.handlers;

@Service
public class UserHandler {
@Autowired
private ReactiveRedisConnection connection;

public Mono<ServerResponse> register(ServerRequest request) {
Mono<Map> body = request.bodyToMono(Map.class);
return body.flatMap(map -> {
String username = (String) map.get("username");
String password = (String) map.get("password");
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
return connection.stringCommands()
.set(ByteBuffer.wrap(username.getBytes()), ByteBuffer.wrap(hashedPassword.getBytes()));
}).flatMap(aBoolean -> {
Map<String, String> result = new HashMap<>();
ServerResponse serverResponse = null;
if (aBoolean){
result.put("message", "successful");
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(result));
}else {
result.put("message", "failed");
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(request));
}
});
}

public Mono<ServerResponse> login(ServerRequest request){
Mono<Map> body = request.bodyToMono(Map.class);
return body.flatMap(map -> {
String username = (String) map.get("username");
String password = (String) map.get("password");
return connection.stringCommands().get(ByteBuffer.wrap(username.getBytes())).flatMap(byteBuffer -> {
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes, 0, bytes.length);
String hashedPassword = null;
try {
hashedPassword = new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Map<String, String> result = new HashMap<>();
if (hashedPassword == null || !BCrypt.checkpw(password, hashedPassword)){
result.put("message", "账号或密码错误");
return ServerResponse.status(HttpStatus.UNAUTHORIZED)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(result));
}else {
result.put("token", "假token");
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(result));
}
});
});
}
}


添加router

package cn.edu.ncu.reactivedemo;

@Configuration
public class Router {
@Autowired private HelloWorldHandler helloWorldHandler;
@Autowired private UserHandler userHandler;

@Bean
public RouterFunction<?> routerFunction(){
return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld)
.andRoute(RequestPredicates.POST("/register"), userHandler::register)
.andRoute(RequestPredicates.POST("/login"), userHandler::login);
}
}


接口很粗糙,没有写model层, 也没有数据验证, 测试也直接用http requester进行测试了

参考:

https://spring.io/blog/2016/11/28/going-reactive-with-spring-data

http://projectreactor.io/docs/core/release/reference/

http://projectreactor.io/docs/core/release/api/

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-fn-handler-functions

demo地址:

https://github.com/ncuwaln/spring-reactive-demo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring webflux demo