南阳网站建设制作价格/电商网站项目
零、写在前面
HBuilderX是DCloud旗下的IDE产品,目前只提供了Windows和Mac版本使用。本项目组在开发阶段经常需要向测试环境提交热更新包,使用Jenkins进行CD是非常有必要的一步。尽管HBuilderX提供了CLI,但Jenkins服务通常都是搭建在Linux环境下的。当前的Uniapp wgt打包服务是使用了Windows Server + HBuilderX CLI的解决方案来进行打包,再用Jenkins远程调用接口。这套方案的弊病有如下几点:
- Jenkins侧仅负责少量参数的传递,如项目名、Git repo地址、分支名等,大部分流程不受控制,流水线的构建阶段显示不透明。
- 核心由一个shell script和一个python脚本实现,代码逻辑存在一定重复,维护难度也比较高。
- 从Git更新代码的流程耗费较长时间,因为每次执行流水线都要删除掉本地的repo并重新拉取。这对于带宽只有个位数的公网测试服务器来说是致命的,每次构建花费在此步的时间就有2分钟以上。
- 后端平台侧获取的Token没有缓存,即使打包提速了也会受到验证码获取1分钟节流的限制。
- Windows服务器上运行的HBuilderX经常出现登录态失效或启动打包任务失败的情况,测试在Jenkins侧只能得到简单的任务失败提示。而且这个提示是出现在Git拉取代码之后的,意味着每次失败前都要干等2分钟。
为了避免服务器资源的浪费,节省不必要的维护开支,我决定在等待测试的gap期研究一下这套流程的优(chong)化(gou)。
一、初次优化-Windows下脱离HBuilderX主程序
打包指令提取
首先从Windows端下手,先打开HBuilderX并登录账号,正常打一个wgt包。在打包前使用DebugView
来查看HBuilderX执行任务时的输出,这是一款用于捕获Windows桌面系统程序中由TRACE和OutputDebugString输出的信息的工具。抓取到的有效日志如下:
[16764] 2023-05-16 09:50:20.583 [INFO:] node "D:/HBuilderX/plugins/node/node.exe"
[16764] 2023-05-16 09:50:20.583 [INFO:] args ("--max-old-space-size=2048", "--no-warnings", "D:/HBuilderX/plugins/uniapp-cli-vite/node_modules/@dcloudio/vite-plugin-uni/bin/uni.js")
盘一下uni.js
require('../dist/cli/index.js')
可以看到备注很完善的函数cli
,这证明Uniapp打包使用的工具就是由node
构建的,原则上来说可以不受系统平台的限制,移植到Linux上使用是没有问题的。至于HBuilderX软件本体就是Qt写的一个壳子,只要剖析整个打包流程就可以脱离HBuilderX本体了。
cli.command('build').option('--outDir <dir>', `[string] output directory (default: dist)`).option('--assetsInlineLimit <number>', `[number] static asset base64 inline threshold in bytes (default: 4096)`).option('--sourcemap', `[boolean] output source maps for build (default: false)`).option('--manifest', `[boolean] emit build manifest json`).option('--ssrManifest', `[boolean] emit ssr manifest json`).option('--emptyOutDir', `[boolean] force empty outDir when it's outside of root`, {default: true,
}).option('-w, --watch', `[boolean] rebuilds when modules have changed on disk`).action(action_1.runBuild);
根据参数表整理一下调用命令:
node --max-old-space-size=2048 --no-warnings "D:/HBuilderX/plugins/uniapp-cli-vite/node_modules/@dcloudio/vite-plugin-uni/bin/uni.js" build --platform app --outDir "D:/test/crp-app-dist"
直接执行上述命令是不行的,因为uniapp的node_modules下根本就没有编译该工程所需的各种依赖,并且缺少很多环境变量。具体逻辑在uniapp-cli-vite/node_modules/@dcloudio/vite-plugin-uni
的/dist/cli/build.js
和/dist/index.js
中都有所表现。
显而易见的是,我们的工程目录下(非cli模式)根本就没有vue
、pinia
之类的包,那么上面的命令是如何确定工作目录(工程目录)和其它依赖所在地的呢?先说其它依赖的问题,分析plugins
目录可以看到HBuilderX自带了一套node
和npm
,那么去看一下npm
的脚本(在Windows版本叫做npm.cmd
):
#!/bin/sh
(set -o igncr) 2>/dev/null && set -o igncr; # cygwin encoding fix
basepath=$(cd `dirname $0`; pwd)
plugin_dir=$(dirname $basepath)which "node" >/dev/null 2>&1
if ! [ $? -eq 0 ]; thennode_Path=$plugin_dir/nodenew_path=$PATH:$node_Pathexport PATH=$new_path
fi
$basepath/node_modules/npm/bin/npm-cli.js $@
环境变量修补
HBuilderX在打包阶段会使用自己的node
和npm
,我们要做的就是修补环境变量使得这些工具都能找到正确的工作目录。
查看打包前的环境变量也很简单,既然调用栈是uniapp-cli-vite/node_modules/@dcloudio/vite-plugin-uni/bin/uni.js
,那么直接在uni.js
的尾部追加console.log(process.env)
后再次在HBuilderX里执行打包,记录控制台输出并去除无关的环境变量即可。
以下是一个使用node.js
成功在Windows
下脱离HbuilderX
主程序调用打包的例子:
/*** @author myd*/
import {exec} from "child_process";
import * as util from "util";
import path from "path";
import os from "os";const execAsync = util.promisify(exec);
export const build = (repoName: string) => {const systemTempFolderPath = os.tmpdir();return new Promise(async (resolve, reject) => {try {const HBUILDER_DIR = "D:\\HBuilderX";const UNI_INPUT_DIR = path.join(systemTempFolderPath, repoName);const VITE_ROOT_DIR = UNI_INPUT_DIR;const UNI_HBUILDERX_PLUGINS = path.join(HBUILDER_DIR, 'plugins');const UNI_CLI_CONTEXT = path.join(UNI_HBUILDERX_PLUGINS, 'uniapp-cli-vite');const UNI_NPM_DIR = path.join(UNI_HBUILDERX_PLUGINS, 'npm');const UNI_NODE_DIR = path.join(UNI_HBUILDERX_PLUGINS, 'node');const NODE_ENV: any = 'production';const NODE = path.join(UNI_NODE_DIR, 'node');const UNI_CLI = path.join(UNI_CLI_CONTEXT, 'node_modules', '@dcloudio', 'vite-plugin-uni', 'bin', 'uni.js');const PATH_ADDONS = process.env.PATH + `;${UNI_INPUT_DIR}/node_modules/.bin;`;const childEnv = {...process.env,PATH: PATH_ADDONS,HBUILDER_DIR,UNI_INPUT_DIR,VITE_ROOT_DIR,UNI_CLI_CONTEXT,UNI_HBUILDERX_PLUGINS,UNI_NPM_DIR,UNI_NODE_DIR,NODE_ENV,NODE};process.chdir(UNI_CLI_CONTEXT);const buildCommand = `"${NODE}" --max-old-space-size=2048 --no-warnings "${UNI_CLI}" build --platform app --outDir ${path.join(systemTempFolderPath, repoName + '-dist')}`const {stdout, stderr} = await execAsync(buildCommand, {env: {...childEnv}});console.error('stderr:', stderr);resolve(1)} catch (error) {console.error('Error during build:', error);reject(0)}})
}
产物是一个文件夹,把这个文件夹以zip
格式压缩后,将后缀重命名为wgt
即可。
做到这一步后,我就用Next.js
写了一个简单的GUI,并配合一系列辅助逻辑完成了beta版的新构建平台,部署到先前的Windows服务器上提供给同事进行测试。这样做的目的主要是为了验证以上工作的正确性和稳定性,还可以从反馈意见中思考一下我对于该流程的重构设想是否正确,还有哪些点没有考虑到。
二、第二次优化-移植到Linux上并集成回Jenkins
思考
首先明确一点,这件事本身就是一个内部需求的解决方案延伸,闭门造车是不可取的。当做出一个阶段性的工作后,立刻部署demo并持续收集同事的反馈意见,及时调整,这样才能避免后续的更多问题,因为最终用户不止是我本人,还有所有参与项目的其他同事。软件的易用性和稳定性同样重要,必须在正式部署前反复地进行预先测试才能推行使用。
在测试了一天后,同事认为打包速度大大提高,但简陋的web控制台不能同时执行多个打包任务,打包期间不能刷新或离开页面也是硬伤。当时我还没有想好如何把这套代码搬到Linux上跑,因为这涉及HBuilderX的平台差异问题。将这套代码的构建核心抽出来集成回Jenkins是最佳选择,但我当时是有一些偷懒的想法的,因为这套轮子也同样提供了工程选择、Git同步的处理逻辑等,已经解决了上面的大部分痛点。但轮子毕竟是轮子,最终我还是决定放弃推行自己的平台给大家使用的想法,而是以这个平台作为测试工具,在此之上研究将HBuilderX的打包器迁移到Linux上的方法。
uniapp-cli-vite
承担了大部分打包功能,按理说它作为一个node
包本来是不挑系统的。但我陷入了思维上的误区,一定要迁移node
和npm
再给plugins
搬家。实际上只要node
环境隔离得当,只迁移npm
即可。而对于npm
来说,Linux和Windows的npm
结构差异较大,但几乎可以直接使用macOS的包。所以迁移工作我选择在macOS平台下研究。
编写脚本&部署
明确一下思路,只要在macOS下写一个接受工程目录的路径、打包产物的路径和HBuilderX plugins的路径,输出打包产物的脚本并测试成功,那么就算成功了80%了。把上面的node
函数用GPT转成sh脚本,自己再微调一下:
#!/bin/bash# HBuilder目录修改此处
HBUILDER_DIR=/root/HBuilderX
NODE_ENV=production
repoDir=$1
# 导出目录修改此处
distExportDir=$2
# Nodejs修改此处
NODE=/root/HBuilderX/plugins/node/nodeUNI_INPUT_DIR="$repoDir"
VITE_ROOT_DIR="$UNI_INPUT_DIR"
UNI_HBUILDERX_PLUGINS="$HBUILDER_DIR/plugins"
UNI_CLI_CONTEXT="$UNI_HBUILDERX_PLUGINS/uniapp-cli-vite"
UNI_NPM_DIR="$UNI_HBUILDERX_PLUGINS/npm"
UNI_NODE_DIR="$UNI_HBUILDERX_PLUGINS/node"
UNI_CLI="$UNI_CLI_CONTEXT/node_modules/@dcloudio/vite-plugin-uni/bin/uni.js"export HBUILDER_DIR
export UNI_INPUT_DIR
export VITE_ROOT_DIR
export UNI_CLI_CONTEXT
export UNI_HBUILDERX_PLUGINS
export UNI_NPM_DIR
export UNI_NODE_DIR
export NODE_ENV
export NODE
export PATH="$PATH:$UNI_INPUT_DIR/node_modules/.bin"cd "$UNI_CLI_CONTEXT"
buildCommand="$NODE --max-old-space-size=2048 --no-warnings $UNI_CLI build --platform app --outDir $distExportDir/${repoDir}-dist"
eval $buildCommand
exitCode=$?
if [ $exitCode -eq 0 ]; thenecho "Build successful"exit 1
elseecho "Error during build"exit 0
fi
效果很好,先把HBuilderX的主目录打包为tar并传到Linux服务器上展开:
tar -cf ~/HbuilderX-3.9.5-darwin.tar /Applications/HBuilderX.app/Contents/HBuilderX
scp -P 22 -r ~/HbuilderX-3.9.5-darwin.tar root@192.168.1.252:/root/
tar -xf ./HbuilderX-3.9.5-darwin.tar
在macOS机器上看一下HBuilderX
使用的Node版本:
myd@myddeMac-Pro ~ % /Applications/HBuilderX.app/Contents/HBuilderX/plugins/node/node -v
v16.17.0
为Linux服务器下载对应系统和架构的Node二进制包并覆盖HBuilderX所使用的node,这里以Linux-amd64-16.17.0
为例:
wget https://nodejs.org/download/release/v16.17.0/node-v16.17.0-linux-x64.tar.gz
tar -xzvf ./node-v16.17.0-linux-x64.tar.gz
cp ./node-v16.17.0-linux-x64/bin/node ./HBuilderX/plugins/node
chmod +x ~/HBuilderX/plugins/node/node
更多版本可以在https://nodejs.org/download/release/
查看。
公司服务器的Ubuntu 18.04
缺少node 18的依赖glibc-2.28
,因此需要进一步对系统环境进行修补。编译glibc-2.28
:
sudo apt-get install g++ make gcc bison
apt install -y gawk
cd ~
wget -c https://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz
tar -zxf glibc-2.28.tar.gz
cd glibc-2.28
mkdir glibc-build
cd glibc-build
../configure --prefix=/opt/glibc-2.28
make -j 6
make install
cd ~
rm -rf ./glibc-2.28 ./glibc-2.28.tar.gz
apt install -y patchelf
# 直装的node使用如下命令
patchelf --set-interpreter /opt/glibc-2.28/lib/ld-linux-x86-64.so.2 --set-rpath /opt/glibc-2.28/lib/:/lib/x86_64-linux-gnu/:/usr/lib/x86_64-linux-gnu/ /usr/local/bin/node
# nvm使用如下命令
patchelf --set-interpreter /opt/glibc-2.28/lib/ld-linux-x86-64.so.2 --set-rpath /opt/glibc-2.28/lib/:/lib/x86_64-linux-gnu/:/usr/lib/x86_64-linux-gnu/ /root/.nvm/versions/node/v18.18.2/bin/node
# 记得修补HBuilderX的node
patchelf --set-interpreter /opt/glibc-2.28/lib/ld-linux-x86-64.so.2 --set-rpath /opt/glibc-2.28/lib/:/lib/x86_64-linux-gnu/:/usr/lib/x86_64-linux-gnu/ ~/HBuilderX/plugins/node/node
接下来修改uniapp-cli-vite
的package.json
vi /root/HBuilderX/plugins/uniapp-cli-vite/package.json
删除devDependencies
节点下的@esbuild/darwin-arm64
、@esbuild/darwin-x64
和fsevents
,并安装对应目标平台的esbuild
:
npm i -D -f @esbuild/linux-x64@0.17.19
npm i -f
领导非常支持这件事,抽出时间将上面的脚本集成回了Jenkins。我也将之前对于几个痛点的思考和解决方案提供了出来,至此整个wgt打包流程得到了巨大的优化,无论是速度还是稳定性。
三、第三次优化-封装Docker进行环境隔离
思考
在第二次优化的过程中,因为出了修补glibc
这档子事,我意识到环境隔离非常重要。Ubuntu 18.04
作为LTS版本,至今仍在广泛使用。这种不算特别老的系统尚且出现环境导致的兼容性问题,假如我要在一个使用musl
库的发行版上部署,比如Alpine Linux
或者Gentoo Linux
的时候又该怎么办呢?
显然,解决这个问题的最好办法就是封装Docker镜像。
精简HBuilderX包
封装Docker镜像自然是产物体积越小越好。HBuilderX macOS版在安装若干打包所需依赖后,主目录膨胀到2个多G,这里的大多数文件都是用不上的。在macOS下复制一份HBuilderX主程序目录,开搞:
- 只有plugins文件夹需要保留,HBuilderX主程序及其相关的文件完全用不上。删之。
- plugins文件夹下的大部分包也用不上,比如为HBuilderX提供代码补全、语法检查之类的IDE所需包,或者项目中根本没有使用到的UTS相关包,统统删之。
- 每删几个包,就执行一遍之前的sh构建脚本,将HBuilderX目录指向当前魔改的目录下,进行可用性测试。
- 删到最后,只留下
about
、complie-dart-sass
、node
、npm
、uniapp-cli-vite
与node_modules
这几个目录。 - 鲁迅说过,
node_modules
的体积比珠穆朗玛峰还要大,结构比马里亚纳海沟还要深。提取出node_modules
目录下的package.json
和package-lock.json
,复制到plugins
目录下,再将node_modules
删之。 - 根据上文的步骤,修改一下
uniapp-cli-vite
的package.json
。
精简以后压缩一下,原来2个多G的plugins只剩7.2M。将压缩包命名为core-3.9.5.zip
备用。
Docker封装前的思考和一些选择
既然要做Docker镜像,那么Docker镜像的系统就最好选个轻量点的。Alpine Linux
只有50多M,采用APK
包管理器,是一个非常理想的选择。然而必须注意的是,Alpine Linux
的libc实现使用的是musl
而非常用的glibc
。但我们既然是为了封装HBuilderX的专属镜像,那nodejs版本理应和原环境一样,也就是v16.17.0
以保障兼容性,但是APK
包管理器只能下载latest版本的nodejs
,nodejs
又依赖于glibc
。从源码编译不太现实,所以这里使用多阶段构建的方法,先通过nodejs
官方提供的node-alpine
来获取可用的nodejs
二进制文件,再进行后续的操作。
至于封装好的镜像如何使用,我的解决方案是启动一个小型的HTTP API
服务。为了减少不必要的依赖,这个服务也使用node
来写。服务器应该向镜像挂载一个项目文件夹,通过API调用进行打包。Git代码同步之类的操作全部交给Jenkins侧执行,Docker容器只负责最核心的打包部分——如无必要,勿增实体。
编写Dockerfile
以下是开发阶段的Dockerfile:
ARG NODE_VERSION=16.17.1
ARG ALPINE_VERSION=3.18
FROM node:${NODE_VERSION}-alpine AS node
ARG ALPINE_VERSION
FROM alpine:${ALPINE_VERSION}
ENV API_SERVER_URL=https://gitclone.com/github.com/hbuilderx-vanilla/api-server.git# Set China APK Manager Mirrors
RUN echo "https://mirrors.aliyun.com/alpine/v3.18/main/" > /etc/apk/repositories \&& echo "https://mirrors.aliyun.com/alpine/v3.18/community/" >> /etc/apk/repositories
RUN apk update && apk add --no-cache bash unzip wget git# Install node-16.17.1
COPY --from=node /usr/lib /usr/lib
COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=node /usr/local/include /usr/local/include
COPY --from=node /usr/local/bin /usr/local/bin# Set China NPM Mirrors
RUN npm install -g yarn --force \ && npm config set registry https://registry.npmmirror.com# Inject HBuilderX 3.9.5 core
COPY core-3.9.5.zip /opt/
RUN unzip /opt/core-3.9.5.zip -d /opt/ \&& rm /opt/core-3.9.5.zip && mkdir /projects# Install HBuilderX core dependencies
COPY core-install.sh /root/
RUN chmod +x /root/core-install.sh
# Need manual run it if minimal version
# RUN /root/core-install.sh# Install and start api server
WORKDIR /root
RUN git clone ${API_SERVER_URL} && \cd api-server && \npm i
EXPOSE 3000
CMD [ "node","/root/api-server/index.js" ]
我把HTTP API
服务单独做了一个Git仓库出来,这样方便后续扩充功能和进行bugfix。在开发环境下,一些反向代理和镜像源的设置是必不可少的。为了减少Docker镜像体积,我选择在镜像运行后再让用户手动安装HBuilderX的巨型npm依赖。
容器的部署和初始化
容器启动示例:
docker run -d --restart=always -v /<user_name>/<projects_folder>:/projects -p 13300:3000 --name hbuilder-vanilla flymyd114/hbuilderx-vanilla:latest
/<user_name>/<projects_folder>
是本机的待打包工程父目录,你的所有工程均应处于该目录下,如/Users/myd/projects
下有hello-uniapp
文件夹。13300
为建议的API端口映射点。
容器首次启动后,执行如下命令以初始化依赖:
docker exec -it <docker_id> /bin/sh
chmod +x /root/core-install.sh && /root/core-install.sh
exit
访问http://127.0.0.1:13300
以检查API服务是否正确启动。
打包示例:
curl --location 'http://localhost:13300/build?project=crp-app'
产物将会在/projects/crp-app/wgt-dist
中生成。
发布到Github上并编写workflows
可以看到,上面的容器已经被发布到了DockerHub。同样的,我也将仓库开源并编写了workflows
以自动构建新版本的Docker镜像。以下是docker-build.yml
:
name: Build and Push Docker imageon:push:branches:- maintags:- 'v*'paths:- 'Dockerfile'workflow_dispatch:jobs:build-and-push:runs-on: ubuntu-lateststeps:- name: Check out the repouses: actions/checkout@v2- name: Set up Docker Buildxuses: docker/setup-buildx-action@v1- name: Log in to Docker Hubuses: docker/login-action@v1with:username: ${{ secrets.DOCKER_HUB_USERNAME }}password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}- name: Extract metadata (tags, labels) for Dockerid: metauses: docker/metadata-action@v3with:images: flymyd114/hbuilderx-vanillatags: |type=semver,pattern={{version}}- name: Build and push Docker imageuses: docker/build-push-action@v2with:context: .file: ./Dockerfilepush: truetags: ${{ steps.meta.outputs.tags }}labels: ${{ steps.meta.outputs.labels }}
当main分支上的commit被打上形如v3.9.5
的Tag时即可触发workflows
。当然,也可以手动触发这个构建。
注意,在Github上发布的Dockerfile
应该去除镜像源和反代的部分,否则构建速度反而会变慢:
ARG NODE_VERSION=16.17.1
ARG ALPINE_VERSION=3.18
FROM node:${NODE_VERSION}-alpine AS node
FROM alpine:${ALPINE_VERSION}
ENV API_SERVER_URL=https://github.com/hbuilderx-vanilla/api-server.gitRUN apk update && \apk add --no-cache bash unzip wget git && \rm -rf /var/cache/apk/*COPY --from=node /usr/lib /usr/lib
COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=node /usr/local/include /usr/local/include
COPY --from=node /usr/local/bin /usr/local/bin
RUN npm install -g yarn --forceCOPY core-3.9.8.zip /opt/
RUN unzip /opt/core-3.9.8.zip -d /opt/ && \rm /opt/core-3.9.8.zip && \mkdir /projectsCOPY core-install.sh /root/
RUN chmod +x /root/core-install.sh# Need manual run it if minimal version
# RUN /root/core-install.shWORKDIR /root
RUN git clone ${API_SERVER_URL} && \cd api-server && \npm i
EXPOSE 3000
CMD [ "node","/root/api-server/index.js" ]
后续的版本升级
公司的项目目前统一使用HBuilderX 3.9.5的基座。但随着未来的升级,我们也需要及时更新打包核心以兼容高版本基座。天下没有不散的筵席,这篇文章既是我对这项工作的一个总结归纳,也是为后来人提供一个维护的文档和应对版本改变的思路。对于plugins
文件夹下的依赖来说,我们只需要在一台电脑上将HBuilderX升级到想要的版本,然后提取里面的package.json
进行依赖版本的替换就好。通常来说,要更新的package.json
涉及about
、uniapp-cli-vite
和根目录。更新后重新将plugins
放到core
文件夹下打包,同步修改Dockerfile
内的zip名即可。
以3.9.5升级至3.9.8举例,修改core/plugins
内的如下文件:
about/package.json
uniapp-cli-vite/package.json
package.json
然后压缩core
文件夹,重命名为core-3.9.8.zip
。修改Dockerfile
:
COPY core-3.9.8.zip /opt/
RUN unzip /opt/core-3.9.8.zip -d /opt/ && \rm /opt/core-3.9.8.zip && \mkdir /projects
Github项目地址
Docker项目:https://github.com/hbuilderx-vanilla/docker
API项目:https://github.com/hbuilderx-vanilla/api-server
联系我
邮箱:flymyd@foxmail.com
或在上方项目处提issue,@flymyd
相关文章:

漫谈Uniapp App热更新包-Jenkins CI/CD打包工具链的搭建
零、写在前面 HBuilderX是DCloud旗下的IDE产品,目前只提供了Windows和Mac版本使用。本项目组在开发阶段经常需要向测试环境提交热更新包,使用Jenkins进行CD是非常有必要的一步。尽管HBuilderX提供了CLI,但Jenkins服务通常都是搭建在Linux环境…...

Axure简单安装与入门
目录 一.Axure简介 二.应用场景 三.安装与汉化 3.1.安装 3.2.汉化 四. 入门 4.1.复制、剪切及粘贴区域 4.2.选择模式 4.3. 插入形状 4.4.预览、共享 感谢大家观看!希望能帮到你哦!!! 一.Axure简介 Axure RP是一款专业的原型…...

前端知识笔记(四十五)———前端开发与后端开发有什么区别
前端开发和后端开发是Web开发中的两个关键领域,它们负责不同的任务和功能。下面是前端开发和后端开发之间的主要区别: 前端开发: 用户界面:前端开发主要关注用户界面的开发,包括网页的布局、样式、交互等方面。前端技…...

Jol-分析Java对象的内存布局
Jol-分析Java对象的内存布局 Open JDK提供的JOL(Java Object Layout)工具为我们方便分析、了解一个Java对象在内存当中的具体布局情况。本文实验环境为64位HotSpot虚拟机。 Java对象的内存布局 Java的实例对象、数组对象在内存中的组成包括:对象头、实例数据和内存…...

基于sfunction builder的c-sfunction编写及案例测试分析
目录 前言 1.前期准备工作及文件说明 1.1前期准备工作 1.2 文件说明 1.3 编译方式...

【Java期末复习资料】(1)知识点总结
本文章主要是知识点,后续会出模拟卷 以下是选择、填空可能考的知识点,多看几遍,混个眼熟 面向对象程序设计的基本特征是:抽象、封装、继承、多态(后三个是三大特性)Java源文件的扩缀名是.java编译Java App…...

进程、容器与虚拟机的区别
进程、容器与虚拟机 参考:关于进程、容器与虚拟机的区别,你想知道的都在这! 进程、容器与虚拟机的结构图 进程 介绍 进程是一个正在运行的程序,它是一个个可执行文件的实例。当一个可执行文件从硬盘加载到内存中的时候…...

全网快递批量查询的得力助手
在当今社会,网络购物已经成为人们日常生活的重要组成部分。随着网购的普及,快递行业也迅速发展壮大。然而,这也带来了一系列问题:如何快速、准确地查询快递信息?如何批量查询多个快递?今天,我们…...

uniapp开发小程序经验记录
uniapp开发小程序的过程中会遇到很多问题,这里记录一下相关工具优化,便于后来者参考。 每次保存代码后,小程序都跳回首页 针对这个问题,常规的做法就是修改pages配置文件,但是这种方式不便于路由参数的设置ÿ…...

PR自动剪辑视频工具AI智能剪辑插件AutoPod
推荐一款可以提高剪辑效率,节约时间成本的AI人工智能自动剪辑视频制作工具pr插件Autopod,辅助你更快地完成视频内容的编辑工作。 Autopod 插件是一款应用于 Adobe Premiere Pro 软件的插件,用于自动剪辑。该插件能够识别和处理视频和音频素材…...

Visual Studio 2022+Python3.11实现C++调用python接口
大家好!我是编码小哥,欢迎关注,持续分享更多实用的编程经验和开发技巧,共同进步。 查了一些资料,不是报这个错,就是报哪个错,没有找到和我安装的环境的一致的案例,于是将自己的摸索分…...

10天玩转Python第2天:python判断语句基础示例全面详解与代码练习
目录 1.课程之前1.1 复习和反馈1.2 作业1.3 今日内容1.4 字符串格式化的补充1.5 运算符1.5.1 逻辑运算符1.5.2 赋值运算符1.5.3 运算符优先 2.判断2.1 if 的基本结构2.1.1 基本语法2.1.2 代码案例2.1.3 练习 2.2 if else 结构2.2.1 基本语法2.2.2 代码案例2.2.3 练习 2.3 if 和…...

2024年网络安全竞赛-网站渗透
网站渗透 (一)拓扑图 1.使用渗透机对服务器信息收集,并将服务器中网站服务端口号作为flag提交; 使用nmap工具对靶机进行信息收集 2.使用渗透机对服务器信息收集,将网站的名称作为flag提交; 访问页面即可 3.使用渗透机对服务器渗透,将可渗透页面的名称作为flag提交…...

kafka学习笔记--基础知识概述
本文内容来自尚硅谷B站公开教学视频,仅做个人总结、学习、复习使用,任何对此文章的引用,应当说明源出处为尚硅谷,不得用于商业用途。 如有侵权、联系速删 视频教程链接:【尚硅谷】Kafka3.x教程(从入门到调优…...

聊聊AsyncHttpClient的KeepAliveStrategy
序 本文主要研究一下AsyncHttpClient的KeepAliveStrategy KeepAliveStrategy org/asynchttpclient/channel/KeepAliveStrategy.java public interface KeepAliveStrategy {/*** Determines whether the connection should be kept alive after this HTTP message exchange.…...

视频推拉流直播点播EasyDSS平台点播文件加密存储的实现方法
视频推拉流直播点播系统EasyDSS平台,可提供流畅的视频直播、点播、视频推拉流、转码、管理、分发、录像、检索、时移回看等功能,可兼容多操作系统,还能支持CDN转推,具备较强的可拓展性与灵活性,在直播点播领域具有广泛…...

LVGL——按钮部件
目录 一、组成部分 二、按钮部件操作 1、创建 2、设置样式 3、添加事件 4、代码例程 三、按钮部件案例 一、组成部分 主体(LV_PART_MAIN) 二、按钮部件操作 1、创建 lv_obj_t *btn lv_btn_create( parent );2、设置样式 lv_obj_set_siz…...

RE2文本匹配调优实战
引言 在RE2文本匹配实战的最后,博主说过会结合词向量以及其他技巧来对效果进行调优,本篇文章对整个过程进行详细记录。其他文本匹配系列实战后续也会进行类似的调优,方法是一样的,不再赘述。 本文所用到的词向量可以在Gensim训练…...

Java - 线程间的通信方式
线程通信的方式 线程中通信是指多个线程之间通过某种机制进行协调和交互 线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公…...

【计算机网络】HTTP响应报文Cookie原理
目录 HTTP响应报文格式 一. 状态行 状态码与状态码描述 二. 响应头 Cookie原理 一. 前因 二. Cookie的状态管理 结束语 HTTP响应报文格式 HTTP响应报文分为四部分 状态行:包含三部分:协议版本,状态码,状态码描述响应头&a…...

2023年度盘点:智能汽车、自动驾驶、车联网必读书单
【文末送书】今天推荐几本自动驾驶领域优质书籍 前言 2023年,智能驾驶和新能源汽车行业仍然有着肉眼可见的新进展。自动驾驶技术继续尝试从辅助驾驶向自动驾驶的过渡,更重要的是相关技术成本的下降。根据《全球电动汽车展望2023》等行业报告,…...

一文讲解如何从 Clickhouse 迁移数据至 DolphinDB
ClickHouse 是 Yandex 公司于2016年开源的 OLAP 列式数据库管理系统,主要用于 WEB 流量分析。凭借面向列式存储、支持数据压缩、完备的 DBMS 功能、多核心并行处理的特点,ClickHouse 被广泛应用于广告流量、移动分析、网站分析等领域。 DolphinDB 是一款…...

[足式机器人]Part2 Dr. CAN学习笔记-数学基础Ch0-5Laplace Transform of Convolution卷积的拉普拉斯变换
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记-数学基础Ch0-5Laplace Transform of Convolution卷积的拉普拉斯变换 Laplace Transform : X ( s ) L [ x ( t ) ] ∫ 0 ∞ x ( t ) e − s t d t X\left( s \right) \mathcal{L} \left[ x\lef…...

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到
生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…...

Elasticsearch mapping 之 性能相关配置
ES 常见类型 通用类型: 二进制: binary 布尔型: boolean 字符串: keyword, constant_keyword, wildcard, text 别名: alias 对象: object, flattened, nested, join 结构化数据类型: Range, ip, version, murmur3 空间数据类型: geo_point, geo_shape, point, shape 性…...

adb push报错:remote couldn‘t create file: Is a directory
adb push报错:remote couldn‘t create file: Is a directory 出现这个问题可能是电脑本地目录中包含中文或者是目录地址中多包含了一个/ 比如说以下两种路径 1. test/测试音频文件1/a.mp3 2.test/test_audio/ 这两种都是不可以的(我是在as中执行的…...

GitLab 服务更换了机器,IP 地址或域名没有变化时,可能会出现无法拉取或提交代码的情况。
当 GitLab 服务更换了机器,但 IP 地址或域名没有变化时,可能会出现无法拉取或提交代码的情况。 这可能是由于 SSH 密钥或 SSL 证书发生了变化。以下是一些可能的解决步骤: 这可能是由于 SSH 密钥或 SSL 证书发生了变化。以下是一些可能的解决…...

【华为OD题库-076】执行时长/GPU算力-Java
题目 为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下&…...

持续集成交付CICD:Jenkins使用GitLab共享库实现前后端项目Sonarqube
目录 一、实验 1.Jenkins使用GitLab共享库实现后端项目Sonarqube 2.优化GitLab共享库 3.Jenkins使用GitLab共享库实现前端项目Sonarqube 4.Jenkins通过插件方式进行优化 二、问题 1.sonar-scanner 未找到命令 2.npm 未找到命令 一、实验 1.Jenkins使用GitLab共享库实现…...

Linux文件结构与文件权限
基于centos了解Linux文件结构 了解一下文件类型 Linux采用的一切皆文件的思想,将硬件设备、软件等所有数据信息都以文件的形式呈现在用户面前,这就使得我们对计算机的管理更加方便。所以本篇文章会对Linux操作系统的文件结构和文件权限进行讲解。 首先…...