JAVA程序员如何转node_04

前言

这个系列的文章已经拖了好久。我一直想着我应该写点什么比较好。想着想着就觉得算了,明天吧,可能明天就有新的思路。我应该写上手一些框架的步骤?这可能比较简单,刚入门上手框架也确实容易对这门语言产生自己的印象。但是现在网站上这种教程没有吗?我想,只要浏览一下cnode,你能很快找到各种各样的教程。

我想分享的是一种体会,一种从无快速上手的体会,一种先入为主导致种种问题而产生的体会,这才是我写这些东西的初心。可能他不会很容易懂,需要你稍微做过一点你才能知道我走过的这些坑是真实存在的。

稍微总结一下之前写的文章内容。第一篇,我写的是什么是异步,以及在代码层面上怎么实现异步。我也说了,是回调函数实现的异步。第二篇,我简单说了一下node的异步处理逻辑,使用的是事件循环机制。此外因为回调函数会产生多层回调,所以为了去除他,说了如何用promise将回调函数包装成async。第三篇,我介绍了node中的包管理工具npm的用法以及简单的使用了一下koa来生成一个网页服务应用。

第四篇说什么呢,咱们继续来说说网页服务应用,因为这是node后端程序员接触的最多的一部分。


如果你写的是一个javaweb应用,最原始的就是使用servlet。首先你会写一个servlet继承httpServlet,然后在类的下面编写doGet方法doPost方法等。写完了这些你需要在web.xml中编写urlPartern来将url对应到你的servlet类中。这些弄完了你会将他打包,放在apache目录下,开启apache服务。这里面,servlet就是mvc中的controller,web.xml实现的就是一个路由转发的功能。

我们再来看看node原生怎么实现一个网页服务。

1、在一个目录下新建一个文件app.js,输入以下代码

const http = require('http');
http.createServer((req,res) => {
    res.end('hello world');
}).listen(8888);
console.log('server is running on 127.0.0.1:8888');

2、命令行中node app.js 启动应用。

这样浏览器访问127.0.0.1:8888就可以看到hello world。就这样简单的4行代码(不算最后一行),就可以实现很高的qps了(每秒8000次左右),node默认是单线程工作,如果开启多线程,那么就可达到一万多的每秒请求。作为参考,apache的qps大概在5000次,go和node差不多,Nginx可达几万。

要知道,网络io是io,硬盘查询也是io。对于网络io,大家都是采用轮询的方式扫描端口,在这一处的io影响是不大的。我个人认为,系统内部的硬盘io才是node对于io处理的优势之处。举个例子,同样发送8000个请求,在没有涉及硬盘存储,直接从内存获取数据返回的时候,大家比较的就只是网络的io。但如果这个时候请求涉及到数据的存储,这时候apache这种传统同步服务器在单个请求中会阻塞到其他的请求,而如果是node的话就能进行异步访问从而达到并行处理的效果。

关于这一点我在第一篇中说过

使用node的话是非阻塞IO,调用了IO操作之后不要求数据直接就能返回,cpu直接就开始处理下一个操作,等到了IO操作结束之后,IO操作会去通知cpu执行接下来的操作。这就使计算机的IO处理速度大大提升。

也就是说,如果增加了数据的存储操作,可能node就是会变慢一点(7000)次左右,而apache会迅速降到(1000)次左右。

我们来看一眼这几行代码,其中最主要的就是这一句。

http.createServer((req,res) => {res.end('hello world'); })

其中(req,res) => {res.end('hello world'); }就是以下的缩写

function func1(request,response) {
    res.end('hello world');
}

也就是说,将写一个带有两个参数的函数放入http.createServer()中就能生成一个服务器对象。

http

为了不让大家太迷糊,我尽量简单讲一下http模块都做了什么。

你将函数放入http.createServer()中之后,http会给你生成一个服务器对象,一直监听着8888这个端口,当他发现端口有连接事件(connect)的时候,他按兵不动(不会触发你的那个函数)。只有当端口收到了一个有效请求的时候,这时候http会生成一个request对象和一个response对象,将这两个对象放入你的函数之中。你的函数处理完之后就会返回给原请求的地址。

有了这个,我们就可以在request对象中获取我们需要的信息,如get请求中地址栏携带的信息、post中body存放的信息、请求地址等等。有了这些你就可以实现一个网络应用了。

但是,此时你的网络应用写起来会零零散散,看起来像这样。

const http = require('http');
const url = require('url');
const qs = require('querystring');
http.createServer((req,res) => {
    // console.log(req.url, req.method);
    const method = req.method;
    let { pathname: path, query } = url.parse(req.url);
    // GET /index1 请求
    if(path === '/index1' && method === 'GET') {
        query = qs.parse(query);
        res.end(`处理来自${method} ${path},数据为 ${JSON.stringify(query)} 的请求`);
        return ;
    }
    // POST /index2
    if(path === '/index2' && method === 'POST') {
        let data = '';
        req.on('data', (chunk) => {
            data += chunk;  
        });
        req.on('end', () => {
            res.end(`处理来自${method} ${path},数据为 ${JSON.stringify(qs.parse(data))}`)
        })
        return;
    }
    res.statusCode = 404;
    res.end('404 NOT FOUND')
}).listen(8888);
console.log('server is running on 127.0.0.1:8888');

理论上,所有的请求都会经过咱们编写的“这一层函数”。但,难道我们要在这里依次写无数个ifelse来判断request的各个参数、路径,来决定response的各个响应消息吗?

不可能吧,一个应用中,你会有日志功能,配置功能,定时功能,路由管理,mvc这些需求。你全写在一个文件中,那真的就是面向过程编程了,还不如直接用c语言去写。

之前我们说过,node中异步函数的调用虽然使用了async和await,但他内部还是使用回调函数,每当有一个事件出现,消息一定是随着函数作为参数层层往下,再层层往上。这是node中一个特性,我们能不能根据这个做点什么呢?

答案已经呼之欲出了,利用回调函数会层层往下又层层往上的特点,我们何不让将逻辑布置成一层一层的,让请求每走一层就处理一部分逻辑?

koa中就是这样,我们称之为洋葱模型。每一层就是一个中间件。

中间件

image

洋葱模型是一个很不错的组织方式,他天然就实现了面向切片编程。你可以写一个中间件,让某一部分请求通过,这样就不用在每一个请求中都调用一次。

当然,我不是说洋葱模型就是完美的,有很多地方依旧用起来会比较别扭,在某些特定的地方你依然会像以前一样封装成工具类这样调用。但由于回调函数对中间件的天然支持,你能感觉到这种形式的编程还是能给你带来很多不错的体验。

只是说的话会有点抽象,让我们运行一段这样的代码。

const Koa = require('koa');
const app = new Koa();
async function middleWare1(ctx,next){
  console.log('----------middleWare1 start------------');
  await next();
  console.log('----------middleWare1 end------------');
}
async function middleWare2(ctx,next){
  console.log('----------middleWare2 start------------');
  await next();
  console.log('----------middleWare2 end------------');
}
async function middleWare3(ctx,next){
  console.log('----------middleWare3 start------------');
  ctx.body = 'hello world';
  console.log('----------middleWare3 end------------');
}
app.use(middleWare1);
app.use(middleWare2);
app.use(middleWare3);
app.listen(8888);
console.log('Server running at http://127.0.0.1:8888/');

访问浏览器会看到输出这样的一段日志。这说明一个请求进来会进入层层的中间件,再层层的离开。

----------middleWare1 start------------
----------middleWare2 start------------
----------middleWare3 start------------
----------middleWare3 end------------
----------middleWare2 end------------
----------middleWare1 end------------

解释一下,每个中间件都是一个函数,规定传入的参数是contxt(上下文)和next(回调函数,调用他就可以执行下一个中间件);

应用中要添加中间件,就要通过app.use(中间件方法)传入,应用会自动按照其传入的顺序执行。

可以尝试注释掉其中的某一个await next(),看看结果是怎么样的。

在koa中,中间件大多都是单独的模块。我们只需要添加到入口文件app.js中,让app.use(middleware)添加到应用中即可运用。

比如最简单的koa-router管理路由的,我们可以看看他是如何管理我们上面原始的粗糙的http请求分发。 这个例子主要用到3个文件

// app.js   主要用于将中间件添加到应用中
const Koa = require('koa');
const app = new Koa();
const router = require('./router');
var bodyParser = require('koa-bodyparser');

app.use(bodyParser());//加入这个 才可以解析post请求中参数
app
  .use(router.routes()) //将路由添加到应用
  .use(router.allowedMethods());
app.listen(8888);
console.log('server is running on 127.0.0.1:8888');

// router.js 管理路由  
const Router = require('koa-router');
const index = require('./controller/index')
const  router = new Router();
router.get('/index1',index.index1); //路由一般与controller对应
router.post('/index2',index.index2);
module.exports =router;

//index.js  具体处理逻辑的地方 controller层
async function index1(ctx, next) {
    const {method,path,query} = ctx.request;
    ctx.body = `处理来自${method} ${path},数据为 ${JSON.stringify(query)} 的请求`;
}
async function index2(ctx, next) {
    const {method,path,body} = ctx.request;
    ctx.body = `处理来自${method} ${path},数据为 ${JSON.stringify(body)} 的请求`;
}
module.exports = {
    index1,
    index2,
}

有了这样的一个框架,我们就可以方便的对代码进行模块化管理、分层管理。

代码已上传到Zeeephr/koa-demo ,如果有需要可以看一看。

后记

这个系列后面可能就是一起看源码了,但是不要慌,node中看源码的体验非常好。node编程中有的时候甚至不用去查api或者百度哪里报错,直接在node_modules文件夹中点开就能看,最夸张的就是他还可以在引入的包中打断点,这样你就能清晰地知道你的数据是如何走向的。

好了这一part就先讲到这吧,觉得有用的话可以点点赞,留下你的评论!

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

推荐阅读更多精彩内容

  • 背景 先介绍一下背景吧,笔者在大学期间主要学的是JAVA,但学的也不是很深,就会基本的课设程度的网站编写水平。找工...
    zeeephr阅读 387评论 0 0
  • 前言 Koa 是运行在 Node.js 中的 web 服务框架,小而美。 Koa2 是 Koa 框架的最新版本,K...
    let_Scott阅读 5,759评论 2 28
  • 参考资料 https://chenshenhai.github.io/koa2-note/note/static/...
    JunChow520阅读 10,476评论 1 8
  • Koa 学习 历史 Express Express是第一代最流行的web框架,它对Node.js的http进行了封...
    Junting阅读 2,800评论 0 0
  • 1.简书 koa是由Express原班人马打造,致力于成为一个更小、更富有表现力、更健壮的Web框架。使用koa编...
    不去解释阅读 2,652评论 0 11