lizf2019 发表于 2024-8-16 18:12

【C#】异步多线程 求助

本帖最后由 lizf2019 于 2024-8-16 19:24 编辑

正在测试自己的服务器的抗打能力,因为市面上
直接可用的不太好找,遂想自己写一个




参考的代码测试后觉得难以使用,求助大佬如何实现这个:
1. C#每秒创建十个线程,每个线程通过不同代{过}{滤}理ip执行对url循环访问,之后每个线程在被创建的30s后自动销毁
2.每创建10个线程之前 使用Getip()更新ip库,之后每个代{过}{滤}理ip【格式为 ip:port 】从字符串数组Myip[]中调用:Myip   n大于等于0小于等于9
3.代码是测自己服务器承受能力用的,如果有更好的模拟访问等方法欢迎大佬指出{:301_997:}

以下为ip格式,我通过
string[] ipPortPairs;
ipPortPairs = ipPortString.Split(' ');
将其存入数组



115.209.48.179:37608 116.7.173.115:31843 116.7.201.221:35131 119.132.91.47:39837 222.189.81.116:35243 175.146.211.176:34454 121.234.46.195:34738 111.72.196.138:33973 42.57.150.201:39846 111.72.134.86:40290


刺心 发表于 2024-8-16 18:24

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class ServerStressTest
{
    // 可配置的常量
    private const int ThreadCount = 10; // 每秒创建的线程数
    private const int RequestTimeoutSeconds = 30; // 每个请求的超时时间(秒)
    private const int DelayBetweenBatches = 1000; // 每批线程之间的延迟时间(毫秒)
    private const string RequestUrl = "http://your-url-here.com"; // 要访问的URL
    private static readonly string[] Myip = new string; // 代{过}{滤}理IP数组

    private static int ipIndex = 0;

    public static void Main()
    {
      // 初始化 Myip 数组
      for (int i = 0; i < Myip.Length; i++)
      {
            Myip = $"127.0.0.{i + 1}:8080"; // 示例IP:端口
      }

      StartProxyRequests();
    }

    public static void StartProxyRequests()
    {
      Task.Run(async () =>
      {
            while (true)
            {
                List<Task> tasks = new List<Task>();

                for (int i = 0; i < ThreadCount; i++)
                {
                  UpdateIpPortPairs(); // 每次循环前更新 IP 库
                  tasks.Add(CreateRequestTask());
                }

                await Task.WhenAll(tasks);
                await Task.Delay(DelayBetweenBatches); // 等待指定的延迟时间后继续创建新的一批任务
            }
      });
    }

    private static Task CreateRequestTask()
    {
      return Task.Run(async () =>
      {
            string ipPort = GetNextIpPort(); // 获取下一个 IP:Port
            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(RequestTimeoutSeconds)); // 设置超时为30秒

            using (HttpClientHandler handler = new HttpClientHandler())
            {
                handler.Proxy = new System.Net.WebProxy($"http://{ipPort}");
                handler.UseProxy = true;

                using (HttpClient client = new HttpClient(handler))
                {
                  try
                  {
                        Console.WriteLine($"使用代{过}{滤}理 {ipPort} 发送请求...");

                        // 发送请求
                        var response = await client.GetAsync(RequestUrl, cts.Token);
                        string result = await response.Content.ReadAsStringAsync();

                        Console.WriteLine($"来自 {ipPort} 的响应: {result.Substring(0, 100)}");
                  }
                  catch (TaskCanceledException)
                  {
                        Console.WriteLine($"代{过}{滤}理 {ipPort} 的任务已取消。");
                  }
                  catch (Exception ex)
                  {
                        Console.WriteLine($"代{过}{滤}理 {ipPort} 的任务发生错误: {ex.Message}");
                  }
                }
            }
      });
    }

    private static string GetNextIpPort()
    {
      lock (Myip)
      {
            string ipPort = Myip;
            ipIndex = (ipIndex + 1) % Myip.Length;
            return ipPort;
      }
    }

    private static void UpdateIpPortPairs()
    {
      // 在这里更新 Myip 数组
      // 例如,可以从某个服务或数据库中获取新的代{过}{滤}理 IP 地址
      Console.WriteLine("更新IP库...");
    }
}

lizf2019 发表于 2024-8-16 19:26

刺心 发表于 2024-8-16 18:24
using System;
using System.Collections.Generic;
using System.Net.Http;


好像有点问题

jidesheng6 发表于 2024-8-16 21:27

你测试服务器压力的话,我更建议你用python的locust框架进行测试,你有c#的基础上手python不会特别难;比你自己费劲写效果要好很多,你想模拟多少就模拟多少,公司服务器做压力测试的时候直接全断开了。给你参考下写法,不是特别难,稍微研究下就行。

zlqhysy 发表于 2024-8-16 21:45

都是高手呀

katelya 发表于 2024-8-16 21:48

用Apache JMeter或locust进行压力测试试试?

cfnm123 发表于 2024-8-16 21:59

本帖最后由 cfnm123 于 2024-8-16 22:01 编辑

using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

class Program
{
    private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(100, 100);
    private static readonly string[] Myip;
    private static readonly IHost host;
    private static readonly HttpClient httpClient;
    private static readonly ILogger logger;
    private static readonly string url = "http://your-target-url-here";

    static Program()
    {
      // 初始化代{过}{滤}理IP列表
      string ipPortString = "115.209.48.179:37608 116.7.173.115:31843 116.7.201.221:35131 119.132.91.47:39837 222.189.81.116:35243 175.146.211.176:34454 121.234.46.195:34738 111.72.196.138:33973 42.57.150.201:39846 111.72.134.86:40290";
      Myip = ipPortString.Split(' ');

      host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                services.AddLogging(builder => builder.AddConsole());
                services.AddHttpClient();
            })
            .Build();

      logger = host.Services.GetRequiredService<ILogger<Program>>();
      httpClient = host.Services.GetRequiredService<IHttpClientFactory>().CreateClient();
    }

    static async Task Main(string[] args)
    {
      for (int i = 0; i < 10; i++)
      {
            await UpdateIpListAsync();
            await CreateThreadsAsync();
            await Task.Delay(1000); // 每秒创建一次
      }

      logger.LogInformation("All threads have been created.");
      Console.ReadKey();
    }

    static async Task CreateThreadsAsync()
    {
      for (int i = 0; i < 10; i++)
      {
            int index = i;
            var task = ExecuteRequestAsync(index);
            await task;
      }
    }

    static async Task ExecuteRequestAsync(int index)
    {
      var handler = new HttpClientHandler
      {
            Proxy = new WebProxy(Myip),
            UseProxy = true
      };

      using var client = new HttpClient(handler);

      try
      {
            var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); // 设置30秒超时
            var cancellationToken = cancellationTokenSource.Token;

            while (!cancellationToken.IsCancellationRequested)
            {
                await semaphore.WaitAsync(cancellationToken);

                try
                {
                  var stopwatch = Stopwatch.StartNew();
                  var response = await client.GetAsync(url, cancellationToken);
                  stopwatch.Stop();

                  if (response.IsSuccessStatusCode)
                  {
                        logger.LogInformation($"Thread {index} made a request to {url} at {DateTime.Now.ToString("HH:mm:ss.fff")}. Response time: {stopwatch.ElapsedMilliseconds} ms.");
                  }
                  else
                  {
                        logger.LogWarning($"Thread {index} received a non-success status code: {response.StatusCode}.");
                  }
                }
                catch (OperationCanceledException)
                {
                  logger.LogInformation($"Thread {index} was canceled.");
                }
                catch (Exception ex)
                {
                  logger.LogError(ex, $"Error in thread {index}: {ex.Message}");
                }
                finally
                {
                  semaphore.Release();
                  await Task.Delay(1000, cancellationToken); // 控制每个线程的请求频率
                }
            }
      }
      finally
      {
            logger.LogInformation($"Thread {index} has been terminated.");
      }
    }

    static async Task UpdateIpListAsync()
    {
      // 这里可以添加逻辑来获取新的代{过}{滤}理IP列表
      logger.LogInformation("Updating IP list...");
      await Task.Delay(100); // 假设这里有一些延迟
    }
}

FitContent 发表于 2024-8-16 22:41

”创建线程又关闭线程“ 的操作可以简化。根据楼主的第一个描述,当程序跑起来时,会有 20 个线程在工作,所以我用的是线程池,共创建 20 个线程。

还有,以下代码是结合 AI 写的。

```c#
using System;
using System.Net;
using System.Threading;
using System.Collections.Concurrent;

class Program
{
    /// <summary>
    /// 最大的线程数
    /// </summary>
    private const int maxThread = 20;
    /// <summary>
    /// 要访问的 URL
    /// </summary>
    private const string targetUrl = "http://xxx.com";
    /// <summary>
    /// 存储 IP 地址的队列,被线程共享
    /// </summary>
    private static readonly ConcurrentQueue<string> MyIp = new();
    /// <summary>
    /// 用于获取 IP 时的锁
    /// </summary>
    private static readonly object locker_for_getip = new();

    static void Main()
    {
      // 循环创建线程到线程池中
      for (int i = 0; i < maxThread; i++)
      {
            ThreadPool.QueueUserWorkItem(FetchUrl, i);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
    }

    /// <summary>
    /// 发起网络请求的线程函数
    /// </summary>
    /// <param name="state"></param>
    private static async void FetchUrl(object? state)
    {
      if (state is int thread_id)
      {

            while (true)
            {
                if (MyIp.TryDequeue(out var ipAddr))
                {
                  Console.WriteLine($"Thread {thread_id} get Ip: {ipAddr}");
                  var proxy = new WebProxy(ipAddr, true);
                  var client = new HttpClient(new HttpClientHandler { Proxy = proxy, UseProxy = true });
                  await client.GetAsync(targetUrl);

                  Thread.Sleep(1000);
                }
                else
                {
                  // 没有可用的 IP 地址,调用 GetIP 方法获取新的 IP 地址
                  GetIP();
                }
            }
      }
    }

    /// <summary>
    /// 更新 IP
    /// </summary>
    /// <param name="state"></param>
    private static void GetIP()
    {
      if (MyIp.IsEmpty)
      {
            lock (locker_for_getip)
            {
                // 双重判断
                if (MyIp.IsEmpty)
                {
                  Console.WriteLine("Start get ip");
                  // 获取 ip,为了本地测试,使用 localhost 了
                  string[] ips = ["localhost:3000", "localhost:3000", "localhost:3000", "localhost:3000", "localhost:3000"];
                  foreach (var ip in ips)
                  {
                        MyIp.Enqueue(ip);
                  }
                }
            }
      }
    }

}
```



这是本机的测试结果,使用 `python -m http.server 3000` 开启了一个本地的服务器进行测试。


wxk0248 发表于 2024-8-16 23:32

压测自己写不是很费劲么,为什么不用现成的,比如Jmetter

lizf2019 发表于 2024-8-17 01:51

wxk0248 发表于 2024-8-16 23:32
压测自己写不是很费劲么,为什么不用现成的,比如Jmetter

主要是要实时从接口获取代{过}{滤}理ip
页: [1] 2
查看完整版本: 【C#】异步多线程 求助