2023年3月

前言

K8S 性能优化系列文章,本文为第一篇:OS sysctl 性能优化参数最佳实践。

参数一览

sysctl 调优参数一览

# Kubernetes Settings
vm.max_map_count = 262144
kernel.softlockup_panic = 1
kernel.softlockup_all_cpu_backtrace = 1
net.ipv4.ip_local_reserved_ports = 30000-32767

# Increase the number of connections
net.core.somaxconn = 32768

# Maximum Socket Receive Buffer
net.core.rmem_max = 16777216

# Maximum Socket Send Buffer
net.core.wmem_max = 16777216

# Increase the maximum total buffer-space allocatable
net.ipv4.tcp_wmem = 4096 87380 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216

# Increase the number of outstanding syn requests allowed
net.ipv4.tcp_max_syn_backlog = 8096


# For persistent HTTP connections
net.ipv4.tcp_slow_start_after_idle = 0

# Allow to reuse TIME_WAIT sockets for new connections
# when it is safe from protocol viewpoint
net.ipv4.tcp_tw_reuse = 1

# Max number of packets that can be queued on interface input
# If kernel is receiving packets faster than can be processed
# this queue increases
net.core.netdev_max_backlog = 16384

# Increase size of file handles and inode cache
fs.file-max = 2097152

# Max number of inotify instances and watches for a user
# Since dockerd runs as a single user, the default instances value of 128 per user is too low
# e.g. uses of inotify: nginx ingress controller, kubectl logs -f
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288

# Additional sysctl flags that kubelet expects
vm.overcommit_memory = 1
kernel.panic = 10
kernel.panic_on_oops = 1

# Prevent docker from changing iptables: https://github.com/kubernetes/kubernetes/issues/40182
net.ipv4.ip_forward=1

如果是 AWS,额外增加如下:

# AWS settings
# Issue #23395
net.ipv4.neigh.default.gc_thresh1=0

如果启用了 IPv6,额外增加如下:

# Enable IPv6 forwarding for network plugins that don't do it themselves
net.ipv6.conf.all.forwarding=1

参数解释

分类 内核参数 说明 参考链接
Kubernetes vm.max_map_count = 262144 限制一个进程可以拥有的VMA(虚拟内存区域)的数量,
一个更大的值对于 elasticsearch、mongo 或其他 mmap 用户来说非常有用
ES Configuration
Kubernetes kernel.softlockup_panic = 1 用于解决 K8S 内核软锁相关 bug root cause kernel soft lockups · Issue #37853 · kubernetes/kubernetes (github.com)
Kubernetes kernel.softlockup_all_cpu_backtrace = 1 用于解决 K8S 内核软锁相关 bug root cause kernel soft lockups · Issue #37853 · kubernetes/kubernetes (github.com)
Kubernetes net.ipv4.ip_local_reserved_ports = 30000-32767 默认 K8S Nodport 端口 service-node-port-range and ip_local_port_range collision · Issue #6342 · kubernetes/kops (github.com)
网络 net.core.somaxconn = 32768 表示socket监听(listen)的backlog上限。什么是backlog?backlog就是socket的监听队列,当一个请求(request)尚未被处理或建立时,他会进入backlog。
增加连接数.
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.core.rmem_max = 16777216 接收套接字缓冲区大小的最大值(以字节为单位)。
最大化 Socket Receive Buffer
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.core.wmem_max = 16777216 发送套接字缓冲区大小的最大值(以字节为单位)。
最大化 Socket Send Buffer
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.ipv4.tcp_wmem = 4096 87380 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
增加总的可分配的 buffer 空间的最大值 Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.ipv4.tcp_max_syn_backlog = 8096 表示那些尚未收到客户端确认信息的连接(SYN消息)队列的长度,默认为1024
增加未完成的syn请求的数量
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.ipv4.tcp_slow_start_after_idle = 0 持久化 HTTP 连接 Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.ipv4.tcp_tw_reuse = 1 表示允许重用TIME_WAIT状态的套接字用于新的TCP连接,默认为0,表示关闭。
允许在协议安全的情况下重用TIME_WAIT 套接字用于新的连接
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.core.netdev_max_backlog = 16384 当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。这个参数表示该队列的最大值
如果内核接收数据包的速度超过了可以处理的速度,这个队列就会增加
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
文件系统 fs.file-max = 2097152 该参数决定了系统中所允许的文件句柄最大数目,文件句柄设置代表linux系统中可以打开的文件的数量。
增加文件句柄和inode缓存的大小
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
文件系统 fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
一个用户的inotify实例和watch的最大数量
由于dockerd作为单个用户运行,每个用户的默认实例值128太低了
例如使用inotify: nginx ingress controller, kubectl logs -f
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
kubelet vm.overcommit_memory = 1 对内存分配的一种策略
=1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何
Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
kubelet kernel.panic = 10 panic错误中自动重启,等待时间为10秒 Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
kubelet kernel.panic_on_oops = 1 在Oops发生时会进行panic()操作 Image: We should tweak our sysctls · Issue #261 · kubernetes-retired/kube-deploy (github.com)
网络 net.ipv4.ip_forward=1 启用ip转发
另外也防止docker改变iptables
Upgrading docker 1.13 on nodes causes outbound container traffic to stop working · Issue #40182 · kubernetes/kubernetes (github.com)
网络 net.ipv4.neigh.default.gc_thresh1=0 修复 AWS
arp_cache: neighbor table overflow!
报错
arp_cache: neighbor table overflow! · Issue #4533 · kubernetes/kops (github.com)

EOF

三人行, 必有我师; 知识共享, 天下为公.
本文由东风微鸣技术博客
EWhisper.cn
编写.

前言

距离去年国庆入手了NAS至今有好几个月时间了,NAS折腾起来有点麻烦,且实际作用因人而异,并没有想象中的好用,所以说好的这个系列一直没有更新~

还有另一方面的原因,这些NAS的系统基于Linux深度定制,对于我这种习惯用Linux的人来说,用着很别扭不自在,处处受限制…

所以在使用了这台威联通的NAS几个月后,我决定把系统重装成正常的Linux Server系统,或者Proxmox VE,真正的NAS应该当一台可定制性很强的服务器,而不是捏着鼻子用威联通改成依托答辩的系统。

PVE
,全称Proxmox Virtual Environment,是基于Debian的Linux系统,虚拟机内核为KVM。硬件兼容性优秀。界面功能不强,很多操作要靠命令行,但扩展能力几乎是无限的。

既然决定了不要用威联通的答辩系统,那么在重装之前,就把它提供的大部分功能都试一遍,做个记录之后再告别这个答辩系统吧~

啥是NAS

NAS,全名 Network Attached Storage ,直译成中文是网络附加存储。

通俗一点说就是个小服务器,专门放在家里(或办公室)当私有云网盘用,当然现在NAS的性能很强,要当服务器放几个网站上去也没啥问题。

主要功能就是存文件,搭配RAID(大部分是软件RAID)实现冗余存储,能在一定程度上保证数据安全。

通常各个NAS厂商还有定制了一套系统,搭配各种App可以实现文件同步、远程下载、分享等功能。

而且大部分NAS的系统都是基于Linux定制的,还可以支持docker,可以跑很多第三方的服务来扩展功能,因此
理论上
可以当成一台Linux服务器使用。
(至于为啥是理论上,我后面解释)

常见NAS品牌

现在比较主流的NAS分为两种,一种是Storage NAS,专注储存型;另一种是Platform NAS,集成平台型,也就是说自带操作系统。

常见的就群辉和威联通,这俩都挺贵的,俗称买系统送硬件,跟苹果一个尿性,但系统还搞得不咋样,这里面群辉好一点,但价格也贵一大截。

实际上NAS的品牌很多,这几年不知道咋的被炒火起来了,很多国产品牌也加入一起卷,不过暂时没有啥特别好的产品出现。

  • 华芸(ASUSTOR) - 华硕子公司,软硬件都不错,但性价比据说很低
  • 西部数据(Western Digital) - 硬件ARM架构,旧版系统基于Linux魔改,新版系统基于Android魔改,阉割得很厉害,扩展性和可玩性不咋样
  • 网件(netgear) - 系统有ESXi认证,好像挺厉害,就是国内没销售
  • 巴法络(Buffalo) - 小日子的公司,软件一般
  • 铁威马(terra master) - 深圳的,性价比不错,软件一般般
  • 桦赋(色卡司Thecus) - 跟威联通、群辉同个地方的公司,据说软件生态也不错,但好像没有在大陆自营
  • 奥睿科(Orico) - 也是深圳的,主要卖硬件,机箱好看
  • 诠力(ITE2) - 老牌厂家,系统居然是Windows 10 IoT Enterprise,主打服务,性价比很低
  • 德宝(drobo) - 美国的,基本是搞企业级的

剩下的还有一些,懂的都懂

  • 惠普(HP)
  • 万由(u-nas)
  • 海康威视(hikvision)
  • 绿联
  • 华为

关于威联通

我自己买的NAS型号是威联通TS-464c,从去年国庆到现在也用了小半年了,谈谈我对这个牌子NAS的感受。

先说结论:

  • (对会Linux操作的玩家而言)
    硬件太贵,性价比极低,软件很鸡肋,可以说体验很差劲。
  • (对普通玩家而言)
    软件使用还是不够友好,整个安装使用过程,充斥着各种技术名词,官方文档里并没有怎么对其进行解释指导,需要用户有一定的自学能力。同时使用中的各种问题不少,不够省心好用。

笔者的职业是程序员,日常工作使用Linux系统打交道很多,所以对Linux的使用还是比较熟悉的,这台NAS到手后,我就想把它打造成一台家庭服务器,殊不知,第一步就遇到一个问题。

这个威联通的定制Linux没有包管理工具,官方提供的软件商店内容很少,常见的git等软件都没有,需要借助第三方包管理器Entware来安装,这倒也不是啥大问题,但它的根目录是只读的,无论我做了啥修改,重启之后就还原回去了,跟网吧的还原卡差不多~

行吧,这个我能理解,威联通怕用户乱折腾把系统搞坏了,那这样就少了很多可玩性了,

还有它支持docker,大部分软件都可以用docker方式启动,这样也能实现开机自动启动了,或者自己写个脚本,每次开机手动执行也可以,毕竟NAS不会经常重启。

但很快我又遇到一个新问题,我要自己搭建DNS服务,结果威联通魔改的这个系统,里面已经有个服务,把53端口占用了…

然后我要使用swag做全HTTPS反向代理,又是这个魔改的系统,把443端口占了…

所以要反向代理就只能用它自带的。

OK,这只是一点小吐槽,这个只是牺牲了一些自定制的可玩性,对日常使用的影响还不是很大。接下来我要说一个很不合理的设计:软件商店安装的软件没有
沙盒
机制!并且还是以
root权限
运行!

威联通的软件生态在NAS里还算可以的,我之前一直以为软件商店里安装的软件,是运行在沙盒里,与用户的数据是有隔离开的,直到有一天我下载了迅雷…

迅雷下载的文件存在
Public
共享目录下,当我想要修改下载的文件名时,它提示我权限不足,真是奇怪,我自己的文件我没权限修改?

于是通过ssh连到NAS上,看了一下迅雷下载的文件,其所有者居然是root,再看一下迅雷的进程,好家伙,是以root权限运行的。

这意味着啥,这迅雷以最高权限运行在NAS里,可以毫无障碍扫描我的所有个人文件,要怎么上传都可以,这些我如果不通过终端根本发现不了,细思极恐啊~

不知道威联通对软件上架有没有什么审核机制,但没有沙盒机制且软件都是root权限运行,这个已经是极大的风险了,什么安全性都免谈了。

小结

威联通的定制系统主要几个问题

  • 根目录只读,导致没办法自定义
  • 有很多奇怪的内置服务占用端口,不利于自己折腾
  • 软件没有沙盒机制且均以root权限运行,数据安全风险极大

说实在的,NAS的什么花里胡哨的功能都是锦上添花,最基础的存文件功能基本都是用SMB协议开共享目录,基础存储文件的功能上,威联通是合格的,但保证数据安全这方面就不能说做得好了,毕竟软件连沙盒功能都没有,只靠上架审核能完全靠谱吗?

然后其他的扩展性啊啥的,一般般吧,有docker的话都不差的

但是用户界面这点要扣分了,太丑了,给人一种很原始的感觉,果然台湾的界面设计都是停留在十几年前的审美吗,感觉跟日本有的一拼,索尼和佳能那些App也是丑的一批… 这点真不如国产哈~

有啥牌子推荐没?

Q:说了这么多,能不能给点啥推荐?

A:无,建议存储空间不够的用户,去买个4T机械硬盘,搭配硬盘扩展坞使用,又便宜又好用。

PS:国产NAS或许在易用性上做得不错,但可扩展性就会差一点,有机会可以尝试体验一下

能折腾Linux的用户,可以自己搞个台式机装普通的Server版Linux玩玩,或者直接上PVE

下一步计划

我的下一步计划是把这台威联通的NAS重装成PVE系统,然后再其上安装OMV之类的开源NAS系统,再来个Ubuntu Server或者Debian作为服务器和测试环境。

在此之前,为了不白花这几千块RMB,我打算先把威联通的大部分功能都体验一下,看看这些复古画风的软件是否真的不堪重用~

参考资料

十二、实现登入


学习ASP.NET Core Blazor编程系列二十二——登录(1)至
学习ASP.NET Core Blazor编程系列二十六——登录(5)

系列
文章中学习了使用AuthenticationStateProvider实现模拟登录。今天的文章实现JWT登录,使用WebAPI接口来实现通过JWT令牌登录。

  1. 在Visual Studio 2022的解决方案资源管理器中,鼠标右键单击“BlazorAppDemo”项目名称,在弹出菜单中选择 “添加—>新建文件夹”,并将新建文件夹改为“Api”。如下图。

2.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Api”文件夹,右键单击,在弹出菜单中选择“添加—>新建项”,在弹出对话框中,选择“API控制器-空”,并将控制器命名为“AuthController”。如下图。并添加如下代码:

usingBlazorAppDemo.Models;usingBlazorAppDemo.Utils;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Identity;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.Extensions.Configuration;usingMicrosoft.IdentityModel.Tokens;usingNewtonsoft.Json.Linq;usingSystem.IdentityModel.Tokens.Jwt;usingSystem.Security.Claims;usingSystem.Text;namespaceBlazorAppDemo.Api
{
[Route(
"api/[controller]")]
[ApiController]
public classAuthController : ControllerBase
{
private readonlyIJWTHelper jwtHelper;publicAuthController(IJWTHelper _IJWTHelper)
{
this.jwtHelper =_IJWTHelper;

}

[HttpPost(
"Login")]public async Task<ActionResult<UserToken>>Login(UserInfo userInfo)
{
//Demo用,更好的做法是查询用户表来实现
if (userInfo.UserName == "admin" && userInfo.Password == "666666666666")
{
returnBuildToken(userInfo);
}
else{
UserToken userToken
= newUserToken()
{
StatusCode
=System.Net.HttpStatusCode.Unauthorized,
IsSuccess
= false};returnuserToken;
}
}
/// <summary>
///建立Token/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
privateUserToken BuildToken(UserInfo userInfo)
{
string jwtToken = jwtHelper.CreateJwtToken<UserInfo>(userInfo);//建立UserToken,回传客户端
UserToken userToken = newUserToken()
{

StatusCode
=System.Net.HttpStatusCode.OK,
Token
=jwtToken,
ExpireTime
= DateTime.Now.AddMinutes(30),
IsSuccess
= true};returnuserToken;
}
}
}

3.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Models”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“UserToken”。并添加如下代码:

usingSystem.Net;namespaceBlazorAppDemo.Models
{
public classUserToken
{
public bool IsSuccess { get ; set; }public HttpStatusCode StatusCode { get; set; }public string Token { get; set; }public DateTime ExpireTime { get; set; }
}
}

4.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Utils”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“TokenManager”。并添加如下代码:

usingBlazorAppDemo.Models;usingSystem.Collections.Concurrent;namespaceBlazorAppDemo.Utils
{
public classTokenManager
{
private const string TOKEN = "authToken";private static readonly ConcurrentDictionary<string, UserToken>tokenManager;staticTokenManager()
{

tokenManager
=new ConcurrentDictionary<string, UserToken>();
}
public static ConcurrentDictionary<string, UserToken> Instance { get { returntokenManager; } }public static string Token { get { returnTOKEN; } }
}
}
5.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Auth”文件夹,右键单击,在弹出菜单中选择“添加—>新建项”,在弹出对话框中,选择“接口”,并将接口命名为“IAuthService”。如下图。并添加如下代码:
usingBlazorAppDemo.Models;namespaceBlazorAppDemo.Auth
{
public interfaceIAuthService
{

Task
<UserToken>LoginAsync(UserInfo userInfo);

Task
<UserToken>LogoutAsync();
}
}

6.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Auth”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“AuthService”。并添加如下代码:
usingBlazorAppDemo.Models;usingBlazorAppDemo.Utils;usingMicrosoft.AspNetCore.Components.Authorization;usingMicrosoft.AspNetCore.Identity;usingNewtonsoft.Json;usingNewtonsoft.Json.Linq;usingSystem.Collections.Concurrent;usingSystem.Net.Http;usingSystem.Text;namespaceBlazorAppDemo.Auth
{
public classAuthService : IAuthService
{
private readonlyHttpClient httpClient;private readonlyAuthenticationStateProvider authenticationStateProvider;private readonlyIConfiguration configuration;private readonlyApi.AuthController authController;private readonly stringcurrentUserUrl, loginUrl, logoutUrl;publicAuthService( HttpClient httpClient, AuthenticationStateProvider authenticationStateProvider, IConfiguration configuration,Api.AuthController authController)
{
this.authController =authController;this.httpClient =httpClient;this.authenticationStateProvider =authenticationStateProvider;this.configuration =configuration;
currentUserUrl
= configuration["AuthUrl:Current"] ?? "Auth/Current/";
loginUrl
= configuration["AuthUrl:Login"] ?? "api/Auth/Login";
logoutUrl
= configuration["AuthUrl:Logout"] ?? "/api/Auth/Logout/";
}
public async Task<UserToken>LoginAsync(UserInfo userInfo)
{
var result =authController.Login(userInfo);var loginResponse =result.Result.Value;if (loginResponse != null &&loginResponse.IsSuccess)
{
TokenManager.Instance.TryAdd(TokenManager.Token, loginResponse);
((ImitateAuthStateProvider)authenticationStateProvider).NotifyUserAuthentication(loginResponse.Token);

httpClient.DefaultRequestHeaders.Authorization
= new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", loginResponse.Token);returnloginResponse;
}
return new UserToken() { IsSuccess = false};
}
public Task<UserToken>LogoutAsync()
{
throw newNotImplementedException();
}
}
}

LoginAsync登录方法的实现功能:

  • 將账号与密码,发送到AuthController做验证,验证成功生成UserToken实例
  • 将token写到TokenManger实例中
  • 通知前面页面更新登录状态
  • 每次request的header将bearer token都带上。

7. 在Visual Studio 2022的解决方案管理器中,使用鼠标左键,双击ImitateAuthStateProvider.cs文件,对代码进行修改。具体代码如下:

usingBlazorAppDemo.Models;usingBlazorAppDemo.Utils;usingMicrosoft.AspNetCore.Components.Authorization;usingSystem.Net.Http;usingSystem.Security.Claims;namespaceBlazorAppDemo.Auth
{
public classImitateAuthStateProvider : AuthenticationStateProvider
{
private readonlyIJWTHelper jwt;privateAuthenticationState anonymous;private readonlyHttpClient httpClient;publicImitateAuthStateProvider(IJWTHelper _jwt, HttpClient httpClient)
{

anonymous
= new AuthenticationState(new ClaimsPrincipal(newClaimsIdentity()));
jwt
=_jwt;this.httpClient =httpClient;
}
bool isLogin = false;string token = string.Empty;public override Task<AuthenticationState>GetAuthenticationStateAsync()
{
//确认是否已经登录
UserToken userToken;
TokenManager.Instance.TryGetValue(TokenManager.Token,
outuserToken);string tokenInLocalStorage=string.Empty;if (userToken != null)
{
tokenInLocalStorage
=userToken.Token;
}
if (string.IsNullOrEmpty(tokenInLocalStorage))
{
//沒有登录,则返回匿名登录者
returnTask.FromResult(anonymous);
}
//將token取出转换为claim
var claims =jwt.ParseToken(tokenInLocalStorage);//在每次request的header中都将加入bearer token
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer",
tokenInLocalStorage);
//回传带有user claim的AuthenticationState
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"))));

}
public voidLogin(UserInfo request)
{
//1.验证用户账号密码是否正确
if (request == null)
{
isLogin
=false;
}
if (request.UserName == "user" && request.Password == "666666666666")
{
isLogin
= true;
token
= jwt.CreateJwtToken<UserInfo>(request);
Console.WriteLine($
"JWT Token={token}");
}
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public void NotifyUserAuthentication(stringtoken)
{
var claims =jwt.ParseToken(token);var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"));var authState = Task.FromResult(newAuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
}

}
}

8. 在Visual Studio 2022的解决方案管理器中,使用鼠标左键,双击Program.cs文件,将之在文本编辑器中打开,将我们写的AuthController和框架中的HttpClient,使用DI方式注入,添加Controller服务。具体代码如下:
usingBlazorAppDemo.Data;usingBlazorAppDemo.Models;usingMicrosoft.AspNetCore.Components;usingMicrosoft.AspNetCore.Components.Web;usingMicrosoft.Extensions.Configuration;usingMicrosoft.EntityFrameworkCore;usingMicrosoft.Extensions.Hosting;usingMicrosoft.AspNetCore.Components.Authorization;usingBlazorAppDemo.Auth;usingMicrosoft.AspNetCore.Authentication.JwtBearer;usingMicrosoft.IdentityModel.Tokens;usingSystem.Text;usingSystem.IdentityModel.Tokens.Jwt;usingBlazorAppDemo.Utils;usingBlazorAppDemo.Api;var builder =WebApplication.CreateBuilder(args);//Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton
<WeatherForecastService>();
IConfiguration config
=ConfigHelper.Configuration;
System.Console.WriteLine(config[
"ConnectionStrings:BookContext"]);
builder.Services.AddDbContextFactory
<BookContext>(opt =>opt.UseSqlServer(ConfigHelper.Configuration["ConnectionStrings:BookContext"]));
builder.Services.AddScoped
<ImitateAuthStateProvider>();
builder.Services.AddScoped
<AuthenticationStateProvider>(implementationFactory =>implementationFactory.GetRequiredService<ImitateAuthStateProvider>());
builder.Services.AddScoped
<JwtSecurityTokenHandler>();//此处的url地址改成自己实际的地址
builder.Services.AddScoped(sp=> new HttpClient { BaseAddress = new Uri("http://localhost:7110") });

builder.Services.AddScoped
<IAuthService, AuthService>();
builder.Services.AddScoped
<AuthController>();//JWT//JWT认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options=>{//取出私钥
var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["Authentication:SecretKey"]);
options.TokenValidationParameters
= newTokenValidationParameters()
{
//验证发布者
ValidateIssuer = true,
ValidIssuer
= builder.Configuration["Authentication:Issuer"],//验证接收者
ValidateAudience = true,
ValidAudience
= builder.Configuration["Authentication:Audience"],//验证是否过期
ValidateLifetime = true,//验证私钥
IssuerSigningKey = newSymmetricSecurityKey(secretByte)
};
});
;
builder.Services.AddScoped
<IJWTHelper,JWTHelper>();var app =builder.Build();//Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(
"/Error");//The default HSTS value is 30 days. You may want to change this for production scenarios, seehttps://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
using (var scope =app.Services.CreateScope())
{
var services =scope.ServiceProvider;try{
Console.WriteLine(
"数据库开始初始化。");var context = services.GetRequiredService<BookContext>();//requires using Microsoft.EntityFrameworkCore;
context.Database.Migrate();//Requires using RazorPagesMovie.Models;
SeedData.Initialize(services);
Console.WriteLine(
"数据库初始化结束。");
}
catch(Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex,
"数据库数据初始化错误.");
}
}


app.UseHttpsRedirection();

app.UseStaticFiles();


app.UseRouting();
app.MapControllers();


app.MapBlazorHub();
app.MapFallbackToPage(
"/_Host");
app.UseAuthentication();
app.UseAuthorization();


app.Run();

9. 在Visual Studio 2022的菜单栏上,找到“调试-->开始调试”或是按F5键,Visual Studio 2022会生成BlazorAppDemo应用程序,并在浏览器使用Rest调试插件,对api/auth/login接口进行调试,只要登入成功就可以取得token。如下图。

10.我们在用户名输入框中输入用户名"admin",在密码输入框中输入密码"666666666666",点击“登录”按钮,进行登录。我们进入了系统。如下图。


1、为什么需要Debug

目的
:开发过程中
查找或定位错误
或者
阅读源码

程序运行的结果(4种情况)

情况1:没有任何bug,程序执行正确!

情况2: 运行以后,出现了错误或异常信息。但是通过
日志文件或控制台
,显示了异常信息的位置。

情况3: 运行以后,得到了结果,但是
结果不是我们想要的

情况4: 运行以后,得到了结果,结果大概率是我们想要的。但是
多次运行
的话,可能会出现不是我们想要的情况。比如:多线程情况下,处理线程安全问题


2、Debug的步骤

1、添加断点
2、启动调试
3、单步执行
4、
观察变量和执行流程
,找到并解决问题

■ 断点的添加、取消、查看

  • 添加/取消某个位置的断点:点击行号位置

  • 查看所有断点:Ctrl + Shift + F8

  • 取消所有断点: 点击查看所有断点,不勾选Java Line Breakpoints


调试中的断点意义
:执行到了即生效了,就会暂停一下。


打多个断点的意义
:整个项目也可以打一个断点,然后执行到想要观察的方法的调用,通过step into 进入观察,但是可能点快了,直接完执行方法调用。但是项目工程比较大,对于想要观察的方法打上一个断点,执行到了,它就会生效暂停,暂停一下更方便而已。

■ 设置好断点后,启动选择debug方式启动:

  • debug 键的位置
    :行号点击三角形可以选择,代码内容空白位置右键可以选择,菜单工具栏的运行臭虫子图标可以选择

  • 代码中的打印、日志语句
    ,执行后,在Debug模式下Console 观察

  • 变量区的变量注意是当前方法栈的


3、idea 调试快捷键


4、总结一下调试快捷键的使用

(1) 常用的:

观看下一步的
单步调试
[F8],
进入方法体内部
[F7],
退出方法体
,执行下一步[Shift+F8],
直接跳到下一个断点
[F9]

(2) 走神了:

回到当前文件断点位置
[Alt+F10],
跳到光标
[Alt+F9],

太走神/观察不细致,那就重来一遍:
回退到上一个方法调用的开始处
[Drop Frame]

(3) 结束:

结束调试[Ctr+F2]、让当前断点后面的所有断点失效[Mute Breakpoints]


5、调试过程的其他小技巧

(1) Ctr+鼠标

  • 点击目标进入,再次点击,如果不能再进入则会出来。目标可以是属性对象,也可以是类、是接口,是方法等等。

(2) Alt+箭头方向←

  • 让光标回到上一个光标位置。有时候,因为Ctr+鼠标点击进入之后,在不移动光标位置,想退回上一个光标位置,那么Alt+←

(3) 全局搜索快捷键

  • Ctr+Shift+F

  • Ctr+Shift+N

(4) 定位当前打开的文件的位置

  • 定位当前文件所在项目目录结构位置:


6、断点细分(重点:条件断点、线程调试)

(1) 方法断点:

  • 刚进入方法会停顿一下

(2) 字段断点:

  • 默认会停顿在字段值发生改变的位置

(3) ☺ 条件断点----
适合观察循环体的代码执行流程

  • 可以在断点位置,设置暂停的条件,右键断点

(4) 异常断点:

  • 设置某种异常暂停

(5)☺ 线程调试:

  • 设置,观察某个线程的执行



7、调试实践和心得总结

举例分布式项目调试,比如有三个应用:
admin应用
-将任务主体存储到redis,
master应用
-将redis任务主体进行执行。其中,master实际上是远程调用了
worker应用
-去执行任务主体。

(1) 多个应用调试观察执行流程:

  • 要注意流程中应用执行,从A应用执行着到了应用B应用,然后再继续执行到应用C,调试的时候,也要跟着流程切换应用的调式模式。

  • 当A应用debug着,突然卡着的时候,就需要考虑这时候执行流程是不是进入了应用B了,切换到应用B的debug模式观察一下。

  • 切换到C应用的时候,观察执行下一步,迟迟不肯进入下一步,考虑是不是A应用是不是得“放行”,B应用执行到下一步,C应用才有资格进行执行。


    举例:B应用[
    master应用
    ],断点停在了调用远程C应用[
    worker应用
    ]的这一行时候:workerWebservice.execChat(chatExecParam);

    然后,因为是远程调用,如果你还点击下一步,会进入的是B应用的反射底层代码,这时候,需要通过打断点跳到下一个断点(C应用)的代码位置,然后观察C应用的工作流程,但是C工作,真正想要执行run方法,需要B应用执行到下一步,相当于“放行”。

(2) 打断点要注意远程调用的代码位置不能打,否则项目跑不起来

举例:在@FeignClient的接口中的方法打了断点,导致项目启动失败。

  • 执行流程:A服务远程调用B服务
// A服务,将chatExecParam 发送给B服务[worker] 去执行
workerWebservice.execChat(chatExecParam);

// 中间的Fegin负载均衡
@FeignClient("chat-worker")
public interface WorkerWebservice {
    @RequestMapping(value = "/execChat", method = RequestMethod.POST)
    boolean execChat(@RequestBody CrawlerExecParam crawlerExecParam);  //《---在这里打了断点,导致A服务启动失败 
}

// B服务
@Override
@RequestMapping(value = "/execChat", method = RequestMethod.POST)
public boolean execCrawler(ChatExecParam chatExecParam) {
   workerContext.getThreadPool().execute(new ExecQueueScanRunnable(chatExecParam, masterWebservice));
   return true;
}
  • 错误信息:
2023-03-11 22:45:38.162 [WARN ] [main] com.netflix.config.sources.URLConfigurationSource - No URLs will be polled as dynamic configuration sources.
2023-03-11 22:45:38.172 [INFO ] [main] com.netflix.config.sources.URLConfigurationSource - To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2023-03-11 22:45:38.855 [WARN ] [main] com.netflix.config.sources.URLConfigurationSource - No URLs will be polled as dynamic configuration sources.
2023-03-11 22:45:38.866 [INFO ] [main] com.netflix.config.sources.URLConfigurationSource - To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2023-03-11 22:46:30.475 [INFO ] [main] o.s.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
2023-03-11 22:47:43.098 [INFO ] [main] o.s.scheduling.concurrent.ThreadPoolTaskScheduler - Initializing ExecutorService 'taskScheduler'
2023-03-11 22:47:46.228 [INFO ] [main] com.alibaba.nacos.client.naming - initializer namespace from System Property :null
2023-03-11 22:47:46.275 [INFO ] [main] com.alibaba.nacos.client.naming - initializer namespace from System Environment :null
2023-03-11 22:47:46.315 [INFO ] [main] com.alibaba.nacos.client.naming - initializer namespace from System Property :null
  • 分析原因:[▷下次抽空补充!◀]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  • 解决:取消掉在@FeignClient接口下的方法打断点


(3) idea 卡着不动/debug 执行太慢:

  • 调试点击下一步,或者调试跳到下一个断点的时候,idea 卡着不动:


判断是否卡着
:按键-结束调试是激活状态;重要的是在Variables 提示了一句:程序正在运行The application is running


程序真正(debug)结束

  • 原因分析

① 可能原因1:
方法断点
会使得正在debug调试的程序变慢【
建议:少用方法断点

② 可能原因2:
断点打太多

建议尽量不要在项目里打过多的断点,调试哪里就在哪里打上,调试完把断点去掉就好。

③ 可能原因3:执行的方法是
远程方法
(和网络有关)或者执行的方式是
数据库操作
有关。


☺ 心得总结

1、
建议:少用方法断点

2、
建议:不要打太多断点,为了观察方便,需要再打,观察完,可以去掉断点。

3、注意:如果项目是分布式项目,负载均衡@Feign的接口中的方法【
本来就不建议在方法的位置打断点了!
】,如果影响了项目启动,请取消断点。




如果本文对你有帮助的话记得给一乐点个赞哦,感谢!

痞子衡嵌入式半月刊: 第 73 期

这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。

本期刊是开源项目(GitHub:
JayHeng/pzh-mcu-bi-weekly
),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。

上期回顾

《痞子衡嵌入式半月刊: 第 72 期》

唠两句

历史上的今天:1782年3月12日,中国古代最大的文化工程《四库全书》编成。

本期共收录 4 个项目、1 个工具,希望对你有帮助!

项目类

1、Slint - 全开源跨平台UI设计库

Slint 是一个工具包,可以有效地为任何显示器(嵌入式设备和桌面应用程序)开发流畅的图形用户界面。Slint支持多种编程语言,如Rust、c++和JavaScript。Slint 官方已经为 STM32 和 RP2040 做了适配。

2、MRI - 用于Cortex-M设备的gdb兼容调试监视器

MRI 是一个调试监视器,它允许 GNU调试器 GDB 调试 Cortex-M3/M4 处理器。这使得使用功能齐全的源级调试器调试在 Cortex-M 设备上运行的应用程序成为可能,而不需要额外的硬件,只需一个串行连接。

3、Reflow-Controller - 全开源回流控制器

Reflow-Controller 是一个回流控制器,用来控制不同种类的回流炉,热板等。它基于 ESP32-S2,因此可以编程为与 WiFi App 或其他应用程序一起使用。不过,它也有一个板载 OLED 显示屏和三个按钮,可以作为独立的解决方案使用。

4、ZSWatch - 基于 Zephyr 的开源智能手表

ZSWatch 是一个基于Zephyr™ RTOS 的从零开始的全开源智能手表项目,包括硬件和软件。ZSWatch 项目分为两代:V1、V2,其中 V1 已经全部完成,V2 正在进行中。

工具类

1、aardio - 专注于桌面软件开发的动态编程语言

aardio 专注于桌面软件开发,17年一直保持非常活跃地更新,aardio 被多年用于生产项目实践,久经测试和锤炼。

aardio 小、轻、快,轻便利索,体积仅 6.5MB,学习和使用成本极低。aardio 虽然小,但提供了惊人数量的开源标准库、扩展库 - 这些库基本都是由纯 aardio 代码实现,涉及到了桌面编程的方方面面。aardio 中的所有库基本都是由作者一个人编写,所以拥有良好的一致性。

区别于其他动态语言,aardio 可以非常方便地支持真多线程,并且提供大量多线程函数库、演示范例等等。

欢迎订阅

文章会同时发布到我的
博客园主页

CSDN主页

知乎主页

微信公众号
平台上。

微信搜索"
痞子衡嵌入式
"或者扫描下面二维码,就可以在手机上第一时间看了哦。