有没有与SFTP服务器建立异步连接的方法?

huangapple go评论133阅读模式
英文:

Is there any asynchronous way to connect to an SFTP server?

问题

我正在处理一个接收消息并将其以 .json 格式上传到 SFTP 服务器的 API。然而,连接时间太长,SFTP 连接需要超过 1 秒,这太久了。
我想知道是否有任何方法可以连接到服务器并发送文件而不必等待这么长时间。

我考虑过保存文件,回复客户端,然后使用以下方法上传文件:

public async Task Upload(string localPath, string remotePath)
{
    await Task.Run(() =>
    {
        // 上传代码在这里
    });
}

然而,如果在上传过程中发生异常,API 可能已经向客户端回复 OK,这样就无法通知客户端上传失败。

这是我目前用于上传文件的现有代码:

public void Upload(string localPath, string remotePath)
{
    try
    {
        using (var sftp = new SftpClient(...))
        {
            sftp.Connect(); // 非常慢
            sftp.UploadFile(...);
            sftp.Disconnect();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error Upload SFTPHandler");
    }
}

有人能提出一种解决方案,可以连接到 SFTP 服务器并在不等待太长时间的情况下发送文件吗?

英文:

I am working on an API that receives messages and uploads them to an SFTP server in a .json format. However, the connection time is taking too long, and the SFTP Connection takes over 1 second, which is too much.
I want to know if there is any way to connect to the server and send the file without waiting for so long.

I have thought about saving the files, answering the client, and then uploading the file with this method:

public async Task Upload(string localPath, string remotePath)
{
    await Task.Run(() =>
    {
        // upload code here
    });
}

However, if an exception occurs during the upload, the API might have already responded with an OK to the client, making it impossible to notify the client that the upload failed.

Here's the existing code that I am currently using to upload files:

public void Upload(string localPath, string remotePath)
{
    try
    {
        using (var sftp = new SftpClient(...))
            {
                sftp.Connect(); // Very slow
                sftp.UploadFile(...);
                sftp.Disconnect();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error Upload SFTPHandler");
    }
}

Can someone suggest a solution to connect to the SFTP server and send the file without waiting for so long?

答案1

得分: 2

你可以存储上传状态,并单独检查是否已上传。例如,你可以将状态缓存到ConcurrentDictionary中。然后,客户端可以定期轮询上传状态。

假设你正在使用SSH.Net客户端,你也可以使用其异步方法。

static ConcurrentDictionary<string, bool> _statuses = new();

[HttpPost]
public async Task UploadFile(string localPath, string remotePath)
{
    _statuses[remotePath] = false;
    Task.Run(() => Upload(localPath, remotePath));
}

private async Task Upload(string localPath, string remotePath)
{
    try
    {
        using var sftp = new SftpClient(...);
        await sftp.ConnectAsync();
        await TaskFactory.FromAsync(
            (input, path, asyncCallback, state) =>
                sftp.BeginUploadFile(input, path, asyncCallback),
            sftp.EndUploadFile,
            ..., // 一些文件流
            remotePath,
            null);
        _statuses[remotePath] = true;
        sftp.Disconnect();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error Upload SFTPHandler");
    }
}


[HttpPost]
public async Task<bool> IsUploaded(string remotePath)
{
    if (!_statuses.TryGetValue(remotePath, out var value))
    {
        // IIS 重新启动了。
        // 检查SFTP以获取已完成的上传。
        _statuses.Add(someStatus);
    }
    return value;
}

在IIS服务器重新启动时,你可能需要考虑一种缓存状态的方式。

英文:

You can store the upload status, and do a separate check to see if it was uploaded. For example, you could cache the status in a ConcurrentDictionary. The client can then poll periodically for an upload status.

Assuming you are using the SSH.Net client, you can also use its asynchronous methods.

static ConcurrentDictionary&lt;string, bool&gt; _statuses = new();

[HttpPost]
public async Task UploadFile(string localPath, string remotePath)
{
    _statuses[remotePath] = false;
    Task.Run(() =&gt; Upload(localPath, remotePath));
}

private async Task Upload(string localPath, string remotePath)
{
    try
    {
        using var sftp = new SftpClient(...);
        sftp.ConnectAsync();
        await TaskFactory.FromAsync(
            (input, path, asyncCallback, state) =&gt;
                sftp.BeginUploadFile(input, path, asyncCallback),
            sftp.EndUploadFile,
            ..., // some filestream
            remotePath,
            null);
        _statuses[remotePath] = true;
        sftp.Disconnect();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, &quot;Error Upload SFTPHandler&quot;);
    }
}


[HttpPost]
public async Task&lt;bool&gt; IsUploaded(string remotePath)
{
    if (!_statuses.TryGetValue(remotePath, out var value))
    {
        // IIS was restarted.
        // check SFTP for finished upload.
        _statuses.Add(someStatus);
    }
    return value;
}

You may want to think about some way of caching the status in the event of an IIS server restart.

答案2

得分: 1

但是,如果在上传过程中发生异常,API 可能已经向客户端发送了 OK,使得不可能通知客户端上传失败。

这对所有“发即忘”的工作都是适用的,这就是为什么它通常是错误的解决方案。

如果您绝对不能等待超过1秒钟(这对我来说听起来不算过分),那么您最好的选择是基本分布式架构,如我在博客中所述。基本分布式架构有两个部分,还有一个可选的第三部分(听起来您可能想要的):

  1. 耐久队列。这是为了防止工作丢失而必需的。
  2. 处理队列的后台进程。
  3. 客户端轮询或通知,以在工作完成时通知客户端。
英文:

> However, if an exception occurs during the upload, the API might have already responded with an OK to the client, making it impossible to notify the client that the upload failed.

This is true for all "fire-and-forget" work, which is why it's often the wrong solution.

If you absolutely can't wait more than 1 second (?? doesn't sound too unreasonable to me), then your best option is a basic distributed architecture, as described on my blog. The basic distributed architecture has two parts, with an optional third part (which it sounds like you would want):

  1. A durable queue. This is necessary to prevent the work from being lost.
  2. A background process that processes the queue.
  3. Client-side polling or notification to inform the client when the work completes.

huangapple
  • 本文由 发表于 2023年3月15日 21:01:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75745090.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定