月盾的博客

gitpage Hugo统计每一篇文章浏览量

月盾

接上篇《博客迁移至hugo gitpage》后,因为缺失了每一篇文章的浏览量,而hugo又不具备这样的功能,原因还是gitpage不具备数据存储能力,自然就没办法统计每一篇文章的浏览量了。原本想使用自己服务器提供一个接口来记录,但发现https协议不支持调用http协议的接口,会出现block:mixed-content错误。

错误:https页面去发送http请求报错(浏览器阻止https发送http请求)

问题是明确了,但是我也没办法提供https的接口,免费的证书也用在了www.yuedun.wang上了。

后来想到了leancloud,直接在前端调用api,将数据存储在云端。

<script src="//cdn.jsdelivr.net/npm/leancloud-storage@4.11.1/dist/av-min.js"></script>
<script>
  // https://leancloud.cn/docs/sdk_setup-js.html#hash14962003 
  // https://leancloud.cn/docs/leanstorage_guide-js.html#hash813593086
  const appId = "xxx";
  const appKey = "xxx";
  const serverURL = "xxx";
  AV.init({ appId, appKey, serverURL });
  function updateCollect() {
    const collect = new AV.Query('Collect');
    const url = location.pathname;
    collect.select(['url', 'pv'])
    collect.equalTo('url', url);
    collect.first().then((col) => {
      if (!col) {
        // 声明 class
        const Collect = AV.Object.extend('Collect');
        // 构建对象
        const collect = new Collect();
        // 为属性赋值
        collect.set('url', url);
        collect.set('pv', 1);
        collect.save()
      } else {
        col.increment('pv', 1)
        col.save();

        // 元素不存在的情况
        try {
          const context = document.querySelector('.post__meta.meta');
          const pv = document.createElement('div');
          pv.setAttribute('class', 'meta__item-author meta__item');
          const span = document.createElement('span');
          span.setAttribute('class', 'meta__text');
          span.innerText = "浏览(" + col.get('pv') + ")";
          pv.appendChild(span)
          context.appendChild(pv)
        } catch (error) {
          console.error(error);
        }
      }
    });
  }

  updateCollect()
</script>

因为hugo-dpsg主题没有浏览量字段,所以通过js在对应的位置插入浏览量字段即可。

博客迁移至hugo gitpage

月盾

为什么迁移?

6月初的时候,3年前买的阿里云服务器到期了,又买了其他服务器,但是只有1年期,于是进行了一番数据,应用迁移,这么一顿操作下来还是挺累的,尤其是在linux上安装mysql,mongodb,安装后又是连接不上,用了好几天时间才搞定,挺烦的。
再想到一年后又是一顿操作,不由得一个激灵。

经过一番的思量后,决定将自建博客迁移至gitpage上。

自建和gitpage优劣对比

首先列出自己博客具备的功能:

  • 首页 包含最近10篇文章的标题和部分内容,最近发表的5篇,分类,标签,友链
  • 目录 所有文章时间线
  • 留言 自维护留言系统
  • 微博 新浪微博,最近出问题不显示了
  • 速记 简单记录
  • 关于 自我介绍

整体比较简单,没什么复杂功能。而很多静态博客也具备这些功能,而且做的更好,所以基本不会有主要功能的缺失。所以迁移到gitpage上也不会有什么问题。

劣势

静态博客在一些细节功能上面确实会有缺失,比如:

  • 评论功能,数据原来是存在自己服务器上的,gitpage不具备数据存储能力,所以需要对接第三方评论或留言功能。
  • 目录缺失,这点主要由博客主题决定,有些主题是没有目录功能的。
  • PV/UV统计,同样是由于数据存储的缺失,所以gitpage也没有这样的统计功能,但是可以添加谷歌,百度统计之类的。
  • 自定义功能较弱,只能使用主题提供的页面模式,除非不使用静态博客工具,完全自己开发。
  • 不能在线编辑。

优势

  • 免费,没有服务器费用。
  • 可以自定义域名,只需要一丁点儿的域名费用。
  • seo良好。
  • 规范的书写格式,使文章内容更统一美观。
  • 更强大的编辑器,可以选择自己喜欢的markdown编辑器。
  • 便于本地检索。
  • 数据更保险,不易丢失。
  • 安全,静态博客可以避免一些网络攻击。

整体来说,除了数据存储功能缺失外,其他都是可以实现的。

迁移过程

将mongodb数据中的博客导出为本地markdown文件。

// mongodb数据转markdown
function genMd() {
	return Blog.find({ status: 1 }, null, { sort: { '_id': -1 } })
		.then(data => {
			data.forEach((b) => {
				debug(b)
				let tags = b.tags.split(",")
				let blog = `---
title: "${b.title}"
description: "${b.content.replace(/<[^<>]+>/g, "").replace(/\r\n/g, "").replace(/\n/g, "").substr(0, 80)}"
date: ${b.updatedAt.toISOString()}
draft: false
authorbox: false

categories:
  - "${b.category}"
tags:
  - "${tags[0]}"
  - "${tags[1]}"
  - "${tags[2]}"
  - "${tags[3]}"

---
${b.content}
                    `
				fs.writeFileSync(`f:/workspace/yuedun\.wang/content/blogdetail/${b.id}.md`, blog, { flag: 'a+' })
				// debug(b.createdAt.toISOString())
			})
			return "data";
		});
}

ok!完成。

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 &