之前的文章集合:
一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客
一些可以参考文章集合2_xuejianxinokok的博客-CSDN博客
一些可以参考的文档集合3_xuejianxinokok的博客-CSDN博客
一些可以参考的文档集合4_xuejianxinokok的博客-CSDN博客
一些可以参考的文档集合5_xuejianxinokok的博客-CSDN博客
一些可以参考的文档集合6_xuejianxinokok的博客-CSDN博客
20220823
4. Zuul2.0
Zuul 2.0 架构图

上图是Zuul2的架构,和Zuul1没有本质区别,两点变化:
- 前端用Netty Server代替Servlet,目的是支持前端异步。后端用Netty Client代替Http Client,目的是支持后端异步。
- 过滤器换了一下名字,用Inbound Filters代替Pre-routing Filters,用Endpoint Filter代替Routing Filter,用Outbound Filters代替Post-routing Filters。
Inbound Filters :路由到 Origin 之前执行,可以用于身份验证、路由和装饰请求
Endpoint Filters :可用于返回静态响应,否则内置的ProxyEndpoint过滤器将请求路由到Origin
Outbound Filters :从Origin那里获取响应后执行,可以用于度量、装饰用户的响应或添加自定义header
有两种类型的过滤器:sync 和 async。因为Zuul是运行在一个事件循环之上的,因此从来不要在过滤中阻塞。如果你非要阻塞,可以在一个异步过滤器中这样做,并且在一个单独的线程池上运行,否则可以使用同步过滤器。
上文提到过Zuul2开始采用了异步模型
优势是异步非阻塞模式启动的线程很少,基本上一个CPU core上只需启一个事件环处理线程,它使用的线程资源就很少,上下文切换(Context Switch)开销也少。非阻塞模式可以接受的连接数大大增加,可以简单理解为请求来了只需要进队列,这个队列的容量可以设得很大,只要不超时,队列中的请求都会被依次处理。
不足,异步模式让编程模型变得复杂。一方面Zuul2本身的代码要比Zuul1复杂很多,Zuul1的代码比较容易看懂,Zuul2的代码看起来就比较费劲。另一方面异步模型没有一个明确清晰的请求->处理->响应执行流程(call flow),它的流程是通过事件触发的,请求处理的流程随时可能被切换断开,内部实现要通过一些关联id机制才能把整个执行流再串联起来,这就给开发调试运维引入了很多复杂性,比如你在IDE里头调试异步请求流就非常困难。另外ThreadLocal机制在这种异步模式下就不能简单工作,因为只有一个事件环线程,不是每个请求一个线程,也就没有线程局部的概念,所以对于CAT这种依赖于ThreadLocal才能工作的监控工具,调用链埋点就不好搞(实际可以工作但需要进行特殊处理)。
总体上,异步非阻塞模式比较适用于IO密集型(IO bound)场景,这种场景下系统大部分时间在处理IO,CPU计算比较轻,少量事件环线程就能处理。
Zuul 与 Zuul 2 性能对比

Netflix给出了一个比较模糊的数据,大致Zuul2的性能比Zuul1好20%左右,这里的性能主要指每节点每秒处理的请求数。为什么说模糊呢?因为这个数据受实际测试环境,流量场景模式等众多因素影响,你很难复现这个测试数据。即便这个20%的性能提升是确实的,其实这个性能提升也并不大,和异步引入的复杂性相比,这20%的提升是否值得是个问题。Netflix本身在其博文22和ppt11中也是有点含糊其词,甚至自身都有一些疑问的。
5. Spring Cloud Gateway
SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
Spring Cloud Gateway 底层使用了高性能的通信框架Netty。
SpringCloud Gateway 特征
SpringCloud官方,对SpringCloud Gateway 特征介绍如下:
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
(2)集成 Hystrix 断路器
(3)集成 Spring Cloud DiscoveryClient
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
(5)具备一些网关的高级功能:动态路由、限流、路径重写
从以上的特征来说,和Zuul的特征差别不大。SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。
简单说明一下上文中的三个术语:
Filter(过滤器)
和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
Route(路由)
网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
Predicate(断言):
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。
几种网关的对比


2. 图像变换的基图像
以二维DCT变换为例介绍图像二维变换的基图像,如下图所示为 4×4 DCT变换的基图像

图1. n=4时的DCT基函数对应的基图像
这是根据基函数绘制出的由16个小块组成的基图像,其中每个小块由 4×4 个元素(子方块)组成。
为了得到左上角块,我们令 u=v=0,并画出 x=y=0,1,2,3 时 r(x,y,0,0)的值。顶部行中的第二块是 r(x,y,0,1) 在 x=y=0,1,2,3 时的值的图像,依次类推可以画出其余所有的图像。
图像被分解为8x8块,每个块分别进行分解(利用基图像)。我们使用一组频率来确定每个像素的亮度或暗度,然后是另外两组用于颜色,一组用于红绿色,另一组用于蓝黄色。我们为每个块使用的频率个数决定了JPEG图像的品质。
这是一个实际的JPEG图像,放大后我们可以看到细节。当我们改变JPEG品质水平时,可以观察出画质的区别。

利用h(u,v),即可计算出基图像

————————————————
只有水平和垂直图像还不足以表达出我们可以看到的图像。我们还需要一些额外的图案,将两者相乘
傅里叶变换交互式入门
https://www.jezzamon.com/fourier/zh-cn.html
要得到一个8x8分辨率的图像,这里是我们需要的所有小图案。

如果我们把这些小图案的对比度调整到适当的值,然后将它们相加,我们就可以得出任意图像。



- 共轭意味着它有共轭分布的关系。
- 在贝叶斯概率论中,如果后验分布 p(θx)与先验概率分布 p(θ)在同一概率分布族中,则先验和后验称为共轭分布,先验称为似然函数的共轭先验。共轭先验维基百科在这里(https://en.wikipedia.org/wiki/Conjugate_prior)。
- 多分类表示随机方差大于 2。
- n 次意味着我们也考虑了先验概率 p(x)。
- 为了进一步了解概率,我建议阅读 [pattern recognition and machine learning,Bishop 2006]。
20220822
投影变换(仿射变换)
在数学中,线性变换是将一个向量空间映射到另一个向量空间的函数,通常由矩阵实现。如果映射保留向量加法和标量乘法,则映射被认为是线性变换。
要将线性变换应用于向量(即,一个点的坐标,在我们的例子中——像素的 x 和 y 值),需要将该向量乘以表示线性变换的矩阵。作为输出,你将获得一个坐标转换后的向量。
投影变换可以用以下矩阵表示:

其中:

是一个旋转矩阵。该矩阵定义了将要执行的变换类型:缩放、旋转等。

是平移向量。它只是移动点。

是投影向量。对于仿射变换,该向量的所有元素始终等于 0。
如果 x 和 y 是一个点的坐标,则可以通过简单的乘法进行变换:

这里,x' 和 y' 是变换点的坐标。
import cv2 as cv2
import numpy as np
# This function will get click pixel coordinate that source image will be pasted to destination image
def get_paste_position(event, x, y, flags, paste_coordinate_list):
cv2.imshow('collect coordinate', img_dest_copy)
if event == cv2.EVENT_LBUTTONUP:
# Draw circle right in click position
cv2.circle(img_dest_copy, (x, y), 2, (0, 0, 255), -1)
# Append new clicked coordinate to paste_coordinate_list
paste_coordinate_list.append([x, y])
if __name__ == '__main__':
# Read source image
img_src = cv2.imread('woman-1807533_960_720.webp', cv2.IMREAD_COLOR)
# cv2.imwrite('source_image.jpg', img_src)
h, w, c = img_src.shape
# Get source image parameter: [[left,top], [left,bottom], [right, top], [right, bottom]]
img_src_coordinate = np.array([[0,0],[0,h],[w,0],[w,h]])
# Read destination image
img_dest = cv2.imread('billboard-g7005ff0f9_1920.jpg', cv2.IMREAD_COLOR)
# copy destination image for get_paste_position (Just avoid destination image will be draw)
img_dest_copy = img_dest.copy()#np.tile(img_dest, 1)
# paste_coordinate in destination image
paste_coordinate = []
cv2.namedWindow('collect coordinate')
cv2.setMouseCallback('collect coordinate', get_paste_position, paste_coordinate)
while True:
cv2.waitKey(1)
if len(paste_coordinate) == 4:
break
paste_coordinate = np.array(paste_coordinate)
# Get perspective matrix
matrix, _ = cv2.findHomography(img_src_coordinate, paste_coordinate, 0)
print(f'matrix: {matrix}')
perspective_img = cv2.warpPerspective(img_src, matrix, (img_dest.shape[1], img_dest.shape[0]))
cv2.imshow('img', perspective_img)
cv2.copyTo(src=perspective_img, mask=np.tile(perspective_img, 1), dst=img_dest)
cv2.imshow('result', img_dest)
cv2.waitKey()
cv2.destroyAllWindows()
Oracle:
搞过Oracle的一定对rowid比较熟悉啦,由下面基本含义组成

| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
可以看到data object number和块号变了,如果move到另外的tablespace那么 file number也会变
PostgreSQL:
| 1 2 3 4 5 6 7 8 9 10 11 12 |
|
可以看到PostgreSQL的行为和Oracle很类似呀,oid对应Oracle的object_id,relfilenode对应Oracle的data_object_id,前者是逻辑对象,后者是实体对象
那么看下PostgreSQL的ctid
vacuum full之后,数据行在块内的物理位置就会移动,即ctid会发生变化,所以ctid不能作为长期的行标识符,应该使用主键来标识一个逻辑行。
ctid由两个数字组成,第一个数字表示物理块号,第二个数字表示在物理块中的行号,所以说 PostgreSQL的ctid是表级别唯一的行,而Oracle中是整个实例中唯一。
| 1 2 3 4 5 6 7 |
|
Oracle可以用rowid 删除表中重复的数据,那么PostgreSQL同样也是可以的
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |
|

收藏!14 种异常检测方法总结-51CTO.COM本文收集整理了公开网络上八类常见的异常检测方法,快来看看吧。
https://www.51cto.com/article/716810.html
20220818

正向先行断言
正向先行断言:(?=表达式),指在某个位置往右看,所在的位置右侧必须匹配表达式。

我们可以看到“/喜欢(?=你)”正确匹配到了“你”前面有“喜欢”的文本。
厉害!这篇正则表达式竟写的如此详尽
https://mp.weixin.qq.com/s/B2am0PS3DavGtxUlxPuA8w
20220817
OpenCV4.0 的深度神经网络(DNN)模块能力大大加强,不仅支持常见的图像分类、对象检测、图像分割网络,还实现了自定义层与通用网络模型支持,同时提供了非最大抑制相关API支持,使用起来十分方便。EAST模型的tensorflow代码实现参见如下:
https://github.com/argman/EAST

OpenCV4.x的EAST场景文字检测
https://mp.weixin.qq.com/s/9T3ZaGCxkaNf6GYXbp9bOw
20220816


20220812
什么是 SSR
服务器端渲染(Server-Side Rendering)是指由服务端完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。
简单理解就是html是由服务端写出,可以动态改变页面内容,即所谓的动态页面。早年的 php[1]、asp[2] 、jsp[3] 这些 Server page 都是 SSR 的。
为什么使用 SSR
-
网页内容在服务器端渲染完成,一次性传输到浏览器,所以
首屏加载速度非常快; -
有利于SEO,因为服务器返回的是一个完整的 html,在浏览器可以看到完整的 dom,对于爬虫、百度搜索等引擎就比较友好;
Nodejs性能分析工具
profile
「NodeJs」自带了「profile」工具,如何使用呢,就是在启动的时候加上**--prof**即可,node --prof index.js,当我们启动服务器的时候,目录下会立马生成一个文件isolate-0x104a0a000-25750-v8.log,我们先不用关注这个文件,我们重新进行一次15秒的压测:
ab -c50 -t15 http://127.0.0.1:3000/index
等待压测结束后,我们的这个文件就发生了变化,但是里面的数据很长我们还需要进行解析
使用「NodeJs」自带的命令 node --prof-process isolate-0x104a0a000-25750-v8.log > profile.txt
这个命令呢就是把我们生成的日志文件转为txt格式存在当前目录下,并且更为直观可以看到,但是这种文字类型的对我来说也不是足够方便,我们大致说说里面的内容吧,就不上图了,里面包含了,里面有js,c++,gc等等的各种调用次数,占用时间,还有各种的调用栈信息等等,这里你可以手动实现之后看看。
总体来说还是不方便查看,所以我们采用另一种方式。
chrome devtools
因为我们知道「NodeJs」是基础「chrome v8引擎」的「javascript运行环境」,所以我们调试「NodeJs」也是可以对「NodeJs」进行调试的。这里我们要使用新的参数--inspect, -brk代表启动调试的同时暂停程序运行,只有我们进入的时候才往下走。
node --inspect-brk index.js
(base) xiaojiu@192 node-share % node --inspect-brk index.js
Debugger listening on ws://127.0.0.1:9229/e9f0d9b5-cdfd-45f1-9d0e-d77dfbf6e765
For help, see: https://nodejs.org/en/docs/inspector
运行之后我们看到他就告诉我们监听了一个websocket,我们就可以通过这个ws进行调试了。
我们进入到「chrome浏览器」然后在地址栏输入chrome://inspect

然后我们可以看到other中有一个「Target」,上面输出了版本,我们只需要点击最后一行的那个「inspect」就可以进入调试了。进入之后我们发现,上面就可以完完整整看到我们写的源代码了。

并且我们进入的时候已经是暂停状态了,需要我们手动下去,这里和前端调试都大同小异了,相信这里大家都不陌生了。
除此之外,我们可以看到其他几个面板,「Console:控制台」、「Memory:内存监控」、「Profile:CPU监控」,
CPU监控
我们可以进入到「Memory面板」,点击左上角的原点表示开始监控,这个时候进行一轮例如上面的15s压测,压测结束后我们点击「stop按钮」,这个时候就可以生成这个时间段的详细数据了,结果如下:

我们也可点击hHeavy按钮切换这个数据展现形式为图表等其他方式,大家自己试试,那么从这个数据中,我们可以得到什么呢?在这其中记录了所有的调用栈,调用时间,耗时等等,我们可以详细的知道,我们代码中每一行或者每一步的花费时间,这样再对代码优化的话是完全有迹可循的,同时我们使用图表的形式也可以更为直观的查看的,当然这里不仅仅可以调试本地的,也可以通过服务器ip在设置中去调试远端服务器的,当然可能速度会相对慢一点,可以自己去尝试。同时我们也可以借助一些其他的三方包,比如「clinic」,有兴趣的各位可以自己去查看一下。
Node多进程使用优化
现在的计算机一般呢都搭载了多核的cpu,所以我们在编程的时候可以考虑怎么去使用「多进程」或者「多线程」来尽量利用这些多核cpu来提高我们的性能。
在此之前,我们要先了解一下进程和线程的概览:
-
进程:拥有系统挂载运行程序的单元 拥有一些独立的资源,比如内存空间
-
线程:进行运算调度的单元 进程内的线程共享进程内的资源 一个进程是可以拥有多个线程的
在「NodeJs」中一般启动一个服务会有一个主线程和四个子线程,我们简单来理解其概览呢,可以把「进程」当做一个公司,「线程」当做公司的职工,职工共享公司的资源来进行工作。
在「NodeJs」中,主线程运行「v8」与「javascript」,主线程相当于公司老板负责主要流程和下发各种工作,通过「时间循环机制」 、「LibUv」再由四个子线程去进行工作。
因为「js」是一门单线程的语言,它正常情况下只能使用到一个「cpu」,不过其「子线程」在 底层也使用到了其他「cpu」,但是依然没有完全解放多核的能力,当计算任务过于繁重的时候,我们就可以也在其他的「cpu」上跑一个「javascript」的运行环境,那么我么先来看看如何用子进程来调用吧
进程的使用 child_process
我们创建两个文件,master.js和child.js,并且写入如下代码,
/* master.js */
/* 自带的子进程模块 */
const cp = require('child_process')
/* fork一个地址就是启动了一个子进程 */
const child_process = cp.fork(__dirname + '/child.js')
/* 通过send方法给子进程发送消息 */
child_process.send('主进程发这个消息给子进程')
/* 通过 on message响应接收到子进程的消息 */
child_process.on('message', (str) => {
console.log('主进程:接收到来自自进程的消息', str);
})
/* chlid.js */
/* 通过on message 响应父进程传递的消息 */
process.on('message', (str) => {
console.log('子进程, 收到消息', str)
/* process是全局变量 通过send发送给父进程 */
process.send('子进程发给主进程的消息')
})
如上,就是一个使用子进程的简单实现了,看起来和「ws」很像。每「fork」一次便可以开启一个子进程,我们可以fork多次,fork多少个合适呢,我们后边再说。
子线程 WOKer Threads
在v10版本之后,「NodeJs」也提供了子线程的能力,在官方文档中解释到,官方认为自己的事件循环机制已经做的够好足够使用了,就没必要去为开发者提供这个接口,并且在文档中写到,他可以对计算有所帮助,但是对io操作是没有任何变化的,有兴趣可以去看看这个模块,除此之外,我们可以有更简单的方式去使用多核的服务,接下来我们聊聊内置模块「cluster」
Cluster模块
在此之前我们来聊聊「NodeJs」的部署,熟悉「NodeJs」的同学应该都使用过「Pm2」,利用其可以进程提高不熟的性能,其实现原理就是基于这种模块,如果我们可以在不同的核分别去跑一个「http服务」那么是不是类似于我们后端的集群,部署多套服务呢,当客户端发送一个「Http请求」的时候进入到我们的「master node」,当我们收到请求的时候,我们把其请求发送给子进程,让子进程自己处理完之后返回给我,由主进程将其发送回去,那么这样我们是不是就可以利用服务器的多核呢?答案是肯定的,同时这些都不需要我们做过多的东西,这个模块就帮我们实现了,然后我们来实现一个这样的服务,我们创建两个文件app.js,cluster.js,第一个文件呢就是我们日常的启动文件,我们来简单的,使用我们的最开始的那个服务即可:
/* cluster.js */
const cluster = require('cluster')
/* 判断如果是主线程那么就启动三个子线程 */
if(cluster.isMaster){
cluster.fork()
cluster.fork()
cluster.fork()
} else {
/* 如果是子进程就去加载启动文件 */
require('./index.js')
}
就这样简单的代码就可以让我们的请求分发到不同的子进程里面去,这一点类似于负载均衡,非常简单,同时我们在启用多线程和没启动的前后分别压测,可以发现启用后的「qps」是前者的「2.5倍」拥有很大的一个提升了,也可以知道进程直接的通信是有损耗的,不然应该就是「三倍」了,那么我们要开启多少个子进程比较合适呢。我们可以使用内置模块「OS」,来获取到当前计算机的「cpu核数」的,我们加一点简单改造:
const cluster = require('cluster')
const os = require('os')
/* 判断如果是主线程那么就启动三个子线程 */
if(cluster.isMaster){
/* 多少个cpu启动多少个子进程 */
for (let i = 0; i < os.cpus().length; i++) cluster.fork()
} else {
/* 如果是子进程就去加载启动文件 */
require('./index.js')
}
这样我们就可以准确知道计算机有多少个「cpu」我们最多可以启动多少个子进程了,这时我们进行压测发现「qps」更多了,当然并不是启动的越多就越好,前面我们说到。「NodeJs」的底层是用到了其他「cpu」的所以,我们这里一般来说只需要「os.cpus().length / 2」的数量最为合适,就这么简单我们就使用到了其他「cpu」实现了一个类似负载均衡概念的服务。
当然这里有一个疑问,我们手动启动多次「node app.js」为什么不行呢?很明显会报错端口占用,我们知道,正常情况下计算机的一个端口只能被监听一次,我们这里监听了多次实际就是有「NodeJs」在其底层完成的,这里的实现呢就相对复杂需要看源码了,这里就不过多了解了,有兴趣的同学可以自己去研究一下。
如果你做完这些操作,相信你的服务性能已经提高了很大一截了。接下来我们来聊聊关于其稳定性的安全。
NodeJs进程守护与管理
基本上各种「NodeJs框架」都会有全局捕获错误,但是一般自己去编码的过程中没有去做「try catch」的操作就可能导致你的服务直接因为一个小错误直接挂掉,为了提高其稳定性,我们要去实现一个守护,我们用原生的node来创建一个服务,不做异常处理的情况下,如果是框架可能很多框架已经帮你做过这部分东西了,所以我们自己来实现看看吧:
const fs = require('fs')
const http = require('http')
const app = http.createServer( function(req,res) {
res.writeHead(200, { 'content-type': 'text/html'})
console.log(window.xxx)
res.end(fs.readFileSync(__dirname + './index.html', 'utf-8'))
} )
app.listen(3000, () => {
console.log(`listen in 3000`);
})
我们在请求时去打印一个不存在的变量,我们去请求的话就会进行一个报错,同时进程直接退出,而我们如果使用多线程启动的话,也会在我们请求多线程的个数之后,主线程退出,因为主线程发现所有子线程全都挂掉了就会退出,基于这种文件我们希望不要发生,我们怎么做可以解决呢,内置了一个事件「uncaughtException」可以用来捕获错误,但是管方建议不要在这里组织塔退出程序,但是我们可以在退出程序前对其进行错误上报,我们对「cluster.js」进行轻微改造即可,同时我们也可以通过「cluster」模块监控,如果有的时候发生错误导致现线程退出了,我们也可以进行重启,那么改造如下:
const cluster = require('cluster')
const os = require('os')
/* 判断如果是主线程那么就启动三个子线程 */
if(cluster.isMaster){
/* 多少个cpu启动多少个子进程 */
for (let i = 0; i < os.cpus().length; i++) cluster.fork()
/* 如果有线程退出了,我们重启一个 */
cluster.on('exit', () => {
setimeout(()=>{
cluster.fork()
}, 5000)
})
} else {
/* 如果是子进程就去加载启动文件 */
require('./index.js')
process.on('uncaughtException', (err) => {
console.error(err)
/* 进程错误上报 */
process.exit(1)
})
}
如上我们就可以在异常错误的时候重启线程并异常上报,但是这样会出现一个问题,那我如果重复销毁创建线程可能会进入死循环,我们不确定这个线程的退出是不是可以挽救的情况,所以我们还需要对齐进行完善,首先我们可以在全局监控中判断其内存使用的数量,如果大于我们设置的限制就让其退出程序。我们做如下改造防止内存泄漏导致的无限重启:
else {
/* 如果是子进程就去加载启动文件 */
require('./index.js')
process.on('uncaughtException', (err) => {
console.error(err)
/* 进程错误上报 */
/* 如果程序内存大于xxxm了让其退出 */
if(process.memoryUsage().rss > 734003200){
console.log('大于700m了,退出程序吧');
process.exit(1)
}
/* 退出程序 */
process.exit(1)
})
}
这样呢我们就可以对内存泄漏问题进行处理了,同时我们还得考虑一种情况,如果子线程假死了怎么办,僵尸进程如何处理?
心跳检测,杀掉僵尸进程
实现这个的思路并不负责,和我们日常做「ws」类似, 主进程发心跳包,子进程接收并回应心跳包,我们分别改造两个文件,
const cluster = require('cluster')
const os = require('os')
/* 判断如果是主线程那么就启动三个子线程 */
if(cluster.isMaster){
/* 多少个cpu启动多少个子进程 */
for (let i = 0; i < os.cpus().length; i++) {
let timer = null;
/* 记录每一个woker */
const worker = cluster.fork()
/* 记录心跳次数 */
let missedPing = 0;
/* 每五秒发送一个心跳包 并记录次数加1 */
timer = setInterval(() => {
missedPing++
worker.send('ping')
/* 如果大于5次都没有得到响应说明可能挂掉了就退出 并清楚定时器 */
if(missedPing > 5 ){
process.kill(worker.process.pid)
worker.send('ping')
clearInterval(timer)
}
}, 5000);
/* 如果接收到心跳响应就让记录值-1回去 */
worker.on('message', (msg) => {
msg === 'pong' && missedPing--
})
}
/* 如果有线程退出了,我们重启一个 */
cluster.on('exit', () => {
cluster.fork()
})
} else {
/* 如果是子进程就去加载启动文件 */
require('./index.js')
/* 心跳回应 */
process.on('message', (msg) => {
msg === 'ping' && process.send('pong')
})
process.on('uncaughtException', (err) => {
console.error(err)
/* 进程错误上报 */
/* 如果程序内存大于xxxm了让其退出 */
if(process.memoryUsage().rss > 734003200){
console.log('大于700m了,退出程序吧');
process.exit(1)
}
/* 退出程序 */
process.exit(1)
})
}
介绍一下流程
-
主线程每隔五秒发送一个心跳包ping,同时记录上发送次数+1,时间根据自己而定 这里五秒是测试方便
-
子线程接收到了ping信号回复一个pong
-
主线程接收到了子线程响应让计算数-1
-
如果大于五次都还没响应可能是假死了,那么退出线程并清空定时器,
至此一个健壮的「NodeJs」服务已经完成了。
以 Webpack 为代表的主流前端 bundler 之所以慢,根源在于它们冷启动时必须递归打包出整个项目的依赖树,并受限于 JavaScript 的天性(解释执行与单线程模型)而存在吞吐量上的瓶颈。为了解决这两个痛点,Vite 另起炉灶切换了路线:
-
对于项目中的业务模块,Vite 利用现代浏览器内置的 ES Module 支持,由浏览器直接向 dev server 逐个请求加载这些模块——因此你往往可以看到本地环境下大量的 HTTP 请求刷屏,这也是 Vite 最鲜明的特征。
-
对于项目中的 node_modules 依赖,Vite 借助 esbuild 这类由原生语言开发的高性能 bundler,将这些库中非 ESM 标准(CommonJS 或 UMD)的模块整体打包为 ESM,即所谓的 Dependency Pre-Bundling。这个过程的打包结果具备缓存,并且冷启动重建缓存的效率也极高。
Vite 的这个设计与 webpack-dev-server 之间的区别,在其文档中也已经展示得很清楚,一图胜千言:

Webpack 式的经典 bundler 示意图

Vite 式的 No-bundler 示意图
1、Web Audio API
Audio API 允许我们在 Web 上操作音频流,它可以用于 Web 上的音频源添加效果和过滤器。音频源可以来自<audio>、视频/音频源文件或音频网络流。
2、Fullscreen API
Fullscreen API 用于在 Web 应用程序中开启全屏模式,使用它就可以在全屏模式下查看页面/元素。在安卓手机中,它会溢出浏览器窗口和安卓顶部的状态栏(显示网络状态、电池状态等的地方)。
3、Web Speech API
Web Speech API 提供了将语音合成和语音识别添加到 Web 应用程序的功能。使用此 API,我们将能够向 Web 应用程序发出语音命令,就像在 Android 上通过其 Google Speech 或在 Windows 中使用 Cortana 一样。
4、Web Bluetooth API
Bluetooth API 让我们可以访问手机上的低功耗蓝牙设备,并使用它将网页上的数据共享到另一台设备。
5、Vibration API
Vibration API 可以使我们的设备振动,作为对我们应该响应的新数据或信息的通知或物理反馈的一种方式。
6、Broadcast Channel API
Broadcast Channel API 允许从同源的不同浏览上下文进行消息或数据的通信。其中,浏览上下文指的是窗口、选项卡、iframe、worker 等。
7、Clipboard API
复制、剪切和粘贴等剪贴板操作是应用程序中最常见的一些功能。Clipboard API 使 Web 用户能够访问系统剪贴板并执行基本的剪贴板操作。
8、Web Share API
Share API 可帮助我们在 web 应用上实现共享功能。它给人以移动原生共享的感觉。它使共享文本、文件和指向设备上其他应用程序的链接成为可能
20220811
PhantomJS是一个基于webkit的JavaScript API。它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码。任何你可以在基于webkit浏览器做的事情,它都能做到。它不仅是个隐形的浏览器,提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,同时也提供了处理文件I/O的操作,从而使你可以向操作系统读写文件等。PhantomJS的用处可谓非常广泛,诸如网络监测、网页截屏、无需浏览器的 Web 测试、页面访问自动化等。
PhantomJS官方地址:http://phantomjs.org/。
PhantomJS官方API:http://phantomjs.org/api/。
PhantomJS官方示例:http://phantomjs.org/examples/。
PhantomJS GitHub:https://github.com/ariya/phantomjs/

https://www.jianshu.com/p/8210a17bcdb8
https://www.jianshu.com/p/8210a17bcdb8
20220809
npm install webpack-bundle-analyzer --save-dev
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports={
configureWebpack:config =>{
return {
plugins:[
new BundleAnalyzerPlugin()
]
}
}
}

20220805
分布式,我认为MySQL的MGR是分布式,Oracle的RAC也是。TiDB Oceanbase polardb TDSQL这些带上paxos和raft的都是。当超过单机能力时候,考虑分布式。那么如果在分布式上是不是可以胡来?比如前后%,答案是否定的。一样会把分布式搞死。
总结:分区、分库、分表、分布式都怕不按照规范开发,比如全表。所谓武功再高也怕菜刀。分区和分布式可取。但凡做分布式的都是单机玩的好的,比如阿里、腾讯等。单机玩不好的,我绝对不相信能用好分布式。比如单刀都能划到自己的,双刀说不定是还没砍到人,自己已经血流了一地了。分库 分表,不可取,同样遇到前后%,就是雪上加霜。
20220805
开源维护者必须维护一个良好的集成测试基础设施:在开源协作的环境下,不可能有一个测试团队来为所有 PR 运行测试,因此社区需要一个持续的集成测试服务来保证所有的 PR 都通过必要的测试再合并。
这个集成测试服务必须:
- 快速:在可容忍的时间内返回结果。PR CI 时间越长,对贡献者来说体验就越差。
- 稳定:能够稳定可靠的返回测试错误,而不是让贡献者经常怀疑是不是 CI 服务器出问题了。
- 公开:能够公开的查阅错误日志,而不是一个完全的黑箱。
- 可重现:贡献者能够方便的在本地重现错误,不需要反复 commit 来触发测试。
达成这些,一方面需要可靠稳定的测试基础设施,另一方面需要维护者维护好测试代码以及测试使用的脚本,缺一不可。

<!--这个依赖用于健康检查,审计,指标收集-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<!--这个依赖用于把数据转换为prometheus格式使用-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.2</version>
</dependency>
20220802
一个富文本编辑器直接粘贴word 不变型

Docx | PlateInstallationhttps://plate.udecode.io/docs/serializing-docx
20220801
vue3可以不使用插件就实现SSR,就是原生支持。
然后写了个小damo发现确实如此。
const express = require('express');
const { createSSRApp } = require('vue');
const { renderToString } = require('@vue/server-renderer');
let app = express();
const vm = createSSRApp({
template: `
<div>123</div>
`,
});
app.get('/', async function (req, res) {
try {
const html = await renderToString(vm);
res.send(html);
} catch (error) {
res.status(500).send('系统内部错误');
}
});
app.listen(3000, () => {
console.log('渲染服务器启动成功!');
});

| 核心概念 | 描述 |
|---|---|
| Route(路由) | 网关最基本的模块。它由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成。 |
| Predicate(断言) | 路由转发的判断条件,我们可以通过 Predicate 对 HTTP 请求进行匹配,例如请求方式、请求路径、请求头、参数等,如果请求与断言匹配成功,则将请求转发到相应的服务。 |
| Filter(过滤器) | 过滤器,我们可以使用它对请求进行拦截和修改,还可以使用它对上文的响应进行再处理。 |
注意:其中 Route 和 Predicate 必须同时声明。
20220729

React技术揭秘React源码解析
https://react.iamkasong.com/
富文件选区
富文本编辑中我们在进行编辑时首先会先选择一块文本区域(即选区),比如选择一段文字并进行字体加粗等操作,那么选区本身包含了哪些信息呢,下面为大家简单介绍一下。
Selection 对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。调用 window.getSelection()(https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getSelection) 可得到此对象,其内部常用属性如下:
anchorNode
返回选中区域对应的节点
anchorOffset
返回选中区域的起始下标,需要注意起始下标会根据左右方向选择的次序不同来展示不同的下标。如果 anchorNode 是字符串则对应文字下标,anchorNode 是元素,则对应选中区域对应它之前的同级节点的数目。
focusNode
返回选中区域终点所在的节点。
focusOffset
与 anchorOffset 类似,如果是 focusNode 是字符串,则对应最后一个选中的字符所在的位置,focusOffset 是元素,则对应选中区域对应同级节点的总数。
rangeCount
返回选中的区域所对应的连续的范围内的数量。
type
返回选中区域所对应的类别是连续 (Range),还是同一个位置的 (Caret)。
我们常通过 anchorNode 与 anchorOffset 属性判断选区起始位置,通过 focusNode 与 focusOffset 属性判断选区终止位置。
20220721

| 接口名称 | 功能含义说明 |
|---|---|
| supplier | 创建新的结果容器,可以是一个容器,也可以是一个累加器实例,总之是用来存储结果数据的 |
| accumlator | 元素进入收集器中的具体处理操作 |
| finisher | 当所有元素都处理完成后,在返回结果前的对结果的最终处理操作,当然也可以选择不做任何处理,直接返回 |
| combiner | 各个子流的处理结果最终如何合并到一起去,比如并行流处理场景,元素会被切分为好多个分片进行并行处理,最终各个分片的数据需要合并为一个整体结果,即通过此方法来指定子结果的合并逻辑 |
| characteristics | 对此收集器处理行为的补充描述,比如此收集器是否允许并行流中处理,是否finisher方法必须要有等等,此处返回一个Set集合,里面的候选值是固定的几个可选项。 |

为了对上述方法有个直观的理解,我们可以看下Collectors.toList()这个收集器的实现源码:
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
对上述代码拆解分析如下:
-
supplier方法:
ArrayList::new,即new了个ArrayList作为结果存储容器。 -
accumulator方法:
List::add,也就是对于stream中的每个元素,都调用list.add()方法添加到结果容器追踪。 -
combiner方法:
(left, right) -> { left.addAll(right); return left; },也就是对于并行操作生成的各个子ArrayList结果,最终通过list.addAll()方法合并为最终结果。 -
finisher方法:没提供,使用的默认的,因为无需做任何处理,属于恒等操作。
-
characteristics:返回的是
IDENTITY_FINISH,也即最终结果直接返回,无需finisher方法去二次加工。注意这里没有声明CONCURRENT,因为ArrayList是个非线程安全的容器,所以这个收集器是不支持在并发过程中使用。

20220718















24 个Docker的疑难杂症处理技巧-51CTO.COM本文介绍了Docker各种问题的处理技巧,非常全面。
https://www.51cto.com/article/714172.html

https://blog.csdn.net/ybdesire/article/details/6527908
http://blog.itpub.net/29990276/viewspace-2909274/
https://www.cnblogs.com/shine-lee/p/10950963.html
https://xuanwo.io/reports/2022-30/
https://xie.infoq.cn/article/b1af639f0cf0daf006611ce04
https://zhuanlan.zhihu.com/p/382296206
https://www.infoq.cn/article/ZQItMitRldI4QXQTOCNa
1万+

被折叠的 条评论
为什么被折叠?



