Node底层机制使用C++写的,所以我们如果想扩展功能,可以选择使用C++从底层扩展,以前已经介绍过何如嵌入V8到自己的程序中,实际上Node就是把V8和libuv等库整合到一起,从而使我们用JavaScript就可以调用很多C++的库来实现自己的功能。 可以查看这两编文章了解一下V8嵌入的一些概念: 嵌入V8的核心概念 嵌入V8的核心概念1 在具体介绍写addon之前,先要讨论一下为啥需要addon,有没有其他方法。
为什么选择addon
实际上要让JavaScript调用c++代码有三种方法:
1.在子进程中调用C++程序
可以阅读automating-a-c-program-from-a-node-js-web-app
看看下面例子,execFile
函数可以帮助我们执行一个程序。
// standard node module
var execFile = require('child_process').execFile
// this launches the executable and returns immediately
var child = execFile("path to executable", ["arg1", "arg2"],
function (error, stdout, stderr) {
// This callback is invoked once the child terminates
// You'd want to check err/stderr as well!
console.log("Here is the complete output of the program: ");
console.log(stdout)
});
// if the program needs input on stdin, you can write to it immediately
child.stdin.setEncoding('utf-8');
child.stdin.write("Hello my child!\n");
var execFile = require('child_process').execFile
// this launches the executable and returns immediately
var child = execFile("path to executable", ["arg1", "arg2"],
function (error, stdout, stderr) {
// This callback is invoked once the child terminates
// You'd want to check err/stderr as well!
console.log("Here is the complete output of the program: ");
console.log(stdout)
});
// if the program needs input on stdin, you can write to it immediately
child.stdin.setEncoding('utf-8');
child.stdin.write("Hello my child!\n");
2.调用C++的dll
可以阅读node-ffi
调用的dll需要导出函数。
var ffi = require('ffi');
var libm = ffi.Library('libm', {
'ceil': [ 'double', [ 'double' ] ]
});
libm.ceil(1.5); // 2
// You can also access just functions in the current process by passing a null
var current = ffi.Library(null, {
'atoi': [ 'int', [ 'string' ] ]
});
current.atoi('1234'); // 1234
3.使用addon(实际上addon也是一个动态链接库)
使用addon在C++这边需要了解V8和libuv的api,可以说是最复杂的,但是可以让JavaScript调用起来比较简单,而且可以实现异步回调,如果你用上面两种方法,是不好实现像node.js这样回调的。我们看看回调的写法。
var server = http.createServer(function(req, res) {
++requests;
var stream = fs.createWriteStream(file);
req.pipe(stream);
stream.on('close', function() {
res.writeHead(200);
res.end();
});
}).listen(common.PORT, function() {
.......
如何写Addon
在写addon的时候我们可以使用第三方包装NAN,但这里我们主要介绍如何直接使用node和v8的api来做addon。 在node的文档中,详细介绍了各种处理方法。不过我喜欢通过阅读完整的代码来学习,所以找了一些资料,在这里列出。
对qt的包装 代码比较多,我对qt没有很多了解,并没有看,只是公司在用,列在这里。
对zmq的包装。使用了NAN。zmq是一个快速的消息队列,里面总结的各种模式对开发分布式程序有指导意义。
ScottFree的demo,一个老外写的比较好的blog,有很多例子。
官方文档demo,比较简单,没有使用到libuv。
大家编译addon的时候注意版本和平台的关系,node版本可以用nvm管理。
Scott Frees写了很多博客介绍node。这里通过阅读他的代码来了解如何写addon。
例子说明
ScotteFree的例子代码结构:
文件
说明
rainfall.js
使用addon的js代码
binding.gyp
编译脚本
makefile
编译脚本
rainfall.cc
c++的逻辑代码
rainfall_node.cc
插件,绑定c++逻辑代码
这里面主要的逻辑就是显示某一经度或者纬度的不同日期的降雨量,并进行相应计算。因为计算需要耗费cpu资源,阻塞主线程,所以希望放到另一个线程中。
我们看看在js中怎么使用插件的,先知道目标是啥,在看代码的时候可以带着问题思考。
1. 创建对象rainfall
我们可以使用require去加载插件
var rainfall = require("./cpp/build/Release/rainfall");
var location = {
latitude : 40.71, longitude : -74.01,
samples : [
{ date : "2015-06-07", rainfall : 2.1 },
{ date : "2015-06-14", rainfall : 0.5},
{ date : "2015-06-21", rainfall : 1.5},
{ date : "2015-06-28", rainfall : 1.3},
{ date : "2015-07-05", rainfall : 0.9}
] };
2. 计算平均降雨量
我们传递一个JavaScript对象给c++使用
console.log("Average rain fall = " + rainfall.avg_rainfall(location) + "cm");
3. 计算降雨数据(不关心,没仔细看算法)
从C++返回JavaScript对象
console.log("Rainfall Data = " + JSON.stringify(rainfall.data_rainfall(location)));
4. 同步计算
传递数组给C++,返回数组
var results = rainfall.calculate_results(locations);
print_rain_results(results);
5. 异步计算
rainfall.calculate_results_async(locations, print_rain_results);
上面只有最后一个函数calculate_results_async
是异步计算,所以我们着重看看这个函数怎么实现的。下面过过代码。
代码分析
头文件
#include
#include
#include
#include "rainfall.h"
#include
#include
#include
#include
#include
#include
using namespace v8;
通过头文件可以看到addon需要和v8,libuv,node打交道。
导出函数
下面的代码很容易看出是把函数加入到exports中。exports就是js中的对象。
void init(Handle exports, Handle module) {
NODE_SET_METHOD(exports, "avg_rainfall", AvgRainfall);
NODE_SET_METHOD(exports, "data_rainfall", RainfallData);
NODE_SET_METHOD(exports, "calculate_results", CalculateResults);
NODE_SET_METHOD(exports, "calculate_results_sync", CalculateResultsSync);
NODE_SET_METHOD(exports, "calculate_results_async", CalculateResultsAsync);
}
NODE_MODULE(rainfall, init)
拿到值和返回值
拿参数中的JavaScript对象,返回double
void AvgRainfall(const v8::FunctionCallbackInfo& args) {
Isolate* isolate = args.GetIsolate();
location loc = unpack_location(isolate, Handle::Cast(args[0]));
double avg = avg_rainfall(loc);
Local retval = v8::Number::New(isolate, avg);
args.GetReturnValue().Set(retval);
}
从上面代码我们看出,首先要获得isolate,下面的api都需要这个作为参数,从这里可以看出,这些api都很底层还是比较繁琐的。
拿参数Handle::Cast(args[0])
返回值给JavaScript:args.GetReturnValue().Set(retval);
void RainfallData(const v8::FunctionCallbackInfo& args) {
Isolate* isolate = args.GetIsolate();
location loc = unpack_location(isolate, Handle::Cast(args[0]));
rain_result result = calc_rain_stats(loc);
Local obj = Object::New(isolate);
pack_rain_result(isolate, obj, result);
args.GetReturnValue().Set(obj);
}
我们看到拿对象是一样的,这里Local obj = Object::New(isolate);是关键代码。创建了一个V8的对象。然后返回。
void CalculateResults(const v8::FunctionCallbackInfo&args) {
Isolate* isolate = args.GetIsolate();
std::vector locations;
std::vector results;
// extract each location (its a list)
Local input = Local::Cast(args[0]);
unsigned int num_locations = input->Length();
for (unsigned int i = 0; i < num_locations; i++) {
locations.push_back(unpack_location(isolate, Local::Cast(input->Get(i))));
}
// Build vector of rain_results
results.resize(locations.size());
std::transform(locations.begin(), locations.end(), results.begin(), calc_rain_stats);
// Convert the rain_results into Objects for return
Local result_list = Array::New(isolate);
for (unsigned int i = 0; i < results.size(); i++ ) {
Local result = Object::New(isolate);
pack_rain_result(isolate, result, results[i]);
result_list->Set(i, result);
}
// Return the list
args.GetReturnValue().Set(result_list);
}
从代码中我们看出来下面两行代码分别表示拿数据和返回数组
Local input = Local::Cast(args[0]);
Local result_list = Array::New(isolate);
异步 node强的地方就是大部分api都是异步的,那么我们来看看他是怎么做到的,我们知道底层c的api都是同步,所以node必须的包装并使用线程来支持异步。我们看看代码。
void CalculateResultsAsync(const v8::FunctionCallbackInfo&args) {
Isolate* isolate = args.GetIsolate();
Work * work = new Work();
work->request.data = work;
// extract each location (its a list) and store it in the work package
// locations is on the heap, accessible in the libuv threads
Local input = Local::Cast(args[0]);
unsigned int num_locations = input->Length();
for (unsigned int i = 0; i < num_locations; i++) {
work->locations.push_back(unpack_location(isolate, Local::Cast(input->Get(i))));
}
// store the callback from JS in the work package so we can
// invoke it later
Local callback = Local::Cast(args[1]);
work->callback.Reset(isolate, callback);
// kick of the worker thread
uv_queue_work(uv_default_loop(),&work->request,WorkAsync,WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
我们看一下关键代码
Work * work = new Work();//堆上创建数据,可以在线程间共享
uv_queue_work(uv_default_loop(),&work->request,WorkAsync,WorkAsyncComplete);
这里把要做的工作放在队列里面了,所以不会阻塞当前线程。WorkAsync
是在工作线程中运行的,WorkAsyncComplete
是回调,由livuv触发,回到工作线程。再来看看WorkAsync
:
struct Work {
uv_work_t request;
Persistent callback;
std::vector locations;
std::vector results;
};
// called by libuv worker in separate thread
static void WorkAsync(uv_work_t *req)
{
Work *work = static_cast(req->data);
// this is the worker thread, lets build up the results
// allocated results from the heap because we'll need
// to access in the event loop later to send back
work->results.resize(work->locations.size());
std::transform(work->locations.begin(), work->locations.end(), work->results.begin(), calc_rain_stats);
// that wasn't really that long of an operation, so lets pretend it took longer...
std::this_thread::sleep_for(chrono::seconds(3));
}
注意从uv_work_t
拿到我们要操作的数据,线程之间可以共享堆上的数据,所以这里访问没有问题。
再看看回调如何执行。
// called by libuv in event loop when async function completes
static void WorkAsyncComplete(uv_work_t *req,int status)
{
Isolate * isolate = Isolate::GetCurrent();
// Fix for Node 4.x - thanks to https://github.com/nwjs/blink/commit/ecda32d117aca108c44f38c8eb2cb2d0810dfdeb
v8::HandleScope handleScope(isolate);
Local result_list = Array::New(isolate);
Work *work = static_cast(req->data);
// the work has been done, and now we pack the results
// vector into a Local array on the event-thread's stack.
for (unsigned int i = 0; i < work->results.size(); i++ ) {
Local result = Object::New(isolate);
pack_rain_result(isolate, result, work->results[i]);
result_list->Set(i, result);
}
// set up return arguments
Handle argv[] = { result_list };
// execute the callback
// https://stackoverflow.com/questions/13826803/calling-javascript-function-from-a-c-callback-in-v8/28554065#28554065
Local::New(isolate, work->callback)->Call(isolate->GetCurrentContext()->Global(), 1, argv);
// Free up the persistent function callback
work->callback.Reset();
delete work;
}
注意看一下关键代码,我们新建了一个function并调用,这个函数就是callback。
Local::New(isolate, work->callback)->Call(isolate->GetCurrentContext()->Global(), 1, argv);
最后我们看一下线程的情况
js执行线程: 事件循环+回调,js代码的执行,都在这里
libuv启动的线程:用来做i/o和计算,比如读取一个文件,这样我们就不会被慢速的i/o拖累了。
从上图可以看到CalculateResultsAsync结束的时候,V8 Locals全部都会销毁,所以我们的回调需要是Persistent。
Persistent callback;
可以在workthread里面访问v8的内存吗?
答案是不能,v8不能多线程访问,如果需要多线程访问,需要加锁,而node在启动的时候在主线程就会获得锁,可以在node.cc中的start函数看到
Locker locker(node_isolate);
所以工作线程是没机会获得锁的。所以上面使用的copy数据的方法。具体的说明可以看这个文章
包装对象
由于上面并没有说明如何包装C++对象并返回给js,这里又切回官方文档demo,说明如何包装C++对象,然后再JavaScript中用new去新建对象。
本文引用的代码是在红框范围内:
#include
#include "myobject.h"
using namespace v8;
void InitAll(Handle exports) {
MyObject::Init(exports);
}
NODE_MODULE(addon, InitAll)
可以看到宏还是那些宏,只是现在调用了类MyObject的静态方法Init来导出函数。
myobject.cc 这个文件要看的比较多,我们先看init函数
void MyObject::Init(Handle exports) {
Isolate* isolate = Isolate::GetCurrent();
// Prepare constructor template
Local tpl = FunctionTemplate::New(isolate, New);
tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
constructor.Reset(isolate, tpl->GetFunction());
exports->Set(String::NewFromUtf8(isolate, "MyObject"),
tpl->GetFunction());
}
这里使用到了FunctionTemplate
,
tpl->InstanceTemplate()->SetInternalFieldCount(1);
设置有每个JavaScript对象有几个暴露的函数或者属性,这边只有一个NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
,注意设置在原型上
设置构造函数constructor
再看看New函数,这个函数会在JavaScript使用new关键字创建对象的时候被调用。
void MyObject::New(const FunctionCallbackInfo& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if (args.IsConstructCall()) {
// Invoked as constructor: `new MyObject(...)`
double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
MyObject* obj = new MyObject(value);
obj->Wrap(args.This());
args.GetReturnValue().Set(args.This());
} else {
// Invoked as plain function `MyObject(...)`, turn into construct call.
const int argc = 1;
Local argv[argc] = { args[0] };
Local cons = Local::New(isolate, constructor);
args.GetReturnValue().Set(cons->NewInstance(argc, argv));
}
}
我们看到几个关键地方
IsConstructCall
来判断是否是用new来调用的,或者是用函数方式直接调用,这里我们只看new,因为这是我们通常使用JavaScript对象的方式。
创建对象obj->Wrap(args.This());
obj->Wrap(args.This());
用来设置this指针,我们知道使用new创建对象的时候,this就是当前创建的对象。
最后返回this
最后我们看看如何使用。
-- addon.js
var addon = require('bindings')('addon');
var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
我们看到这次换了一种方式去加载c++模块,在node内部,调用native的都是这样的,我们写addon的时候,可以不这样加载。
再看看plus函数
void MyObject::PlusOne(const FunctionCallbackInfo& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
MyObject* obj = ObjectWrap::Unwrap(args.Holder());
obj->value_ += 1;
args.GetReturnValue().Set(Number::New(isolate, obj->value_));
}
可以看到使用了ObjectWrap::Unwrap
函数,和上面的wrap函数对应,另外C++中plusone第一个参数this,所以可以访问内部私有变量。
好了,其他的几个demo都大同小异,这里就不写下来了,希望这篇文章能帮助大家理解node addon的原理。
总结
本文介绍了ScotteFree的例子,掌握了JavaScript和C++传递数据的方法。
理清了js线程和工作线程的区别。
在现实环境中,v8接口和libuv的接口都会改变,这给我们编写addon带来了麻烦,NAN库可以帮我们解决,所以如果真的要写addon,应该看看NAN。
本文参考了以下文章: https://nodejs.org/api/addons.html#addons_wrapping_c_objects https://developers.google.com/v8/embed?hl=en#accessing-dynamic-variables http://code.tutsplus.com/tutorials/writing-nodejs-addons--cms-21771 http://blog.scottfrees.com/c-processing-from-node-js https://blog.scottfrees.com/how-not-to-access-node-js-from-c-worker-threads http://blog.scottfrees.com/c-processing-from-node-js-part-4-asynchronous-addons http://blog.scottfrees.com/c-processing-from-node-js-part-2 http://blog.scottfrees.com/c-processing-from-node-js-part-3-arrays
你可能感兴趣的:(Node.js介绍4-Addon)
使用Node.js连接MySQL数据库
技术探宝
数据库 node.js mysql 编程
在Node.js中,可以使用各种库和模块来连接和操作MySQL数据库。本文将介绍一种常用的方法,以及相应的源代码示例。步骤1:安装MySQL模块首先,需要在Node.js项目中安装MySQL模块。可以使用npm包管理器来安装mysql模块,执行以下命令:npminstallmysql步骤2:导入MySQL模块在代码中导入mysql模块,以便使用模块中提供的功能。可以使用以下语句实现导入:const
Node.js Buffer 教程
神秘代码行者
Node.js javascript node.js
Node.jsBuffer教程Buffer是Node.js中的一个重要概念,它用于处理二进制数据流。Buffer类在全局作用域中可用,不需要通过require引入。本文将全面介绍Buffer的概念、使用方法和最佳实践。1.Buffer基础概念Buffer是一个类似于数组的对象,但它专门用于存储字节数据。Buffer的大小在创建时确定,且无法调整。每个元素的取值范围是0-255(即一个字节)。1.1
Node.js 连接 mysql 数据库
7 号
Node.js 数据库 node.js mysql
安装mysql2模块npminstallmysql2使用mysql2连接池的方式,示例1importmysqlfrom'mysql2';//创建连接池constpool=mysql.createPool({host:'127.0.0.1',port:3306,user:'root',password:'123456',database:'test'});//获取连接并执行sql语句,execute
GPU与FPGA加速:硬件赋能AI应用
AI天才研究院
DeepSeek R1 & 大数据AI人工智能大模型 AI大模型企业级应用开发实战 计算科学 神经计算 深度学习 神经网络 大数据 人工智能 大型语言模型 AI AGI LLM Java Python 架构设计 Agent RPA
GPU与FPGA加速:硬件赋能AI应用1.背景介绍1.1人工智能的兴起人工智能(AI)在过去几年中经历了爆炸式增长,成为推动科技创新的核心动力。从语音识别和计算机视觉,到自然语言处理和推荐系统,AI已广泛应用于各个领域。然而,训练和部署AI模型需要大量计算资源,这对传统的CPU架构提出了巨大挑战。1.2硬件加速的必要性为满足AI算法对计算能力的巨大需求,硬件加速技术应运而生。专用硬件如GPU(图形
腾讯 tRPC-Go 教学——(5)filter、context 和日志组件
go腾讯rpchttp
本文咱们来介绍一下在tRPC中的filter机制、context用法,以及在相关机制上可以实现的tracinglog能力。说实话,这一部份是我个人最不喜欢的tRPC的实现模式,不过这不妨碍咱们使用它——只要把它封装成让人更为舒服的模式也未尝不可。系列文章腾讯tRPC-Go教学——(1)搭建服务腾讯tRPC-Go教学——(2)trpcHTTP能力腾讯tRPC-Go教学——(3)微服务间调用腾讯tRP
智能优化算法应用:基于哈里斯鹰算法与双伽马校正的图像自适应增强算法
智能算法研学社(Jack旭)
智能优化算法应用 图像增强 算法 计算机视觉 人工智能
智能优化算法应用:基于哈里斯鹰算法与双伽马校正的图像自适应增强算法-附代码文章目录智能优化算法应用:基于哈里斯鹰算法与双伽马校正的图像自适应增强算法-附代码1.全局双伽马校正2.哈里斯鹰算法3.适应度函数设计4.实验与算法结果5.参考文献6.Matlab代码摘要:本文主要介绍基于哈里斯鹰算法与双伽马校正的图像自适应增强算法。1.全局双伽马校正设图像的灰度值范围被归一化到[0,1]范围之内,基于全局
(14)FPGA与GPU区别
宁静致远dream
FPGA入门与提升(培训课程) fpga开发
(14)FPGA与GPU区别1文章目录1)文章目录2)FPGA入门与提升课程介绍3)FPGA简介4)FPGA与GPU区别5)技术交流6)参考资料2FPGA入门与提升课程介绍1)FPGA入门与提升文章目的是为了让想学FPGA的小伙伴快速入门以及能力提升;2)FPGA基础知识;3)VerilogHDL基本语法;4)FPGA入门实例;5)FPGA设计输入,包括代码输入、原语输入;6)FPGA设计技巧;7
第2章:如何基于LangChain4j实现聊天记忆
一起学开源
智能体开发 LangChain java 数据库 人工智能 langchain 智能体开发
本章主要介绍基于LangChain4J如何实现聊天记忆的功能,解决大模型无状态下有上下文的聊天1.聊天记忆(ChatMemory)的介绍手动管理和维护ChatMessage(聊天消息)是繁琐的。因此,LangChain4j提供了一个ChatMemory抽象概念,以及多种现成的实现方式。ChatMemory可以作为独立的低级组件使用,也可以作为高级组件(如AI服务)的一部分。ChatMemory充当
【信息系统项目管理师-案例真题】2022下半年案例分析答案和详解
数据知道
软考高级 高项 信息系统项目管理师 软考 高项案例分析
更多内容请见:备考信息系统项目管理师-专栏介绍和目录文章目录试题一(24分)【问题1】(6分)【问题2】(10分)【问题3】(8分)试题二(26分)【问题1】(8分)【问题2】(8分)【问题3】(4分)【问题4】(6分)试题三(25分)【问题1】(12分)【问题2】(7分)【问题3】(6分)试题一(24分)阅读下列说明,回答问题1至问题3,将解答填入答题纸对应栏内。【说明】某集团为提升企业服务水平
WPF 编译功能命名空间
GhostKINGC
WPF故事模式(标准版) wpf c# .net microsoft 开发语言
本节我们来关注到XAML的编译功能命名空间。xmlns声明时,有一个命名空间的映射前缀为x:,其指的是XAML的首字母。这个命名空间是专门写给XAML编译器看的,用来引导编译器将XAML代码编译成CLR代码的编译功能命名空间。下面为大家简单介绍编译功能命名空间中常见常用的功能。4.1XAML命名空间概述一般WPF程序中都会包含有一个映射前缀为x的命名空间,其声明语句为xmlns:x="http:/
MicrobeCensus:快速估计宏基因组数据中的平均基因组大小
邪恶的凹凸曼
笔记 算法
MicrobeCensus:快速估计宏基因组数据中的平均基因组大小在微生物组学研究中,了解微生物群落的平均基因组大小(AGS)对于揭示群落结构和功能至关重要。今天,我将为大家介绍一个非常实用的工具——MicrobeCensus。它能够从宏基因组数据中快速估计微生物群落的平均基因组大小,帮助我们更好地理解微生物群落的组成和功能。接下来,我将详细分享如何安装和使用这个强大的工具。一、为什么选择Micr
Qt/C++音视频开发-本地摄像头推流/桌面推流/文件推流/监控推流
鱼弦
Qt学习与实践 音视频开发系列实践 qt c++ 音视频
Qt/C++音视频开发-本地摄像头推流/桌面推流/文件推流/监控推流介绍Qt/C++音视频开发是一种结合使用Qt框架和C++语言进行音视频处理与传输的技术。通过这种技术,可以实现本地摄像头推流、桌面推流、文件推流以及监控推流等功能。这些应用在视频会议、远程教育、在线直播等场景中非常常见。应用使用场景本地摄像头推流:用于将本地摄像头采集的视频数据进行实时编码并通过网络传输。桌面推流:用于捕获用户桌面
清零函数的简单介绍
好的。。。
开发语言 c语言 单片机 笔记
bzero#includevoidbzero(void*s,size_tn);功能:数组内容清零参数:s:数组首地址n:字节大小返回值:无#include#includeintmain(intargc,charconst*argv[]){inta[5]={97,98,99,100,101};for(inti=0;ivoid*memset(void*s,intc,size_tn);功能:数组元素清零
MySQL数据库——索引结构之B+树
Good Note
MySQL Cookbook 数据库 面试 缓存 春招 redis mysql sql
大家好,这里是编程Cookbook。本文先介绍数据结构中树的演化过程,之后介绍为什么MySQL数据库选择了B+树作为索引结构。文章目录树的演化为什么其他树结构不行?为什么不使用二叉查找树(BST)?为什么不使用平衡二叉树(AVL树)?为什么不使用B树?为什么选择B+树1.B+树节点结构2.优点举例Q&AHash比B+树更快,为什么Mysql用B+树来存储索引呢?增加树的路数可以降低树的高度,那么无
MySQL数据库笔记——日志介绍
Good Note
MySQL数据库笔记 数据库 mysql sql 数据库架构
大家好,这里是GoodNote,关注公主号:Goodnote,本文详细介绍MySQL的日志类型及其作用,包括RedoLog、UndoLog、BinaryLog和ErrorLog等,在事务维护,主从同步,慢查询等方面都至关重要。文章目录日志类型1.RedoLog(重做日志)2.UndoLog(回滚日志)3.BinaryLog(二进制日志)4.ErrorLog(错误日志)5.SlowQueryLog(
MySQL数据库笔记——多版本并发控制MVCC
Good Note
MySQL数据库笔记 数据库 mysql 笔记
大家好,这里是GoodNote,关注公主号:Goodnote,本文详细介绍MySQL的并发控制:多版本并发控制MVCC。文章目录背景介绍数据库并发控制——锁机制悲观锁和乐观锁悲观锁乐观锁数据库并发控制——MVCC的引入MVCC和锁机制的对比MySQL的多版本并发控制(MVCC)快照读和当前读快照读和当前读的对比隐藏的系统列UndoLog(回滚日志)ReadView(读视图)可见性算法(Visibi
MySQL数据库笔记——索引潜规则(最左前缀原则)
Good Note
MySQL数据库笔记 mysql 数据库
大家好,这里是GoodNote,关注公主号:Goodnote,专栏文章私信限时Free。本文详细介绍MySQL索引的关键潜规则——最左前缀原则。文章目录图示单值索引和联合索引单值索引联合索引最左前缀原则示例分析1.全值匹配查询时2.匹配左边的列时3.匹配列前缀(%)4.匹配范围值5.精确匹配某一列并范围匹配另外一列6.排序总结历史文章图示单值索引和联合索引单值索引单值索引(唯一索引、主键索引、全文
MySQL数据库笔记——常见慢查询优化方式
Good Note
MySQL数据库笔记 数据库 mysql sql adb
大家好,这里是GoodNote,关注公主号:Goodnote,本文详细介绍MySQL的慢查询相关概念,分析步骤及其优化方案等。文章目录什么是慢查询日志?慢查询日志的相关参数如何启用慢查询日志?方式一:修改配置文件方式二:通过命令动态启用分析慢查询日志方式一:直接查看日志文件方式二:使用`EXPLAIN`分析查询常见的慢查询优化1.数据类型优化2.索引优化3.SQL查询优化4.分库分表慢查询日志的适
基于STM32设计的俄罗斯方块小游戏
鱼弦
单片机系统合集 stm32 嵌入式硬件 单片机
鱼弦:公众号【红尘灯塔】,CSDN博客专家、内容合伙人、新星导师、全栈领域优质创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)基于STM32设计的俄罗斯方块小游戏1.介绍基于STM32设计的俄罗斯方块小游戏是一款利用STM32微控制器、LCD显示屏和按键实现的经典休闲游戏。该游戏具有
运筹说 第130期 | 对策论引言
运筹说
运筹学
通过对对策论基础知识进行梳理和总结,小编绘制了《对策论思维导图》,如下图所示,对策论章节一共包含4个小节。第1小节是对策论引言。介绍了对策论的基本概念,包含对策行为和对策论、对策现象的三要素、对策问题举例及对策的分类。第2小节是矩阵对策的基本理论。介绍了矩阵对策的纯策略、矩阵对策的混合策略和矩阵对策的基本定理。第3小节是矩阵对策的解法。分别介绍了图解法、方程组法和线性规划法3种矩阵对策的求解方法。
使用 CefSharp 在 C# WinForms 应用程序中嵌入 Chromium 浏览器:全面指南
墨夶
C#学习资料2 c# 开发语言
亲爱的小伙伴们,今天我们要深入探讨如何在C#WinForms应用程序中使用CefSharp嵌入Chromium浏览器。CefSharp是一个开源的.NET绑定库,允许你在WindowsForms或WPF应用程序中轻松集成Chromium浏览器引擎。这使得你可以构建功能强大的桌面应用程序,拥有现代化的Web技术支持。本文将详细介绍如何在C#WinForms项目中安装和配置CefSharp,并通过一些
Redis数据库面试——数据结构类型知识
Good Note
补档 数据库 redis 面试 服务端 后端 数据结构 缓存
大家好,这里是GoodNote,关注公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Redis提供的5种基本数据结构类型和4种特殊类型,除此之外,还有8种底层数据结构,每种结构类型有其特点和适用场景。文章目录基本数据类型1.String(字符串)使用场景缓存计数器ID生成器分布式锁2.Hash(哈希)3.List(链表/列表)4.Set(集合)5.SortedSet(有序集合)特殊
面试真题 | B站C++渲染引擎
TrustZone_
ARM/Linux嵌入式面试 面试 c++ java
一、基础与语法自我介绍请简要介绍自己的背景、专业技能和工作经验。实习介绍详细描述你在实习期间参与的项目、职责和成果。二、智能指针相关问题回答unique_ptr是如何实现的?它有哪些特点和优势?unique_ptr是C++11引入的一种智能指针,用于管理动态分配的内存资源。其实现基于独占所有权的概念,即每个unique_ptr实例拥有对其所指向对象的唯一所有权。特点:独占所有权:在任何给定的时刻,
C#串口通信上位机程序编写指南
屁伦
本文还有配套的精品资源,点击获取简介:上位机软件通常用于控制设备并与之通信,本课程将介绍如何使用C#语言编写一个串口通信的上位机程序。内容包括C#编程基础、串口通信概念、SerialPort类的使用、异常处理、以及如何实现数据解析等。通过实践案例,学生将学会如何在C#环境下与各种硬件设备进行通信,如嵌入式系统和PLC等。1.上位机软件定义和作用1.1上位机软件的定义在工业自动化、测试测量、通信等领
ubuntu mysql查看所有用户_MySQL数据库之ubuntu中查看已有的mysql用户并修改用户名和密码...
茧居一十三
ubuntu mysql查看所有用户
本文主要向大家介绍了MySQL数据库之ubuntu中查看已有的mysql用户并修改用户名和密码,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助。你先进入/etc/mysql然后该目录下会有一个debian.cnf文件,用sudocatdebian.cnf查看里面的内容。如果用户名为debian-sys-maint使用mysql-udebian-sys-maint-p命令并输入上面
C# 机器视觉工控通讯------HTTP上位机通讯
pi2705230
http c# 服务器 人工智能
C#机器视觉工控通讯------HTTP上位机通讯前言HTTP介绍超文本传输协议(HyperTextTransferProtocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应HTTP会话HTTP会话是一系列网络请求-响应事务。HTTP客户端通过建立到服务器上特定端口的传输控制协议(TCP)连接(通常是端口80,偶尔
RISC-V 快速中断控制器(CLIC)项目常见问题解决方案
gitblog_00881
RISC-V快速中断控制器(CLIC)项目常见问题解决方案riscv-fast-interruptProposalforaRISC-VCore-LocalInterruptController(CLIC)项目地址:https://gitcode.com/gh_mirrors/ri/riscv-fast-interrupt项目基础介绍RISC-V快速中断控制器(CLIC)项目是一个开源项目,旨在为R
立体匹配常用数据集整理
Scurry﹉
人工智能 深度学习 计算机视觉
文章目录前言一、常用数据集1.SceneFlow数据集2.KITTI数据集3.Middlebury数据集二、关于自己构建数据集训练的一些问题前言本文主要对立体匹配算法常用的公开数据集进行整理,包括数据集的简要介绍和下载链接,以及自己构建数据集训练的一些问题。一、常用数据集1.SceneFlow数据集Sceneflow数据集是CVPR2016提出的,其目的就是构建一个大规模的合成数据集,用来训练深度
目前(2025年2月)计算机视觉(CV)领域一些表现优异的深度学习模型
空空转念
深度学习系列 计算机视觉 深度学习 人工智能
按任务类型分类介绍:图像分类CoCa:结合对比学习和生成学习,通过对比损失对齐图像和文本嵌入,并使用标题生成损失预测文本标记。它在图像分类、跨模态检索和图像描述等任务中表现出色,且仅需极少的任务特定微调。PaLI:这是一个多模态模型,结合了40亿参数的视觉Transformer(ViT)和多种大型语言模型(LLM),并在包含100多种语言的100亿图像和文本数据集上进行训练。PaLI在图像描述、视
小白购买莆田鞋时怎么辨别莆田鞋是顶级纯原还是普货?
MM老狼爱吃肉
生活
速览观察外观细节,纯原级鞋子外观精致,仿真度高;而普货做工粗糙,细节处理不足3。闻气味辨材质,纯原级鞋子材质好,无刺鼻气味7。查看卖家信誉和产品介绍,靠谱卖家会提供详细细节图和材质说明2。推荐一家专门做顶级纯原莆田鞋的良心商家,找它家购买你只管选款式就可以了,其它的不用管.karry潮鞋(k12319888)详答辨别方法1.观察外观细节纯原级:鞋子外观与正品几乎无异,无论是鞋面的材质、颜色,还是鞋
Dom
周华华
JavaScript html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml&q
【Spark九十六】RDD API之combineByKey
bit1129
spark
1. combineByKey函数的运行机制
RDD提供了很多针对元素类型为(K,V)的API,这些API封装在PairRDDFunctions类中,通过Scala隐式转换使用。这些API实现上是借助于combineByKey实现的。combineByKey函数本身也是RDD开放给Spark开发人员使用的API之一
首先看一下combineByKey的方法说明:
msyql设置密码报错:ERROR 1372 (HY000): 解决方法详解
daizj
mysql 设置密码
MySql给用户设置权限同时指定访问密码时,会提示如下错误:
ERROR 1372 (HY000): Password hash should be a 41-digit hexadecimal number;
问题原因:你输入的密码是明文。不允许这么输入。
解决办法:用select password('你想输入的密码');查询出你的密码对应的字符串,
然后
路漫漫其修远兮 吾将上下而求索
周凡杨
学习 思索
王国维在他的《人间词话》中曾经概括了为学的三种境界古今之成大事业、大学问者,罔不经过三种之境界。“昨夜西风凋碧树。独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。学习技术,这也是你必须经历的三种境界。第一层境界是说,学习的路是漫漫的,你必须做好充分的思想准备,如果半途而废还不如不要开始。这里,注
Hadoop(二)对话单的操作
朱辉辉33
hadoop
Debug:
1、
A = LOAD '/user/hue/task.txt' USING PigStorage(' ')
AS (col1,col2,col3);
DUMP A;
//输出结果前几行示例:
(>ggsnPDPRecord(21),,)
(-->recordType(0),,)
(-->networkInitiation(1),,)
web报表工具FineReport常用函数的用法总结(日期和时间函数)
老A不折腾
finereport 报表工具 web开发
web报表工具FineReport常用函数的用法总结(日期和时间函数)
说明:凡函数中以日期作为参数因子的,其中日期的形式都必须是yy/mm/dd。而且必须用英文环境下双引号(" ")引用。
DATE
DATE(year,month,day):返回一个表示某一特定日期的系列数。
Year:代表年,可为一到四位数。
Month:代表月份。
c++ 宏定义中的##操作符
墙头上一根草
C++
#与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { &nbs
分析Spring源代码之,DI的实现
aijuans
spring DI 现 源代码
(转)
分析Spring源代码之,DI的实现
2012/1/3 by tony
接着上次的讲,以下这个sample
[java]
view plain
copy
print
for循环的进化
alxw4616
JavaScript
// for循环的进化
// 菜鸟
for (var i = 0; i < Things.length ; i++) {
// Things[i]
}
// 老鸟
for (var i = 0, len = Things.length; i < len; i++) {
// Things[i]
}
// 大师
for (var i = Things.le
网络编程Socket和ServerSocket简单的使用
百合不是茶
网络编程基础 IP地址端口
网络编程;TCP/IP协议
网络:实现计算机之间的信息共享,数据资源的交换
协议:数据交换需要遵守的一种协议,按照约定的数据格式等写出去
端口:用于计算机之间的通信
每运行一个程序,系统会分配一个编号给该程序,作为和外界交换数据的唯一标识
0~65535
查看被使用的
JDK1.5 生产消费者
bijian1013
java thread 生产消费者 java多线程
ArrayBlockingQueue:
一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。
ArrayBlockingQueue的常用方法:
JAVA版身份证获取性别、出生日期及年龄
bijian1013
java 性别 出生日期 年龄
工作中需要根据身份证获取性别、出生日期及年龄,且要还要支持15位长度的身份证号码,网上搜索了一下,经过测试好像多少存在点问题,干脆自已写一个。
CertificateNo.java
package com.bijian.study;
import java.util.Calendar;
import
【Java范型六】范型与枚举
bit1129
java
首先,枚举类型的定义不能带有类型参数,所以,不能把枚举类型定义为范型枚举类,例如下面的枚举类定义是有编译错的
public enum EnumGenerics<T> { //编译错,提示枚举不能带有范型参数
OK, ERROR;
public <T> T get(T type) {
return null;
【Nginx五】Nginx常用日志格式含义
bit1129
nginx
1. log_format
1.1 log_format指令用于指定日志的格式,格式:
log_format name(格式名称) type(格式样式)
1.2 如下是一个常用的Nginx日志格式:
log_format main '[$time_local]|$request_time|$status|$body_bytes
Lua 语言 15 分钟快速入门
ronin47
lua 基础
-
-
单行注释
-
-
[[
[多行注释]
-
-
]]
-
-
-
-
-
-
-
-
-
-
-
1.
变量 & 控制流
-
-
-
-
-
-
-
-
-
-
num
=
23
-
-
数字都是双精度
str
=
'aspythonstring'
java-35.求一个矩阵中最大的二维矩阵 ( 元素和最大 )
bylijinnan
java
the idea is from:
http://blog.csdn.net/zhanxinhang/article/details/6731134
public class MaxSubMatrix {
/**see http://blog.csdn.net/zhanxinhang/article/details/6731134
* Q35
求一个矩阵中最大的二维
mongoDB文档型数据库特点
开窍的石头
mongoDB文档型数据库特点
MongoDD: 文档型数据库存储的是Bson文档-->json的二进制
特点:内部是执行引擎是js解释器,把文档转成Bson结构,在查询时转换成js对象。
mongoDB传统型数据库对比
传统类型数据库:结构化数据,定好了表结构后每一个内容符合表结构的。也就是说每一行每一列的数据都是一样的
文档型数据库:不用定好数据结构,
[毕业季节]欢迎广大毕业生加入JAVA程序员的行列
comsci
java
一年一度的毕业季来临了。。。。。。。。
正在投简历的学弟学妹们。。。如果觉得学校推荐的单位和公司不适合自己的兴趣和专业,可以考虑来我们软件行业,做一名职业程序员。。。
软件行业的开发工具中,对初学者最友好的就是JAVA语言了,网络上不仅仅有大量的
PHP操作Excel – PHPExcel 基本用法详解
cuiyadll
PHP Excel
导出excel属性设置//Include classrequire_once('Classes/PHPExcel.php');require_once('Classes/PHPExcel/Writer/Excel2007.php');$objPHPExcel = new PHPExcel();//Set properties 设置文件属性$objPHPExcel->getProperties
IBM Webshpere MQ Client User Issue (MCAUSER)
darrenzhu
IBM jms user MQ MCAUSER
IBM MQ JMS Client去连接远端MQ Server的时候,需要提供User和Password吗?
答案是根据情况而定,取决于所定义的Channel里面的属性Message channel agent user identifier (MCAUSER)的设置。
http://stackoverflow.com/questions/20209429/how-mca-user-i
网线的接法
dcj3sjt126com
一、PC连HUB (直连线)A端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 二、PC连PC (交叉线)A端:(568A): 白绿,绿,白橙,蓝,白蓝,橙,白棕,棕; B端:(标准568B):白橙,橙,白绿,蓝,白蓝,绿,白棕,棕。 三、HUB连HUB&nb
Vimium插件让键盘党像操作Vim一样操作Chrome
dcj3sjt126com
chrome vim
什么是键盘党?
键盘党是指尽可能将所有电脑操作用键盘来完成,而不去动鼠标的人。鼠标应该说是新手们的最爱,很直观,指哪点哪,很听话!不过常常使用电脑的人,如果一直使用鼠标的话,手会发酸,因为操作鼠标的时候,手臂不是在一个自然的状态,臂肌会处于绷紧状态。而使用键盘则双手是放松状态,只有手指在动。而且尽量少的从鼠标移动到键盘来回操作,也省不少事。
在chrome里安装 vimium 插件
MongoDB查询(2)——数组查询[六]
eksliang
mongodb MongoDB查询数组
MongoDB查询数组
转载请出自出处:http://eksliang.iteye.com/blog/2177292 一、概述
MongoDB查询数组与查询标量值是一样的,例如,有一个水果列表,如下所示:
> db.food.find()
{ "_id" : "001", "fruits" : [ "苹
cordova读写文件(1)
gundumw100
JavaScript Cordova
使用cordova可以很方便的在手机sdcard中读写文件。
首先需要安装cordova插件:file
命令为:
cordova plugin add org.apache.cordova.file
然后就可以读写文件了,这里我先是写入一个文件,具体的JS代码为:
var datas=null;//datas need write
var directory=&
HTML5 FormData 进行文件jquery ajax 上传 到又拍云
ileson
jquery Ajax html5 FormData
html5 新东西:FormData 可以提交二进制数据。
页面test.html
<!DOCTYPE>
<html>
<head>
<title> formdata file jquery ajax upload</title>
</head>
<body>
<
swift appearanceWhenContainedIn:(version1.2 xcode6.4)
啸笑天
version
swift1.2中没有oc中对应的方法:
+ (instancetype)appearanceWhenContainedIn:(Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION;
解决方法:
在swift项目中新建oc类如下:
#import &
java实现SMTP邮件服务器
macroli
java 编程
电子邮件传递可以由多种协议来实现。目前,在Internet 网上最流行的三种电子邮件协议是SMTP、POP3 和 IMAP,下面分别简单介绍。
◆ SMTP 协议
简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)是一个运行在TCP/IP之上的协议,用它发送和接收电子邮件。SMTP 服务器在默认端口25上监听。SMTP客户使用一组简单的、基于文本的
mongodb group by having where 查询sql
qiaolevip
每天进步一点点 学习永无止境 mongo 纵观千象
SELECT cust_id,
SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
HAVING total > 250
db.orders.aggregate( [
{ $match: { status: 'A' } },
{
$group: {
Struts2 Pojo(六)
Luob.
POJO strust2
注意:附件中有完整案例
1.采用POJO对象的方法进行赋值和传值
2.web配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee&q
struts2步骤
wuai
struts
1、添加jar包
2、在web.xml中配置过滤器
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.st