Blogdetails

vscode正则查找替换

vscode正则查找替换

月盾

查找某一类型字符串:

正则表达式onclick=.*" 会查找到所有: onclick="_msq.push(['trackEvent', '210074305d6b0409-09c7759e04e98528', ''pcpid', '']);"

onclick=是固定一样的字符,

.代表除\r和\n之外的任意字符,等价于[^\r\n]

*代表匹配前面的模式 0或多次 {0,}

"这是字符串最后一个字符

在vscode中的效果如下: vscode正则查找替换 至于要替换成什么就看自己需求了,如果要给选中的字符串包裹字符串则需要修改成这样:

查找替换

查找:(onclick=.*")

替换:aaa($1)

结果:

vscode正则查找替换

替换字符串两头,保留中间

vscode正则查找替换

两部分文字交换位置

相同模式的文字交换位置。

查找:(\(\d{4}-\d{1,2}-\d{2}\)) (\[.*\))

替换:$2 $1

结果:

vscode正则查找替换

vscode中一对括号()代表一个变量。

第一组正则 (\(\d{4}-\d{1,2}-\d{2}\)) 对应 $1

第二组正则 (\[.*\))对应 $2,以此类推。

所以,可以查找多组数据,在替换部分将两个对应变量交换位置即可。

typescript不检查node_moduls

月盾

tsconfig.json 中 exclude node_modules,但 tsc 还是报错。

node_modules/connect-mongo/src/types.d.ts:113:66 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

113         get: (sid: string, callback: (err: any, session: Express.SessionData | null) => void) => void;                                                                     ~~~~~~~~~~~

node_modules/connect-mongo/src/types.d.ts:114:45 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

114         set: (sid: string, session: Express.SessionData, callback?: (err: any) => void) => void;
                                                ~~~~~~~~~~~

node_modules/connect-mongo/src/types.d.ts:118:47 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

118         touch: (sid: string, session: Express.SessionData, callback?: (err: any) => void) => void;

另一种解决方案:

朱雀发布系统支持scp(rsync)发布

朱雀发布系统支持scp(rsync)发布

月盾

去年花了三天时间开发了一个简易版的nodejs发布系统,它是基于pm2自带的deploy机制开发的,主要原理就是在两台装有pm2的机器直接通信,并执行相应的命令。再往简单了说就是在发布机上远程执行命令,而朱雀发布系统提供了一个图形界面而已。

当时把这个系统定位为nodejs专用发布系统,因为它依赖了pm2,而pm2则是nodejs专用的进程管理工具,其他语言用不到。这个系统的上线也算是解决了我司一直以来没有合适的nodejs发布系统的空缺。

经过9个月的使用,也算比较稳定。但是也存在问题,最大的问题是部署应用方面比较繁琐。

基本流程如下:

  • 发布机和应用服务器设置ssh通信配置。
  • 应用服务器安装git(有自带,但是版本太旧)。
  • 配置git用户名,邮箱。
  • 生成ssh公钥。
  • 把应用服务器的公钥配置到git代码服务器上。以便能拉代码。
  • 发布机远程执行git pull来代码操作,各应用服务把代码拉取下来。
  • 执行编译打包操作。
  • 执行重启服务操作。

部署一次系统还是比较麻烦的,如果应用服务器有多台,可能还要重复这样的操作多次,实在比较麻烦。

使用scp同步代码的方式会比较简单一些,但是像nodejs这样的项目,node_module占了很大比重,如果每次都打包的话会拖慢同步速度,而scp又不具备排除文件夹的能力。

最后找到了rsync命令可以满足需求。然后就是基于rsync实现了一版。不用再依赖pm2,应用服务器也不用强制使用git了。

朱雀发布系统

朱雀和Jenkins对比如何?

要说Jenkins那绝对是持续集成领域的老大哥,自然是功能强大。但是每个团队和产品有其特殊性,Jenkins并不完全适用。而朱雀也有其优势。

  • 朱雀本身部署简单,使用go开发,不依赖运行时,无需安装,开箱即用。
  • 目前支持和测试过的数据库有sqlite3(目前用的,不需要繁琐的安装过程)和mysql。
  • 配置简单。
  • 部署发布一键完成,不需要单独的部署过程。
  • 并行发布。
  • 审批,通知,权限。
  • 开源,可定制开发。

项目地址:朱雀发布系统

一体LED可伸缩变焦手电筒拆解

一体LED可伸缩变焦手电筒拆解

月盾

先看看一体LED可伸缩变焦手电筒长什么样子

一体LED可伸缩变焦手电筒

一体LED可伸缩变焦手电筒

一体LED可伸缩变焦手电筒

这种手电筒刚买回来几个月就不亮了,以为没电了,充电了也不行,想拆开看看却发现无从下手,手电筒是一体的,并没有地方可扭开。

按钮部位看起来是可拆解的,想要转动一下,发现无法转动,最后抱着死马当活马医的心态,大不了拆坏了,随便用工具撬了下尾部凹槽部分,发现有活动迹象,然后就继续撬,果然是大力出奇迹。最后就是下面图展示的样子,有明显损坏痕迹,不过也没办法,实在不好拆。拆开后发现电池线断了,将就着接上后就好了。

最后,虽然不建议买这种手电筒,但是既然您看到本文了,那也就有救了。 祝大家五一快乐!

一体LED可伸缩变焦手电筒

easy-monitor qps监控

月盾

Easy-Monitor是一款轻量级的Node性能监控工具,仅仅需要项目入口 require 一次,就可以非常便捷地展示出进程的状态细节。

Easy-Monitor主要提供以下的功能:

  • 找出执行时长耗费最久的5个或者更多的函数
  • 找出那些执行时间超出预期的函数
  • 找出v8引擎无法优化的函数

Easy-Monitor的特点:

  • 轻量级:非传统C/S物理分离模式,require 后即可使用,没有额外的监控server/agent部署成本。
  • 运行时:针对的是运行时的函数性能以及内存细节进行处理展示,可用于线上生产环境项目。
  • 无状态:永远展示的是开发者访问时的业务进程状态

关于监控qps,作者在文档中并没有提到qps这样的关键字,很多人也不知道怎么监控qps。 不过在监控界面中有这样的指标数据: easy-monitor-qps

所以我从源码入手,找到了该数据指标的源头是这样的:

const data = {
      osCpu: Number((used_cpu * 100).toFixed(2)),
      osMem: Number((used_memory_percent * 100).toFixed(2)),
      maxDisk: max_disk_usage,
      disks: disks_json,
      load1: Number(load1.toFixed(2)),
      load5: Number(load5.toFixed(2)),
      load15: Number(load15.toFixed(2)),
      nodeCount: node_count,
      scavengeTotal: total_scavange_duration,
      scavengeAverage: scavange_duration_last_record,
      marksweepTotal: total_marksweep_duration,
      marksweepAverage: marksweep_duration_last_record,
      qps: Number((http_response_sent / 60).toFixed(2)),
      rtExpired: http_patch_timeout,
      rtAverage: http_rt,
    };

这是接口返回的数据,包含了qps数据,那么qps实际上就是http_response_sent,所以监控中就可以这样设置qps了: @http_response_sent/60 > 10

使用sveltekit开发一个服务端渲染(SSR)项目

月盾

上篇简单介绍了sapper和sveltekit的发展,目前sveltekit还只是Beta版本,有很多不确定因素存在,有可能会有大的变更,所以还不推荐在生产环境中使用,不过在个人项目和小项目中可以大胆尝试。

今天我们就正式使用sveltekit开发一个web项目。

第一步:创建项目

mkdir my-app

cd my-app

npm init svelte@next

npm install

npm run dev

这样就可以创建一个简单的项目了,不过和我们真实需求还有些差距,既然是使用sveltekit,那么最重要的原因是其支持服务端渲染了。这就需要从服务端获取数据,接下来就实现这样的需求。

第二步:路由

和sapper一样,sveltekit也是基于文件系统的的路由器,这就需要我们来合理的组织目录结构。路由的核心目录是src/routes,当然,这个也是可配置的,按照自己的需求修改svelte.config.cjs,参考文档:https://kit.svelte.dev/docs#configuration

我们以一个博客系统为例,在scr/routes下创建blog目录,光有目录还不行,如果想要访问 /blog 路由,还需要创建index.svelte文件,内容如下:

<script context="module">
    /**
	 * @type {import('@sveltejs/kit').Load}
	 */
	export async function load({ page, fetch, session, context }) {
		return fetch(`blog.json`)// index.json.js = blog.json或blog/blog.json
			.then((r) => r.json())
			.then((posts) => {
				console.log(posts);
				return {
					props: {
						posts
					}
				};
			});
	}
</script>

<script>
	export let posts;
</script>

<svelte:head>
	<title>Blog</title>
</svelte:head>

<h1>Recent posts</h1>

<ul>
	{#each posts as post}
		<!-- we're using the non-standard `rel=prefetch` attribute to
				tell Sapper to load the data for the page as soon as
				the user hovers over the link or taps it, instead of
				waiting for the 'click' event -->
		<li><a rel="prefetch" href="blog/{post.slug}">{post.title}</a></li>
	{/each}
</ul>

<style lang="less">
	ul {
		margin: 0 0 1em 0;
		line-height: 1.5;
	}
</style>

index.svelte中load函数是一个关键函数,它接收四个参数:

关于svelte框架——sapper和sveltekit的发展

月盾

虽然您可能现在还没有听说过svelte,但是其实svelte的发展速度超过了你的想象。

本文主要讲的是关于sapper和sveltekit这两款框架的发展。

svelte作者里奇·哈里斯(Rich Harris)在2020年10月的svelte峰会上表示:sapper永远不会发布1.0版本。

也就是说sapper不会发布正式版,一直处于非稳定版本。也可能放弃更新。

主要原因是sapper多年来代码库变得凌乱,但更主要的原因是最近网络发生了很大变化。

而作者放弃sapper后的另一种选择是开发SvelteKit

SapperSvelteKit都是svelte的开发框架,类似于vue的nuxt框架。

sveltekit包含的功能有:

  • 服务端渲染(SSR)

  • 路由

  • typescript支持

  • less, scss支持

  • serverless

  • vite打包

可以看到,sveltekit几乎包含了所有我们想要的功能,既能高效开发,又有高性能。

创建sveltekit的方法:


mkdir my-app

cd my-app

npm init svelte@next

npm install

npm run dev

需要注意,您的nodejs版本需要更新到v12以上,否则可能出现以下错误:


$ npm run dev -- --open


> sveltekit-app@0.0.1 dev D:\workspace\sveltekit-app

> svelte-kit dev "--open"

D:\workspace\sveltekit-app\node_modules\@sveltejs\kit\svelte-kit.js:2

import './dist/cli.js';

	SyntaxError: Unexpected string
	at Module._compile (internal/modules/cjs/loader.js:723:23)
	at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
	at Module.load (internal/modules/cjs/loader.js:653:32)
	at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
	at Function.Module._load (internal/modules/cjs/loader.js:585:3)
	at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
	at startup (internal/bootstrap/node.js:283:19)
	at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
									   
	npm ERR! code ELIFECYCLE
	npm ERR! errno 1
	npm ERR! sveltekit-app@0.0.1 dev: `svelte-kit dev "--open"`
	npm ERR! Exit status 1
	npm ERR!
	npm ERR! Failed at the sveltekit-app@0.0.1 dev script.
	npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
   

使用sveltekit开发一个服务端渲染(SSR)项目

/bin/rm: argument list too long

月盾

有人在服务器上不小心执行了rm -fr /*,而我想在删除某个文件夹下面的文件却遇到/bin/rm: argument list too long

意思是我删除的文件太多了,这倒有点稀奇。

那怎么才能删除呢?

试着直接删除目录也不行,这问题能难倒我,但难不倒百度。

使用ls | xargs -n 1000 rm -fr ls删除,可是一直在输出错误提示:


rm: invalid option -- 's'

Try 'rm --help' for more information.

出现这个情况的原因是文件名是这种类型:-abc.txt。就是前面带了-

删除方式是:


rm -- -foo


rm ./-foo

只看见报错,到底有没有在删文件?想要知道,那么把删除命令改成这样

ls | xargs -n 1000 rm -frv ls

rm命令加个参数v,用来显示删除信息。

最终会输出这样的提示信息,

removed ‘zMJ1MgKdzgbjNsWawUcX7OO3WspyxZEU.json’

为了让它在后台执行可以这样:

ls | xargs -n 1000 rm -frv ls &

firefox火狐新标签中打开书签

月盾

火狐浏览器新建标签总是在当前打开标签之后,而不是在最后一个标签后新建。 1.about:config 2.browser.tabs.insertAfterCurrent设为false。

新标签中打开书签 browser.tabs.loadBookmarksInTabs设置true。

新标签中打开搜索 browser.search.openintab设置true.

nestjs中使用携程Apollo配置中心

月盾

nest框架官方文档中使用的是本地文件配置,也就是@nestjs/config包。本地配置文件的好处是使用简单,但是对于一些更新较快的项目,难免会增加配置数据,曾经吃过不少配置文件的亏,在发布的时候很容易因为缺少配置文件直接把服务发挂了,或者需要在服务器上修改配置很容易修改错误导致服务发布失败。

集中的配置中心可以解决上面问题,本文以apollo配置中心为例来说明。 在使用的过程中需要注意以下问题:从配置中心获取数据库连接信息,再去连接会连接失败,因为在连接的时候还没有获取到配置信息。 先看代码再解释。

// main.ts
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter, NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { MyLogger } from './libs/mylog.service';
import { join } from 'path';
const Apollo = require('node-apollo');
const dotenv = require('dotenv');

async function bootstrap() {
    try {
        const root = join(__dirname, '../');
        let envFile = join(root, '.env')
        dotenv.config({ "path": envFile })
        const {
            APOLLO_APPID,
            APOLLO_ENV,
            APOLLO_HOST,
            APOLLO_NAMESPACE,
            APOLLO_PORT,
            APOLLO_TOKEN,
            APOLLO_ClUSTER
        } = process.env;
        let apolloEnv = {
            configServerUrl: `http://${APOLLO_HOST}:${APOLLO_PORT}`,
            appId: `${APOLLO_APPID}`,
            clusterName: `${APOLLO_ClUSTER}`,
            apolloEnv: `${APOLLO_ENV}`,
            token: `${APOLLO_TOKEN}`,
            namespaceName: [`${APOLLO_NAMESPACE}`]
        };
        // 获取到的配置信息
        let zmConf = await Apollo.remoteConfigService(apolloEnv);
        console.log(">>>>>>>main.config", zmConf);
        process.env = Object.assign(process.env, zmConf);
    } catch (err) {
        console.log(`获取环境变量异常:${err}`)
    }

    const app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter());
    app.useLogger(app.get(MyLogger));

    await app.listen(3434, () => {
        const logger = new MyLogger('main.ts');
        logger.debug(process.env.NODE_ENV, 'main.ts');
        logger.log('server start on http://localhost:3434');
    });
}
bootstrap();
// app.module.ts
@Module({
    imports: [
        // MongooseModule.forRoot(`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DATABASE}`),
        MongooseModule.forRootAsync({
            useFactory: () => ({
                uri: `mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DATABASE}`
            }),
        }),
        //  Nest can't resolve dependencies of the AppService (?). Please make sure that the argument ArticleService at index [0] is available in the AppModule context.
        // 在article.service中exports:[ArticleService]
        ArticleModule,
        LoggerModule,
        // HttpModule,
    ], // 导入模块所需的导入模块列表
    controllers: [AppController], // 必须创建的一组控制器
    providers: [AppService, MyLogger], // 由 Nest 注入器实例化的提供者,并且可以在整个模块中共享
})
export class AppModule implements NestModule {
    // 中间件模块在此处添加,可以给某一部分增加中间件,如果要全局增加则在main.ts中使用app.use添加
    configure(consumer: MiddlewareConsumer) {
        consumer
            .apply()
            // .with('中间件参数')
            .forRoutes('/*');
    }
}

使用MongooseModule.forRoot连接数据库肯定是不行的,需要改成异步的: