Java 微服务框架新选择:Spring 5

在这篇文章中,我们将讨论即将发布的第五代 Spring 框架中的新概念—— “Functional Web Framework”,来看看它如何帮助我们构建轻量级的微服务。

file

你可能会对标题中出现的 “Spring” 和 “微服务框架”感到惊讶。但是确实如此,Spring 5非常适合成为你 Java Web 微服务框架的新选择。首先,为了避免混淆让我们来对“微服务”中的“微”达成共识:

简洁 - 无需样板工程,无需额外设置

简单 - 没有任何“魔法”

易于部署 - 产生单一的可部署工件

容易运行 - 没有额外的依赖

轻量级 - 最小内存占用/CPU 使用

非阻塞 - 更好的并发性

虽然 Spring Boot 已经能做到上述的一些点,但 Spring MVC 本身依然引入了很多魔法。比如 @Controller 这个广泛使用的注解就有点含糊不清,更不用说 Spring 的自动配置和组件扫描等特性了。通常来说,这是开发一个大规模应用时可以承受的烦恼,毕竟 Spring 帮我们搞定了依赖注入、请求路由、各类复杂的配置等。然而,在微服务的世界中,应用程序只是类似一个大机器中运行的小齿轮,Spring Boot 就显得有些“杀鸡用牛刀”了。

为了解决这些问题,Spring 团队引出了一个名为“Functional Web Framework”的新概念,它是 Spring WebFlux(以前称为 Spring Reactive Web)这个大项目的一部分。同时也是我们现在要讨论的。

首先,让我们回顾一下基础知识,看看一个 Web 应用程序到底是什么样的,以及由什么组件构成。很显然,最基本的部分就是网络服务器(Web server)本身,为了避免手工解析 HTTP 请求,然后委派给应用程序的某个方法,我们需要一个请求路由器(router),同时我们也需要一个请求处理器或处理程序(handler),其实就是一段代码,它可以接受请求,做实际的逻辑处理,并最终返回一个响应。所有这些也正是 Spring Functional Web 所做的,它剥离了所有的抽象层(beans 和 contexts)。注意,这并不意味着它脱离并放弃了成熟的 Spring MVC 模型,而是提供了使用 Spring 来构建 Web 应用程序的另一种选择。

请求处理器

我们来看一下这个例子。 开始前请访问 http://start.spring.io 使用项目创建器创建一个新的空白工程,使用 Spring Boot 2.0 和 Reactive Web 作为唯一的依赖。 接着我们就可以定义第一个请求处理器或处理方法(handler)了,很简单,它接受请求并返回响应。

file

从上述代码可以看出来,它是 HandlerFunction 接口的一个实现,定义了一个方法来获取一个请求(类型为 ServerRequest),并返回具有 "Hello" 字符串的 ServerResponse 对象。 Spring 还提供了方便的构建器(builder)来构造响应。在我们的例子中,我们使用 ok() 自动将返回码设置为 HTTP 200 。为了构造响应体,我们使用另一个叫 Mono 的概念,它代表 single reactive value ,但我们这里先不管它,只要明白 Mono.just(...) 是一种通过返回 Publisher 类型对象(其实是类似 Promise)来实现非阻塞编程范式的方式。Reactive Web 是Spring 5 的一部分,它是通过 Java 9 的 Reactive Stream 来实现的。你可以参考 Dave Syer 的这篇文章。

我们还可以使用 Java 8 的 lambdas 表达式使代码更简洁,如下:

file

请求路由器

上面我们已经有一个 handler 了,现在我们可以定义一个请求路由器了。 假设我们要使用 GET 方法请求 "/" 时调用我们的 handler。 为此,我们可以使用 RouterFunction 。

file

route 和 GET 都是 RequestPredicates 和 RouterFunctions 的静态方法,它们可以用来构建 RouterFunction 。它接受一个请求,检查它是否能匹配现有handler(比如请求路径(path)、请求方法(method)或者是内容类型(content type)等)。如果匹配则调用 handler。 在我们的例子中,HTTP 方法是 GET,请求路径是 "/", handler 函数是上面定义的 hello。

file

Web 服务器

现在我们可以把他们组装在一起来完成整个应用程序。我们将使用非常轻量、简单的 Reactive Netty 作为 Web 服务器。要将我们的请求路由器集成到 Web 服务器中,我们需要将其转换为 HttpHandler。

file

接着这样来启动 Web 服务器:

file

其中 ReactorHttpHandlerAdapter 只是一个包装了 HttpHandler 的 Netty 中的类,其余的代码非常简单直白。我们创建一个新的 Web 服务器,监听 localhost 地址的8080端口,并且添加我们的 HTTP handler,实际上这是我们的请求路由器的入口。

好了!整个应用程序已经差不多了,完整的代码如下:

file

最后一行只是用来保持 JVM 进程一直运行。 你可能会发现整个应用程序启动飞快,这是因为没有任何组件扫描或配置注入发生,就像以前你们使用 Spring 会遇到的。

同时整个程序可以作为一个简单的 Java 应用程序来运行,不需要任何容器。

为了将整个应用打包和部署,我们仍然可以利用 Spring Maven 插件,只需执行以下操作:

file

此命令将生成一个包含所有依赖关系的 fat jar,可以在仅安装了 JRE 的环境来部署和执行:

file

另外,如果我们想查看整个应用的内存使用情况,大概只有32MB左右,包括了22MB的 metaspace(你们知道用来存放加载的 classes)和大约10MB的 heap。就像前面提到的,整个框架和运行时环境只需要很少的资源。

支持JSON

在上面的示例中,我们返回一个字符串作为响应,但是想返回 JSON 对象也非常容易。

让我们创建一个新的可以返回 JSON 的 API endpoint 来扩展我们的应用。这个 data class 非常简单,只有一个名为 name 的字符串类型的字段。为了避免写那些冗长的 Java 样板代码(就像你们厌恶的 setter, getter),我们使用 Project Lombok 特性:使用 @Data 注解。通过在类上添加此注解,我们可以透明得获得 getter,setters,equals 和 hashCode 方法,而无须手动实现。

file

然后,我们需要扩展我们的请求路由器,以便为 "/json" 路径的 GET 请求提供新的响应。 这可以通过在现有路由上调用 andRoute(...) 方法来完成。

file

我们还优化了一点代码,将新的返回 JSON 的 handler 以内连的方式声明,同时将 ok() 静态导入,这使得代码变得更简洁。

重新启动后,应用程序将通过 "/json" 路径返回 {"name": "world"},同时将响应头部中的内容类型(content-type)设置为 application/json。

应用上下文

你可能已经注意到整个代码中并没有定义应用上下文(Application Context)。是的,我们不再需要它!Spring WebFlux 中支持 RouterFunction,这样一个简单且轻量的 JSON 服务不再需要应用上下文。

测试

为了测试 reactive web application,Spring 提供了新的名为 WebTestClient 的客户端(类似于 MockMvc)。 我们将它绑定到我们的请求路由器上:

file

WebTestClient 有一组针对返回结果的断言,以验证返回状态码,返回体,以及内容类型等等。

总结

Spring 5 引入了新的编程范式用来开发小型的、轻量级的、微服务式 Web应用程序。 我们显式得定义请求路由器和请求处理函数,在完全不需要应用上下文的情况下快速运行并部署。

file

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容