深度优先

这个家伙好懒,除了文章什么都没留下

0%

删除三天前的数据的sql

1
DELETE FROM table WHERE created_on < DATE_SUB(CURDATE(),INTERVAL 3 DAY);

CURDATE() 返回当前日期

CURNOW() 返回当前datetime

INTERVAL 是mysql间隔值,用法为 INTERVAL expr unitINTERVAL 3 DAY 表示三天的间隔

DATE_SUB(start_date,INTERVAL expr unit);

写一个存储过程

存储过程相当于mysql的函数,它是存储在数据库服务器中的一组sql语句,通过调用这个函数的名称来执行这些sql语句命令。

1
2
3
4
5
6
DELIMITER // 
create procedure del_data()
BEGIN
DELETE FROM table WHERE created_on < DATE_SUB(CURDATE(),INTERVAL 3 DAY);
END//
DELIMITER ;

DELIMITER是分割符的意思,声明存储过程前将”//“声明为分隔符,这样存储过程中的“;”才不会被当作分隔符处理。声明结束后再还原分隔符。
存储过程也可以带参数, 存储过程名(参数)
在声明存储过程前要先用 use database_name 切换到想要应用的数据库,否则存储过程会应用到默认数据库中

查看以及使用存储过程

查看存储过程

1
select * from mysql.proc where db=’数据库名’;

使用存储过程

1
call del_data()

写一个事件

开启事件调度器

1
SET GLOBAL event_scheduler = ON;

创建事件

1
2
3
4
5
create event del_event  
on schedule
EVERY 1 day
STARTS '2019-3-28 00:00:00'
do call del_data()

从明天的零点开始,每隔一天执行del_data存储过程

原文地址:https://www.jianshu.com/p/be1e581acb8e

1、crontab 是用来让使用者在固定时间或固定间隔执行程序之用
手动启动crontab服务:

1
service crond start

crontab相关知识:参考,https://blog.csdn.net/qq_32688731/article/details/88203654

2、mysqldump

docker中mysqldump的完整路径docker exec /usr/bin/mysqldump。

mysqldump详解

3、数据库备份脚本
功能:mysql 每天定时备份, 并删除7天以前的备份
mysql_dumps.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

docker_name = mysql
data_dir = "/opt/docker/mysql/backup/"
docker exec -it ${docker_name} mysqldump -uroot -pmysql@Pwd.. --all-databases > "$data_dir/data_`date +%Y%m%d`.sql"
send=`date '+%Y-%m-%d %H:%M:%S'`
if [ $? -ne 0 ];
then
# 任务失败,发送邮件
echo "$send 任务失败,发送邮件"
exit -1
else
echo "$send 数据备份成功!"
fi

find $data_dir -mtime +7 -name 'data_[1-9].sql' -exec rm -rf {} \;

4、通过linux cron设置定时任务
crontab -e:

1
2
0 2 * * * sh /opt/docker/mysql/mysql_dumps.sh > /opt/docker/mysql/mysql_dumps.log 2>&1

5、遇到的问题

1
2
/bin/sh^M: bad interpreter: No such file or directory

原因:这是不同系统编码格式引起的:在windows系统中编辑的.sh文件可能有不可见字符,所以在Linux系统下执行会报以上异常信息。
解决:
1).在windows下转换:
利用一些编辑器如UltraEdit或EditPlus等工具先将脚本编码转换,再放到Linux中执行。转换方式如下(UltraEdit):File–>Conversions–>DOS->UNIX即可。
2).在linux中编写改脚本。

SignalR

SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式.

SignalR基于这三种技术构建, 抽象于它们之上, 它让你更好的关注业务问题而不是底层传输技术问题.

SignalR这个框架分服务器端和客户端, 服务器端支持ASP.NET Core 和 ASP.NET; 而客户端除了支持浏览器里的javascript以外, 也支持其它类型的客户端, 例如桌面应用.

回落机制

SignalR使用的三种底层传输技术分别是Web Socket, Server Sent Events 和 Long Polling.

其中Web Socket仅支持比较现代的浏览器, Web服务器也不能太老.

而Server Sent Events 情况可能好一点, 但是也存在同样的问题.

所以SignalR采用了回落机制, SignalR有能力去协商支持的传输类型.

Web Socket是最好的最有效的传输方式, 如果浏览器或Web服务器不支持它的话, 就会降级使用SSE, 实在不行就用Long Polling.

一旦建立连接, SignalR就会开始发送keep alive消息, 来检查连接是否还正常. 如果有问题, 就会抛出异常.

因为SignalR是抽象于三种传输方式的上层, 所以无论底层采用的哪种方式, SignalR的用法都是一样的.

SignalR默认采用这种回落机制来进行传输和连接.

但是也可以禁用回落机制, 只采用其中一种传输方式.

RPC

RPC (Remote Procedure Call). 它的优点就是可以像调用本地方法一样调用远程服务.

SignalR采用RPC范式来进行客户端与服务器端之间的通信.

SignalR利用底层传输来让服务器可以调用客户端的方法, 反之亦然, 这些方法可以带参数, 参数也可以是复杂对象, SignalR负责序列化和反序列化.

Hub

Hub是SignalR的一个组件, 它运行在ASP.NET Core应用里. 所以它是服务器端的一个类.

Hub使用RPC接受从客户端发来的消息, 也能把消息发送给客户端. 所以它就是一个通信用的Hub.

在ASP.NET Core里, 自己创建的Hub类需要继承于基类Hub.

在Hub类里面, 我们就可以调用所有客户端上的方法了. 同样客户端也可以调用Hub类里的方法.

这种Hub+RPC的方式还是非常适合实时场景的.

之前说过方法调用的时候可以传递复杂参数, SignalR可以将参数序列化和反序列化. 这些参数被序列化的格式叫做Hub 协议, 所以Hub协议就是一种用来序列化和反序列化的格式.

Hub协议的默认协议是JSON, 还支持另外一个协议是MessagePack. MessagePack是二进制格式的, 它比JSON更紧凑, 而且处理起来更简单快速, 因为它是二进制的.

此外, SignalR也可以扩展使用其它协议..

横向扩展

随着系统的运行, 有时您可能需要进行横向扩展. 就是应用运行在多个服务器上.

这时负载均衡器会保证每个进来的请求按照一定的逻辑分配到可能是不同的服务器上.

在使用Web Socket的时候, 没什么问题, 因为一旦Web Socket的连接建立, 就像在浏览器和那个服务器之间打开了隧道一样, 服务器是不会切换的.

但是如果使用Long Polling, 就可能有问题了, 因为使用Long Polling的情况下, 每次发送消息都是不同的请求, 而每次请求可能会到达不同的服务器. 不同的服务器可能不知道前一个服务器通信的内容, 这就会造成问题.

针对这个问题, 我们需要使用Sticky Sessions (粘性会话).

Sticky Sessions 貌似有很多中实现方式, 但是主要是下面要介绍的这种方式.

作为第一次请求的响应的一部分, 负载均衡器会在浏览器里面设置一个Cookie, 来表示使用过这个服务器. 在后续的请求里, 负载均衡器读取Cookie, 然后把请求分配给同一个服务器.

在ASP.NET Core 中使用SignalR

创建项目,新建SignalRHub:

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
49
50
51
52
53
54
55
56
57
58
59
60
using Core.Parameters;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace XXXXX.SignalR
{
public class SignalRHub : Hub
{
private readonly DcListService _dcListService;
public SignalRHub(DcListService dcListService)
{
_dcListService = dcListService;
}
public void AddMachineCode(string dcCode)
{
_dcListService.Add(dcCode, Context.ConnectionId);
}

public void Exec(Paramete paramete)
{
string connectionId = Context.ConnectionId;
var toClient = _dcListService.FirstOrDefault(paramete.MachineCode);
if (toClient == null)
{
Clients.Client(Context.ConnectionId).SendAsync("Result", connectionId, false, "系统消息:当前设备不在线!");
}
else
{
Clients.Client(toClient.ConnectionId).SendAsync("Exec", connectionId, paramete);
}
}

public void Result(string connectionId,bool flag, string data)
{
Clients.Client(connectionId).SendAsync("Result", connectionId, flag, data);
}

public async override Task OnDisconnectedAsync(Exception exception)
{
_dcListService.Remove(Context.ConnectionId);
}

public async override Task OnConnectedAsync()
{
//string connectionId = Context.ConnectionId;
}
}
public class Paramete
{
public string MachineCode { get; set; }
public string ProcessPath { get; set; }
public string ExecType { get; set; }
public string ViewType { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
}

在Startup里添加:

1
2
services.AddScoped<DcListService>();
services.AddSignalR();
1
2
3
4
app.UseSignalR(routes =>
{
apHub<SignalRHub>("/signalR");
});

Winform客户端代码:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsSample
{
public partial class ChatForm : Form
{
private HubConnection _connection;

public ChatForm()
{
InitializeComponent();
}

private void ChatForm_Load(object sender, EventArgs e)
{
addressTextBox.Focus();
}

private void addressTextBox_Enter(object sender, EventArgs e)
{
AcceptButton = connectButton;
}

private async void connectButton_Click(object sender, EventArgs e)
{
UpdateState(connected: false);

_connection = new HubConnectionBuilder()
.WithUrl(addressTextBox.Text)
.Build();

_connection.On<string, Paramete>("Exec", Exec);

Log(Color.Gray, "Starting connection...");
try
{
await _connection.StartAsync();
await _connection.InvokeAsync("AddMachineCode", this.dcCodeTextBox.Text);
}
catch (Exception ex)
{
Log(Color.Red, ex.ToString());
return;
}

Log(Color.Gray, "Connection established.");

UpdateState(connected: true);

messageTextBox.Focus();
}

private async void disconnectButton_Click(object sender, EventArgs e)
{
Log(Color.Gray, "Stopping connection...");
try
{
await _connection.StopAsync();
}
catch (Exception ex)
{
Log(Color.Red, ex.ToString());
}

Log(Color.Gray, "Connection terminated.");

UpdateState(connected: false);
}

private void messageTextBox_Enter(object sender, EventArgs e)
{
AcceptButton = sendButton;
}

private async void sendButton_Click(object sender, EventArgs e)
{
try
{
await _connection.InvokeAsync("SendSingle", this.toDcCodeTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
Log(Color.Red, ex.ToString());
}
}

private void UpdateState(bool connected)
{
disconnectButton.Enabled = connected;
connectButton.Enabled = !connected;
addressTextBox.Enabled = !connected;

messageTextBox.Enabled = connected;
sendButton.Enabled = connected;
}

private async void Exec(string connectionId, Paramete paramete)
{
Log(Color.Black, "");
Log(Color.Black, "消息来了");
Log(Color.Black, $"ConnectionId:{connectionId}");
Log(Color.Black, $"MachineCode:{paramete.MachineCode}");
Log(Color.Black, $"ProcessPath:{paramete.ProcessPath}");
Log(Color.Black, $"ExecType:{paramete.ExecType}");
Log(Color.Black, $"ViewType:{paramete.ViewType}");
Log(Color.Black, $"StartDate:{paramete.StartDate}");
Log(Color.Black, $"EndDate:{paramete.EndDate}");

await _connection.InvokeAsync("Result", connectionId, true, "客户端执行成功!");
}

private void Log(Color color, string message)
{
Action callback = () =>
{
messagesList.Items.Add(new LogMessage(color, message));
};

Invoke(callback);
}

private class LogMessage
{
public Color MessageColor { get; }

public string Content { get; }

public LogMessage(Color messageColor, string content)
{
MessageColor = messageColor;
Content = content;
}
}

private void messagesList_DrawItem(object sender, DrawItemEventArgs e)
{
var message = (LogMessage)messagesList.Items[e.Index];
e.Graphics.DrawString(
message.Content,
messagesList.Font,
new SolidBrush(message.MessageColor),
e.Bounds);
}
}
public class Paramete
{
public string MachineCode { get; set; }
public string ProcessPath { get; set; }
public string ExecType { get; set; }
public string ViewType { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
}

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
using Core.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace XXXX.SignalR
{
public class DcListService
{
private static List<OnlineDcCode> DcList = new List<OnlineDcCode>();
public OnlineDcCode FirstOrDefault(string dcCode)
{
return DcList.Where(p => p.DcCode == dcCode).FirstOrDefault();
}

public void Add(string dcCode, string connectionId)
{
if (FirstOrDefault(dcCode) == null)
{
DcList.Add(new OnlineDcCode()
{
ConnectionId = connectionId,
DcCode = dcCode
});
}
else
{
DcList.Where(p => p.DcCode == dcCode).FirstOrDefault().ConnectionId = connectionId;
}
}

public void Remove(string connectionId)
{
var dc = DcList.FirstOrDefault(p => p.ConnectionId == connectionId);
if (dc != null)
{
DcList.Remove(dc);
}
}
}
}

参考:https://www.cnblogs.com/cgzl/p/9515516.html

参考:https://github.com/aspnet/SignalR-samples

SignalR in ASP.NET Core behind Nginx
在Nginx中转发,配置:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

add_header Access-Control-Allow-Origin *;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
}

location ^~ /api/signalR
{
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
proxy_pass http://192.168.1.231:8091;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}

location ^~ /api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
proxy_pass http://192.168.1.231:8091;
}
#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# root /usr/share/nginx/html;
# }

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

参考:https://stackoverflow.com/questions/48300288/signalr-in-asp-net-core-behind-nginx

断开后自动重新连接:

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
//2、彻底断开 重新连接
_connection.Closed += async (error) =>
{
Log(Color.Red, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ff")} 断开连接: " + error);
await Task.Delay(new Random().Next(0, 5) * 1000);
int count = 0;
while (_connection.State == HubConnectionState.Disconnected)
{
try
{
//4、开启会话
await _connection.StartAsync();
//5、将 机器码 和 exe文件路径 与 当前连接关联上
await _connection.InvokeAsync("AddMachineCode", this.dcCodeTextBox.Text);
Log(Color.Green, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ff")} ----- 重新连接成功! ------");
}
catch (Exception ex)
{
Log(Color.Red, ex.ToString());
}
Log(Color.Red, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ff")} ----- 第 {++count} 彻底断开 尝试重连 ------");
await Task.Delay(new Random().Next(0, 5) * 1000);
}
};

JWT认证:

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
#region Authentication
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
if (context.Request.Path.ToString().StartsWith("/api/signalR"))
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
},
};
});
#endregion

主要是这一段:

1
2
3
4
5
6
7
8
9
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
if (context.Request.Path.ToString().StartsWith("/api/signalR"))
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
},
};

客户端配置:

服务器端配置: