Koa源码学习
前言
koa是一个非常流行的Node.js http框架。本文我们来学习下它的使用和相关源码
来自官网的介绍:
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序
为什么使用koa
使用koa而不直接使用Node.js的http模块
-
高度可定制性:koa中实现了一套中间件机制以及在koa中万物皆中间件,我们通过中间件处理请求和响应并可以按需自由添加和修改中间件,并且koa的中间件生态非常丰富。而使用http需要自己编写全部的请求处理逻辑
-
异步编程:koa基于async/await语法,可以让异步编程变得更加简单和优雅。而直接使用http模块,则需要使用回调函数或事件监听的方式进行异步编程,不够直观
-
错误处理:koa内置的错误处理机制可以很好的捕获和处理错误,让代码更加健壮和可靠。而使用http模块,则需要自己编写错误处理逻辑,容易出现漏洞
-
扩展性:koa内置的扩展机制可以让开发者在不改变核心代码的情况下,轻松地扩展和定制koa的功能。而使用http模块,则需要自己编写全部的扩展逻辑,不够便捷
使用
koa的使用非常简单,引入koa后只需要6行代码即可访问3000端口的http服务返回一个Hello koa
const Koa = require('koa');
const app = new Koa();app.use(ctx => {ctx.body = 'Hello Koa';
});app.listen(3000);
中间件
koa本身几乎没有封装任何进一步处理http请求的能力,而是实现了一套中间件的机制,所有的逻辑均由相关的中间件进行实现,中间件可以说是koa的灵魂
koa的中间件本质是一个函数,接收一个上下文对象(context)和一个next函数作为参数,然后对请求和响应进行处理,并将控制权传递给下一个中间件。中间件可以实现各种功能,例如路由、请求处理、错误处理等
const myMiddleware = async (ctx, next) => {// 处理请求// ...// 调用下一个中间件await next();// 处理响应// ...
}
例如我们实现一个错误处理中间件,在服务端发生任何错误时给客户端返回一个500的状态码,可以以下实现即可
const errorHandler = async (ctx, next) => {try {// 处理请求// ...// 调用下一个中间件await next();// 处理响应// ...} catch (err) {// 处理错误ctx.status = 500;ctx.body = err.message;}
}app.use(errorHandler)
以两个最常用的中间件为例
- koa-router
koa默认也是没有封装对于特定的请求方法进行处理的功能,像很多http中处理路由相关的逻辑则需要引入koa-router 进行使用。koa router提供了基础的路由路径处理、嵌套路由等一些基础路由能力
var Koa = require('koa');
var Router = require('koa-router');var app = new Koa();
var router = new Router();router.get('/', (ctx, next) => {// ctx.router available
});app.use(router.routes()).use(router.allowedMethods());
koa-router的源码就不展开了,原理基本上在中间件中读取req.url
、 req.method
和相关req
上的一些属性进行分发到相应的路由注册的回调返回中进行处理
- koa-body
另一个常用的功能就是将请求的请求体数据解析成js对象,方便代码进行消费
对于node原生的http服务,我们需要监听请求对象的data
和end
事件,在data 事件中接收二进制buffer数据,在end事件中将buffer转成字符串再序列化成js对象
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');const app = new Koa();
app.use(bodyParser());app.use(async ctx => {// the parsed body will store in ctx.request.body// if nothing was parsed, body will be an empty object {}ctx.body = ctx.request.body;
});
这样对于这类请求我们通过ctx.request.body
就能获取到json请求的数据,无需关心从请求流关心如何获取请求体。koa-body不止处理json类型,它还会对form、text、xml等类型做相应的处理
源码实现
koa的源码非常简洁,一共只有4个文件
application
application.js定义了Koa类,用于创建koa app对象,下面是koa类的构造函数
// ...
const Emitter = require('events')
const compose = require('koa-compose');
const http = require('http');
const context = require('./context');
const request = require('./request');
const response = require('./response');
// ...class Koa extends Emitter {constructor() {super();this.middleware = [];this.context = Object.create(context);this.request = Object.create(request);this.response = Object.create(response);// constructor中其它的逻辑忽略}// ...
}
Koa类继承了Emitter
类,用于实现事件的发布和订阅。还定义了一些属性,主要包括middleware
、context
、request
和response
。其中,middleware
是中间件函数数组,用于存储所有的中间件函数;context
是koa的请求上下文对象、reques
t是请求对象实例、response
是响应对象实例
koa实例上也暴露了几个对外使用的方法
app.listen
上面的使用demo,可以看到调用listen后就是监听指定端口运行起我们的http服务
通过查看app.listen
的实现本质是调用了app.callback
获取到回调函数处理逻辑,再传给http.createSerever
。所以也等价于以下调用
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
app.callback
返回可以直接传递给 http.createServer()
方法的回调函数来处理请求
callback () {const fn = this.compose(this.middleware)if (!this.listenerCount('error')) this.on('error', this.onerror)const handleRequest = (req, res) => {const ctx = this.createContext(req, res)return this.handleRequest(ctx, fn)}return handleRequest}handleRequest (ctx, fnMiddleware) {const res = ctx.resres.statusCode = 404const onerror = err => ctx.onerror(err)const handleResponse = () => respond(ctx)onFinished(res, onerror)return fnMiddleware(ctx).then(handleResponse).catch(onerror)}
主要有以下几个逻辑
-
判断我们是否有监听错误事件进行处理(
this.listenerCount
是继承的EventEmiter
上用于获取某个事件监听次数的方法),如果没有则使用koa自带的默认错误处理 -
使用回调入参的
request
对象和response
对象构造请求上下文对象并传递给this.handleRequest
函数进行处理 -
在
handleRequest
中,就是调用了被compose完成后的中间件函数,在处理完成后调用respond
进行结束整个请求的流程 -
在koa中我们无需像Node.js中http需要显式调用
res.end
或者res.pipe
进行响应的结束发送,因为在handleResponse
的respond
函数中处理了。它会根据我们在业务逻辑设置的不同的body的类型进行相关调用,例如如果是一个流则调用pipe
进行流式返回、特定状态码不返回body、非buffer和string的body序列化成字符串等
- 洋葱模型
koa的洋葱模型是一种中间件处理机制其核心是将请求和响应对象传递给一系列中间件函数,每个中间件函数都可以对请求和响应进行处理,并将控制权传递给下一个中间件函数,最终将响应返回给客户端。中间件函数在请求处理过程中像是一个个套在一起的“洋葱”,请求从外层中间件函数开始处理,逐层深入,直到最内层中间件函数,然后逐层返回,最终响应从最外层中间件函数返回给客户端
在洋葱模型中,每个中间件函数都是一个异步async函数。在处理请求时,每个中间件函数都接收一个context
对象和一个next
函数作为参数,context对象包含了请求和响应的信息,next函数可以调用下一个中间件函数
处理顺序如下
-
请求从外层中间件函数开始处理,先经过第一个中间件函数
-
第一个中间件函数处理请求,然后调用
next
函数,将控制权传递给下一个中间件函数 -
下一个中间件函数也处理请求,然后调用
next
函数,将控制权传递给下一个中间件函数,直到最内层中间件函数 -
最内层中间件函数处理请求完成后逐层返回每个中间件函数在返回时可以对响应进行处理
-
最后,响应从最外层中间件函数返回给客户端
洋葱模型的优点是可以将请求和响应的处理逻辑分解成多个模块,每个模块只需关注自己的逻辑,提高了代码的可维护性。由于每个中间件函数都可以对请求和响应进行处理,因此可以实现一些复杂的功能例如身份验证、日志记录、错误处理等
主要是koa-compose
包的实现将中间件函数组合在一起,compoose
实现代码如下
function compose (middleware) {return function (context, next) {// last called middleware #let index = -1return dispatch(0)function dispatch (i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))index = ilet fn = middleware[i]if (i === middleware.length) fn = nextif (!fn) return Promise.resolve()try {return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))} catch (err) {return Promise.reject(err)}}}
}// 使用const fn = this.compose(this.middleware)
compose
函数接收一个中间件函数数组作为参数,返回一个新的中间件。新的中间件函数接收context
和next
对应于常规中间件的入参
函数内部实现了dispatch
,用于递归调用中间件数组中的每个函数。
dispatch
函数接收一个参数i
,表示当前调用的中间件函数在数组中的索引。如果i小于等于上一次调用的索引index
,则表示next
函数被多次调用,koa中间件中next
只能被调用一次,调用多次会抛出一个错误。然后,dispatch
将index
赋值为i,表示当前调用的中间件函数已经被执行。然后dispatch
函数会从中间件数组中取出当前索引对应的函数fn
,如果当前索引i
等于数组长度则说明已经到达中间件函数数组的末尾然后将fn
设置为next
函数。如果fn
不存在则直接返回一个已经resolve
的Promise
。最后dispatch
函数通过Promise.resolve
调用当前中间件函数,并将dispatch.bind(null, i + 1)
作为下一个中间件函数的next参数传入,以便递归调用下一个中间件函数。如果当前中间件函数抛出了一个错误则通过Promise.reject
将错误传递给下一个中间件函数
总结原理是通过递归调用中间件函数数组中的每个函数,并将next
函数作为参数传入,实现洋葱模型中间件的处理顺序。在递归调用的过程中,如果某个中间件函数抛出了错误则通过Promise.reject
将错误逐层传递给下一个中间件函数,直到最终返回错误响应或者成功响应
context
请求上下文对象,对应中间件的ctx
入参
context.js
文件主要是对外导出了一个对象,以及执行了一系列delegate
操作
-
导出的对象主要是封装了cookie的读取逻辑
-
delegate
方法是从delegates
npm包进行导入(这个包的解读见# 每天阅读一个 npm 模块(7)- delegates - 掘金)
简单来说就是将对context
对象上的操作代理到koa封装的request
和response
对象中去
// proto这里是context
delegate(proto, 'response').method('append').access('body')
这个执行后的结果就是
-
context.append
方法调用实际调用的是context.response.append
-
context.body
的读写实际调的是context.response.body
的读写
而context.response
则在下面的createContext
时将koa
的response
对象设置在context
对象中去
在application
中通过createContext
方法构造后传入请求处理回调函数
class Koa extends Emitter {constructor() {//....this.context = Object.create(context);//....}// ...callback () {const fn = this.compose(this.middleware)if (!this.listenerCount('error')) this.on('error', this.onerror)const handleRequest = (req, res) => {const ctx = this.createContext(req, res)return this.handleRequest(ctx, fn)}return handleRequest}createContext (req, res) {/** @type {Context} */const context = Object.create(this.context)/** @type {KoaRequest} */const request = context.request = Object.create(this.request)/** @type {KoaResponse} */const response = context.response = Object.create(this.response)// 挂载context.app = request.app = response.app = thiscontext.req = request.req = response.req = reqcontext.res = request.res = response.res = resrequest.ctx = response.ctx = contextrequest.response = responseresponse.request = requestcontext.originalUrl = request.originalUrl = req.urlcontext.state = {}return context}
}
主要是将我们koa中常用的几个对象挂载到相应的地方,经过createContext
的操作,我们可以得到可以通过以下方式获取相关对象
-
koa
app
实例app === context.app === context.request.app === context.response.app
-
koa 请求
context
对象context === context.request.ctx === context.response.ctx
-
koa
request
对象ctx.request === ctx.response.request
-
koa
response
对象ctx.response === ctx.request.response
-
原生
req
对象context.req === context.request.req === context.response.req
-
原生
res
对象context.res === context.response.res === context.request.res
request
koa中的请求对象封装。基本上都是基于Node.js的http请求的request做一些便捷使用的二次封装的属性和方法,并挂载在ctx.request
中
一个例子就是Node.js 的http server回调函数入参的req对象http.ImcomingMessage
是没有提供便捷的获取query参数信息,它只有一个url属性
而koa的request
对象则实现了query
的解析、获取、设置等
// request.js
get query () {const str = this.querystringconst c = this._querycache = this._querycache || {}return c[str] || (c[str] = qs.parse(str))}get querystring () {if (!this.req) return ''return parse(this.req).query || ''}
response
koa中的响应对象封装,基于Node.js的http请求的response做一些封装的属性和方法,挂载在ctx.response
中
一个比较常用到的就是会有根据我们的ctx.body
设置的值(会delegate到ctx.response.body
中)帮我们去设置response的Content-Type
的值,例如给ctx.body
设置一个普通js对象的话,会将Content-Type
设置为json类型并将js对象json序列化(序列化逻辑在上面提到的respond函数中)
最近更新
作为一个代码实现非常精简且已经非常稳定的广泛使用的框架,一般来说不会有什么更新了,2.x也已经稳定了很久。但是在1/2却更新了3.0.0-alpha.0版本,翻看更新记录这个大版本目前只更新了一个功能
可以直接使用app.currentContext
来获取当前的请求上下文对象,这个功能可以方便不少我们的代码开发
通过上面我们知道,koa的contxt
对象是每次请求维度的一个新对象,如果我们想在一些封装的方法中获拿到当前请求的context
对象,必须层层传递context
对象会比较麻烦
// fn.js
const fn = (ctx) => {console.log(ctx.url)
}
exports.fn = fn// app.js
const app = new Koa();
app.use(ctx => {ctx.body = 'Hello Koa';fn(ctx)
}).listen(3000);
而支持了app.currentContext
后,我们在任意地方想获取当前的请求上下文对象直接app.currentContext
即可,无需再多层透传context对象
// fn.js
const fn = () => {console.log(app.currentContext.url)
}
这个功能的实现利用了Node.js的async_hooks
模块提供的AsyncLocalStorage
。AsyncLocalStorage
是 Node.js 在v14.8.0 版本中引入的一个模块,是官方推荐的在异步代码中管理数据的方式之一,会将我们保存的数据与异步操作所在的上下文关联起来,确保在异步操作中访问到相应正确的数据
AsyncLocalStorage
有两个主要的方法
-
run()
:用于在异步操作中保存数据。接收一个回调函数作为参数,该回调函数会在异步操作执行期间被调用,并且在该回调函数中保存的数据会与异步操作所在的上下文关联起来 -
getStore()
:用于在异步操作中获取数据。它会返回与异步操作所在的上下文关联的数据
所以在koa中实现app.currentContext
功能主要就是以下代码
// application.js
class Application extends Emitter {constructor (options) {//....if (options.asyncLocalStorage) {const { AsyncLocalStorage } = require('async_hooks')this.ctxStorage = new AsyncLocalStorage()this.use(this.createAsyncCtxStorageMiddleware())}}// ...createAsyncCtxStorageMiddleware () {const app = thisreturn async function asyncCtxStorage (ctx, next) {await app.ctxStorage.run(ctx, async () => {return await next()})}}// ....get currentContext () {if (this.ctxStorage) return this.ctxStorage.getStore()}
}
-
如果初始化时配置了
option.asyncLocalStorage
,就注册一个放在第一位的koa中间件 -
在请求进入中间件时会执行
ctxStorage.run
存入当前的context
对象并马上在回调函数中执行next
(即请求后续所有的操作) -
在后续获取即可通过
getStore()
获取到当前请求的context
对象
总结
通过本文的学习我们了解到了koa的一些使用和实现,koa的源码是非常精简的没有太多耦合功能,但是设计了巧妙的中间件机制设计来方便让我们开发各种功能
相关文章:

Koa源码学习
前言 koa是一个非常流行的Node.js http框架。本文我们来学习下它的使用和相关源码 来自官网的介绍: Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。…...

一种延迟加载自定义元素的方法
您可能实际上并不需要所有这些;通常有一个更简单的方法。如果有意使用,此处显示的技术可能仍然对您的工具集有用。 为了保持一致性,我们希望我们的自动加载器也成为一个自定义元素——这也意味着我们可以通过 HTML 轻松配置它。但首先&#…...

Pytho经典面试题荟萃:第一期
目录 一、面试题 二、参考答案 解释器和编译器的区别 解释器 编译器 Python 的解释过程 Python 内存管理 Python 内存分配 引用计数 垃圾回收 其他内存管理技术 多重继承 多重继承带来的问题 命名冲突 菱形继承问题 解决多重继承带来的问题 方法重写 调用 su…...

01背包问题(大彻大悟版)
背包问题身为一个非常经典的动态规划问题,理清思路很重要,在经过多次观看y总视频和b站解析,加上CSDN的文章辅助,我终于从很多不理解到大彻大悟,下面是我对于背包问题思路的总结,有问题的话欢迎指出。谈到背…...

【麒麟服务器操作系统忘记开机密码怎么办?---银河麒麟服务器操作系统更改用户密码】
银河麒麟服务器操作系统更改用户密码 1.启动主机进入 grub 菜单,如图 1.1 以最新版本 Kylin-Server-10-SP2-x86-Release-Build09-20210524 为例。 图 1.1 grub 菜单 2 编辑 kernel 2.1按下”e”输入,输入用户名和密码(root/Kylin123123&…...

华为OD机试(20222023)考点分类
字符串,数组,集合操作 题库分值序号题目考点 or 实现Old1001敏感字段加密字符串,数组,集合操作Old1002IPv4地址转换成整数字符串,数组,集合操作Old1006字符串分割字符串,数组,集合操作Old1007...

初级篇 3 - HTML 或 CSS 文件中不懂的标签属性详解
目录一、遇到的不懂的标签属性详解1、meta 标签的 http-equiv 属性(元标签)二、遇到的 CSS 不懂的属性详解vertical-align三、如何规避 HTML 自动换行 - 脱离文档流配置属性 display: inline-block理解 inline、inline-block、blockinline总结:四、导航栏自动弹出子…...

【C语言】每日刷题 —— 牛客语法篇(4)
🚀🚀前言 大家好,继续更新专栏 c_牛客,不出意外的话每天更新十道题,难度也是从易到难,自己复习的同时也希望能帮助到大家,题目答案会根据我所学到的知识提供最优解。 🏡个人主页&am…...

HashMap ConcurrentHashMap介绍
目录 HashMap 数据结构 重要成员变量 Jdk7-扩容死锁分析 单线程扩容 多线程扩容 Jdk8-扩容 ConcurrentHashMap 数据结构 并发安全控制 源码原理分析 重要成员变量 协助扩容helpTransfer 扩容transfer 总结 CopyOnWrite机制 源码原理 HashMap 数据结构 数组…...

C++语法规则3(C++面向对象)
多态 C多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数; 形成多态必须具备三个条件: 必须存在继承关系;继承关系必须有同名虚函数(其中虚函数是在基类中使用关键字 virtual 声明的函数&#…...

Python tkinter 如何实现网站下载工具?将所有数据一键获取
前言 铁汁们有没有想过,如何把几个代码的功能结合到一起呢? 有想过的话,有没有实现过呢? 其实很简单的啊,咱就写一个界面就好了,想要哪个代码运行,鼠标轻轻一点就行 开发环境 python 3.8: 解…...

第六章:C语言数据结构与算法初阶之栈
系列文章目录 文章目录系列文章目录前言一、栈二、栈的实现三、接口函数的实现1、初始化2、销毁栈3、压栈与出栈4、判空5、元素个数6、返回栈顶元素四、栈中元素的访问总结前言 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 一、…...

Android学习之WebView
什么是WebView WebView是Android中UI组件的一种,WebView基于webkit内核,不过由于兼容性的原因在Android5.0后改为了Chromium内核。 WebView可以用来展示网页,常用于我们不想打开浏览器但又想浏览网页的情况。 WebView的使用 WebVeiw的常用…...

3/11 考试总结
时间安排 7:30–7:50 读题,T1 是个利用随机性的题目,T2 dp,T3 不知道是啥。 7:50–8:30 T1,对于随机有个结论时最值突变不超过 log ,于是可以处理出所有 log 个区间然后统计答案,但这暴力做是个 3log 铁定过不去。 8:30–8:50 T2…...

Leetcode 141.环形链表 142环形链表II
141环形链表 文章目录快慢指针快慢指针 代码思路: slow 和fast 指向 head slow走一步,fast走两步 没有环: fast每次走2步 ,如果 fast 最终遇到NULL(链表中的元素是 偶数)或者fast->next(链表中的元素是 奇数)遇到NULL…...

hibernate学习(五)
hibernate学习(五) hibernate的一对多关联映射: 一、数据库表与表之间关系 一对多建表原则: 多对多的建表原则: 一对一建表原则: (1)唯一外键对应: (…...

STM32CubeIDE 快速开发入门指南
描述 STM32CubeIDE是一体式多操作系统开发工具,是STM32Cube软件生态系统的一部分。 STM32CubeIDE是一种高级C/C开发平台,具有STM32微控制器和微处理器的外设配置、代码生成、代码编译和调试功能。它基于Eclipse/CDT™框架和用于开发的GCC工具链…...

华为OD机试 - 火星文计算(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:火星文计…...

超超超超保姆式详解——字符函数和字符串函数(学不会打我)上
目录 长度不受限制的字符串函数 strlen部分 strlen函数的易错小知识 strlen函数的实现 strcpy部分 strcat部分 自己实现strcat strstr函数部分 简单例子: 分析 strcmp部分 长度受限制的字符串函数 strncpy 简单例子 strncat strncmp 简单例子 &…...

Data mesh 笔记
有用的网站 https://www.datamesh-architecture.com/ https://www.agilelab.it/data-mesh-in-action https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/well-architected-framework https://www.datamesh-architecture.com…...

(八十三)大白话透彻研究通过explain命令得到的SQL执行计划(2)
今天我们就一步一步的来讲解不同的SQL语句的执行计划长什么样子,先来看第一条SQL语句,特别的简单,就是: explain select * from t1 就这么一个简单的SQL语句,那么假设他这个里面有大概几千条数据,此时执行计…...

案例18-面向对象之开门小例子
目录 一:背景介绍 二:思路&方案 1.面向过程 2.面向对象 3.面向对象(反射) 三:过程 1.面向过程:原本何老师的作用交给我了米老师来完成。 2.面向对象:把开门的方法完全交个何老师,米老师不需要有…...

【碎片化知识总结】三月第一周
目录 前言 1、开发中常用的 IDEA 编辑器,如何做到不用每次都重新配置? 2、如何使用 Python 获取视频文件信息? 3、使用 Java 的 try-with-resources 优化代码 4、使用 shell 脚本批量修改服务器某一目录下的文件后缀名称 5、MySQL优化&…...

从零开始的JSON库(1):启程
1. JSON 是什么 JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为ECMA-404 。 虽然 JSON 源自于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具有类似功能的格式有X…...

【Java】数组
目录 1.数组的定义与初始化 2.遍历数组 3.认识null 4.引用变量 5.返回多个值 6.数组拷贝 7.数组逆序 8.数组填充 9.小练习 //将整形数组转化为字符串 //二分查找优化 //冒泡排序优化 10.二维数组 //遍历二维数组 //不规则的二维数组 1.数组的定义与初始化 int…...

【C++】非类型的模板参数,特化
目录 1.类型模板参数和非类型模板参数 2.特化 3. 模板的分离编译 4.模板的优缺点 1.类型模板参数和非类型模板参数 之前写模板传的都是类型——类型模板参数 现在想定义两个静态数组,数组长度不同,就可以用模板参数传数值而不是传类型 非类型模板…...

核方法(kernel Method)
核方法 核方法定义 一种能够将在原始数据空间中的非线性数据映射到高维线性可分的方法。 核方法的用处 1、低维数据非线性,当其映射到高维空间(feature space)时,可以用线性方法对数据进行处理。 2、线性学习器相对于非线性学…...

消息队列MQ用来做什么的,市场上主流的四大MQ如何选择?RabbitMQ带你HelloWorld!
文章目录MQ用来做什么的MQ会有什么样的麻烦MQ消息队列模式分类MQ消息队列常用协议市场主流四大MQRabbitMQ项目开发RabbitMQ中的组成部分MQ用来做什么的 省流 :系统解耦、异步调用、流量削峰 系统解耦 首先举例下面这个场景,现有ABCDE五个系统ÿ…...

2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛) A — E
2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛) 文章目录A -- A Xor B Problem题目分析codeB -- 吃苹果题目分析codeC -- n皇后问题题目分析codeD -- 分苹果题目分析codeE -- 完型填空题目分析codeA – A…...

一文分析Linux v4l2框架
说明: Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:Source Insight 3.5, Visio 1. 概述 V4L2(Video for Linux 2):Linux内核中关于视频设备驱动的框架,对上向应用层提供…...