之前的文章集合:

一些可以参考文章集合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。

几种网关的对比

五种API网关技术选型,yyds-51CTO.COM假如你要去找集团老板(这儿只是举个例子), 大家都知道老板肯定不是谁想见就能见的, 也怕坏人嘛, 那么你去老板所在的办公楼,假如是集团总部, 大楼这个门就充当了网关的角色, 大门一般都有看门员 ,看门员会做哪些事情呢?https://www.51cto.com/article/716833.html


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品质水平时,可以观察出画质的区别。


基函数与基图像_ybdesire的博客-CSDN博客_基函数是什么意思有了基函数的概念,就容易找到离散图像的线性变换(离散傅里叶变换、离散余弦变换、离散沃尔什变换、离散哈达玛变换)之间的联系,各种变换的不同之处,也仅是基函数不同而已了。就是说不同的基函数对应不同的变换,记住一个离散傅里叶变换,根据他们的基函数就可以推导出其他变换的公式,也就不用记那么多变换的公式了。   离散图像的变换可以用两种表达式来描述,一种是代数表达式,另一种是矩阵https://blog.csdn.net/ybdesire/article/details/6527908

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

————————————————

DCT变换的基函数与基图像_独孤呆博的博客-CSDN博客_dct变换基利用MATLAB产生DCT基图像,使用了矩阵相乘的方法,没有通过for循环实现。https://blog.csdn.net/dugudaibo/article/details/78701487


只有水平和垂直图像还不足以表达出我们可以看到的图像。我们还需要一些额外的图案,将两者相乘

傅里叶变换交互式入门https://www.jezzamon.com/fourier/zh-cn.html

代码地址GitHub - Jezzamonn/fourier: An Interactive Introduction to Fourier TransformsAn Interactive Introduction to Fourier Transforms. Contribute to Jezzamonn/fourier development by creating an account on GitHub.https://github.com/Jezzamonn/fourier

要得到一个8x8分辨率的图像,这里是我们需要的所有小图案。

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


  • 共轭意味着它有共轭分布的关系。
  • 在贝叶斯概率论中,如果后验分布 p(θx)与先验概率分布 p(θ)在同一概率分布族中,则先验和后验称为共轭分布,先验称为似然函数的共轭先验。共轭先验维基百科在这里(https://en.wikipedia.org/wiki/Conjugate_prior)。
  • 多分类表示随机方差大于 2。
  • n 次意味着我们也考虑了先验概率 p(x)。
  • 为了进一步了解概率,我建议阅读 [pattern recognition and machine learning,Bishop 2006]。

深度学习必须掌握的13种概率分布-51CTO.COM作为机器学习从业者,你需要知道概率分布相关的知识。这里有一份最常见的基本概率分布教程,大多数和使用 python 库进行深度学习有关。https://www.51cto.com/article/716898.html


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 imagedef 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 positioncv2.circle(img_dest_copy, (x, y), 2, (0, 0, 255), -1)# Append new clicked coordinate to paste_coordinate_listpaste_coordinate_list.append([x, y])
if __name__ == '__main__':# Read source imageimg_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 imageimg_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 imagepaste_coordinate = []cv2.namedWindow('collect coordinate')cv2.setMouseCallback('collect coordinate', get_paste_position, paste_coordinate)while True:cv2.waitKey(1)if len(paste_coordinate) == 4:breakpaste_coordinate = np.array(paste_coordinate)# Get perspective matrixmatrix, _ = 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()

使用 OpenCV 进行图像投影变换投影变换(仿射变换)在数学中,线性变换是将一个向量空间映射到另一个向量空间的函数,通常由矩阵实现。如果映射保https://mp.weixin.qq.com/s/HB2UIcEhnxH6vfhqbcelMg


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

SELECT ROWID,

       DBMS_ROWID.ROWID_OBJECT(ROWID) AS OBJECT,

       DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) AS FILENUM,

       DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) AS BLOCK,

       DBMS_ROWID.ROWID_ROW_NUMBER(ROWID) AS ROWN

  FROM emp;

  ROWID                  OBJECT    FILENUM      BLOCK       ROWN

------------------ ---------- ---------- ---------- ----------

AAAUIRAABAAAg8pAAA      82449          1     134953          0

AAAUIRAABAAAg8pAAB      82449          1     134953          1

AAAUIRAABAAAg8pAAC      82449          1     134953          2

AAAUIRAABAAAg8pAAD      82449          1     134953          3

AAAUIRAABAAAg8pAAE      82449          1     134953          4

AAAUIRAABAAAg8pAAF      82449          1     134953          5

AAAUIRAABAAAg8pAAG      82449          1     134953          6

AAAUIRAABAAAg8pAAH      82449          1     134953          7

AAAUIRAABAAAg8pAAI      82449          1     134953          8

AAAUIRAABAAAg8pAAJ      82449          1     134953          9

AAAUIRAABAAAg8pAAK      82449          1     134953         10

AAAUIRAABAAAg8pAAL      82449          1     134953         11

AAAUIRAABAAAg8pAAM      82449          1     134953         12

AAAUIRAABAAAg8pAAN      82449          1     134953         13

SQL>select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where OBJECT_NAME='EMP';

OBJECT_NAME           OBJECT_ID DATA_OBJECT_ID

-------------------- ---------- --------------

EMP                       82449          82449

SQL>alter table emp move; 

SQL>select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where OBJECT_NAME='EMP';

OBJECT_NAME           OBJECT_ID DATA_OBJECT_ID

-------------------- ---------- --------------

EMP                       82449          84675  

ROWID                  OBJECT    FILENUM      BLOCK       ROWN

------------------ ---------- ---------- ---------- ----------

AAAUrDAABAAAhSpAAA      84675          1     136361          0

AAAUrDAABAAAhSpAAB      84675          1     136361          1

AAAUrDAABAAAhSpAAC      84675          1     136361          2

AAAUrDAABAAAhSpAAD      84675          1     136361          3

AAAUrDAABAAAhSpAAE      84675          1     136361          4

AAAUrDAABAAAhSpAAF      84675          1     136361          5

AAAUrDAABAAAhSpAAG      84675          1     136361          6

AAAUrDAABAAAhSpAAH      84675          1     136361          7

AAAUrDAABAAAhSpAAI      84675          1     136361          8

AAAUrDAABAAAhSpAAJ      84675          1     136361          9

AAAUrDAABAAAhSpAAK      84675          1     136361         10

AAAUrDAABAAAhSpAAL      84675          1     136361         11

AAAUrDAABAAAhSpAAM      84675          1     136361         12

AAAUrDAABAAAhSpAAN      84675          1     136361         13

14 rows selected.

可以看到data object number和块号变了,如果move到另外的tablespace那么 file number也会变

PostgreSQL:

1

2

3

4

5

6

7

8

9

10

11

12

lightdb@postgres=# select relname,oid,relfilenode from pg_class where relname = 'emp';

 relname |  oid  | relfilenode 

---------+-------+-------------

 emp     | 31396 |       31396

(1 row)

lightdb@postgres=# truncate table emp;

TRUNCATE TABLE

lightdb@postgres=# select relname,oid,relfilenode from pg_class where relname = 'emp';

 relname |  oid  | relfilenode 

---------+-------+-------------

 emp     | 31396 |       31407

(1 row)

可以看到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

lightdb@postgres=# select ctid,empno from emp;

  ctid  | empno 

--------+-------

 (0,1)  |  7369

 (0,2)  |  7499

 (0,3)  |  7521

 (0,4)  |  7566

Oracle可以用rowid 删除表中重复的数据,那么PostgreSQL同样也是可以的

1

2

3

4

5

6

7

8

9

10

11

12

13

lightdb@postgres=# select * from t_test;

 id

----

  1

  2

  2

  3

(4 rows)

delete from t_test where ctid in (

select ctid from

(SELECT ctid, id, count(*) OVER (PARTITION BY id ORDER BY ctid) as cnt

                            FROM t_test

                           ) where cnt > 1) returning *;

PostgreSQL ctid与Oracle rowid的区别_ITPUB博客Oracle:搞过Oracle的一定对rowid比较熟悉啦,由下面基本含义组成SELECT ROWID,       DBMS_ROWID.ROWID_OBJECT(ROWID) AS OBJECT,       DBMSITPUB博客每天千篇余篇博文新资讯,40多万活跃博主,为IT技术人提供全面的IT资讯和交流互动的IT博客平台-中国专业的IT技术ITPUB博客。http://blog.itpub.net/29990276/viewspace-2909274/


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


20220818

GitHub - kenjihiranabe/The-Art-of-Linear-Algebra: Graphic notes on Gilbert Strang's "Linear Algebra for Everyone"Graphic notes on Gilbert Strang's "Linear Algebra for Everyone" - GitHub - kenjihiranabe/The-Art-of-Linear-Algebra: Graphic notes on Gilbert Strang's "Linear Algebra for Everyone"https://github.com/kenjihiranabe/The-Art-of-Linear-Algebra

线性代数的艺术最近,一位日本老哥将MIT大佬 Gilbert Strang 的线性代数课程中关于矩阵的各种操作进行了可视化,下图就是他发布的推文,项目名为“The Art of Linear Algebra” ,分享给大家~https://mp.weixin.qq.com/s/q9VTT0PrMb4GYJMsKNXhvA


正向先行断言

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

我们可以看到“/喜欢(?=你)”正确匹配到了“你”前面有“喜欢”的文本。

厉害!这篇正则表达式竟写的如此详尽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

仿射变换及其变换矩阵的理解 - shine-lee - 博客园博客:blog.shinelee.me | 博客园 | CSDN 写在前面 2D图像常见的坐标变换如下图所示: 这篇文章不包含透视变换(projective/perspective transformhttps://www.cnblogs.com/shine-lee/p/10950963.html

20220812

什么是 SSR

服务器端渲染(Server-Side Rendering)是指由服务端完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。

简单理解就是html是由服务端写出,可以动态改变页面内容,即所谓的动态页面。早年的 php[1]、asp[2] 、jsp[3] 这些 Server page 都是 SSR 的。

为什么使用 SSR

  • 网页内容在服务器端渲染完成,一次性传输到浏览器,所以 首屏加载速度非常快

  • 有利于SEO,因为服务器返回的是一个完整的 html,在浏览器可以看到完整的 dom,对于爬虫、百度搜索等引擎就比较友好;

koa2 + vite + ts + vue3 + pinia 构建前端 SSR 企业级项目学到了!https://mp.weixin.qq.com/s?__biz=Mzk0MDMwMzQyOA==&mid=2247494747&idx=1&sn=7046801c6e1d0301d54d27826431f11e&chksm=c2e11970f5969066f7e0b8d158fa5f859d8a6b2757c83e56c8ea5a6f9a34cd945ed9bfe63c1a&scene=178&cur_album_id=2160486616459444228#rd


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」,有兴趣的各位可以自己去查看一下。

「NodeJs进阶」超全面的 Node.js 性能优化相关知识梳理从前端面向后端之后,我们会在很多方面会稍显的有些陌生 ...https://mp.weixin.qq.com/s?__biz=Mzk0MDMwMzQyOA==&mid=2247494669&idx=1&sn=4bb7aacc3d0a300790f8e8fce2121489&chksm=c2e11926f59690301a5f6cf9e2fda090124b423da2bbcccd5d298fc237cf2d082af9306e3912&cur_album_id=2160486616459444228&scene=189#wechat_redirect

Node多进程使用优化

现在的计算机一般呢都搭载了多核的cpu,所以我们在编程的时候可以考虑怎么去使用「多进程」或者「多线程」来尽量利用这些多核cpu来提高我们的性能。

在此之前,我们要先了解一下进程和线程的概览:

  • 进程:拥有系统挂载运行程序的单元 拥有一些独立的资源,比如内存空间

  • 线程:进行运算调度的单元 进程内的线程共享进程内的资源 一个进程是可以拥有多个线程的

在「NodeJs」中一般启动一个服务会有一个主线程和四个子线程,我们简单来理解其概览呢,可以把「进程」当做一个公司,「线程」当做公司的职工,职工共享公司的资源来进行工作。

在「NodeJs」中,主线程运行「v8」与「javascript」,主线程相当于公司老板负责主要流程和下发各种工作,通过「时间循环机制」 、「LibUv」再由四个子线程去进行工作。

因为「js」是一门单线程的语言,它正常情况下只能使用到一个「cpu」,不过其「子线程」在 底层也使用到了其他「cpu」,但是依然没有完全解放多核的能力,当计算任务过于繁重的时候,我们就可以也在其他的「cpu」上跑一个「javascript」的运行环境,那么我么先来看看如何用子进程来调用吧

进程的使用 child_process

我们创建两个文件,master.jschild.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 示意图

前端历史项目的 Vite 迁移实践总结大家好,我是 ConardLi。当前,前端社区用 Vite 替代 Webpack 的呼声正日趋高涨。但对于长https://mp.weixin.qq.com/s/kpi82Rb66bUQuA_G0hrqBw

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 应用上实现共享功能。它给人以移动原生共享的感觉。它使共享文本、文件和指向设备上其他应用程序的链接成为可能

八个鲜为人知但很实用的Web API-51CTO.COMAudio API 允许我们在 Web 上操作音频流,它可以用于 Web 上的音频源添加效果和过滤器。音频源可以来自 、视频/音频源文件或音频网络流。https://www.51cto.com/article/716117.html

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/8210a17bcdb8https://www.jianshu.com/p/8210a17bcdb8

20220809

npm install webpack-bundle-analyzer --save-devconst BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports={configureWebpack:config =>{return {plugins:[new BundleAnalyzerPlugin()]}}
}

『Webpack系列』—— externals用法详解_公孙元二的博客-CSDN博客_externals本文用项目例子是用Vue Cli3搭建的Vue项目如果小伙伴有做过首屏加载时间优化,应该会遇到chunk-vendors.js这个文件,巨大无比,加载时间超长,是首屏加载时间过长的罪魁祸首之一。下面通过一个实际的项目来演示,先通过插件webpack-bundle-analyzer来可视化地查看chunk-vendors.js这个文件里面地内容。npm install webpack-bundle-analyzer --save-dev在vue.config.js中引入这插件const Bu.https://blog.csdn.net/Amnesiac666/article/details/121075114

webpack externals详解_唐霜的博客在众多的webpack配置教程中,对externals这个配置选项,总是一带而过,把文档中提到的几种方式都复述一遍,但是对于开发者而言,根本没法完全理解。本文试图通过一整篇文章,详细的对externals这个参数进行讲解。 几种用法 externals这个参数的传入形式有多种,但是总结而言,实际上就是array > object,reg,这三...https://www.tangshuang.net/3343.html

20220805

分布式,我认为MySQL的MGR是分布式,Oracle的RAC也是。TiDB Oceanbase polardb TDSQL这些带上paxos和raft的都是。当超过单机能力时候,考虑分布式。那么如果在分布式上是不是可以胡来?比如前后%,答案是否定的。一样会把分布式搞死。

总结:分区、分库、分表、分布式都怕不按照规范开发,比如全表。所谓武功再高也怕菜刀。分区和分布式可取。但凡做分布式的都是单机玩的好的,比如阿里、腾讯等。单机玩不好的,我绝对不相信能用好分布式。比如单刀都能划到自己的,双刀说不定是还没砍到人,自己已经血流了一地了。分库 分表,不可取,同样遇到前后%,就是雪上加霜。

所谓武功再高也怕菜刀-分区、分库、分表和分布式的优劣_ITPUB博客我相信做过数据库对以上的都不陌生。基本上主流数据库都支持分区。分区是一种对应用透明的管理表方式。尽管可以在网上找不到不少黑MySQL分区的,但是时至今日我用MySQL分区没发现一点问题,当然PostgreSQL和Oracle的我也没发现问题。我猜测那些问题应该来自于使用上的不规范。我以前做公安系统由于单表几十亿上百亿,还采用过二级分区,效果非常好。    分区一般使ITPUB博客每天千篇余篇博文新资讯,40多万活跃博主,为IT技术人提供全面的IT资讯和交流互动的IT博客平台-中国专业的IT技术ITPUB博客。http://blog.itpub.net/637517/viewspace-2908503/

20220805

开源维护者必须维护一个良好的集成测试基础设施:在开源协作的环境下,不可能有一个测试团队来为所有 PR 运行测试,因此社区需要一个持续的集成测试服务来保证所有的 PR 都通过必要的测试再合并。

这个集成测试服务必须:

  • 快速:在可容忍的时间内返回结果。PR CI 时间越长,对贡献者来说体验就越差。
  • 稳定:能够稳定可靠的返回测试错误,而不是让贡献者经常怀疑是不是 CI 服务器出问题了。
  • 公开:能够公开的查阅错误日志,而不是一个完全的黑箱。
  • 可重现:贡献者能够方便的在本地重现错误,不需要反复 commit 来触发测试。

达成这些,一方面需要可靠稳定的测试基础设施,另一方面需要维护者维护好测试代码以及测试使用的脚本,缺一不可。

2022-30: 如何维护一个开源项目An infrastructure engineer, focused on distributed storage systemhttps://xuanwo.io/reports/2022-30/

electron 应用开发优秀实践-51CTO.COM本文介绍了我们对桌面端技术的调研、确定技术选型,以及用 electron 开发过程中,总结的实践经验。https://www.51cto.com/article/715612.html

<!--这个依赖用于健康检查,审计,指标收集-->
<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>

不会吧,十分钟就能上手Prometheus与Grafana监控SpringBoot项目_开源_知识浅谈_InfoQ写作社区

一些可以参考的文档集合7相关推荐

  1. 一些可以参考的文档集合10

    之前的文章集合: 一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客 一些可以参考文章集合2_xuejianxinokok的博客-CSDN博客 一些可以参考的文档集合3_xuej ...

  2. 一些可以参考的文档集合9

    之前的文章集合: 一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客 一些可以参考文章集合2_xuejianxinokok的博客-CSDN博客 一些可以参考的文档集合3_xuej ...

  3. 一些可以参考的文档集合8

    一些可以参考的文档集合7_xuejianxinokok的博客-CSDN博客 之前的文章集合: 一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客 一些可以参考文章集合2_xuej ...

  4. 一些可以参考的文档集合11

    之前的文章集合: 一些可以参考文章集合1_xuejianxinokok的博客-CSDN博客 一些可以参考文章集合2_xuejianxinokok的博客-CSDN博客 一些可以参考的文档集合3_xuej ...

  5. 一些可以参考的文档集合4

    20220523 RabbitMQ 的五种消息模型_RabbitMQ_Ayue._InfoQ写作社区RabbitMQ 提供了 6 种消息模型,但常用的是前面 5 种,第 6 种实际上为RPC,所以一般 ...

  6. 一些可以参考的文档集合1

    20211029 MyNikko.com 微處理器博物館 - Intel CPU Museumhttps://www.mynikko.com/CPU/index.html 20211021 阿里开源的 ...

  7. 一些可以参考的文档集合3

    20220419 java18 向量API https://openjdk.java.net/jeps/417https://openjdk.java.net/jeps/417 java虚拟线程用户级 ...

  8. unity3d 脚本参考-技术文档

    unity3d 脚本参考-技术文档 核心提示:一.脚本概览这是一个关于Unity内部脚本如何工作的简单概览.Unity内部的脚本,是通过附加自定义脚本对象到游戏物体组成的.在脚本对象内部不同志的函数被 ...

  9. 清软英泰plm服务器安装文档,操作文档集合 (安装和管理 AnswerBook2 服务器 - zh)

    操作文档集合 AnswerBook2 集合 是一个逻辑书组.例如,您可有一个包括所有 SolarisTM 面向开发者的书的集合.此集合可能是一个 AnswerBook2 (SGML) 集合或一个 An ...

最新文章

  1. mongoDB分页的两种方法
  2. android 换到iphone,从安卓换到苹果到底是什么感受?最后一个让我彻底放弃了iPhone!...
  3. 设计模式学习笔记——工厂(Factory)模式
  4. 华为P50造型没跑了,后摄造型有点吓人!
  5. 95-38-025-Buffer-Buffer1
  6. c语言 string.h部分常用函数的实现
  7. python 卡方检验_【技术】卡方检验及其Python实现
  8. HTML(三)选择器--复杂选择器
  9. Datalogic得利捷Memor™ 10入选“安卓企业推荐计划”
  10. GAN变种介绍 - DCGAN、InfoGAN、CycleGAN、WGAN、Self-Attention GAN、BigGAN
  11. 李宏毅2020机器学习深度学习(完整版)国语课程PPT
  12. Node实现支付宝网页支付流程(沙箱环境)
  13. 【投屏】Scrcpy源码分析三(Client篇-投屏阶段)
  14. 港科报道 | 香港科技大学(广州)专题新闻发布会成功举办
  15. C++动态库*.dll文件的Debug/Release版本是否可以混用(交叉用)?
  16. C语言实现简单的四则运算计算器
  17. 前端学习--js.2
  18. 借助Aspose.Cells组件,实现PowerBuilder Datawindow 数据导出到Excel
  19. 微信小程序调用豆瓣电影API(详细)
  20. 公司倒闭真相(深刻)

热门文章

  1. 2017-12-16 机器视觉表面缺陷检测综述
  2. 不要和自己的大脑抗争,将大脑的能耗降到最低
  3. Redis学习笔记——快速入门
  4. cnn风格迁移_机器学习:利用卷积神经网络实现图像风格迁移 (一)
  5. 2020 Leetcode 春季赛战队赛 题目汇总
  6. QT调用opencv的videowrite类输出生成视频打不开(已解决)
  7. 打印机上显示无法连接到服务器,服务器连接打印机无法连接到服务器
  8. 千锋教育+计算机四级网络-计算机网络学习-02
  9. 永嘉县公安退休干部李建初诗词创作助力正能量
  10. javaScript关系运算符总结