2023年3月

摘要:
本文介绍了昇腾平台日志分类、日志级别设置、日志内容格式,以及如何获取日志文件的方法。

本文分享自华为云社区《
如何查看并读懂昇腾平台的应用日志
》,作者:昇腾CANN。

当您完成训练/推理工程开发后,将工程放到昇腾平台运行,以调试工程是否正常运行,此时,可能会出现各种各样、五花八门的异常状况。

当问题发生时,我们的第一反应是不是查看日志,看看哪里报错了。昇腾平台有哪些日志呢?日志文件又在哪呢?本期带您了解如何使用昇腾平台的日志功能。

01 了解日志分类

根据工程运行过程中日志产生的场景不同,日志通常被分为以下4类:

  • 调试日志(debug):记录调试级别的相关信息,一般用于跟踪运行路径,如记录函数的进入和退出等,大部分为代码级的信息输出,调试日志用于开发人员定位复杂问题。
  • 操作日志(operation):记录设备操作维护人员下发或通过设置相关的自动化任务下发的操作和操作结果。
  • 安全日志(security):记录系统用户登录、注销和鉴权,增加、删除用户,用户的锁定和解锁,角色权限变更,系统相关安全配置(如安全日志内容配置)变更等活动。
  • 运行日志(run):记录系统的运行状况或执行流程中的一些关键信息,包括异常的状态、动作,关键的事件等。

02 如何获取日志文件

我们现在知道了昇腾AI处理器有4类日志,那我们需要到哪里查看这些日志呢?本节来揭秘。

昇腾AI处理器具有EP和RC两种形态,针对不同的硬件形态,日志文件存放位置不同,需根据实际硬件形态获取。

EP场景日志获取

  • 应用类日志

用户应用进程在Host侧和Device侧产生的日志。例如,一个推理/训练任务下发后,通常与本次推理/训练直接相关的日志都存放在应用类日志中。

这类日志默认存放在“$HOME/ascend/log”路径下,格式如下:

├── debug
│ ├── device
-0│ │ └── device-pid_*.log //Device侧产生的日志 │ └── plog
│ └── plog
-pid_*.log //Host侧产生的日志 ├── operation
│ ├── device
-0│ │ └── device-pid_*.log
│ └── plog
│ └── plog
-pid_*.log
├── run
│ ├── device
-0│ │ └── device-pid_*.log
│ └── plog
│ └── plog
-pid_*.log
└── security
├── device
-0│ └── device-pid_*.log
└── plog
└── plog
-pid_*.log
  • 系统类日志

非用户应用进程在Device侧产生的日志,即Device侧常驻进程运行产生的日志通常都在存放在系统类日志中。

这类日志生成时默认存放在Device侧/var/log/npu/slog路径下,需要在Host侧通过msnpureport工具将其导出到Host侧再进行查看。

命令举例:

$Driver_HOME/driver/tools/msnpureport -a

通过msnpureport工具导出到Host侧后,系统类日志按Device侧文件夹存放,格式如下:

├── debug
│ ├── device
-os
│ └── device
-os_*.log
├── operation
│ ├── device
-os
│ └── device
-os_*.log
├── run
│ ├── device
-os
│ └── device
-os_*.log
├──security
│ ├── device
-os
│ └── device
-os_*.log
└── slog
└── slogdlog

RC形态日志获取

RC形态日志获取比较简单,不管是应用类日志还是系统类日志,均存放在/var/log/npu/slog路径下。存放格式与EP场景类似:

├── debug
│ ├── device
-app-pid
│ │ └── device
-app-pid_*.log
│ └── device
-os
│ └── device
-os_*.log
├── operation
│ ├── device
-app-pid
│ │ └── device
-app-pid_*.log
│ └── device
-os
│ └── device
-os_*.log
├── run
│ ├── device
-app-pid
│ │ └── device
-app-pid_*.log
│ └── device
-os
│ └── device
-os_*.log
├── security
│ ├── device
-app-pid
│ │ └── device
-app-pid_*.log
│ └── device
-os
│ └── device
-os_*.log
└── device
-id
└──device
-id_*.log

03 如何读懂日志内容

日志内容严格按照日志规范打印,每条日志格式一致、字段含义明确,便于阅读。日志格式、字段含义如下:

日志内容可以通过cat、grep等命令查看。

04 如何设置日志级别

日志级别定义

不同日志级别打印的日志内容详细程度不同,因此,问题定位时可以使用最详细日志(DEBUG)打印,而验证性能时使用最简日志(ERROR)打印即可。日志功能提供了5种级别供大家选择使用:

说明:各级别日志详细程度:DEBUG > INFO > WARNING > ERROR

设置日志级别

通过上面日志级别介绍,我们了解到可以根据自己需求设置不同的日志级别、打印不同详细程度的日志。那应该如何设置呢?

  • 应用类日志级别设置,通过在Host侧设置ASCEND_GLOBAL_LOG_LEVEL环境变量实现,例如设置INFO级别:
export ASCEND_GLOBAL_LOG_LEVEL=1
  • 系统类日志级别设置,针对EP和RC形态提供了不同的方法。

RC形态通过修改/var/log/npu/conf/slog/slog.conf配置文件、重启日志进程生效。

#note, 0:debug, 1:info, 2:warning, 3:error, 4:null(no output log), default(1)
global_level
=1# Event Type Log Flag,0:disable, 1:enable, default(1)
enableEvent
=1# note,0:debug, 1:info, 2:warning, 3:error, 4:null(no output log), 5:invalid(follow global_level)
SLOG
=5# Slog
IDEDD
=5# ascend debug device agent
DVPP
=5# DVPP
CCE
=5 # CCE

EP形态通过msnpureport工具设置系统类日志级别。例如:

$Driver_HOME/driver/tools/msnpureport –g info

05 更多介绍


关于昇腾平台日志更多介绍,请登录
昇腾文档中心
查阅。

点击关注,第一时间了解华为云新鲜技术~

随着AR的发展,虚拟角色被广泛应用在游戏、直播、社交等App中。例如在直播App里,商家可以自由打造虚拟主播的形象,通过AR算法可以让虚拟形象在介绍时做到不遮挡实物商品,提升直播真实性和趣味性。那么,如何让虚拟角色自然融入现实,实现与用户的真实交互呢?

华为
HMS Core AR Engine
提供单人或双人身体轮廓的识别和跟踪能力,实时输出人体轮廓Mask信息和对应的骨骼点信息。其中人体Mask能力可以识别和跟踪当前画面人体所在区域,支持多人识别,识别率达90%,并提供该区域的深度信息。

通过人体轮廓跟踪能力,开发者们可利用人体的轮廓Mask信息对虚拟物体和场景进行遮蔽。比如在AR拍照时更换虚拟背景、让虚拟玩偶躲到人身后等,都可使用Mask能力来实现更为自然的遮挡效果,这可进一步提升AR应用的真实感和观看体验。

Demo演示

开发步骤

开发准备

1 .注册成为开发者

在开发应用前需要在
华为开发者联盟网站
上注册成为开发者并完成实名认证,具体方法请参见
帐号注册认证。

2 .创建应用

参见
创建项目
和在
项目下创建应用
完成应用的创建,配置如下:

“选择平台”:选择“Android”。

“支持设备”:选择“手机”。

“应用分类”:选择“应用”或“游戏”。

3 .集成AR Engine SDK

华为提供了Maven仓集成方式的AR Engine SDK包,在开始开发前,需要将AR Engine SDK集成到您的开发环境中。

4 .配置AR Engine SDK的Maven仓地址

Android Studio的代码库配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle插件版本,选择对应的
配置过程

5 .添加编译依赖
  1. 打开项目中应用级的“build.gradle”文件。

  1. 在“dependencies”中添加如下编译依赖。
dependencies {
    implementation 'com.huawei.hms:arenginesdk:{version}'
}
  1. 重新打开修改完的build.gradle文件,右上方出现Sync Now链接。点击“Sync Now”等待同步完成。

应用开发

运行前验证

检查当前设备是否安装了AR Engine,若已经安装则正常运行,若没有安装,App应采用合适的方式提醒用户安装AR Engine,如主动跳转应用市场,请求安装AR Engine。具体实现代码如下(详细请参见
示例代码
)。

boolean isInstallArEngineApk = AREnginesApk.isAREngineApkReady(this);
if (!isInstallArEngineApk) {
    // ConnectAppMarketActivity.class为跳转应用市场的Activity。
    startActivity(new Intent(this, com.huawei.arengine.demos.common.ConnectAppMarketActivity.class));
    isRemindInstall = true;
}
  1. 创建BodyActivity用来展示AR Engine识别能力,展示身体骨骼,输出人体特征。
Public class BodyActivity extends BaseActivity{
Private BodyRendererManager mBodyRendererManager;
Protected void onCreate(){
	//初始化surfaceView
	mSurfaceView = findViewById();
	//保持OpenGL ES运行上下文。
	mSurfaceView.setPreserveEGLContextOnPause(true);
	//设置OpenGLES版本。
	mSurfaceView.setEGLContextClientVersion(2);
	//设置EGL配置选择器,包括颜色缓冲区的位数和深度位数。
	mSurfaceView.setEGLConfigChooser(……);
	mBodyRendererManager = new BodyRendererManager(this);
	mSurfaceView.setRenderer(mBodyRendererManager);
mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
		}
Protected void onResume(){
	//初始化ARSession,用于管理AR Engine的整个运行状态
If(mArSession == null){
mArSession = new ARSession(this.getApplicationContext());
mArConfigBase = new ARBodyTrackingConfig(mArSession);
mArConfigBase.setEnableItem(ARConfigBase.ENABLE_DEPTH | ARConfigBase.ENABLE_MASK);
mArConfigBase.setFocusMode(ARConfigBase.FocusMode.AUTO_FOCUS
mArSession.configure(mArConfigBase);
	}
	//给setBodyMask传入需要的参数
mBodyRendererManager.setBodyMask(((mArConfigBase.getEnableItem() & ARConfigBase.ENABLE_MASK) != 0) && mIsBodyMaskEnable);
sessionResume(mBodyRendererManager);
		}
}
  1. 创建BodyRendererManager, 此类渲染AR Engine获取的个人数据。
Public class BodyRendererManager extends BaseRendererManager{
	Public void drawFrame(){
	//获取所有指定类型的可跟踪对像集合
Collection<ARBody> bodies = mSession.getAllTrackables(ARBody.class);
		 for (ARBody body : bodies) {
if (body.getTrackingState() != ARTrackable.TrackingState.TRACKING){
                continue;
          }
mBody = body;
hasBodyTracking = true;
    }
	//更新屏幕上显示的身体识别信息。
StringBuilder sb = new StringBuilder();
        updateMessageData(sb, mBody);
Size textureSize = mSession.getCameraConfig().getTextureDimensions();
if (mIsWithMaskData && hasBodyTracking && mBackgroundDisplay instanceof BodyMaskDisplay) {
            ((BodyMaskDisplay) mBackgroundDisplay).onDrawFrame(mArFrame, mBody.getMaskConfidence(),
            textureSize.getWidth(), textureSize.getHeight());
      }
	//在屏幕上显示更新后的身体信息。
mTextDisplay.onDrawFrame(sb.toString());
for (BodyRelatedDisplay bodyRelatedDisplay : mBodyRelatedDisplays) {
             bodyRelatedDisplay.onDrawFrame(bodies, mProjectionMatrix);
        } catch (ArDemoRuntimeException e) {
             LogUtil.error(TAG, "Exception on the ArDemoRuntimeException!");
        } catch (ARFatalException | IllegalArgumentException | ARDeadlineExceededException |
        ARUnavailableServiceApkTooOldException t) {
            Log(…);
        }
}
//更新手势相关数据以进行显示。
Private void updateMessageData(){
	   if (body == null) {
            return;
        }
      float fpsResult = doFpsCalculate();
      sb.append("FPS=").append(fpsResult).append(System.lineSeparator());
      int bodyAction = body.getBodyAction();
sb.append("bodyAction=").append(bodyAction).append(System.lineSeparator());
}
}
  1. 自定义相机预览类,用于实现基于一定置信度的人体绘制。
Public class BodyMaskDisplay implements BaseBackGroundDisplay{}
  1. 获取骨架数据并将其传递给OpenGL ES,OpenGL ES将渲染数据并在屏幕上显示。
public class BodySkeletonDisplay implements BodyRelatedDisplay {
  1. 获取骨架点连接数据,并将其传递给OpenGL ES以便在屏幕上渲染。
public class BodySkeletonLineDisplay implements BodyRelatedDisplay {}

其他类内容请参考
示例代码
集成。

了解更多详情>>

访问
华为开发者联盟官网
获取
开发指导文档
华为移动服务开源仓库地址:
GitHub

Gitee

关注我们,第一时间了解 HMS Core 最新技术资讯~

大家好,我是小富~

个人资源分享网站:
FIRE

本文收录在
Springboot-Notebook 面试锦集

前言

之前有个小伙伴在技术交流群里咨询过一个问题,我当时还给提供了点排查思路,是个典型的八股文转实战分析的案例,我觉得挺有意思,趁着中午休息简单整理出来和大家分享下,有不严谨的地方欢迎大家指出。

问题分析

我们先来看看他的问题,下边是他在群里对这个问题的描述,我大致的总结了一下。

他们有很多的 IOT 设备与服务端建立连接,当增加设备并发请求变多,
TCP
连接数在接近1024个时,可用
TCP
连接数会降到200左右并且无法建立新连接,而且分析应用服务的GC和内存情况均未发现异常。

从他的描述中我提取了几个关键值,
1024

200

无法建立新连接

看到这几个数值,直觉告诉我大概率是TCP请求溢出了,我给的建议是先直接调大
全连接队列

半连接队列
的阀值试一下效果。

那为什么我会给出这个建议?

半连接队列和全连接队列又是个啥玩意?

弄明白这些回顾下TCP的三次握手流程,一切就迎刃而解了~

回顾TCP

TCP三次握手,熟悉吧,面试八股里经常全文背诵的题目。

话不多说先上一张图,看明白TCP连接的整个过程。

TCP三次握手

第一步:客户端发起
SYN_SEND
连接请求,服务端收到客户端发起的
SYN
请求后,会先将连接请求放入半连接队列;

第二步:服务端向客户端响应
SYN+ACK

第三步:客户端会返回
ACK
确认,服务端收到第三次握手的
ACK
后标识连接成功。如果这时全连接队列没满,内核会把连接从半连接队列移除,创建新的连接并将其添加到全连接队列,等待客户端调用
accept()
方法将连接取出来使用;

TCP协议三次握手的过程,
Linux
内核维护了两个队列,
SYN
半连接队列和
accepet
全连接队列。即然叫队列,那就存在队列被压满的时候,这种情况我们称之为
队列溢出

当半连接队列或全连接队列满了时,服务器都无法接收新的连接请求,从而导致客户端无法建立连接。

全连接队列

队列信息

全连接队列溢出时,首先要查看全连接队列的状态,服务端通常使用
ss
命令即可查看,
ss
命令获取的数据又分为
LISTEN
状态 和
非LISTEN
两种状态下,通常只看
LISTEN
状态数据就可以。

LISTEN
状态

Recv-Q:当前全连接队列的大小,表示上图中已完成三次握手等待可用的 TCP 连接个数;

Send-Q:全连接最大队列长度,如上监听8888端口的TCP连接最大全连接长度为128;

# -l 显示正在Listener 的socket
# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]#  ss -lnt | grep 8888
State  Recv-Q Send-Q  Local Address:Port   Peer Address:Port
LISTEN     0   100       :::8888                  :::*               

非LISTEN
状态下Recv-Q、Send-Q字段含义有所不同

Recv-Q:已收到但未被应用进程读取的字节数;

Send-Q:已发送但未收到确认的字节数;

# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]#  ss -nt | grep 8888
State  Recv-Q Send-Q  Local Address:Port   Peer Address:Port
ESTAB     0   100       :::8888                  :::*               

队列溢出

一般在请求量过大,全连接队列设置过小会发生全连接队列溢出,也就是
LISTEN
状态下 Send-Q < Recv-Q 的情况。
接收到的请求数大于TCP全连接队列的最大长度,后续的请求将被服务端丢弃,客户端无法创建新连接

# -l 显示正在Listener 的socket
# -n 不解析服务名称
# -t 只显示tcp
[root@VM-4-14-centos ~]#  ss -lnt | grep 8888
State  Recv-Q Send-Q  Local Address:Port   Peer Address:Port
LISTEN     200   100       :::8888                  :::*               

如果发生了全连接队列溢出,我们可以通过
netstat -s
命令查询溢出的累计次数,若这个
times
持续的增长,那就说明正在发生溢出。

[root@VM-4-14-centos ~]# netstat -s | grep overflowed
  7102 times the listen queue of a socket overflowed #全连接队列溢出的次数

拒绝策略

在全连接队列已满的情况,Linux提供了不同的策略去处理后续的请求,默认是直接丢弃,也可以通过
tcp_abort_on_overflow
配置来更改策略,其值 0 和 1 表示不同的策略,默认配置 0。

# 查看策略
[root@VM-4-14-centos ~]# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

tcp_abort_on_overflow = 0
:全连接队列已满时,服务端直接丢弃客户端发送的
ACK
,此时服务端仍然是
SYN_RCVD
状态,在该状态下服务端会重试几次向客户端推送
SYN + ACK

重试次数取决于
tcp_synack_retries
配置,重试次数超过此配置后后,服务端不在重传,此时客户端发送数据,服务端直接向客户端回复
RST
复位报文,告知客户端本次建立连接已失败。

RST
: 连接 reset 重置消息,用于连接的异常关闭。常用场景例如:服务端接收不存在端口的连接请求;客户端或者服务端异常,无法继续正常的连接处理,发送 RST 终止连接操作;长期未收到对方确认报文,经过一定时间或者重传尝试后,发送 RST 终止连接。

[root@VM-4-14-centos ~]# cat /proc/sys/net/ipv4/tcp_synack_retries
0

tcp_abort_on_overflow = 1
:全连接队列已满时,服务端直接丢弃客户端发送的
ACK
,直接向客户端回复
RST
复位报文,告知客户端本次连接终止,客户端会报错提示
connection reset by peer

队列调整

解决全连接队列溢出我们可以通过调整TCP参数来控制全连接队列的大小,全连接队列的大小取决于 backlog 和 somaxconn 两个参数。

这里需要注意一下,两个参数要同时调整,因为取的两者中最小值
min(backlog,somaxconn)
,经常发生只挑调大其中一个另一个值很小导致不生效的情况。

backlog
是在
socket 创建的时候 Listen() 函数传入的参数
,例如我们也可以在 Nginx 配置中指定 backlog 的大小。

server {
   listen 8888 default backlog = 200
   server_name fire100.top
   .....
}

somaxconn
是个 OS 级别的参数,默认值是 128,可以通过修改
net.core.somaxconn
配置。

[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 128
[root@localhost core]# sysctl -w net.core.somaxconn=1024
net.core.somaxconn = 1024
[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 1024

如果服务端处理请求的速度跟不上连接请求的到达速度,队列可能会被快速填满,导致连接超时或丢失。应该及时增加队列大小,以避免连接请求被拒绝或超时。

增大该参数的值虽然可以增加队列的容量,但是也会占用更多的内存资源。一般来说,
建议将全连接队列的大小设置为服务器处理能力的两倍左右

半连接队列

队列信息

上边TCP三次握手过程中,我们知道服务端
SYN_RECV
状态的TCP连接存放在半连接队列,所以直接执行如下命令查看半连接队列长度。

[root@VM-4-14-centos ~]  netstat -natp | grep SYN_RECV | wc -l
6666661

队列溢出

半连接队列溢出最常见的场景就是,客户端没有及时向服务端回
ACK
,使得服务端有大量处于
SYN_RECV
状态的连接,导致半连接队列被占满,得不到
ACK
响应半连接队列中的 TCP 连接无法移动全连接队列,以至于后续的
SYN
请求无法创建。
这也是一种常见的DDos攻击方式。

查看TCP半连接队列溢出情况,可以执行
netstat -s
命令,
SYNs to LISTEN
前的数值表示溢出的次数,如果反复查询几次数值持续增加,那就说明半连接队列正在溢出。

[root@VM-4-14-centos ~]# netstat -s | egrep “listen|LISTEN”
1606 times the listen queue of a socket overflowed
1606 SYNs to LISTEN sockets ignored

队列调整

可以修改 Linux 内核配置
/proc/sys/net/ipv4/tcp_max_syn_backlog
来调大半连接队列长度。

[root@VM-4-14-centos ~]# echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog

为什么建议

看完上边对两个队列的粗略介绍,相信大家也能大致明白,为啥我会直接建议他去调大队列了。

因为从他的描述中提到了两个关键值,TCP连接数增加至1024个时,可用连接数会降至200以内,一般
centos
系统全连接队列长度一般默认 128,半连接队列默认长度 1024。所以队列溢出可以作为第一嫌疑对象。

全连接队列默认大小 128

[root@localhost core]# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 128

半连接队列默认大小 1024

[root@iZ2ze3ifc44ezdiif8jhf7Z ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024

总结

简单分享了一点TCP全连接队列、半连接队列的相关内容,讲的比较浅显,如果有不严谨的地方欢迎留言指正,毕竟还是个老菜鸟。

全连接队列、半连接队列溢出是比较常见,但又容易被忽视的问题,往往上线会遗忘这两个配置,一旦发生溢出,从
CPU

线程状态

内存
看起来都比较正常,偏偏连接数上不去。

定期对系统压测是可以暴露出更多问题的,不过话又说回来,就像我和小伙伴聊的一样,即便测试环境程序跑的在稳定,到了线上环境也总会出现各种奇奇怪怪的问题。

我是小富,下期见~

技术交流,欢迎关注公众号:程序员小富

安装
MasaFramework
模板

执行以下命令安装最新Masa的模板

dotnet new --install Masa.Template

安装完成将出现四个模板

Masa Blazor App

Masa Blazor App的模板创建的是一个没有携带解决方案的项目模板,默认项目结构如图:

一个简单的
Masa Blazor Server
项目

Masa Blazor Pro Web

Masa Blazor Pro Web的模板创建类型有多种

Wasm
就是单纯的
Wasm
模式

Wasm-Host
就是启动一个Server托管
Wasm

Wasm-PWA
支持浏览器安装

Server
就是单纯的
Blazor Server
模式

ServerAndWasm
是提供一个
razor
类库作为界面,支持
Blazor Server

Blazor Wasm
俩种模式

对于上面五种模式更推荐第五种模式,这样就可以在部署的时候部署
Blazor Server

Blazor Wasm
俩种模式,可让用户自行切换,解剖以下
Masa Blazor Pro Web
的项目结构

MasaWebPro1
项目就是
Razor
类库,提供界面逻辑和实际业务

MasaWebPro1.Server
项目就只是以
Blazor Server
模式托管
MasaWebPro1
项目的界面

MasaWebPro1.WebAssembly
项目就只是以
Blazor WebAssembly
模式托管
MasaWebPro1
项目的界面

运行项目将得到一个精美的项目模板

可对其修改进行二次开发,也可以将Pro和MasaFramework结合一块使用

Masa Blazor Website

Masa Blazor Website项目结构

Masa Blazor Website算是老版本的文档站点的模板,简单描述一下,默认使用了全球化

Masa Framework Project

Masa Framework Project就是我们的主角了

需要使用
MasaFramework
的同志们就需要创建这个模板了,之前的模板都是单纯的Blazor

当我们创建
MasaFramework
的时候存在多个选项

Use Controllers
:使用控制器启用以后不使用MiniApis(更推荐使用MiniApis)

Enable OpenAPI Support
: 其实是否默认使用Swagger

Add Dapr Support
:添加Dapr的支持

Use Dapr Actor
:使用Dapr Actor

Add Authorization An Authentication
:添加授权和认证

Add Fluent Validation Middleware
:添加校验中间件

分别讲解一下
Choice Add Service Project and Mode

Basic

Cqrs

Ddd

Cqrs&Ddd
四个项目模板,
Choice Add Web Project
其实就是Blazor的托管模式

Basic

一个最基本的
MasaFramework
的项目结构

Cqrs

MasaFramework
的Cqrs结构,对比基本的
MasaFramework
项目来说有些差异的

Ddd

MasaFramework

Ddd
项目和基本模板的差异也很明显

Cqrs&Ddd

Cqrs&Ddd
集成了
Cqrs

Ddd
俩个项目模板的特性,是一个稍微复杂的框架

项目使用

如果你想使用
MasaFramework
的话,可以将
Masa Pro
的模板和
MasaFramework
的模板结合起来一块使用

这个是我目前使用到
MasaFramework
的项目,Web是将
Pro
的模板嵌入进来,并进行修改,当前项目还在完善,这也是我第一个接触
MasaFramework
实践的项目,因为符合我需要的,体积小,依赖少。

结尾

来着token的分享

技术交流群:737776595

MasaFramework
学习地址:
[MASA Framework](https://docs.masastack.com/framework/getting-started/overview)

最近写了一个demo:
demo的github地址

一. 简单介绍

1. Server端

它是一个WebApi服务,把它当成一个黑盒就行了。

2. MiddleServer端

是重点,它是一个WebApi服务,包含一个GetValues接口和一个Query2接口。
Query2接口是一个简单的接口。
GetValues接口通过请求Server端的GetCounts接口和GetValues接口获取数据。

3. Client端

请求500次MiddleServer端的GetValues接口和请求500次Query2接口。
并行度200。

二. 这个demo主要测试什么?

  1. 测试MiddleServer端两个接口的吞吐量,MiddleServer端需要请求143000次Server端的接口。同时它需要响应Client端1000次请求。
  2. 测试MiddleServer端接口的平均耗时。

三. 想得出什么结论?

  1. MiddleServer端所面对的场景,使用异步实现肯定是优于使用多线程实现的。
  2. MiddleServer端的GetValues接口,需要请求286次Server端的接口,如果使用顺序执行的异步,那么耗时会很长,所以需要并行执行异步。
  3. MiddleServer端的GetValues接口,为什么不只请求1次Server端的接口呢?一是因为业务逻辑可能很复杂,二是因为数据量较大无法一次性获取。
  4. MiddleServer端的GetValues接口,为什么写了两层Parallel.ForEachAsync,一层不可以吗?如果第一层循环数据量很少,第二层循环存在数据倾斜,那么写两层Parallel.ForEachAsync可能会好一点。
  5. 虽然Client端测试了并发请求GetValues接口,但这样的接口,并不是为了高并发,需要做限流。但测试一下是必要的。
  6. 可能真的不建议写两层Parallel.ForEachAsync,因为会导致并行度较大。但是,我可以不写,你不能不支持。
  7. 由于精力和水平有限,希望看看别人用java和go语言怎么写的。
  8. 我觉得这里面可能是有坑的,想看看别人写的,会不会掉坑里。

四.最后

希望有兴趣的可以用java和go语言写一下这个demo。可以对比一下:

  1. 性能,这里并不专业,只是粗略对比,以及看一下大家对异步的理解,以及会不会掉坑里。
  2. 代码是否容易编写,容易阅读,容易维护。