eggjs笔记

一、egg.js

1. 什么是egg.js

  • express是基于es5的web开发框架
  • koa1.x 是express原班人员打造的基于es6的web开发框架
  • koa2.x 是express原班人员打造的基于es7的web开发框架
  • egg 是阿里基于koa有约束和规范的企业级web开发框架

2. egg.js的基本使用

2.1 安装

# 初始化
npm init -y
# 安装egg,egg模块是egg.js的核心模块
npm i egg -S
# 安装egg-bin,这个模块用于快速启动项目
npm i egg-bin -D

2.2 启动:快速启动项目,用于本地开发调试的模块

// package.json
"scripts": {
    "dev": "egg-bin dev"
}

2.3 目录结构

egg-project
├── package.json
├── app.js(可选)
├── agent.js(可选)
├── app
|   ├── router.js
│   ├── controller
│   │   └── home.js
│   ├── service(可选)
│   │   └── user.js
│   ├── middleware(可选)
│   │   └── response_time.js
│   ├── schedule(可选)
│   │   └── my_task.js
│   ├── public(可选)
│   │   └── reset.css
│   ├── view(可选)
│   │   └── home.tpl
│   └── extend(可选)
│       ├── helper.js(可选)
│       ├── request.js(可选)
│       ├── response.js(可选)
│       ├── context.js(可选)
│       ├── application.js(可选)
│       └── agent.js(可选)
├── config
|   ├── plugin.js
|   ├── config.default.js
│   ├── config.prod.js
|   ├── config.test.js(可选)
|   ├── config.local.js(可选)
|   └── config.unittest.js(可选)
└── test
    ├── middleware
    |   └── response_time.test.js
    └── controller
        └── home.test.js

由框架约定的目录:

  • app/router.js 用于配置 URL 路由规则,具体参见 Router。
  • app/controller/** 用于解析用户的输入,处理后返回相应的结果,具体参见 Controller。
  • app/service/** 用于编写业务逻辑层,建议使用,具体参见 Service。
  • app/middleware/** 用于编写中间件,具体参见 Middleware。
  • app/public/** 用于放置静态资源,具体参见内置插件 egg-static。
  • app/extend/** 用于框架的扩展,具体参见 框架扩展。
  • config/config.{env}.js 用于编写配置文件,具体参见 配置。
  • config/plugin.js 用于配置需要加载的插件,具体参见 插件。
  • test/** 用于单元测试,具体参见 单元测试。
  • app.js 和 agent.js 用于自定义启动时的初始化工作,具体参见 启动自定义。关于 agent.js 的作用,参见 Agent 机制。

由内置插件约定的目录:

  • app/public/** 用于放置静态资源,具体参见内置插件 egg-static。
  • app/schedule/** 用于定时任务,具体参见 定时任务。

若需自定义自己的目录规范,参见 Loader API

  • app/view/** 用于放置模板文件,具体参见 模板渲染。
  • app/model/** 用于放置领域模型,如 egg-sequelize 等领域类相关插件。

2.4 app/router.js

在router.js中必须暴露出去一个方法,这个方法接受一个参数,这个参数就算服务器的实例对象

module.exports = app => {
    console.log(app);
    /**
     * {
        env: 'local',
        name: 'egg',
        baseDir: 'C:\\Users\\Administrator\\Desktop\\egg',
        subdomainOffset: 2,
        config: '<egg config>',
        controller: '<egg controller>',
        httpclient: '<egg httpclient>',
        loggers: '<egg loggers>',
        middlewares: '<egg middlewares>',
        router: '<egg router>',
        serviceClasses: '<egg serviceClasses>'
        }
     */
    // 从服务器实例上解构出处理路由的对象和处理控制器的对象
    const { router, controller } = app

    // 监听路由请求
    // controller.home:相当于拿到了controller目录下的home.js(如果有多级可以通过.语法使用)
    router.get('/', controller.home.index)
}

2.5 app/controller/xx.js(xx.js可自定义名称)

这里是app/controller/home.js

const Controller = require('egg').Controller

class HomeController extends Controller {
    async index() {
        this.ctx.body = 'hello egg.js'
    }
}

module.exports = HomeController

在eggjs中会自动给控制器挂载一些属性:

  • this.ctx:当前请求的上下文 Context 对象的实例,通过它我们可以拿到框架封装好的处理当前请求的各种便捷属性和方法。
  • this.app:当前应用 Application 对象的实例,通过它我们可以拿到框架提供的全局对象和方法。
  • this.service:应用定义的 Service,通过它我们可以访问抽象出的业务层,等价于 this.ctx.service。
  • this.config:应用运行时的配置项。
  • this.logger:logger 对象,上面有四个方法(debug、info、warn、error),分别代表打印四个不同级别的日志。使用方法和效果与 context logger 中介绍的相同,但是通过这个 logger 对象记录的日志,在日志前面会加上打印该日志的文件路径,以便快速定位日志打印位置。

3. egg.js 处理get/post请求

app/router.js

module.exports = app => {
    const { router, controller } = app

    router.get('/getquery', controller.home.getquery)
    router.get('/getparams/:name/:age', controller.home.getparams)
    router.post('/postbody', controller.home.postbody)
}

app/controller/home.js

const Controller = require('egg').Controller

class HomeController extends Controller {
    // 获取query参数
    async getquery() {
        // let query = this.ctx.request.query
        let query = this.ctx.query
        this.ctx.body = query
    }

    // 获取动态路由参数
    async getparams() {
        let params = this.ctx.params
        this.ctx.body = params
    }

    // 获取post请求参数,post请求默认会被阻止掉,需要在 config/config.default.js 中添加配置
    async postbody() {
        let body = this.ctx.request.body
        this.ctx.body = body
    }
}

module.exports = HomeController

注意:post请求默认会被阻止掉,需要在 config/config.default.js 中添加配置

// config/config.default.js
module.exports = {
    security: {
        // 跨站点请求
        csrf: {
            ignoreJSON: true, // 默认为 false,设置为 true 时,会忽略所有 content-type 为 `application/json` 的请求
        }
    },
};

4. 处理静态资源

app目录下,新建public目录,在浏览器中通过http://xxx.com/public/xxx.png即可访问

5. 处理动态资源

需要使用插件(插件:特殊的中间件)

5.1 插件的使用

  1. 安装
npm i egg-view-ejs
  1. 对插件进行配置,在config目录下新建plugin.js文件
// exports.xxx
exports.ejs = {
    enable: true,
    package: 'egg-view-ejs' // 配置使用的插件
}
  1. config.default.js中添加配置
// config/config.default.js
module.exports = {
    // view目录下的文件
    view: {
        mapping: {
            '.html': 'ejs' // 在哪个后缀的文件使用插件
        }
    }
};
  1. app目录中新建view目录,将动态网页放到这个目录下,在控制器中通过上下文render方法渲染
const Controller = require('egg').Controller

class HomeController extends Controller {
    async index() {
        // app/view/demo.html
        await this.ctx.render('demo', { msg: '数据' })
    }
}

module.exports = HomeController

6. 数据处理

在eggjs中无论是数据库中的数据还是处理网络数据,都是在Service中处理的

每一次用户请求,框架都会实例化对应的 Service 实例。因为它继承自 egg.Service。和控制器一样,Service类的this上也挂载了很多属性:

  • this.ctx:当前请求的上下文 Context 对象实例。通过它,我们可以获取框架封装的处理当前请求的各种便捷属性和方法。

  • this.app:当前应用 Application 对象实例。通过它,我们可以访问框架提供的全局对象和方法。

  • this.service:应用定义的 Service。通过它,我们可以访问到其他业务层,等同于 this.ctx.service。

  • this.config:应用运行时的 配置项。

  • this.logger:logger 对象。它有四个方法(debug,info,warn,error),分别代表不同级别的日志。使用方法和效果与 context logger 所述一致。但通过这个 logger 记录的日志,在日志前会加上文件路径,方便定位日志位置。

this.ctx上下文对象上还挂载了其他的属性:

  • 使用 this.ctx.curl 发起网络调用。
  • 通过 this.ctx.service.otherService 调用其他 Service。
  • 调用 this.ctx.db 发起数据库操作,db 可能是插件预挂载到 app 上的模块。
  1. app目录下,新建service目录,新建js文件
// app/service/home.js
const Service = require('egg').Service

class HomeService extends Service {
    async findNews() {
        // 在Service定义的方法中处理数据库和网络的数据即可
        // 1. 发送get请求,不带参数
        let response = await this.ctx.curl('http://jsonplaceholder.typicode.com/posts')
        // 2. 发送get请求,带参数
        let response = await this.ctx.curl('http://jsonplaceholder.typicode.com/comments?postId=1')
        // 3. 发送post请求,不带参数
        let response = await this.ctx.curl('http://jsonplaceholder.typicode.com/posts', {
            method: 'POST'
        })
        // 4. 发送post请求,带参数
        let response = await this.ctx.curl('xxx', {
            method: 'POST',
            data: {
                id: 1
            }
        })
        return response.data
    }
}

module.exports = HomeService

注意:

  1. service目录必须放在app目录中
  2. service目录支持多级目录,如果是多级目录,在调用的时候可以通过链式调用
  3. service目录下的js文件,如果是以_首字母大写,那么在调用的时候必须转换成驼峰命名:
    get_user.js => getUser
    GetUser.js => getUser
  1. 在控制器中调用

通过ctx上下文的service获取

// app/controller/home.js
const Controller = require('egg').Controller

class HomeController extends Controller {
    async news() {
        let data = await this.ctx.service.home.findNews()
        this.ctx.body = data
    }
}

module.exports = HomeController
  1. 路由写接口
// app/router.js
module.exports = app => {
    const { router, controller } = app
    router.get('/news', controller.home.news)
}

7. 处理cookie

生成cookie和获取cookie

  1. 不加密
class HomeController extends Controller {
    async setCookie() {
        this.ctx.cookies.set('name', 'xiaotian', {
            path: '/',
            maxAge: 24 * 60 * 60 * 1000, // 有效时间
            httpOnly: true, // 只允许在服务端修改
            signed: true, // 生成cookie的时候,同时生成一个签名,根据config/config.default.js的keys设置。默认为true
        })

        this.ctx.body = '设置成功'
    }

    async getCookie() {
        let cookie = this.ctx.cookies.get('name')
        this.ctx.body = '获取cookie成功, ' + cookie
    }
}
  1. 加密和解密 encrypt: true
class HomeController extends Controller {
    async setCookie() {
        this.ctx.cookies.set('name', 'xiaotian', {
            path: '/',
            maxAge: 24 * 60 * 60 * 1000, // 有效时间
            httpOnly: true, // 只允许在服务端修改
            signed: true, // 生成cookie的时候,同时生成一个签名,根据config/config.default.js的keys设置。默认为true
            encrypt: true // 对cookie加密后再保存
        })

        this.ctx.body = '设置成功'
    }

    async getCookie() {
        let cookie = this.ctx.cookies.get('name', {
            signed: true,
            encrypt: true
        })
        this.ctx.body = '获取cookie成功, ' + cookie
    }
}

8. 处理日志

eggjs自动生成logs目录

8.1 日志分类

  • ${appInfo.name}-web.log,例如 example-app-web.log,应用相关日志,供应用开发者使用的日志。我们在绝大多数情况下都在使用它。
  • egg-web.log:框架内核、插件日志。
  • common-error.log:ctx.logger.err 输出的错误日志
  • egg-agent.log:进程日志,框架和使用到 agent 进程执行任务的插件会打印一些日志到这里。
  • egg-schedule.log:定时任务的日志。

8.2 日志切割

在eggjs中默认会自动切割日志,每一天就是一个新的日志文件

9. 定时任务

9.1 使用场景:

  • 定时进行文件切割,临时文件删除
  • 定时上报应用状态

9.2 如何编写定时任务:

  1. app目录下新建schedule目录

所有的定时任务都统一存放在 app/schedule 目录下,每一个文件都是一个独立的定时任务,可以配置定时任务的属性和要执行的方法。

const Subscription = require('egg').Subscription;

class UpdateCache extends Subscription {
  // 通过 schedule 属性来设置定时任务的执行间隔等配置
  static get schedule() {
    return {
      interval: '1m', // 1 分钟间隔
      type: 'all', // all表示当前服务器上所有相同node进程(之前说过同一个node项目可以开启多个进程)都执行
    };
  }

  // subscribe 是真正定时任务执行时被运行的函数
  async subscribe() {
    const res = await this.ctx.curl('http://www.api.com/cache', {
      dataType: 'json',
    });
    // cache 是自定义的
    this.ctx.app.cache = res.data;
  }
}

module.exports = UpdateCache;

还可以简写为:

module.exports = {
  schedule: {
    interval: '1m', // 1 分钟间隔
    type: 'all',
  },
  async task(ctx) {
    const res = await ctx.curl('http://www.api.com/cache', {
      dataType: 'json',
    });
    ctx.app.cache = res.data;
  },
};

这个定时任务会在每一个 Worker 进程上每 1 分钟执行一次,将远程数据请求回来挂载到 app.cache 上。(cache是自定义的)

10. 启动自定义

我们常常需要在应用启动期间进行一些初始化工作,待初始化完成后,应用才可以启动成功,并开始对外提供服务。

框架提供了统一的入口文件(app.js)进行启动过程自定义。这个文件需要返回一个 Boot 类。我们可以通过定义 Boot 类中的生命周期方法来执行启动应用过程中的初始化工作。

框架提供了以下 生命周期函数 供开发人员处理:

  • 配置文件即将加载,这是最后动态修改配置的时机(configWillLoad);
  • 配置文件加载完成(configDidLoad);
  • 文件加载完成(didLoad);
  • 插件启动完毕(willReady);
  • worker 准备就绪(didReady);
  • 应用启动完成(serverDidReady);
  • 应用即将关闭(beforeClose)。

在根目录下新建app.js

// app.js
class AppBootHook {
    constructor(app) {
        this.app = app;
    }

    configWillLoad() {
        // 此时 config 文件已经被读取并合并,但还并未生效
        // 这是应用层修改配置的最后机会
        // 注意:此函数只支持同步调用

        // 例如:参数中的密码是加密的,在此处进行解密
        this.app.config.mysql.password = decrypt(this.app.config.mysql.password);
        // 例如:插入一个中间件到框架的 coreMiddleware 之间
        const statusIdx = this.app.config.coreMiddleware.indexOf('status');
        this.app.config.coreMiddleware.splice(statusIdx + 1, 0, 'limit');
    }

    async didLoad() {
        // 所有配置已经加载完毕
        // 可以用来加载应用自定义的文件,启动自定义服务

        // 例如:创建自定义应用的实例
        this.app.queue = new Queue(this.app.config.queue);
        await this.app.queue.init();

        // 例如:加载自定义目录
        this.app.loader.loadToContext(path.join(__dirname, 'app/tasks'), 'tasks', {
            fieldClass: 'tasksClasses',
        });
    }

    async willReady() {
        // 所有插件已启动完毕,但应用整体尚未 ready
        // 可进行数据初始化等操作,这些操作成功后才启动应用

        // 例如:从数据库加载数据到内存缓存
        this.app.cacheData = await this.app.model.query(QUERY_CACHE_SQL);
    }

    async didReady() {
        // 应用已启动完毕

        const ctx = await this.app.createAnonymousContext();
        await ctx.service.Biz.request();
    }

    async serverDidReady() {
        // http/https 服务器已启动,开始接收外部请求
        // 此时可以从 app.server 获取 server 实例

        // 可以执行一些需要初始化的文件
    }
}

module.exports = AppBootHook;

11. 操作mysql

11.1 安装

npm i --save egg-mysql

11.2 开启插件

// config/plugin.js
exports.mysql = {
  enable: true,
  package: 'egg-mysql'
};

11.3 配置

// config/config.default.js
exports.mysql = {
  // 单数据库信息配置
  client: {
    // host
    host: 'mysql.com',
    // 端口号
    port: '3306',
    // 用户名
    user: 'test_user',
    // 密码
    password: 'test_password',
    // 数据库名
    database: 'test'
  },
  // 是否加载到 app 上,默认开启
  app: true,
  // 是否加载到 agent 上,默认关闭
  agent: false
};

11.4 使用

await app.mysql.query(sql, values); // 单实例可以直接通过 app.mysql 访问

11.5 如何编写 CRUD 语句

Create

可以直接使用 insert 方法插入一条记录

// 插入
const result = await this.app.mysql.insert('posts', { title: 'Hello World' }); // 在 posts 表中,插入 title 为 Hello World 的记录

// SQL 语句相当于
// INSERT INTO `posts`(`title`) VALUES('Hello World');

console.log(result);
// 输出为
// {
//   fieldCount: 0,
//   affectedRows: 1,
//   insertId: 3710,
//   serverStatus: 2,
//   warningCount: 2,
//   message: '',
//   protocol41: true,
//   changedRows: 0
// }

// 判断插入成功
const insertSuccess = result.affectedRows === 1;
Read

可以直接使用 get 方法或 select 方法获取一条或多条记录。select 方法支持条件查询与结果定制。 可以使用 count 方法对查询结果的所有行进行计数。

  • 查询一条记录
const post = await this.app.mysql.get('posts', { id: 12 });

// SQL 语句相当于
// SELECT * FROM `posts` WHERE `id` = 12 LIMIT 0, 1;
  • 查询全表
const results = await this.app.mysql.select('posts');

// SQL 语句相当于
// SELECT * FROM `posts`;
  • 条件查询和结果定制
const results = await this.app.mysql.select('posts', { // 搜索 posts 表
  where: { status: 'draft', author: ['author1', 'author2'] }, // WHERE 条件
  columns: ['author', 'title'], // 要查询的字段
  orders: [['created_at','desc'], ['id','desc']], // 排序方式
  limit: 10, // 返回数据量
  offset: 0, // 数据偏移量
});

// SQL 语句相当于
// SELECT `author`, `title` FROM `posts`
// WHERE `status` = 'draft' AND `author` IN('author1','author2')
// ORDER BY `created_at` DESC, `id` DESC LIMIT 0, 10;
  • 统计查询结果的行数
const total = await this.app.mysql.count('posts', { status: 'published' }); // 统计 posts 表中 status 为 published 的行数

// SQL 语句相当于
// SELECT COUNT(*) FROM `posts` WHERE `status` = 'published'
Update

可以直接使用 update 方法更新数据库记录。

// 修改数据
const row = {
  id: 123,
  name: 'fengmk2',
  otherField: 'other field value', // 其他想要更新的字段
  modifiedAt: this.app.mysql.literals.now, // 数据库服务器上的当前时间
};
const result = await this.app.mysql.update('posts', row); // 更新 posts 表中的记录

// SQL 语句相当于
// UPDATE `posts` SET `name` = 'fengmk2', `modifiedAt` = NOW() WHERE `id` = 123;

// 判断更新成功
const updateSuccess = result.affectedRows === 1;

// 如果主键是自定义的 ID 名称,如 custom_id,则需要在 `where` 里配置
const row2 = {
  name: 'fengmk2',
  otherField: 'other field value', // 其他想要更新的字段
  modifiedAt: this.app.mysql.literals.now, // 数据库服务器上的当前时间
};

const options = {
  where: {
    custom_id: 456
  }
};
const result2 = await this.app.mysql.update('posts', row2, options); // 更新 posts 表中的记录

// SQL 语句相当于
// UPDATE `posts` SET `name` = 'fengmk2', `modifiedAt` = NOW() WHERE `custom_id` = 456 ;

// 判断更新成功
const updateSuccess2 = result2.affectedRows === 1;
Delete

可以直接使用 delete 方法删除数据库记录。

const result = await this.app.mysql.delete('posts', {
  author: 'fengmk2',
});

// SQL 语句相当于
// DELETE FROM `posts` WHERE `author` = 'fengmk2';

11.6 直接执行 SQL 语句

插件本身也支持拼接与直接执行 SQL 语句。使用 query 方法可以执行合法的 SQL 语句。

const postId = 1;
const results = await this.app.mysql.query('update posts set hits = (hits + ?) where id = ?', [1, postId]);

// => update posts set hits = (hits + 1) where id = 1;

12. eggjs的配置文件

config
|- config.default.js  所有环境都会加载(如果其他文件有同名,会覆盖掉默认的)
|- config.prod.js  只有上线环境才会加载
|- config.unittest.js  只有测试环境才会加载
|- config.local.js  只有开发环境才会加载

config.default.js 为默认的配置文件,所有环境都会加载这个配置文件,一般也会作为开发环境的默认配置文件。

当指定 env 时,会同时加载默认配置和对应的配置(具名配置)文件。具名配置和默认配置将合并成最终配置,具名配置项会覆盖默认配置文件的同名配置。例如,prod 环境会加载 config.prod.jsconfig.default.js 文件,config.prod.js 会覆盖 config.default.js 的同名配置。

但通常会用 cross-env 第三方库来配置项目环境

二、pm2

1. pm2 的好处

  1. pm2 进程守护可以在程序崩溃后自动重启
  2. pm2 自带日志记录功能,可以很方便的记录错误日志和自定义日志
  3. pm2 可以启动多个node进程,充分利用服务器资源

2. pm2 的基本使用

2.1 安装pm2

npm i pm2 -g

2.2 查看pm2版本

pm2 --version

2.3 启动pm2

pm2 start index.js

3. pm2 常用指令

3.1 启动应用程序

pm2 start index.js

3.2 列出启动的所有应用程序

pm2 list

3.3 重启应用程序

pm2 restart 应用id/name

3.4 杀死并重启所有进程

pm2 restart all

3.5 查看应用程序详细信息

pm2 info 应用id/name

pm2 show

3.6 显示指定应用程序的日志

pm2 log 应用id/name

3.7 监控应用程序

pm2 monit 应用id/name

3.8 停止应用程序

pm2 stop 应用id/name

3.9 停止所有应用程序

pm2 stop all

3.10 关闭并删除指定应用程序

pm2 delete 应用id/name

3.11 关闭并删除所有应用程序

pm2 delete all

3.12 杀死pm2管理的所有进程

pm2 kill

4. pm2常用配置

  1. 新建pm2.confif.json文件
{
    "name": "应用程序名称",
    "script": "入口文件名称",
    "watch": true, // 文件被修改是否自动重启
    "ignore_watch": [ // 忽略监听哪些文件的改变
        "node_modules",
        "logs"
    ],
    "error_file": "logs/错误日志文件名称.log",
    "out_file": "logs/自定义日志文件名称.log",
    "log_date_format": "yyyy-MM-dd HH:mm:ss" // 给日志添加时间
}
  1. 运行
pm2 start pm2.config.json

5. 负载均衡

node是单线程的,服务器是多核的,要充分利用服务器资源,可以使用pm2启动多个node进程,只需要在配置文件中增加 instances 配置,可以启动多个node进程(想启动几个就启动几个,但是不能超过服务器cpu的核数)

{
    "name": "应用程序名称",
    "script": "入口文件名称",
    "watch": true, // 文件被修改是否自动重启
    "ignore_watch": [ // 忽略监听哪些文件的改变
        "node_modules",
        "logs"
    ],
    "error_file": "logs/错误日志文件名称.log",
    "out_file": "logs/自定义日志文件名称.log",
    "log_date_format": "yyyy-MM-dd HH:mm:ss", // 给日志添加时间
    "instances": 4 // 开启多个node进程,不能超过cpu核数
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Go语言--函数类型、匿名函数和闭包

在Go语言中&#xff0c;函数也是一种数据类型&#xff0c;我们可以通过 type 来定义它&#xff0c;它的类型就是所有拥有相同的参数&#xff0c;相同的返回值的一种类型。 语法 通过type给函数类型起名&#xff0c;然后通过名字进行函数的调用 好处&#xff1a;多态 通过统…

【营销策划模型大全】私域运营必备

营销策划模型大全&#xff1a;战略屋品牌屋、电商运营模型、营销战略、新媒体运营模型、品牌模型、私域运营模型…… 该文档是一份策划总监工作模型的汇总&#xff0c;包括战略屋/品牌屋模型、营销战略模型、品牌相关模型、电商运营模型、新媒体运营模型和私域运营模型等&…

​​服务拆分的原则

目录 一、单一职责原则 二、服务自治原则 三、单向依赖 一、单一职责原则 单⼀职责原则原本是面向对象设计中的⼀个基本原则, 它指的是⼀个类应该专注于单⼀功能. 不要存在多于⼀个导致类变更的原因 在微服务架构中, ⼀个微服务也应该只负责⼀个功能或业务领域, 每个服务应该…

C++ | Leetcode C++题解之第216题组合总和III

题目&#xff1a; 题解&#xff1a; class Solution { private:vector<vector<int>> res;void backtracking(int k, int n, vector<int> ans){if(k 0 || n < 0){if(k 0 && n 0){res.emplace_back(ans);}return;}int start (ans.size() 0 ?…

恒创科技:HTTP错误码403禁止意味着什么,怎么修复它?

HTTP错误码403禁止意味着客户端无权访问特定网页或服务器。403 错误表示客户端存在问题&#xff0c;无论用户使用的是哪种网络浏览器&#xff0c;都可能发生这种情况。幸运的是&#xff0c;阻止服务器允许访问特定页面的问题通常可以修复。以下是一些常见原因和相应的解决方案。…

【Android源码】Gerrit安装

前言 如果你打开 https://android.googlesource.com/platform/manifest&#xff0c;就会发现&#xff0c;google官方管理Android源码&#xff0c;使用的是Gerrit。Android系统源码是非常大的&#xff0c;用Git肯定是不适合。对于大型项目&#xff0c;得用Gerrit&#xff0c;今…

背包问题(一)

一.P3985 不开心的金明(01背包变式) 解析: 一开始没有看数据范围,直接当01背包直接写了,结果最后4个测试点RE,一看到数据范围就老实了,1e9的数据,数组直接炸,所以不能直接使用一维的01背包.看了一下题解,部分人是通过极差对数据进行分类,按照300进行分开,使用贪心和dp一起做. …

7_1_SVPWM概述

1、SPWM 正弦脉宽调制法&#xff08;SPWM&#xff09;是将每一正弦周期内的多个脉冲作自然或规则的宽度调制&#xff0c;使其依次调制出相当于正弦函数值的相位角和面积等效于正弦波的脉冲序列&#xff0c;形成等幅不等宽的正弦化电流输出。 通过调整占空比使等效电流近似为正弦…

DT浏览器很好用

DT浏览器是一款简单的浏览器&#xff0c;又是强大的浏览器&#xff0c;界面简洁大方&#xff0c;软件使用流畅。DT浏览器的网址收藏&#xff0c;人工智能写作&#xff0c;书法笔记等功能与众不同。DT浏览器的图文识别功能和笔记本搭配使用&#xff0c;可以对内容编辑修改和保存…

时序模型综述论文

时序模型综述论文&#xff1a; A Survey of Time Series Foundation Models: Generalizing Time Series Representation with Large Language Model

c++ String

1.string类 还记得我们数据结构学的串吗&#xff0c;现在在c中&#xff0c;我们有了c提供的标准库&#xff0c;它是一个写好的类&#xff0c;非常方便使用 1. string是表示字符串的字符串类 2. 该类的接口与常规容器的接口基本相同&#xff0c;再添加了一些专门用来操作strin…

学习笔记——动态路由——OSPF工作原理(SPF算法)

3、SPF算法 SPF算法(最短路径优先算法&#xff0c;也称Dijkstra算法)由荷兰科学家狄克斯特拉于1959年提出的。 SPF算法将每一个路由器作为根(ROOT)来计算其到每一个目的地路由器的距离&#xff0c;每一个路由器根据一个统一的数据库会计算出路由域的拓扑结构图&#xff0c;该…

MySQL:MySQL总结

文章目录 MySQL思维导图基础实际在 Innodb 存储引擎中&#xff0c;会用一个特殊的记录来标识最后一条记录&#xff0c;该特殊的记录的名字叫 supremum pseudo-record &#xff0c;所以扫描第二行的时候&#xff0c;也就扫描到了这个特殊记录的时候&#xff0c;会对该主键索引加…

基于Bootstrap Blazor开源的.NET通用后台权限管理系统

前言 今天大姚给大家分享一个基于Bootstrap Blazor开源的.NET通用后台权限管理系统&#xff0c;后台管理页面兼容所有主流浏览器&#xff0c;完全响应式布局&#xff08;支持电脑、平板、手机等所有主流设备&#xff09;&#xff0c;可切换至 Blazor 多 Tabs 模式&#xff0c;…

JVM原理(十六):JVM虚拟机类型擦除与泛型发展

1. 泛型 泛型的本质是参数化类型或者参数化多态的应用&#xff0c;即可以将操作的数据类型指定为方法签名中的一种特殊参数&#xff0c;这种参数类型能够用在类、接口和方法的创建中&#xff0c;分别构成泛型类、泛型接口和泛型方法。 泛型让程序员能够以针对泛化的数据类型编…

手动访问mongo和ES插入和查询

1、手动访问mongo 1.1、mongo连接数据库 1.2、mongo插入和查询 db.hmf_test.insert( { "aoeId": "1", "aoeAes": "吴秀梅", "aoeSm4": "北京xx网络技术有限公司.", "aoeSm4_a": "…

【BUUCTF-PWN】4-ciscn_2019_n_1

参考&#xff1a;BUUCTF-ciscn_2019_n_1 - 纸鸢asahi - 博客园 (cnblogs.com) buuctf 刷题记录_PWN ciscn_2019_n_1 - MuRKuo - 博客园 (cnblogs.com) 从题海中入门&#xff08;四&#xff09;ciscn_2019_n_1 - FreeBuf网络安全行业门户 ciscn_2019_n_1 ——两种解法_0x4134800…

抗震支吊架安装

抗震支吊架系统安装指导 设计要求&#xff1a; 本工程采用抗震支吊架系统&#xff0c;请根据深化设计提供的图纸及安装材料表等进行安装。 材料要求&#xff1a; 符合 CJ/T476-2015《建筑机电设备抗震支吊架通用技术条件》及 CECS 420:2015《抗震支吊架安装及验收规程》 槽…

Go语言--延迟调用defer、获取命令行参数、局部变量以及全局变量

延迟调用defer 关键字 defer 用于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意&#xff0c;defer语句只能出现在函数或方法的内部。 defer 语句经常被用于处理成对的操作&#xff0c;如打开、关闭、连接、断开连接、加锁、释放锁。通过defer 机制&#xff0…

生态共建 | 华宇TAS应用中间件与新华三服务器完成兼容互认证

近日&#xff0c;华宇TAS应用中间件完成与新华三技术有限公司的R4930系列和R4970 G7服务器的兼容适配&#xff0c;认证测试报告显示&#xff0c;双方产品兼容性良好&#xff0c;运行稳定、安全&#xff0c;可以满足用户对双方功能的要求。 新华三技术有限公司 新华三技术有限公…