记录一下。

eggjs项目发布配置步骤 linux eggjs项目发布配置 立即下载

项目背景

上文的代码,是我基于egg.js做的一个傻瓜版发布版本工具。机制如下:根据前端请求的参数(一个项目列表,每个项目有一些指令),然后后端根据不同指令和项目id去跑对应的逻辑。

  • 1.创建临时目录
  • 2.拉取指定仓库最新代码
  • 3.自动安装依赖
  • 4.npm run build
  • 5.ftp/git同步代码到生产环境。

界面以及核心代码

在这里插入图片描述
在这里插入图片描述
下面是核心代码。通过长链接做项目发布的整个流程前后端交互方式.代码随便瞎写的,凑合看。
egg.js + bootstrap样式库 + socket.io.js +axios。

后端业务核心代码

'use strict';
const Controller = require('egg').Controller;
const build = require('@root/config/build');
const fs = require('fs');
const shell = require('shelljs');
const FtpService = require('@root/lib/ftp');
const utils = require('@root/lib/utils');
const Log = require('@root/lib/log');
const logCtx = Log.getInstance();

class ProcessController extends Controller {

  constructor(props) {
    super(props);
    this.releasePath = '';
    this.id = '';
    this.tempDirName = '';
    this.buildConfig = build.config;
    this.logFilePath = '';
  }

  log({ msg, id, toast = true, showLine = true } = {}) {
    if (typeof arguments[0] === 'string')msg = arguments[0];
    if (!id)id = this.id;
    if (!id) return;
    const { ctx } = this;
    ctx.socket.emit('log', ctx.helper.parseMsg('process-log', { id, msg, toast, showLine }));
  }

  recordFileLog({ msg }) {
    Log.recordFileLog({ filePath: this.logFilePath, msg });
  }


  async index() {
    const { ctx } = this;
    const { action, id } = ctx.args[0];
    // 重要
    this.id = id;
    const taskId = '';
    const projectInfoData = await this.service.project.getProjectById(id);
    const projectInfo = utils.objTranslate(projectInfoData);
    if (action === 'release') {
      try {
        const startTime = new Date().getTime();
        await ctx.helper.sleep(100);
        // 先说流程已经启动
        this.log('流程已启动......');

        // 1.创建虚拟目录
        const tempDirPath = this.buildConfig.tempDirPath;

        const nowTime = new Date().getTime();
        const tempDirName = `${projectInfo.repository_name}_${nowTime}_${id}`;
        const tempPath = `${tempDirPath}/${tempDirName}`;
        this.releasePath = tempPath;
        this.tempDirName = tempDirName;

        this.logFilePath = `${this.releasePath}/${projectInfo.repository_name}_${this.tempDirName}_${this.id}_log.txt`;

        this.log({ msg: `打包路径:${tempPath}`, toast: false });
        fs.mkdirSync(`${tempPath}`, { recursive: true });

        this.log('目录创建成功...' + tempPath);
        shell.cd(tempPath);
        // 2.拉取代码
        const { repository_url } = projectInfo;
        const { name, pwd } = this.buildConfig.gitlab;
        this.log({ msg: `从仓库${repository_url}拉取代码`, showLine: false });
        const url = repository_url.replace('http://', '');

        await ctx.helper.sleep(100);
        // 只能这样嵌套了嘛。。
        await new Promise((resolve, reject) => {
          shell.exec(`git clone http://${name}:${pwd}@${url} .`, {}, (code, stdout, stderr) => {
            const logFileRowContent = JSON.stringify({ code, stdout, stderr });
            this.recordFileLog(logFileRowContent);
            // update project version form package.json
            // release/60a708c766f1a146b79d475d_1621560699290/package.json
            const packageJsonObj = require(`@root/${tempPath}/package.json`);
            if (packageJsonObj && packageJsonObj.hasOwnProperty('version')) {
              const currentVersion = packageJsonObj.version;
              projectInfo.version = currentVersion;
            }
            resolve(true);
          });
        });
        this.log('代码拉取成功...');

        this.log('开始安装依赖');
        await ctx.helper.sleep(100);
        // 3.安装依赖
        // shell.exec('yarn');
        await new Promise((resolve, reject) => {
          shell.exec('yarn', {}, (code, stdout, stderr) => {
            const logFileRowContent = JSON.stringify({ code, stdout, stderr });
            this.recordFileLog(logFileRowContent);
            resolve(true);
          });
        });

        this.log('依赖安装成功...');
        // 4.打包
        this.log('开始打包');
        await ctx.helper.sleep(100);
        // shell.exec('npm run build');
        await new Promise((resolve, reject) => {
          shell.exec('npm run build', {}, (code, stdout, stderr) => {
            const logFileRowContent = JSON.stringify({ code, stdout, stderr });
            this.recordFileLog(logFileRowContent);
            resolve(true);
          });
        });
        this.log('打包成功...');

        // 5.上传

        const { ftp_host, ftp_name, ftp_pwd, ftp_port } = projectInfo;
        if (ftp_host && ftp_name && ftp_pwd) {
          this.log('开始上传');
          // 这里的dist最好是可以配置,因为不同工程打包出来不一样。比如hbuilder打包出来的就不是unpackage/dist...
          await FtpService.up({ host: ftp_host, user: ftp_name, password: ftp_pwd, port: ftp_port, localRoot: `${tempPath}/dist` }).then(res => {
            this.log('上传成功...');
          }).catch(err => {
            this.log('上传失败:' + err.message);
          });
        }
        await ctx.helper.sleep(100);
        // 6.压缩备份


        // 更新数据
        const newProjectInfo = { ...projectInfo, build_count: Number(projectInfo.build_count) + 1 };
        await ctx.service.project.save(newProjectInfo);

        ctx.socket.emit('process', ctx.helper.parseMsg('process-success', { id }));
        const endTime = new Date().getTime();
        this.recordFileLog({ msg: '编译成功,耗时' + (endTime - startTime) / 1000 + '秒' });
        this.log('任务完成');

      } catch (e) {
        this.log('编译失败啦');
        this.recordFileLog({ msg: '编译失败' + e.message });
        const newProjectInfo = { ...projectInfo, err_count: Number(projectInfo.err_count) + 1 };
        await ctx.service.project.save(newProjectInfo);
        ctx.socket.emit('process', ctx.helper.parseMsg('process-fail', { id }));
      }
    }
  }

}

module.exports = ProcessController;

前端核心代码

{% extends "layout.html" %}

{% block right %}
<div class="action-head m-b-15">
    <a href="/project/form" type="button" class="btn btn-primary btn-sm">添加项目</a>
</div>
<div class="container-box">
    <table class="table fz-14">
        <thead>
        <tr>
            <th>序号</th>
            <th>名字</th>
            <th>描述</th>
            <th>仓库地址</th>
            <th>版本</th>
            <th>编译成功次数</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for project in projectList %}
        <tr>
            <th data-pid="{{project._id}}">{{loop.index}}</th>
            <td>{{project.title}}</td>
            <td>{{project.description}}</td>
            <td><a target="_blank" href="{{project.repository_url}}">{{project.repository_url}}</a></td>
            <td>{{project.version}}</td>
            <td><span id="build-count-{{project._id}}">{{project.build_count}}</span></td>
            <td>
                <div id="status-{{project._id}}">空闲</div>
            </td>
            <td style="white-space: nowrap;">
                <button onclick="projectRelease(this)" id="release-btn-{{project._id}}" data-id="{{project._id}}"
                        type="button" class="btn btn-primary btn-sm">
                    <i class="bi bi bi-cloud-upload m-r-4"></i>
                    一键发布
                </button>
                <button onclick="projectBuild(this)" data-id="{{project._id}}" type="button" class="btn btn-success btn-sm">
                    编译
                    一键发布
                </button>
                <a href="/project/form?id={{project.id}}" class="btn btn-info btn-sm m-l-10"><i
                        class="bi bi bi-gear m-r-4"></i>编辑配置</a>
                <button type="button" class="btn btn-danger btn-sm m-l-10"><i class="bi bi-back m-r-4"></i>回滚版本</button>
            </td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
</div>


{% endblock %}

{% block script %}
<script>

  function addNotify({ msg, title = '提示', time = false }) {
    if (!document.getElementById('notifyBox')) {
      $('#wrap')
        .append(`<div id="notifyBox" style="position: fixed; top: 10px; right: 10px;z-index: 66"></div>`);
    }
    // <div id="notifyBox" style="position: fixed; top: 0; right: 0;z-index: 66"></div>
    if (!time) {
      time = dayjs()
        .format('YYYY-MM-DD HH:mm:ss');
    }
    const name = new Date().getTime();
    const tmplStr = `<div id="${name}" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-delay="4000">
        <div class="toast-header">
            <i class="bi bi-info-circle m-r-4"></i>
            <strong class="mr-auto">${title}</strong>
            <small class="text-muted">${time}</small>
            <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
        </div>
        <div class="toast-body">
            ${msg}
        </div>
    </div>`;
    $('#notifyBox')
      .append(tmplStr);
    $(`#${name}`)
      .toast('show');
    $(`#${name}`)
      .on('hidden.bs.toast', function() {
        $(`#${name}`)
          .remove();
      });
  }

  var socketReady = false

  $(function(){
    // if(!socketReady){
    //   socketCtx.open()
    //   socketReady = true
    // }
  })

  //别超时
  const socketCtx = io('/',{autoConnect:false});

  function ping() {
    console.log('ping')
    socketCtx.emit('ping', { time: new Date().getTime() });
    socketCtx.emit('chat', '123456789');
  }



  socketCtx.on('connect', () => {
    console.log('connect!',socketCtx.id);

    //不需要搞心跳
    // ping();
    // //心跳
    // setInterval(ping, 3* 1000);
    //socketCtx.emit('process', '123456789');
  });

  socketCtx.on("disconnect", () => {
    console.log('socket disconnect'); // undefined
  });

  socketCtx.on("connect_error", () => {
    console.log('connect_error emit')
    // socketCtx.connect();
  });

  socketCtx.on('log', res => {
    console.log('res from server:', JSON.stringify(res));
    const { action, payload } = res.data;
    const { id: projectId } = payload;
    const $currentProjectStatusEl = $(`#status-${projectId}`);
    const $currentProjectEl = $(`#release-btn-${projectId}`);
    switch (action) {
      case 'process-log':
        payload.toast && addNotify({ msg: payload.msg });
        payload.showLine && $currentProjectStatusEl.html(`${payload.msg}`);
        break;
      default:
        break;
    }
  })

  socketCtx.on('res', res => {
    if (typeof res === 'string') {
      console.log('res from server:', res);
      return;
    }
    console.log('res from server:', JSON.stringify(res));

  });


  socketCtx.on('process', res => {
    if (typeof res === 'string') {
      console.log('res from server:', res);
      return;
    }
    console.log('res from server:', JSON.stringify(res));
    const { action, payload } = res.data;
    const { id: projectId } = payload;
    const $currentProjectStatusEl = $(`#status-${projectId}`);
    const $currentProjectEl = $(`#release-btn-${projectId}`);
    switch (action) {
      case 'process-fail':
        // $currentProjectEl.html('一键发布')
        $currentProjectStatusEl.html(`<span class="color-red">发布失败</span>`);
        $currentProjectEl.attr('disabled', false);
        break;
      case 'process-success':
        $currentProjectStatusEl.html(`<span class="color-green">发布成功</span>`);
        // $currentProjectEl.html('一键发布')
        $currentProjectEl.attr('disabled', false);
        $(`#build-count-${projectId}`).html(Number($(`#build-count-${projectId}`).html())+1)
        break;
      default:
        break;
    }
  });

  async function projectBuild(el){
    const projectId = $(el).data('id');
    axios.post('/project/build',{projectId}).then(()=>{

    }).cache((err)=>{
      console.log(err)
    })
  }

  async function projectRelease(el) {
    if(!socketReady){
        socketCtx.open()
        socketReady = true
    }
    const projectId = $(el)
      .data('id');
    //alert(projectId)
    $(el)
      .attr('disabled', true);
    $(`#status-${projectId}`)
      .html('启动中...');
    const $currentProjectStatusEl = $(`#status-${projectId}`);
    $currentProjectStatusEl.prepend(`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" id="${projectId}_loading"></span>`);
    socketCtx.emit('process', { id: projectId, action: 'release' });
    // await axios.post('/project/release',{id:projectId}).then(res=>{
    //   console.log(res)
    // }).catch(err=>{
    //   console.log(err)
    // }).finally(()=>{
    //
    // })
  }

</script>
{% endblock %}

错误截图

vue-cli的filenameHashing默认是true的,所以打包后的js应该是hash的

手动打包是正常的
手动打包
用了我的傻瓜工具
就好像吃了见手青一样看见了小人人

用这个代码,复现问题是必现的,会导致奇怪的问题。

'use strict';
const shell = require('shelljs');
shell.cd('release/vocen-engine-admin_1621566342725_60a72099ff8dfb0af9029188');
shell.exec('yarn');
shell.exec('npm run build');

问题分析

个人想法

1.第一层 首先编译后文件名不符合预期,就是webpack这里的配置加载有问题,一般自己手写就是这样
webpack.config.js

const path = require('path');
module.exports = {
  //就是output
  output: {
    filename: 'my-first-webpack.bundle.js',
    chunkFilename: (pathData) => {
      return pathData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js';
    }
  },
};

2.第二层
而之前手撸webpack的过程中,一般都会在指令中或者配置文件去约定mode是production还是development。代码如下。

//package.json
"scripts": {
   "serve": "webpack-dev-server --mode development --progress --open",
   "build": "webpack --env.production --mode production --progress"
}

然后想到有可能是shelljs和真实的cmd差别,有可能就是他被设置成了development

开始捣鼓代码

然后就开始找filename和chunkFilename相关而配置.
不过一开始没有方向,只能从头开始把vue-cli代码看了一下,居然看了好久。虽然看高手代码很开心,但是还是有硬性的目标要弄完。在这个过程中,基本看了一下vue-cli的打包过程(dev的没有过多关注,只看了vue-cli-service build相关的逻辑)。
在读代码的过程中遗漏了一个重要代码,导致耽误了两个小时。

//node_modules/@vue/cli-service/lib/Service.js,
//概179行左右,自己找
const builtInPlugins = [
      './commands/serve',//我认真看了这个,下意识认为服务相关的都在这里了
      './commands/build',
      './commands/inspect',
      './commands/help',
		
      //我把后面的配置初始化这几个文件忽视了,失误太大了
      // config plugins are order sensitive
      './config/base',
      './config/css',
      './config/prod',
      './config/app'
    ].map(idToPlugin)

在这里插入图片描述
因为看错了文件,导致在commands/service上花费太多事件。去了解指令的注册、以及调用过程(这里面包含了好多次2个对照组的多次运行,然后控制台打印生成的webpack配置文件对比,花费不少时间)。
在没有溯源到output.filename相关配置时,我尝试用了vscode在node_modules目录中搜索关键字filename,结果vscode欺骗了我。。。没有结果。

然后又是继续愚公移山一样去走完该指令的所有过程,走完之后发现配置还是不一样,但是无法定位关键步骤在哪里。。。
这个时候想到是不是vscode忽略了node_modules中的代码,没有参与检索(我之前用webstrom等,搜索结果会忽略node_modules中的包,只展示业务代码的搜索结果),尝试着用了一下webstrom打开node_modules目录(不打开项目目录,直接打开node_modules作为根目录),一搜索filename,我发现好多符合结果的。。。一下子就觉得之前白干了,微软坑我。

然后开始快速浏览结果,浏览到一个app.js中有符合的结果,再一看,这个文件居然是在vue-cli包里面,福至心灵了。。。基本就确定尤大写的东西,还是可以层级很清晰而且是高度可以配置的,没必要去深究到webpack包那一层。

然后打开这文件好好看,看到这里基本就知道了。。。

 const isProd = process.env.NODE_ENV === 'production'
 const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
 const outputDir = api.resolve(options.outputDir)

 const getAssetPath = require('../util/getAssetPath')
 const outputFilename = getAssetPath(
      options,
      `js/[name]${isLegacyBundle ? `-legacy` : ``}${isProd && options.filenameHashing ? '.[contenthash:8]' : ''}.js`
    )
 console.log('outputFilename is',outputFilename,isProd)
 webpackConfig
      .output
        .filename(outputFilename)
        .chunkFilename(outputFilename)

在控制台一打,确实如此。

 const isProd = process.env.NODE_ENV === 'production'
 console.log('isProd is',isProd)

傻瓜工具下运行 shell.exce(‘npm run build’)

outputFilename is false 

手动运行npm run build

outputFilename is true

分析为什么我绕了弯路

没有认真看代码。

node_modules/@vue/cli-service/lib/Service.js

在使用shelljs跑npm run build等指令(也就是vue-cli-service build时),Servie逻辑跑的时候时序很重要。

module.exports = class Service {
  constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
	//在这个resolvePlugins方法里面,配置了要引入的一堆东西。
	//关键里面的config/app.js
    this.plugins = this.resolvePlugins(plugins, useBuiltIn)
   
  }

 resolvePlugins (inlinePlugins, useBuiltIn) {
    const idToPlugin = id => ({
      id: id.replace(/^.\//, 'built-in:'),
      apply: require(id)
    })

    let plugins

    const builtInPlugins = [
      './commands/serve',
      './commands/build',
      './commands/inspect',
      './commands/help',
      // config plugins are order sensitive
      './config/base',
      './config/css',
      './config/prod',
      //这里面有用到process.env.NODE_ENV这个变量,来区分是dev还是prod.
      './config/app'
    ].map(idToPlugin)
 }

}
// node_modules/@vue/cli-service/lib/Service.js loadEnv方法 ,大概96行。
//我看到过如下代码。。
  loadEnv (mode) {
  	//省略一些代码。。。
  	
    // by default, NODE_ENV and BABEL_ENV are set to "development" unless mode
    // is production or test. However the value in .env files will take higher
    // priority.
    if (mode) {
      // always set NODE_ENV during tests
      // as that is necessary for tests to not be affected by each other
      const shouldForceDefaultEnv = (
        process.env.VUE_CLI_TEST &&
        !process.env.VUE_CLI_TEST_TESTING_ENV
      )

      const defaultNodeEnv = (mode === 'production' || mode === 'test')
        ? mode
        : 'development'
      if (shouldForceDefaultEnv || process.env.NODE_ENV == null) {
        process.env.NODE_ENV = defaultNodeEnv
      }
      if (shouldForceDefaultEnv || process.env.BABEL_ENV == null) {
        process.env.BABEL_ENV = defaultNodeEnv
      }
    }
    //省略一些代码。。。
  }

原因记录

@vue\cli-service\lib\config\app.js

const isProd = process.env.NODE_ENV === 'production'
/*
do something
*/
const outputFilename = getAssetPath(
      options,
      `js/[name]${isLegacyBundle ? `-legacy` : ``}${isProd && options.filenameHashing ? '.[contenthash:8]' : ''}.js`
    )

具体位置在22行左右(版本不一致,自己找下)
在这里插入图片描述
就是这个错误

shelljs和cmd运行有什么差别呢??

又去看了shelljs的包介绍。
在这里插入图片描述
英文看不太懂,然后看源码

  //node_modules/shelljs/src/exec.js 29行
  opts = common.extend({
    silent: common.config.silent,
    cwd: _pwd().toString(),
    env: process.env,
    maxBuffer: DEFAULT_MAXBUFFER_SIZE,
    encoding: 'utf8',
  }, opts);

解决办法


shell.exec('npm run build', { env: Object.assign({}, process.env, { NODE_ENV: 'production' }) });

在这里插入图片描述

经验总结

仔细看文档,多看源码,善用工具。

BLDC方波控制 vs. FOC:技术选型指南与未来演进 本文深入对比BLDC电机方波控制与FOC技术的核心差异,分析两者在成本、性能和应用场景上的权衡。方波控制凭借低成本和易实现性,在电动工具和家电领域优势明显;而FOC则以平滑高效控制见长,适用于高端精密应用。文章为工程师提供了实用的技术选型指南,并展望了未来技术融合的发展趋势。 阅读详情

相关推荐

SAP成本核算变式OKKN配置详解:从CK11N到CK24,手把手教你搞定标准成本发布

本文深入解析SAP成本核算变式OKKN的配置原理与实战应用,涵盖从CK11N到CK24的标准成本发布全流程。详细讲解数量结构控制、价值结构配置及间接费用计算规则,帮助成本会计精准掌握标准成本计算逻辑,解决BOM版本选择、工艺路线获取等常见问题,提升企业成本管理效率。

weixin_27219587的博客 287

Egg.js线上打包部署

打包Node.js代码的工具有很多,有些工具在打包时将自己写的代码打包为二进制文件配合node_modules一起使用,有些工具将全部代码打包为二进制,个人比较偏向于全部打包,例如pkg,都可以支持express、koa等框架打包,但是对于将koa框架封装后的Egg.js框架打包都没有示例,官方也没有给出好的解决办法,我在多次尝试后,加之阅读一部分Egg.js源码后终于利用pkg成功的打包。介绍一...

小刘的博客 1万+

AI图像去噪实战:5种最新深度学习模型在超声图像处理中的效果对比(附代码)

本文对比了2022至2024年间五种前沿深度学习模型在超声图像去噪领域的实战效果,包括FDDL-Net、MG-CNFNet、Dual-TranSpeckle、可控去噪模型及Wave-Mamba。文章深入剖析了各模型的核心创新与架构,并提供了可运行的代码片段,旨在帮助医学影像算法工程师根据不同的性能、速度与临床需求,选择最合适的AI图像去噪解决方案。

1a2s3d4f5g的博客 915

egg项目合集(一)——手动部署egg项目

阿里云服务器(二)——手动部署egg项目前言一、准备工作以及介绍1、什么是egg2、前期准备二、手动部署项目1.本地运行项目2.服务端运行项目的代码总结 前言 上期我们对如何利用ssh和sftp的方式来连接远程服务器,那么有人问连接到自己服务器干什么呢?那自然是通过自己内部服务器上到公网上然别人访问自己的服务器,这样一个简单的网站就诞生了。简单理解就是ssh这种方式可以远程操作自己的服务器,而sftp可以将本地和远程服务器进行进行文件传输,今天以egg项目为例,如何部署egg(即nodejs)项目 一、准

Lopez Lu的博客 2263

神经网络学习笔记-波尔兹曼(Boltzmann)机

Boltzmann机是一种反馈随机神经网络 两种输出状态,单极性0或1。输出状态的取值根据概率统计法则决定,这种概率统计法则类似Boltzmann分布,部分反馈 它在神经元状态变化中引入了统计概率,网络的平衡状态服从Boltzmann分布,网络运行机制基于模拟退火算法。 离散Hopfield神经网络+模拟退火+隐单元=Boltzman机 DHNN全反馈,转移函数:符号函数 BM机网络拓扑...

求易学堂的博客 2612

Windows下Python多版本共存与切换的终极指南(含环境变量详解)

本文详细解析了在Windows系统下实现Python多版本共存与切换的完整方案。通过深入讲解环境变量Path的底层机制,提供了从安装策略、精细化Path管理到创建快速切换脚本的实战指南,并介绍了py启动器和虚拟环境等高级技巧,帮助开发者彻底解决版本冲突问题,提升开发效率。

v6b7n8m9q0的博客 537

将egg项目部署至服务器

将egg项目部署至服务器

qq_48617322的博客 1607

GMSL转eDP芯片MAX9686X(ADI)

MAX96860/MAX96862/MAX96864解串器可以把单路或双路GMSL转换成eDP V1.4a,支持820MHzPCLK的无压缩的24bpp(每个像素的位数)视频,或660MHz的30bpp视频。芯片的GMSL支持NRZ编码的6Gbps/187Mbps的GMSL2,PAM4编码的12Gbps/187Mbps的GMSL3。同时支持单线(COAX模式)双向全双工传输控制数据和video数据。支持DSC压缩的芯片,最大支持像素时钟1080MHz@24bpp或者864MHz@30bpp,传输方式包括同轴

qflook的博客 2838

使用pkg打包Egg.js项目 Egg.js线上部署

使用pkg打包egg.js项目 pkg原理 安装pkg 配置egg.js临时文件目录 修改package.json文件 配置打包入口文件pkg-build.js 打包发布 部署启动 pkg原理 pkg打包工具主要会按平台(支持window、mac、linux)分别打包。 pkg中会包含node的可执行文件,还会包含你要打包进去的代码。代码通过一个虚拟的文件系统把所有的代码和资源文件都挂载到 /snapshot/${被打包项目的文件夹名} 下面(pkg hack了 fs 的很多方法,拦截.

Cool的博客 2908

AMBA-CHI协议详解(十五)

Write transactions事务描述以及请求属性,cache line状态转换。

Crazzy_M的博客 650

docker安装部署openclaw详细教程(安装、卸载、问题处理)

摘要: 本文详细介绍了使用Docker部署OpenClaw本地AI智能体框架的全流程,包括代码下载、容器化安装、交互式配置及Token设置。Docker方案解决了环境配置复杂、依赖冲突等痛点,提供开箱即用的隔离环境。教程涵盖常见错误(如EACCES权限问题、网关启动失败、设备配对需求)的解决方案,并提供了完整的卸载指南。通过Docker部署,用户可快速构建安全、一致的AI智能体开发环境,实现大模型与本地工具的高效连接。

少说,多做 4003

毕业设计答辩技巧&降重技巧

文章目录问题1:你这个项目用到了SSM/SSH/JSP/JavaWeb,是怎么体现的?问题2:你在进行数据库设计的时候,有什么技巧吗?问题3:给我找到XX模块的代码,并讲解一下[XX流程是怎么实现的]?问题4:你觉得这个系统还有什么需要完善的地方?问题5:整个项目你的工作流程是怎么进行的?问题6:如果讲解的过程中,遇到不会的怎么办?问题7:论文降重了多少次?问题8:论文降重技巧? 作者提供收费...

东语的专栏 6549

C# WinForms 多语种切换实现:打造国际化应用程序

合理实现多语种切换功能可以显著提升应用程序的国际化水平。模块化设计:将本地化逻辑模块化,便于管理和维护。异常处理:始终捕获和处理可能的异常,确保程序的健壮性。资源管理:合理管理资源文件,避免重复和遗漏。测试:在不同语言环境下进行全面测试,确保所有字符串都能正确显示。文档和注释:为关键代码添加详细的文档和注释,便于后续维护。

墨夶的博客 1691

A记录、MX记录、NS记录、CNAME、PTR记录、AAAA记录、SRV记录、TXT记录、SOA记录 分别是什么意思

nslookup是一种网络管理命令行工具,可用于查询DNS域名和IP地址 输入指令nslookup A记录: 此记录列出特定主机名的 IP 地址。这是名称解析的重要记录。 MX记录:MX记录就是邮件交换记录,是域名在DNS服务器上的一个记录,告诉那台计算机负责为系统处理邮件。MX记录存在于域名的 DNS文件中,用于将某个域名的电子邮件指向到对应的邮件服务器处理。 NS记录:NS...

m0_37477061的博客 9330

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

邮件系统DNS解析记录配置示例汇总-MX记录、A记录、SPF记录、DKIM记录、DMARC记录

一套邮件系统,当其在公网上为广大用户提供邮件的发送与接收服务时,其核心功能之一即是确保邮件能够准确无误地抵达目标地址。为了实现这一目标,邮件系统不仅需要高效稳定的内部运行机制,更需依赖于外部网络服务的支持,其中DNS服务便是不可或缺的一环。DNS服务不仅负责将人们易于记忆的域名转换为计算机能够理解的IP地址,还在邮件传输过程中扮演着至关重要的角色。本文主要围绕邮件系统可能涉及的DNS记录及其设置方法展开详细阐述。

MailABC致力于科普电子邮件知识。如需交流,可以关注mailabc微信公众号或邮件feedback@mailabc.cn 4845

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

A记录、MX记录、CNAME记录、TXT记录、AAAA记录、NS记录

常用域名记录解释:A记录、MX记录、CNAME记录、TXT记录、AAAA记录、NS记录A记录 ¶A记录是用来创建到IP地址的记录。A记录设置技巧1、如果想创建不带www的记录,即ezloo.com,在主机记录中填写@或者留空,不同的注册商可能不一样。2、创建多个域名到同一个IP,比如给博客建了二级域名,可以使用*.blog.ezloo.com来指向一个IP,这样

技术资料库 1万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

应用nslookup命令查看A记录、MX记录、CNAME记录和NS记录

nslookup是一种网络管理命令行工具,可用于查询DNS域名和IP地址输入指令nslookup默认服务器和Address是当前上网所用的DNS服务器域名和地址A记录A(Address)记录指的是用来指定主机名或域名对应的IP记录。在提示符&gt;后直接输入域名,可以查看该域名的A记录:MX记录MX(mail exchanger)记录,邮件交换记录,它指向一个邮件服务器,用于电子邮件系统发邮件时根...

qq_38058202的博客 7万+

DNS基础之通过dig命令理解DNS域名解析中的A记录,AAAA记录,CNAME记录,MX记录,NS记录

文中内容参考DNS 原理入门—阮一峰。DNS(Domain Name System) 即域名系统。 作用就是根据域名查出 IP 地址。我们使用(Dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX记录等相关信息的工具)来显示 DNS的查询过程。 ......

zxl1990_ok的博客 1万+

DNS解析中的A记录、AAAA记录、CNAME记录、MX记录、NS记录、TXT记录、SRV记录、URL转发等

AA记录: 将域名指向一个IPv4地址(例如:100.100.100.100),需要增加A记录 NS NS记录: 域名解析服务器记录,如果要将子域名指定某个域名服务器来解析,需要设置NS记录 SOA SOA记录: SOA叫做起始授权机构记录,NS用于标识多台域名解析服务器,SOA记录用于在众多NS记录中标记哪一台是主服务器 MX MX记录: 建立电子邮箱服务,将指向邮件服务器地址,需要设置MX记录。建立邮箱时,一般会根据邮箱服务商提供的MX记录填写此记录 TXT TXT记录: 可任意填写,可为空。一

u011029104的专栏 7918

DNS记录类型介绍(A记录、MX记录、NS记录等)

DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 建站名词解释:DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 泛域名 泛解析 域名绑定 域名转向 1.DNS:Domain Name System 域名管理系统 域名是由圆点分开一串单词或缩写组成的,每一个域名都对应一个惟一的IP地址,这一命名的方法或这样管理域名的系统叫做域名管理系统。 DNS:Domain Name Server 域名服务器 域名虽然便于人们记忆,但网络中的计

s13596191285的博客 6088

DNS A记录 NS记录 CNAME记录 TXT记录 TTL值

DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值  建站名词解释:DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 泛域名 泛解析 域名绑定 域名转向   1. DNS  DNS:Domain Name System 域名管理系统 域名是由圆点分开一串单词或缩写组成的,每一个域名都对应一个惟一的IP地址,这一命名的方法或这样

linux/unix 1万+

A记录、MX记录、CNAME记录这些名词是什么意思?

A 记录(Address) 是用来指定主机名(或域名)对应的IP地址记录。用户可以将该域名下的网站服务器指向到自己的web server上。同时也可以设置您域名的二级域名。 别名记录(CNAME) 也被称为规范名字。这种记录允许您将多个名字映射到同一台计算机。 通常用于同时提供WWW和MAIL服务的计算机。 例如,有一台计算机名为“host.eblhost.cn”(A记录),它同时提供W...

一个吃水不忘挖井人的博客。希望能帮助到你 7503

深入解析域名解析记录:A记录、MX记录等全面指南,附带记录操作方法

本文全面解析了DNS系统中各类记录的功能与配置方法。详细介绍了A记录(IPv4解析)、AAAA记录(IPv6解析)、MX记录(邮件服务器)、CNAME记录(别名)等核心记录类型,以及TXT、NS、SOA、SRV、PTR等特殊用途记录。文章包含记录示例、优先级设置、TTL参数说明,并提供了查看/修改DNS记录的实用工具和命令。最后总结了DNS配置的最佳实践,包括冗余设置、安全防护和监控建议,并解答了常见问题。这篇指南既适合初学者理解DNS基础,也能帮助管理员优化域名解析配置。

仕别三日的博客 3460

定长记录与不定长记录

文件是操作系统的一种基本的数据结构。在逻辑上可看作是记录的序列,物理上这些记录被映射到磁盘块中。一般数据模型中的记录在文件中有两种不同的组织格式:定长记录和不定长记录。 把数据库映射到文件的一种方法是使用多个文件,每个关系表被映射到一个文件中存储,这样每个文件中就只存储一个固定长度的记录,这就是定长记录格式的文件。另一种方法是自己构造文件,使之能够容纳多种长度的记录,这样整个数据库就可以被映射到...

hammring的博客 7525

DNS记录这件小事

DNS记录基本上大家都很熟悉,但是能全部说清楚各种记录的用途并不容易。所以我就把记录全部都测试了一遍,记录在此篇文章里,以备以后查阅。DNS中不止以下列出来的类型,还有一些不常用的,没再测试也没在列出,以后测试了再补上去。 此文章使用 nslookup 工具查询,也可以使用 dig 工具查询这些数据。

一个金牛座猿猿子的技术分享空间 3291

域名的A记录、mx记录、ns记录

什么是A记录?什么是别名记录(CNAME)?什么是MX记录?什么是NS记录? 1、A记录:WEB服务器的IP指向     A (Address)记录是用来指定主机名(或域名)对应的IP地址记录。     说明:用户可以将该域名下的网站服务器指向到自己的web server上。同时也可以设置自己域名的二级域名。就是说:通过A记录,大家可以设置自己的不同域名转到不同的IP上去!如:

2963
上一篇: 趣味百家姓基于tree.js
下一篇: 组件json数据格式说明
汪小君
博客等级 码龄12年 120粉丝 94原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值