第5讲:建立自己的C函数库,js调用自己写的C/C++函数,并包含依赖C/C++第三方静态库。

在javascript中,Array有很多内置的功能,比如Array.map,Array.filter,Array.find等等,能用内置的功能就用内置的功能,最好不要自己实现一套,因为底层调用的可能压根就不是js语言本身,底层的实现可能由C/C++实现的。如果我们要做的一些功能,需要高性能密集计算,但是JavaScript内置函数无法满足我们要求的时候,这时候我们就要自己用C/C++编写一个程序,然后封装成wasm文件给JavaScript调用了,此时wasm还包含了.a文件这样的第三方库。

我们这里有个需求,就是在地球上有两艘船,船A和船B在某个经纬度位置触发,以某个航向、速度行驶,求它们间最小距离是多少,达到最小距离的时候,经过时间是多少秒?
首先这个功能用C/C++来编写,并且还要用到开源第三方库。
下图的红圈注释里面有几个参数,分别表示经度、纬度、速度、航向,当然getCPA最后一个参数6.5表示6.5分钟的时间长度。表示计算6.5分钟以内,两船最小距离是多少,并且到达最小距离时,经过时间是多少。
在这里插入图片描述

打开Visual Studio 2022,新建一个cmake工程,项目名称为GeoCompute。这仅仅是一个测试项目,如果测试通过,没有问题了,就把该代码交给emcc或者em++去编译。
CMakeLists.txt文件内容如下:
在这里,我采用vcpkg来安装GeographicLib库
可以执行如下命令安装。

vcpkg install GeographicLib:x64-windows
# CMakeList.txt: GeoCompute 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
set(VCPKG_ROOT "D:/CppPkg/WinVcpkg/vcpkg" CACHE PATH "")
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)cmake_policy(SET CMP0141 NEW)set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()project ("GeoCompute")# 将源代码添加到此项目的可执行文件。
add_executable (GeoCompute "GeoCompute.cpp"  )find_package (GeographicLib CONFIG REQUIRED)
target_link_libraries (GeoCompute PRIVATE ${GeographicLib_LIBRARIES})if (CMAKE_VERSION VERSION_GREATER 3.12)set_property(TARGET GeoCompute PROPERTY CXX_STANDARD 20)
endif()# TODO: 如有需要,请添加测试并安装目标。

然后编写一个GeoCompute.cpp文件

#include <iostream>
#include <GeographicLib/Geodesic.hpp>
#include <GeographicLib/Constants.hpp>
#include <cmath>
#include <vector>const double EARTH_RADIUS = 6377830.0;  // 地球的平均半径,单位为千米
const double M_PI = 3.14159265359;struct LatLon {double first;double second;
};double deg2rad(double deg) {return deg * M_PI / 180.0;
}double haversine_distance(double lat1, double lon1, double lat2, double lon2) {double dlat = deg2rad(lat2 - lat1);double dlon = deg2rad(lon2 - lon1);double a = std::sin(dlat / 2) * std::sin(dlat / 2) +std::cos(deg2rad(lat1)) * std::cos(deg2rad(lat2)) *std::sin(dlon / 2) * std::sin(dlon / 2);double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a));return EARTH_RADIUS * c;
}LatLon new_position_with_geolib(double lat, double lon, double speed, double cog, double T) {const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84();double s12 = speed * T;double lat2, lon2;// Direct method gives the destination point given start point, initial azimuth, and distancegeod.Direct(lat, lon, cog, s12, lat2, lon2);return { lat2, lon2 };
}double new_distance(double T, double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB) {auto resA = new_position_with_geolib(latA, lonA, speedA, cogA, T);auto resB = new_position_with_geolib(latB, lonB, speedB, cogB, T);return haversine_distance(resA.first, resA.second, resB.first, resB.second);
}LatLon getCPA(double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB, double tcpa) {double RES_TCPA = INFINITY;double RES_DCPA = INFINITY;double prev_dist = INFINITY;double cur_dist = INFINITY;std::vector<int> status;int t_lim = tcpa * 60;int step = 1;if (t_lim > 600) {step = int(double(t_lim) / 300.0);}for (int t = 0;t < t_lim; t += step) {prev_dist = new_distance(t, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);cur_dist = new_distance(t + step, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);if (prev_dist < RES_DCPA) {RES_DCPA = prev_dist;}if (cur_dist - prev_dist <= 0) {if (status.size() == 0) {status.emplace_back(-1);}}else {if (status.size() == 0) {status.emplace_back(1);break;}else {if (status[0] == -1) {status.emplace_back(1);}}}if (status.size() == 2 && status[0] == -1 && status[1] == 1) {RES_TCPA = t;break;}}return { RES_TCPA, RES_DCPA };
}//1.  一开始距离就变大
// 2. 从0时刻到指定tcpa一直减小
// 3. 从0时刻到指定tcpa,先减小后增大
int main() {double lat = 40, lon = 100, speed = 10, cog = 45, T = 3600;auto result = new_position_with_geolib(lat, lon, speed, cog, T);std::cout << "New Latitude: " << result.first << ", New Longitude: " << result.second << std::endl;/*latA, lonA, speedA, cogA = 21.3058, 109.1014, 15.12, 187.13latB, lonB, speedB, cogB = 21.288205, 109.118725, 3.909777, 254.42*/int i = 0;while (true) {// 先减小后增大auto res_ = getCPA(21.3058, 109.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.5);//auto res_ = getCPA(22.3058, 108.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.0);//auto res_ = getCPA(0.0, 0.0, 15.12, 225.0, 0.0000001, 0.0000001, 3.909777, 45.0, 6.0);std::cout << res_.first << " --- " << res_.second << std::endl;i++;printf("%d\n", i);}return 0;
}

好了,如果代码测试完成了。现在我们创建一个cmake工程,项目名为EmscriptenTest,这是用来生成wasm文件和js文件来给JavaScript调用的。

由于JavaScript运行在浏览器,不能直接支持windows的lib静态库,所以想办法得到.a库。
首先从github拉取geographiclib库的源码

 git clone https://github.com/geographiclib/geographiclib.git

然后进入到根目录:

cd geographiclib

最后用emcmake和emmake命令编译代码(前提是要安装emsdk:官网有教程说明:https://emscripten.org/docs/getting_started/downloads.html)

emcmake cmake .
emmake make

编译完成之后:
![(https://img-blog.csdnimg.cn/direct/3e76d9aba3834e01b2894e163ee5fcc8.png)

转到目录geographiclib/src
找到libGeographicLib.a文件,然后把该文件拷贝到EmscriptenTest项目的lib目录下(如果没有lib目录则自己新建)

然后是CMakeLists.txt文件:

# CMakeList.txt: EmscriptenTest 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
set(CMAKE_TOOLCHAIN_FILE "D:/CppPkg/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake")
# 手动设置GeographicLib的路径
set(GEOGRAPHICLIB_INCLUDE_DIR "/path/to/vcpkg/installed/x64-windows/include")
#set(GEOGRAPHICLIB_LIB_DIR "/path/to/vcpkg/installed/x64-windows/lib")
set(GEOGRAPHICLIB_LIB_DIR "填写你的EmscriptenTest/lib目录的绝对路径")# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)cmake_policy(SET CMP0141 NEW)set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()project ("EmscriptenTest")# 将源代码添加到此项目的可执行文件。
add_executable (EmscriptenTest "GeoCompute.cpp")include_directories(D:/CppPkg/WinVcpkg/vcpkg/installed/x64-windows/include )
target_link_libraries(EmscriptenTest ${GEOGRAPHICLIB_LIB_DIR}/libGeographicLib.a)add_library(GeoCompute STATIC GeoCompute.cpp)
set_target_properties(GeoCompute PROPERTIES SUFFIX ".wasm")
set_target_properties(GeoCompute PROPERTIES LINK_FLAGS "--bind -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME='GeoComputeModule' -s EXPORTED_FUNCTIONS='[\"getCPA\"]'")if (CMAKE_VERSION VERSION_GREATER 3.12)set_property(TARGET EmscriptenTest PROPERTY CXX_STANDARD 20)
endif()# TODO: 如有需要,请添加测试并安装目标。

这个EmscriptenTest.cpp没什么用,里面写个main函数,直接return 0;就完事儿了。
在项目根目录下新建GeoCompute.cpp文件
GeoCompute.cpp文件的内容:
可能会提示报错,但是如果点击重新生成,生成成功的话,是没事儿的。
在这个文件中,我修改了getCPA函数的返回类型为double* , 因为直接返回TDCPA结构体,JavaScript是无法识别的,一定要返回一个指针。

#include <iostream>
#include <GeographicLib/Geodesic.hpp>
#include <GeographicLib/Constants.hpp>
#include <cmath>
#include <vector>
#include <emscripten/emscripten.h>
const double EARTH_RADIUS = 6377830.0;  // 地球的平均半径,单位为千米
const double _M_PI = 3.14159265359;struct LatLon {double lat;double lon;
};struct TDCPA {double res_tcpa;double res_dcpa;
};extern "C" {EMSCRIPTEN_KEEPALIVEdouble deg2rad(double deg) {return deg * _M_PI / 180.0;}EMSCRIPTEN_KEEPALIVEdouble haversine_distance(double lat1, double lon1, double lat2, double lon2) {double dlat = deg2rad(lat2 - lat1);double dlon = deg2rad(lon2 - lon1);double a = std::sin(dlat / 2) * std::sin(dlat / 2) +std::cos(deg2rad(lat1)) * std::cos(deg2rad(lat2)) *std::sin(dlon / 2) * std::sin(dlon / 2);double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a));return EARTH_RADIUS * c;}EMSCRIPTEN_KEEPALIVELatLon new_position_with_geolib(double lat, double lon, double speed, double cog, double T) {const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84();double s12 = speed * T;double lat2, lon2;// Direct method gives the destination point given start point, initial azimuth, and distancegeod.Direct(lat, lon, cog, s12, lat2, lon2);return { lat2, lon2 };}EMSCRIPTEN_KEEPALIVEdouble new_distance(double T, double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB) {auto resA = new_position_with_geolib(latA, lonA, speedA, cogA, T);auto resB = new_position_with_geolib(latB, lonB, speedB, cogB, T);return haversine_distance(resA.lat, resA.lon, resB.lat, resB.lon);}EMSCRIPTEN_KEEPALIVEdouble* getCPA(double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB, double tcpa) {double RES_TCPA = INFINITY;double RES_DCPA = INFINITY;double prev_dist = INFINITY;double cur_dist = INFINITY;double* tdcpaPtr = new double[2];std::vector<int> status;int t_lim = tcpa * 60;int step = 1;if (t_lim > 600) {step = int(double(t_lim) / 300.0);}for (int t = 0; t < t_lim; t += step) {prev_dist = new_distance(t, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);cur_dist = new_distance(t + step, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);if (prev_dist < RES_DCPA) {RES_DCPA = prev_dist;}if (cur_dist - prev_dist <= 0) {if (status.size() == 0) {status.emplace_back(-1);}}else {if (status.size() == 0) {status.emplace_back(1);break;}else {if (status[0] == -1) {status.emplace_back(1);}}}if (status.size() == 2 && status[0] == -1 && status[1] == 1) {RES_TCPA = t;break;}}tdcpaPtr[0] = RES_TCPA;tdcpaPtr[1] = RES_DCPA;return tdcpaPtr;}}

好了,现在就测试全部重新生成。如果没有报错,则通过测试。通过测试之后。证明可以大胆使用em++命令直接编译GeoCompute.cpp为wasm文件了。

打开cmd进入到EmscriptenTest工程根目录。

em++ GeoCompute.cpp -O1 -o libGeoCompute.js -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="GeoComputeModule" -s "EXPORTED_FUNCTIONS=['_deg2rad', '_haversine_distance', '_new_position_with_geolib', '_new_distance', '_getCPA']" -s "EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -I D:/CppPkg/WinVcpkg/vcpkg/installed/x64-windows/include -L D:/HighPerformanceProjects/CppProjects/EmscriptenTest/lib -lGeographicLib

我这里导出了所有的函数,函数前面要加上下划线。但是在cmakelists.txt中不需要加下划线。
执行完成命令之后,会生成libGeoCompute.js和libGeoCompute.wasm文件。

在这里插入图片描述

现在可以使用npm创建一个原生的JavaScript项目。
这是工程目录结构:
在这里插入图片描述
最重要是main.js的写法:

const fs = require('fs');
const path = require('path');// 加载 Emscripten 生成的模块
const Module = require('./libGeoCompute.js');async function main() {try {const instance = await Module({locateFile: (filename) => path.join(__dirname, filename),});console.log('Module instance:', instance);console.log(typeof instance.cwrap)// await new Promise((resolve) => {//   console.log('Waiting for runtime initialization...');//   instance.onRuntimeInitialized = () => {//     console.log('Runtime initialized.');//     resolve();//   };// });// 使用 cwrap 包装 getCPA 函数const getCPA = instance.cwrap('getCPA', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number']);console.log(typeof getCPA)// 调用导出的 getCPA 函数const resultPtr = getCPA(21.3058, 109.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.5);console.log(typeof resultPtr)// 解析返回值(假设返回指向结构体的指针)const res_tcpa = instance.HEAPF64[resultPtr >> 3]; // 读取double指针的第0个元素const res_dcpa = instance.HEAPF64[(resultPtr >> 3) + 1]; // 读取地址偏移量+1console.log('TCPA:', res_tcpa);console.log('DCPA:', res_dcpa);console.log('Continuing after runtime initialization.');// 继续下面的逻辑...} catch (error) {console.error('Error:', error);}
}main();

index.html的内容:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Blank Window</title>
</head>
<body><!-- 这里可以添加窗体的内容 -->
</body>
</html>

package.json的内容:

{"name": "nodedevtest","version": "1.0.0","description": "A minimal Electron application","main": "main.js","scripts": {"start": "node main.js"},"keywords": [],"author": "","license": "ISC","dependencies": {"axios": "^1.6.8","electron": "^30.0.1","pixi.js": "^8.1.0","request": "^2.88.2"}
}

得出的结果:
在这里插入图片描述

得出的结果是154和1519.5687501879786
表示经过154秒以后,两船达到最小距离,并且最小距离为1519米多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/37226.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Wails 安装初体验

文章目录 Wails 安装说明1. 系统要求2. 安装步骤3. 构建应用 结论 Wails 安装说明 Wails 是一个用于构建桌面应用的 Go 框架&#xff0c;结合了现代前端技术。以下是安装步骤&#xff1a; 1. 系统要求 Go 1.16 或更高版本Node.js 和 npm可选&#xff1a;适用于 Windows、mac…

【机器学习】机器学习的重要方法——强化学习:理论,方法与实践

目录 一、强化学习的核心概念 二、强化学习算法的分类与示例代码 三.强化学习的优势 四.强化学习的应用与挑战 五、总结与展望 强化学习&#xff1a;理论&#xff0c;方法和实践 在人工智能的广阔领域中&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&…

基于源码详解ThreadPoolExecutor实现原理

个人博客地址 基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog 内容拆分 这里算是一个总集&#xff0c;内容太多&#xff0c;拆分成几个比较重要的小的模块&#xff1a; ThreadPoolExecutor基于ctl变量的声明周期管理 | iwts’s blog ThreadPoolExecutor 工作线程…

模板方法模式在金融业务中的应用及其框架实现

引言 模板方法模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它在一个方法中定义一个算法的框架&#xff0c;而将一些步骤的实现延迟到子类中。模板方法允许子类在不改变算法结构的情况下重新定义算法的某些步骤。在金融业务中&#xff…

可信和可解释的大语言模型推理-RoG

大型语言模型&#xff08;LLM&#xff09;在复杂任务中表现出令人印象深刻的推理能力。然而&#xff0c;LLM在推理过程中缺乏最新的知识和经验&#xff0c;这可能导致不正确的推理过程&#xff0c;降低他们的表现和可信度。知识图谱(Knowledge graphs, KGs)以结构化的形式存储了…

Python变量的命名规则与赋值方式

第二章&#xff1a;Python 基础语法 第一节&#xff1a;变量的命名规则与赋值方式 2.1.1 引言 在编程中&#xff0c;变量是存储数据的基本单元。变量的命名和赋值是编程语言中表达和操作数据的基础。了解和遵循变量命名规则对于编写清晰、可维护的代码至关重要。 2.1.2 变量…

【linux】网络基础(1)

文章目录 网络基本概念网络的定义网络的类型局域网&#xff08;LAN&#xff09;广域网&#xff08;WAN&#xff09; 网络协议OSI七层模型TCP/IP模型TCP/IP模型的结构 网络传输的基本流程计算机与计算机之间的通信计算机的信息处理封装报头 网络基本概念 网络的定义 1.网络是指…

专题一: Spring生态初探

咱们先从整体脉络上看下Spring有哪些模块&#xff0c;重要的概念有个直观印象。 从Spring框架的整体架构和组成对整体框架有个认知。 Spring框架基础概念 Spring基础 - Spring和Spring框架组成 上图是从官网4.2.x获取的原图&#xff0c;目前我们使用最广法的版本应该都是5.x&am…

一区算法MPA|海洋捕食者算法原理及其代码实现(Matlab/Python))

Matlab/Python&#xff1a; 本文KAU将介绍一个2020年发表在1区期刊ESWA上的优化算法——海洋捕食者算法 (Marine Predators Algorithm&#xff0c;MPA)[1] 该算法由Faramarzi等于2020年提出&#xff0c;其灵感来源于海洋捕食者之间不同的觅食策略、最佳相遇概率策略、海洋记…

【Linux】IO多路复用——select,poll,epoll的概念和使用,三种模型的特点和优缺点,epoll的工作模式

文章目录 Linux多路复用1. select1.1 select的概念1.2 select的函数使用1.3 select的优缺点 2. poll2.1 poll的概念2.2 poll的函数使用2.3 poll的优缺点 3. epoll3.1 epoll的概念3.2 epoll的函数使用3.3 epoll的优点3.4 epoll工作模式 Linux多路复用 IO多路复用是一种操作系统的…

MCU复位时GPIO是什么状态?

大家一定遇到过上电或者复位时外部的MOS电路或者芯片使能信号意外开启&#xff0c;至此有经验的工程师就会经常关心一个问题&#xff0c;MCU复位时GPIO是什么状态&#xff1f;什么电路需要外部加上下拉&#xff1f; MCU从上电到启动&#xff0c;实际可分为复位前和复位后、初始…

【WPF】Windows系统桌面应用程序编程开发新手入门-打造自己的小工具

电脑Windows系统上的桌面程序通常是用Visual Studio 开发工具编写出来的&#xff0c;有两种开发方式供选择&#xff0c;一种是WindowForm&#xff0c;简称WinForm&#xff0c;另一种是Windows Presentation Foundation&#xff0c;简称WPF&#xff0c;这里将学习WPF项目。 文章…

大物3错题整理

平衡位置&#xff1a;在O点上的位置 相位&#xff1a; 当N很大的时候&#xff0c;wxwywz。因此&#xff0c;平均平动动能除以3&#xff0c;就是能量均分定理。 W F在x上的积分 Π时无单位 180&#xff0c;就是单位 1rad&#xff0c;rad就是单位 左手定则、右手定则、安培定…

如何解决三菱软件提示 起动MELSOFT Mediative Server失败

前言&#xff1a; 注意&#xff0c;这篇文章仅针对如何解决 起动MELSOFT Mediative Server失败 的问题。对于其他相关的问题&#xff0c;请搜索其他相应的解决办法。 本人是在重装三菱GX Works软件时遇到此问题的。后来搜索发现无人能妥善的关闭这个提示。因此本文介绍如何关…

【Web3项目案例】Ethers.js极简入门+实战案例:实现ERC20协议代币查询、交易

苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏 ←持续记录本人自学智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 目录 简介 前景科普-ERC20 Ethers极简入门教程&#xff1a;HelloVitalik&#xff08;非小白可跳&#xff09; 教程概览 开发工具 V…

魔行观察-烤匠麻辣烤鱼-开关店监测-时间段:2011年1月 至 2024年6月

今日监测对象&#xff1a;烤匠麻辣烤鱼&#xff0c;监测时间段&#xff1a;2011年1月 至 2024年6月 本文用到数据源获取地址 魔行观察http://www.wmomo.com/ 品牌介绍&#xff1a; 2013年&#xff0c;第一家烤匠在成都蓝色加勒比广场开业&#xff0c;随后几年成都国金中心店…

超详细的tomcat安装以及简略项目的部署

一、安装包 安装路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1JzPQQ2zUdnXi_FaTTG0pvg?pwdriht 提取码&#xff1a;riht 安装完之后我们打开&#xff0c;可看见以下目录结构 二、环境变量配置 首先打开我们电脑的高级环境变量配置 我们先配置一个系统变量…

Variables Reference for vscode

Predefined variables Visual Studio Code 支持在调试、任务配置文件以及一些特定的设置中使用变量替换。这些变量可以使用 ${variableName} 语法在 launch.json 和 tasks.json 文件的某些键和值字符串中使用。 Predefined variables Visual Studio Code 支持以下预定义变量…

Zookeeper:Zookeeper JavaAPI操作与分布式锁

文章目录 一、Zookeeper JavaAPI操作1、Curator介绍2、创建、查询、修改、删除节点3、Watch事件监听 二、Zookeeper分布式锁原理 一、Zookeeper JavaAPI操作 1、Curator介绍 Curator是Apache Zookeeper的Java客户端。常见的Zookeeper Java API&#xff1a; 原生Java API。ZkC…

天气网站爬虫及可视化

摘要&#xff1a;随着互联网的快速发展&#xff0c;人们对天气信息的需求也越来越高。本论文基于Python语言&#xff0c;设计并实现了一个天气网站爬虫及可视化系统。该系统通过网络爬虫技术从多个天气网站上获取实时的天气数据&#xff0c;并将数据进行清洗和存储。同时&#…