Sync README files from source repository [skip ci]
This commit is contained in:
@@ -0,0 +1,339 @@
|
|||||||
|
# Service quản trị Resource trong hệ thống,
|
||||||
|
- Hỗ trợ 2 giao thức: http(31249), grpc(31287)
|
||||||
|
- Quản trị 2 resource: proxy pool, token pool
|
||||||
|
|
||||||
|
# ResourcePool Usage Guide
|
||||||
|
|
||||||
|
This guide provides instructions on how to set up and use the **TokenPool** **ProxyPool** in your project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Add the following NuGet package references to your project:
|
||||||
|
```xml
|
||||||
|
Icomm.ResourcePool.Abstractions
|
||||||
|
```
|
||||||
|
```xml
|
||||||
|
Icomm.TokenPool
|
||||||
|
```
|
||||||
|
```xml
|
||||||
|
Icomm.ProxyPool
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service Registration
|
||||||
|
|
||||||
|
Register the necessary services in the `Startup.cs` or equivalent service configuration file:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services
|
||||||
|
.AddConfigManager(hostContext.Configuration)
|
||||||
|
.AddTokenPool(
|
||||||
|
hostContext.Configuration,
|
||||||
|
opt =>
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
opt.Host = "10.9.3.70";
|
||||||
|
opt.protocol = ResourcePool.Core.Protocol.Grpc;
|
||||||
|
opt.Port = 31287;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.AddProxyPool(
|
||||||
|
hostContext.Configuration,
|
||||||
|
opt =>
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
opt.Host = "10.9.3.70";
|
||||||
|
opt.protocol = ResourcePool.Core.Protocol.Grpc;
|
||||||
|
opt.Port = 31287;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage TokenPool
|
||||||
|
|
||||||
|
Once the services are registered, you can inject and use the `ITokenPoolService` in your application.
|
||||||
|
|
||||||
|
### Example:
|
||||||
|
```csharp
|
||||||
|
private readonly ITokenPoolService _tokenPoolService;
|
||||||
|
|
||||||
|
public MyService(ITokenPoolService tokenPoolService)
|
||||||
|
{
|
||||||
|
_tokenPoolService = tokenPoolService;
|
||||||
|
}
|
||||||
|
// example with EstimateRequest
|
||||||
|
public async Task UseTokenPool()
|
||||||
|
{
|
||||||
|
// Example usage of the _tokenPoolService
|
||||||
|
var token = await _tokenPoolService.RequestToken(
|
||||||
|
new TokenRequest()
|
||||||
|
{
|
||||||
|
EstimateRequest = 1,
|
||||||
|
Action = action,
|
||||||
|
Platform = "1",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Console.WriteLine($"Retrieved Token: {token}");
|
||||||
|
}
|
||||||
|
// example withouy EstimateRequest
|
||||||
|
public async Task UseTokenPool()
|
||||||
|
{
|
||||||
|
// Example usage of the _tokenPoolService
|
||||||
|
var token = await _tokenPoolService.RequestToken(
|
||||||
|
new TokenRequest()
|
||||||
|
{
|
||||||
|
Action = action,
|
||||||
|
Platform = "1",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await _tokenPoolService.UsedToken(token);
|
||||||
|
Console.WriteLine($"Retrieved Token: {token}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The `ITokenPoolService` provides methods to interact with the Token Pool.
|
||||||
|
- Ensure that the service is properly registered and injected where needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage ProxyPool
|
||||||
|
|
||||||
|
Once the services are registered, you can inject and use the `IProxyPoolService` in your application.
|
||||||
|
|
||||||
|
### Example:
|
||||||
|
```csharp
|
||||||
|
private readonly IProxyPoolService _proxyPool;;
|
||||||
|
|
||||||
|
public MyService(IProxyPoolService proxyPool)
|
||||||
|
{
|
||||||
|
_proxyPool = proxyPool;
|
||||||
|
}
|
||||||
|
// example with EstimateRequest
|
||||||
|
public async Task UseProxyPoolWithRestSharp()
|
||||||
|
{
|
||||||
|
// Example usage of the _tokenPoolService
|
||||||
|
var proxy = await _proxyPool.GetProxyServerAsync(
|
||||||
|
cancellationToken: cancellationToken
|
||||||
|
);
|
||||||
|
var clientOptions = new RestClientOptions(url)
|
||||||
|
{
|
||||||
|
Proxy = proxy
|
||||||
|
};
|
||||||
|
Console.WriteLine($"Retrieved Token: {token}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// example with EstimateRequest
|
||||||
|
public async Task UseRawProxyPoolWithRestSharp()
|
||||||
|
{
|
||||||
|
// Example usage of the _tokenPoolService
|
||||||
|
var proxy = await _proxyPool.GetRawProxyServerAsync(
|
||||||
|
cancellationToken: cancellationToken
|
||||||
|
);
|
||||||
|
var clientOptions = new RestClientOptions(url)
|
||||||
|
{
|
||||||
|
Proxy = new WebProxy(proxy.Host, proxy.Port)
|
||||||
|
};
|
||||||
|
Console.WriteLine($"Retrieved Token: {token}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The `ITokenPoolService` `IProxyPoolService` provides methods to interact with the Token Pool.
|
||||||
|
- Ensure that the service is properly registered and injected where needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The `opt.Host`, `opt.Protocol`, and `opt.Port` configuration should match your environment settings.
|
||||||
|
- In production, replace the debug configurations with the appropriate production values.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
### Proxy Pool API - GetProxyByFeature
|
||||||
|
|
||||||
|
Endpoint: `POST /api/resource-pool/v1/ProxyPool/get-v2`
|
||||||
|
|
||||||
|
Lấy proxy server theo features và chiến lược lọc cụ thể.
|
||||||
|
|
||||||
|
#### 📋 Request Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
|-----------|------|----------|-------------|
|
||||||
|
| `target_domain` | string | Optional | Domain đích cần truy cập (vd: facebook.com, x.com) |
|
||||||
|
| `type` | string | **Required** | Loại proxy ("http", "httpv6", "socks") |
|
||||||
|
| `country` | string | Optional | Mã quốc gia proxy ("VN", "US", "JP") |
|
||||||
|
| `strategy` | string | **Required** | Chiến lược chọn proxy |
|
||||||
|
| `referer_proxy` | object | Optional | Proxy tham chiếu để tìm proxy thay thế tốt nhất |
|
||||||
|
|
||||||
|
#### ⚠️ Logic Ưu Tiên Tham Số
|
||||||
|
|
||||||
|
**Nếu `referer_proxy.id` khác null**: Hệ thống sẽ ưu tiên lấy thông tin từ proxy referer làm tham số lọc chính, sau đó mới sử dụng các tham số `type`, `country`, `target_domain` để lọc bổ sung.
|
||||||
|
|
||||||
|
**Cách hoạt động**:
|
||||||
|
1. **Bước 1**: Hệ thống lấy thông tin của proxy có ID = `referer_proxy.id`
|
||||||
|
2. **Bước 2**: Sử dụng thông tin proxy đó (type, country, location) làm tiêu chí lọc chính
|
||||||
|
3. **Bước 3**: Áp dụng các tham số `type`, `country`, `target_domain` như điều kiện lọc bổ sung
|
||||||
|
4. **Bước 4**: Thực hiện strategy được chọn trên pool đã được lọc
|
||||||
|
|
||||||
|
**Lợi ích**:
|
||||||
|
- Tìm proxy thay thế có đặc tính tương tự với proxy đang sử dụng
|
||||||
|
- Đảm bảo tính nhất quán trong quality và performance
|
||||||
|
- Hỗ trợ thuật toán fallback khi proxy hiện tại gặp vấn đề
|
||||||
|
|
||||||
|
#### 🎯 Available Strategies
|
||||||
|
|
||||||
|
##### 1. 🎲 Random Strategy (`strategy: "random"`)
|
||||||
|
- **Tham số bắt buộc**: `type`
|
||||||
|
- **Tham số tùy chọn**: `country`
|
||||||
|
- **Mô tả**: Chọn ngẫu nhiên proxy từ pool phù hợp
|
||||||
|
- **Ví dụ**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "httpv6",
|
||||||
|
"strategy": "random",
|
||||||
|
"country": "VN"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 2. ⚖️ Round Robin Strategy (`strategy: "round_robin"`)
|
||||||
|
- **Tham số bắt buộc**: `type`
|
||||||
|
- **Tham số tùy chọn**: `country`
|
||||||
|
- **Mô tả**: Phân phối request đều khắp các proxy trong pool
|
||||||
|
- **Ví dụ**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "http",
|
||||||
|
"strategy": "round_robin",
|
||||||
|
"country": "US"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 3. 📊 Least Used Strategy (`strategy: "least_used"`)
|
||||||
|
- **Tham số bắt buộc**: `referer_proxy.id`
|
||||||
|
- **Mô tả**: Chọn proxy có tần suất sử dụng thấp nhất
|
||||||
|
- **Logic đặc biệt**: Sử dụng thông tin referer_proxy làm tham số lọc chính
|
||||||
|
- **Ví dụ**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"strategy": "least_used",
|
||||||
|
"referer_proxy": {
|
||||||
|
"id": 123
|
||||||
|
},
|
||||||
|
"type": "httpv6",
|
||||||
|
"country": "VN"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 4. ⚡ Least Delay Strategy (`strategy: "least_delay"`)
|
||||||
|
- **Tham số bắt buộc**: `target_domain`, `type`
|
||||||
|
- **Tham số tùy chọn**: `country`
|
||||||
|
- **Mô tả**: Chọn proxy có độ trễ thấp nhất cho domain cụ thể
|
||||||
|
- **Ví dụ**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"target_domain": "facebook.com",
|
||||||
|
"type": "httpv6",
|
||||||
|
"strategy": "least_delay",
|
||||||
|
"country": "US"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 5. 🏆 Adaptive Ranking Strategy (`strategy: "adaptive_ranking"`)
|
||||||
|
- **Tham số bắt buộc**: `target_domain`, `type`
|
||||||
|
- **Tham số tùy chọn**: `country`
|
||||||
|
- **Mô tả**: Chọn proxy có điểm số cao nhất (tỷ lệ thành công + tốc độ response)
|
||||||
|
- **Ví dụ**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"target_domain": "instagram.com",
|
||||||
|
"type": "http",
|
||||||
|
"strategy": "adaptive_ranking",
|
||||||
|
"country": "JP"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 📤 Response Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"proxy": {
|
||||||
|
"id": 123,
|
||||||
|
"host": "192.168.1.100",
|
||||||
|
"port": 8080,
|
||||||
|
"type": "httpv6",
|
||||||
|
"country": "VN",
|
||||||
|
"location": "Ho Chi Minh City",
|
||||||
|
"status": 1,
|
||||||
|
"auth_username": "user",
|
||||||
|
"auth_password": "password",
|
||||||
|
"cluster": "cluster1",
|
||||||
|
"source": "manual",
|
||||||
|
"updateTime": "2024-01-15T10:30:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🔧 Example Usage
|
||||||
|
|
||||||
|
**Sử dụng với HttpClient:**
|
||||||
|
```csharp
|
||||||
|
var request = new ProxyRequestByFeature
|
||||||
|
{
|
||||||
|
Type = "httpv6",
|
||||||
|
Strategy = "adaptive_ranking",
|
||||||
|
TargetDomain = "facebook.com",
|
||||||
|
Country = "VN"
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await httpClient.PostAsJsonAsync(
|
||||||
|
"/api/resource-pool/v1/ProxyPool/get-v2",
|
||||||
|
request
|
||||||
|
);
|
||||||
|
|
||||||
|
var proxyData = await response.Content.ReadFromJsonAsync<ProxyResponse>();
|
||||||
|
|
||||||
|
// Sử dụng proxy với HttpClient
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Proxy",
|
||||||
|
$"http://{proxyData.Proxy.AuthUsername}:{proxyData.Proxy.AuthPassword}@{proxyData.Proxy.Host}:{proxyData.Proxy.Port}");
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sử dụng với RestSharp:**
|
||||||
|
```csharp
|
||||||
|
var request = new RestRequest("/api/resource-pool/v1/ProxyPool/get-v2", Method.Post);
|
||||||
|
request.AddJsonBody(new ProxyRequestByFeature
|
||||||
|
{
|
||||||
|
Type = "http",
|
||||||
|
Strategy = "least_delay",
|
||||||
|
TargetDomain = "instagram.com",
|
||||||
|
Country = "US"
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await client.ExecuteAsync<ProxyResponse>(request);
|
||||||
|
var proxy = new WebProxy($"{response.Data.Proxy.Host}:{response.Data.Proxy.Port}")
|
||||||
|
{
|
||||||
|
Credentials = new NetworkCredential(response.Data.Proxy.AuthUsername, response.Data.Proxy.AuthPassword)
|
||||||
|
};
|
||||||
|
|
||||||
|
var restClient = new RestClient("https://api.example.com")
|
||||||
|
{
|
||||||
|
Proxy = proxy
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Proxy Pool Diagram
|
||||||
|

|
||||||
|
## Token Pool Diagram
|
||||||
|

|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Gitea Actions - Sync README Files
|
||||||
|
|
||||||
|
Workflow này tự động đồng bộ các file README.md từ repository này sang một repository công khai khác.
|
||||||
|
|
||||||
|
## Cấu hình
|
||||||
|
|
||||||
|
### 1. Thiết lập Secrets trong Gitea
|
||||||
|
|
||||||
|
Vào **Settings > Secrets** của repository và thêm 2 secrets sau:
|
||||||
|
|
||||||
|
- `TARGET_REPO_URL`: URL của repository đích (ví dụ: `https://gitea.example.com/username/public-repo.git`)
|
||||||
|
- `TOKEN_ACTION_PUBLIC_README`: Token có quyền push vào repository đích
|
||||||
|
|
||||||
|
### 2. Tạo Token trong Gitea
|
||||||
|
|
||||||
|
1. Vào **Settings > Applications > Generate New Token**
|
||||||
|
2. Đặt tên token (ví dụ: "README Sync Token")
|
||||||
|
3. Chọn quyền: `write:repository` hoặc `repo`
|
||||||
|
4. Copy token và lưu vào secret `TOKEN_ACTION_PUBLIC_README`
|
||||||
|
|
||||||
|
### 3. Cách hoạt động
|
||||||
|
|
||||||
|
Workflow sẽ tự động chạy khi:
|
||||||
|
- **Push events**: Có push vào các branch `main` hoặc `master` (sau khi PR được merge)
|
||||||
|
- **Pull Request events**: Có PR mới, cập nhật, hoặc reopen vào `main`/`master` với thay đổi file `.md`
|
||||||
|
- **Manual trigger**: Chạy thủ công qua **Actions > Sync README files > Run workflow**
|
||||||
|
|
||||||
|
**Lưu ý quan trọng:**
|
||||||
|
- Khi chạy trên **PR event**: Workflow sẽ validate và chuẩn bị files nhưng **KHÔNG push** vào repo đích (chỉ để preview)
|
||||||
|
- Khi chạy trên **Push event** (sau khi merge): Workflow sẽ **push** files vào repo đích
|
||||||
|
|
||||||
|
### 4. Cấu trúc file được sync
|
||||||
|
|
||||||
|
- File `readme.md` hoặc `README.md` ở root → được copy thành `README.md` ở root của repo đích
|
||||||
|
- Các file `README.md` trong các thư mục con → được copy vào `docs/` của repo đích, giữ nguyên cấu trúc thư mục
|
||||||
|
|
||||||
|
### 5. Kiểm tra kết quả
|
||||||
|
|
||||||
|
Sau khi workflow chạy:
|
||||||
|
- Vào tab **Actions** để xem log
|
||||||
|
- Kiểm tra repository đích để xác nhận các file đã được sync
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Lỗi: "TARGET_REPO_URL secret is not set"
|
||||||
|
- Kiểm tra đã thêm secret `TARGET_REPO_URL` chưa
|
||||||
|
- Đảm bảo tên secret chính xác (case-sensitive)
|
||||||
|
|
||||||
|
### Lỗi: "Failed to clone target repository"
|
||||||
|
- Kiểm tra URL repository đích có đúng không
|
||||||
|
- Kiểm tra token có quyền đọc repository không
|
||||||
|
- Đảm bảo repository đích là public hoặc token có quyền truy cập
|
||||||
|
|
||||||
|
### Lỗi: "Push failed"
|
||||||
|
- Kiểm tra token có quyền push vào repository đích
|
||||||
|
- Kiểm tra branch mặc định của repository đích (main/master)
|
||||||
|
|
||||||
|
## Tùy chỉnh
|
||||||
|
|
||||||
|
Nếu cần thay đổi:
|
||||||
|
- **Branch trigger**: Sửa phần `branches` trong workflow
|
||||||
|
- **File pattern**: Sửa phần `paths` trong workflow
|
||||||
|
- **Target directory**: Sửa phần copy files trong step "Find and copy README files"
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# HttpToSocks5Proxy - .NET 8.0
|
||||||
|
|
||||||
|
HTTP(S) to SOCKS5 proxy adapter for .NET 8.0
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This library implements `IWebProxy` interface to act as an HTTP(S) proxy while connecting to a SOCKS5 server behind the scenes. It has been upgraded from .NET Standard 2.0 to .NET 8.0 for better performance and modern features.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ Implements `IWebProxy` interface
|
||||||
|
- ✅ Transparent HTTP to SOCKS5 conversion
|
||||||
|
- ✅ Support for authentication
|
||||||
|
- ✅ Custom DNS resolver support
|
||||||
|
- ✅ .NET 8.0 optimizations
|
||||||
|
- ✅ Nullable reference types enabled
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using SockNet;
|
||||||
|
|
||||||
|
// Create proxy without authentication
|
||||||
|
var proxy = new HttpToSocks5Proxy("socks5-server.com", 1080);
|
||||||
|
|
||||||
|
// Use with HttpClient
|
||||||
|
var handler = new HttpClientHandler { Proxy = proxy };
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
|
||||||
|
var response = await httpClient.GetAsync("https://example.com");
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Authentication
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Create proxy with SOCKS5 authentication
|
||||||
|
var proxy = new HttpToSocks5Proxy(
|
||||||
|
"socks5-server.com",
|
||||||
|
1080,
|
||||||
|
"username",
|
||||||
|
"password"
|
||||||
|
);
|
||||||
|
|
||||||
|
var handler = new HttpClientHandler { Proxy = proxy };
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
```
|
||||||
|
|
||||||
|
### With SmartPool
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Icomm.SmartPool.Abstractions.Responses;
|
||||||
|
|
||||||
|
var proxyServer = new SmartProxyServer
|
||||||
|
{
|
||||||
|
host = "socks5-server.com",
|
||||||
|
port = 1080,
|
||||||
|
protocol = "socks5",
|
||||||
|
auth_username = "user",
|
||||||
|
auth_password = "pass"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert to IWebProxy
|
||||||
|
var webProxy = proxyServer.ToWebProxy();
|
||||||
|
|
||||||
|
// Use with HttpClient
|
||||||
|
var handler = new HttpClientHandler { Proxy = webProxy };
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrade Notes
|
||||||
|
|
||||||
|
### Changes from Previous Version
|
||||||
|
|
||||||
|
- **Target Framework**: Changed from `netstandard2.0;net45` to `net8.0`
|
||||||
|
- **Version**: Bumped to `2.0.0`
|
||||||
|
- **Language Features**: Enabled nullable reference types
|
||||||
|
- **Performance**: Benefits from .NET 8.0 runtime improvements
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
- Minimum requirement is now .NET 8.0
|
||||||
|
- No longer supports .NET Framework 4.5 or .NET Standard 2.0
|
||||||
|
|
||||||
|
## Integration with SmartPool
|
||||||
|
|
||||||
|
This library is used by SmartPool system to support SOCKS5 proxies:
|
||||||
|
|
||||||
|
```
|
||||||
|
SmartPool Client
|
||||||
|
↓
|
||||||
|
SmartProxyServer.ToWebProxy()
|
||||||
|
↓
|
||||||
|
HttpToSocks5Proxy (if protocol is socks5)
|
||||||
|
↓
|
||||||
|
SOCKS5 Server
|
||||||
|
↓
|
||||||
|
Target Website
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Namespace
|
||||||
|
|
||||||
|
- Root namespace: `SockNet`
|
||||||
|
- Assembly name: `SockNet.HttpToSocks5Proxy`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- .NET 8.0 Runtime
|
||||||
|
- No external NuGet packages required
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **2.0.0** (2026-01-22)
|
||||||
|
- Upgraded to .NET 8.0
|
||||||
|
- Enabled nullable reference types
|
||||||
|
- Added modern C# language features
|
||||||
|
|
||||||
|
- **1.4.0** (Previous)
|
||||||
|
- .NET Standard 2.0 / .NET Framework 4.5
|
||||||
|
- Original implementation
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues related to SmartPool integration, see the main SmartPool documentation.
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
# Icomm.API.SmartPool
|
||||||
|
|
||||||
|
SmartPool API Backend - Intelligent proxy management system with multiple selection strategies.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Multiple Selection Strategies**:
|
||||||
|
- Random: Random selection from available proxies
|
||||||
|
- Round Robin: Balanced distribution across proxies
|
||||||
|
- Least Delay: Select fastest proxy based on response time
|
||||||
|
- Adaptive Ranking: Select proxy with highest success rate
|
||||||
|
- Alternative: Find similar proxy to replace failed one
|
||||||
|
|
||||||
|
- **Dual Protocol Support**:
|
||||||
|
- gRPC (MagicOnion) for high-performance RPC
|
||||||
|
- HTTP REST API for standard web clients
|
||||||
|
|
||||||
|
- **ClickHouse Integration**:
|
||||||
|
- High-performance logging
|
||||||
|
- Real-time analytics
|
||||||
|
- Automatic aggregation with materialized views
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "No proxy available matching criteria"
|
||||||
|
|
||||||
|
If you get this error, check:
|
||||||
|
|
||||||
|
1. **Run initialization script:**
|
||||||
|
```bash
|
||||||
|
clickhouse-client < clickhouse_init.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Add test data:**
|
||||||
|
```bash
|
||||||
|
clickhouse-client < clickhouse_test_data.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Debug the issue:**
|
||||||
|
```bash
|
||||||
|
clickhouse-client < clickhouse_debug.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Verify your access_token has mappings:**
|
||||||
|
```sql
|
||||||
|
SELECT * FROM smart_pool_meta.proxy_access_mapping FINAL
|
||||||
|
WHERE access_token = 'YOUR_TOKEN';
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Verify proxies exist in those clusters:**
|
||||||
|
```sql
|
||||||
|
SELECT m.*
|
||||||
|
FROM smart_pool_meta.proxy_metadata FINAL m
|
||||||
|
INNER JOIN smart_pool_meta.proxy_access_mapping FINAL a
|
||||||
|
ON m.cluster = a.cluster
|
||||||
|
WHERE a.access_token = 'YOUR_TOKEN'
|
||||||
|
AND m.status = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Empty database**: Run `clickhouse_init.sql` and `clickhouse_test_data.sql`
|
||||||
|
2. **No access mapping**: Your access_token needs to be mapped to clusters
|
||||||
|
3. **All proxies inactive**: Check `status = 1` in proxy_metadata
|
||||||
|
4. **Wrong cluster names**: Ensure cluster names match between metadata and mappings
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Setup ClickHouse
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run ClickHouse initialization script
|
||||||
|
clickhouse-client < clickhouse_init.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure Settings
|
||||||
|
|
||||||
|
Edit `appsettings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ClickHouse": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 8123,
|
||||||
|
"Database": "smart_pool",
|
||||||
|
"Username": "default",
|
||||||
|
"Password": ""
|
||||||
|
},
|
||||||
|
"Redis": {
|
||||||
|
"Configuration": "localhost:6379"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run the API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
The API will be available at:
|
||||||
|
- HTTP: `http://localhost:5000`
|
||||||
|
- gRPC: `http://localhost:5001`
|
||||||
|
- Swagger UI: `http://localhost:5000/swagger`
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### Get Proxy
|
||||||
|
|
||||||
|
**POST** `/api/smart-pool/v1/SmartProxy/get-proxy`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"access_token": "your-token",
|
||||||
|
"strategy": "least_delay",
|
||||||
|
"target_domain": "facebook.com",
|
||||||
|
"ipVersion": "v6",
|
||||||
|
"protocol": "http",
|
||||||
|
"country": "US"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Usage
|
||||||
|
|
||||||
|
**POST** `/api/smart-pool/v1/SmartProxy/log-usage`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"access_token": "your-token",
|
||||||
|
"proxy_id": 123,
|
||||||
|
"target_domain": "facebook.com",
|
||||||
|
"status_code": 200,
|
||||||
|
"response_time_ms": 450
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## gRPC Usage
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var channel = GrpcChannel.ForAddress("http://localhost:5001");
|
||||||
|
var client = MagicOnionClient.Create<ISmartProxyService>(channel);
|
||||||
|
|
||||||
|
var response = await client.GetProxy(new GetProxyRequest
|
||||||
|
{
|
||||||
|
access_token = "your-token",
|
||||||
|
strategy = "adaptive_ranking"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Client Request
|
||||||
|
↓
|
||||||
|
API Layer (gRPC/HTTP)
|
||||||
|
↓
|
||||||
|
Strategy Factory
|
||||||
|
↓
|
||||||
|
Strategy Implementation
|
||||||
|
↓
|
||||||
|
Repository Layer
|
||||||
|
↓
|
||||||
|
ClickHouse Database
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- .NET 8.0
|
||||||
|
- MagicOnion.Server 5.1.11
|
||||||
|
- ClickHouse.Client 7.7.0
|
||||||
|
- EasyCaching.Redis 1.9.2
|
||||||
|
- Serilog
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Internal use only.
|
||||||
@@ -0,0 +1,790 @@
|
|||||||
|
# Icomm.SmartPool.Proxy
|
||||||
|
|
||||||
|
Client SDK for SmartPool proxy management system. Supports both gRPC and HTTP protocols.
|
||||||
|
|
||||||
|
> 📖 **Xem tài liệu chi tiết**: [SDK_DOCUMENTATION.md](./SDK_DOCUMENTATION.md)
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- ✅ **Dual Protocol**: gRPC (MagicOnion) và HTTP REST API
|
||||||
|
- ✅ **5 Chiến lược**: Random, Round Robin, Least Delay, Adaptive Ranking, Alternative
|
||||||
|
- ✅ **Tối ưu hiệu suất**: Keep-alive, Connection pooling, Client caching, Compression
|
||||||
|
- ✅ **Batch Logging**: Ghi log hiệu quả với batch processing
|
||||||
|
- ✅ **Fire-and-Forget**: Non-blocking logging
|
||||||
|
- ✅ **Compact Response**: Giảm payload size ~50-60%
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Đảm bảo bạn đã cấu hình NuGet source `ic` trong project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Thêm NuGet source (nếu chưa có)
|
||||||
|
dotnet nuget add source <nuget-feed-url> --name ic
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install via NuGet Package
|
||||||
|
|
||||||
|
Cài đặt package từ NuGet repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package --source ic --version 10.0.1 Icomm.SmartPool.Proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
Hoặc thêm vào `.csproj` file:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Icomm.SmartPool.Proxy" Version="10.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
Và cấu hình NuGet source trong `nuget.config`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="ic" value="<nuget-feed-url>" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Add to `appsettings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"Protocol": "Grpc",
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 5001,
|
||||||
|
"UseSecureConnection": false,
|
||||||
|
"DefaultAccessToken": "your-access-token",
|
||||||
|
"TimeoutMs": 30000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Register in DI Container
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Icomm.SmartPool.Proxy.Extensions;
|
||||||
|
|
||||||
|
// In Startup.cs or Program.cs
|
||||||
|
services.AddSmartPoolClient(configuration);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use in Your Code
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyService
|
||||||
|
{
|
||||||
|
private readonly ISmartPoolClient _smartPoolClient;
|
||||||
|
|
||||||
|
public MyService(ISmartPoolClient smartPoolClient)
|
||||||
|
{
|
||||||
|
_smartPoolClient = smartPoolClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MakeRequestAsync()
|
||||||
|
{
|
||||||
|
// Get proxy as IWebProxy (ready for HttpClient)
|
||||||
|
var proxy = await _smartPoolClient.GetProxyAsync(
|
||||||
|
strategy: "least_delay",
|
||||||
|
targetDomain: "facebook.com",
|
||||||
|
ipVersion: "v6"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (proxy != null)
|
||||||
|
{
|
||||||
|
var handler = new HttpClientHandler { Proxy = proxy };
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
|
||||||
|
var response = await httpClient.GetAsync("https://facebook.com");
|
||||||
|
|
||||||
|
// Log usage
|
||||||
|
await _smartPoolClient.LogProxyUsageAsync(
|
||||||
|
accessToken: "your-token",
|
||||||
|
proxyId: 123,
|
||||||
|
targetDomain: "facebook.com",
|
||||||
|
statusCode: (int)response.StatusCode,
|
||||||
|
responseTimeMs: 450
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Get Raw Proxy Information
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var request = new GetProxyRequest
|
||||||
|
{
|
||||||
|
access_token = "your-token",
|
||||||
|
strategy = "adaptive_ranking",
|
||||||
|
target_domain = "facebook.com",
|
||||||
|
ip_version = "v6",
|
||||||
|
protocol = "http",
|
||||||
|
country = "US"
|
||||||
|
};
|
||||||
|
|
||||||
|
var proxyServer = await _smartPoolClient.GetRawProxyAsync(request);
|
||||||
|
|
||||||
|
if (proxyServer != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Proxy: {proxyServer.host}:{proxyServer.port}");
|
||||||
|
Console.WriteLine($"Country: {proxyServer.country}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternative Proxy Strategy
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var request = new GetProxyRequest
|
||||||
|
{
|
||||||
|
access_token = "your-token",
|
||||||
|
strategy = "alternative",
|
||||||
|
referer_proxy = new SmartProxyServer { id = 123 } // Current proxy that failed
|
||||||
|
};
|
||||||
|
|
||||||
|
var alternativeProxy = await _smartPoolClient.GetRawProxyAsync(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Strategies
|
||||||
|
|
||||||
|
- **random**: Random selection
|
||||||
|
- **round_robin**: Balanced distribution
|
||||||
|
- **least_delay**: Fastest proxy (requires targetDomain)
|
||||||
|
- **adaptive_ranking**: Highest success rate (requires targetDomain)
|
||||||
|
- **alternative**: Similar proxy replacement (requires refererProxy)
|
||||||
|
|
||||||
|
## Protocol Selection
|
||||||
|
|
||||||
|
### gRPC (Recommended)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.Protocol = SmartPoolProtocol.Grpc;
|
||||||
|
options.Host = "api.example.com";
|
||||||
|
options.Port = 5001;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros**: High performance, binary protocol, streaming support
|
||||||
|
|
||||||
|
### HTTP
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.Protocol = SmartPoolProtocol.Http;
|
||||||
|
options.Host = "api.example.com";
|
||||||
|
options.Port = 5000;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros**: Standard REST API, easier debugging, firewall-friendly
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
SDK cung cấp exception handling chi tiết với các exception types tương ứng với error codes từ server:
|
||||||
|
|
||||||
|
### Exception Types
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Icomm.SmartPool.Proxy.Exceptions;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var proxy = await _smartPoolClient.GetRawProxyAsync(request);
|
||||||
|
// Use proxy...
|
||||||
|
}
|
||||||
|
catch (AccessTokenRequiredException)
|
||||||
|
{
|
||||||
|
// Access token không được cung cấp
|
||||||
|
Console.WriteLine("Access token is required");
|
||||||
|
}
|
||||||
|
catch (AccessTokenInvalidException ex)
|
||||||
|
{
|
||||||
|
// Access token không hợp lệ hoặc không được phân quyền
|
||||||
|
Console.WriteLine($"Invalid access token: {ex.AccessToken}");
|
||||||
|
}
|
||||||
|
catch (NoAvailableProxiesException ex)
|
||||||
|
{
|
||||||
|
// Không có proxy available cho access token này
|
||||||
|
Console.WriteLine($"No proxies available for token: {ex.AccessToken}");
|
||||||
|
}
|
||||||
|
catch (NoMatchingProxiesException)
|
||||||
|
{
|
||||||
|
// Không có proxy match với filters (ip_version, protocol, country, etc.)
|
||||||
|
Console.WriteLine("No proxies match the specified filters");
|
||||||
|
}
|
||||||
|
catch (StrategyNotFoundException ex)
|
||||||
|
{
|
||||||
|
// Strategy không tồn tại
|
||||||
|
Console.WriteLine($"Strategy not found: {ex.StrategyName}");
|
||||||
|
}
|
||||||
|
catch (RefererProxyRequiredException)
|
||||||
|
{
|
||||||
|
// Alternative strategy cần referer_proxy
|
||||||
|
Console.WriteLine("Referer proxy is required for alternative strategy");
|
||||||
|
}
|
||||||
|
catch (SmartPoolException ex)
|
||||||
|
{
|
||||||
|
// Các lỗi khác từ SmartPool
|
||||||
|
Console.WriteLine($"SmartPool error ({ex.ErrorCode}): {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Network errors, timeout, etc.
|
||||||
|
Console.WriteLine($"Network error: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Try Methods (Không Throw Exception)
|
||||||
|
|
||||||
|
SDK cung cấp các method `Try*` để bỏ qua exceptions và trả về `null` nếu lỗi:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// TryGetRawProxyAsync - không throw exception
|
||||||
|
var proxy = await _smartPoolClient.TryGetRawProxyAsync(request);
|
||||||
|
if (proxy != null)
|
||||||
|
{
|
||||||
|
// Sử dụng proxy
|
||||||
|
Console.WriteLine($"Proxy: {proxy.host}:{proxy.port}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Xử lý khi không lấy được proxy (silent failure)
|
||||||
|
Console.WriteLine("Failed to get proxy");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryGetProxyAsync - không throw exception
|
||||||
|
var webProxy = await _smartPoolClient.TryGetProxyAsync(request);
|
||||||
|
if (webProxy != null)
|
||||||
|
{
|
||||||
|
var handler = new HttpClientHandler { Proxy = webProxy };
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
// Use httpClient...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng Try methods:**
|
||||||
|
- Khi bạn muốn xử lý lỗi một cách silent (không cần biết chi tiết lỗi)
|
||||||
|
- Trong retry logic, khi bạn chỉ cần biết success/failure
|
||||||
|
- Khi performance quan trọng hơn error details
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var isHealthy = await _smartPoolClient.PingAsync();
|
||||||
|
if (!isHealthy)
|
||||||
|
{
|
||||||
|
Console.WriteLine("SmartPool API is not responding");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Optimizations
|
||||||
|
|
||||||
|
SDK đã được tối ưu với các tính năng:
|
||||||
|
|
||||||
|
- **Connection Pooling & Keep-Alive**: Giảm latency reconnect từ ~50-100ms xuống ~5ms
|
||||||
|
- **Client-side Caching**: Giảm network calls ~90% khi cache hit
|
||||||
|
- **Response Compression**: Giảm payload size ~30-50%
|
||||||
|
- **Batch Logging**: Giảm N network calls → 1 call
|
||||||
|
- **Fire-and-Forget**: Non-blocking logging, không block main flow
|
||||||
|
|
||||||
|
Xem chi tiết trong [SDK_DOCUMENTATION.md](./SDK_DOCUMENTATION.md#tối-ưu-hiệu-suất).
|
||||||
|
|
||||||
|
## Chi Tiết Các Modes và Cách Hoạt Động
|
||||||
|
|
||||||
|
### 1. Protocol Modes
|
||||||
|
|
||||||
|
#### gRPC Mode (Khuyến nghị)
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Sử dụng HTTP/2 với binary protocol (MessagePack)
|
||||||
|
- Connection pooling tự động với keep-alive
|
||||||
|
- Compression tự động (gzip/deflate)
|
||||||
|
- Streaming support cho batch operations
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"Protocol": "Grpc",
|
||||||
|
"Host": "api.example.com",
|
||||||
|
"Port": 5001,
|
||||||
|
"UseSecureConnection": true,
|
||||||
|
"EnableCompression": true,
|
||||||
|
"CompressionAlgorithm": "gzip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ High-performance scenarios (scraping, batch processing)
|
||||||
|
- ✅ Internal services (microservices communication)
|
||||||
|
- ✅ Khi cần throughput cao
|
||||||
|
- ✅ Khi có control over network (không bị firewall block HTTP/2)
|
||||||
|
|
||||||
|
**Ưu điểm:**
|
||||||
|
- Performance cao hơn HTTP ~30-50%
|
||||||
|
- Binary protocol → payload nhỏ hơn
|
||||||
|
- Connection pooling tự động
|
||||||
|
- Keep-alive giảm latency
|
||||||
|
|
||||||
|
**Nhược điểm:**
|
||||||
|
- Cần HTTP/2 support
|
||||||
|
- Khó debug hơn (binary format)
|
||||||
|
- Có thể bị firewall block
|
||||||
|
|
||||||
|
#### HTTP Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Standard HTTP/1.1 REST API
|
||||||
|
- JSON serialization
|
||||||
|
- Standard HttpClient với connection pooling
|
||||||
|
- Dễ debug với browser DevTools
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"Protocol": "Http",
|
||||||
|
"Host": "api.example.com",
|
||||||
|
"Port": 5000,
|
||||||
|
"UseSecureConnection": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ External clients (web browsers, mobile apps)
|
||||||
|
- ✅ Khi cần debug dễ dàng
|
||||||
|
- ✅ Khi firewall chỉ cho phép HTTP
|
||||||
|
- ✅ Development/testing environments
|
||||||
|
|
||||||
|
**Ưu điểm:**
|
||||||
|
- Dễ debug (JSON, có thể test với Postman/curl)
|
||||||
|
- Universal support (mọi client đều hỗ trợ HTTP)
|
||||||
|
- Firewall-friendly
|
||||||
|
- Standard REST API
|
||||||
|
|
||||||
|
**Nhược điểm:**
|
||||||
|
- Performance thấp hơn gRPC
|
||||||
|
- Payload lớn hơn (JSON vs binary)
|
||||||
|
- Không có streaming support
|
||||||
|
|
||||||
|
### 2. Client-Side Caching Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Cache proxy results trong memory (MemoryCache)
|
||||||
|
- Cache key dựa trên: access_token, strategy, filters (ip_version, protocol, country, target_domain)
|
||||||
|
- TTL: `ClientCacheDurationSeconds` (default: 30s)
|
||||||
|
- Sliding expiration: 50% của TTL
|
||||||
|
- Max entries: `ClientCacheMaxEntries` (default: 100)
|
||||||
|
|
||||||
|
**Flow:**
|
||||||
|
```
|
||||||
|
1. Request proxy với filters
|
||||||
|
2. Check cache → Cache hit? Return cached proxy
|
||||||
|
3. Cache miss? Call API → Cache result → Return proxy
|
||||||
|
4. Next request với same filters → Return từ cache (không call API)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableClientCache": true,
|
||||||
|
"ClientCacheDurationSeconds": 30,
|
||||||
|
"ClientCacheMaxEntries": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ Khi request cùng filters nhiều lần
|
||||||
|
- ✅ Random/Round-robin strategies (không phụ thuộc vào real-time data)
|
||||||
|
- ✅ Giảm load cho API server
|
||||||
|
- ✅ Giảm latency cho client
|
||||||
|
|
||||||
|
**Khi KHÔNG nên dùng:**
|
||||||
|
- ❌ Least Delay strategy (cần real-time data)
|
||||||
|
- ❌ Adaptive Ranking strategy (cần real-time data)
|
||||||
|
- ❌ Khi cần proxy mới mỗi lần request
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```csharp
|
||||||
|
// Request 1: Call API, cache result
|
||||||
|
var proxy1 = await _client.GetProxyAsync(new GetProxyRequest
|
||||||
|
{
|
||||||
|
strategy = "random",
|
||||||
|
ip_version = "v6"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Request 2: Return từ cache (không call API)
|
||||||
|
var proxy2 = await _client.GetProxyAsync(new GetProxyRequest
|
||||||
|
{
|
||||||
|
strategy = "random",
|
||||||
|
ip_version = "v6"
|
||||||
|
});
|
||||||
|
// proxy1 và proxy2 giống nhau (cùng từ cache)
|
||||||
|
|
||||||
|
// Request 3: Khác filters → Call API mới
|
||||||
|
var proxy3 = await _client.GetProxyAsync(new GetProxyRequest
|
||||||
|
{
|
||||||
|
strategy = "random",
|
||||||
|
ip_version = "v4" // Khác filter
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Logging Modes
|
||||||
|
|
||||||
|
#### Fire-and-Forget Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Logging chạy trong background task (không block main thread)
|
||||||
|
- Nếu có batch logging → Queue vào channel
|
||||||
|
- Nếu không có batch → Fire task riêng
|
||||||
|
- Không đợi response từ server
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableFireAndForgetLogging": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ High-throughput scenarios
|
||||||
|
- ✅ Khi logging không critical (best-effort)
|
||||||
|
- ✅ Khi không muốn block main flow
|
||||||
|
- ✅ Background processing
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```csharp
|
||||||
|
// Non-blocking - không đợi response
|
||||||
|
await _client.LogProxyUsageAsync(new ProxyUsageLogRequest
|
||||||
|
{
|
||||||
|
proxy_id = 123,
|
||||||
|
status_code = 200,
|
||||||
|
response_time_ms = 450
|
||||||
|
});
|
||||||
|
// Code tiếp tục chạy ngay, không đợi log complete
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Batch Logging Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Queue log requests vào bounded channel
|
||||||
|
- Background task collect logs theo batch
|
||||||
|
- Flush khi đạt `LogBatchSize` hoặc `LogBatchFlushIntervalMs`
|
||||||
|
- Gửi batch đến server
|
||||||
|
|
||||||
|
**Flow:**
|
||||||
|
```
|
||||||
|
1. LogProxyUsageAsync() → Queue vào channel
|
||||||
|
2. Background task collect logs
|
||||||
|
3. Khi đủ LogBatchSize (50) hoặc timeout (5s) → Flush batch
|
||||||
|
4. Gửi batch đến server
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableBatchLogging": true,
|
||||||
|
"LogBatchSize": 50,
|
||||||
|
"LogBatchFlushIntervalMs": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ High-throughput scenarios (1000+ requests/second)
|
||||||
|
- ✅ Khi muốn giảm network calls
|
||||||
|
- ✅ Khi có nhiều logs cần gửi
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```csharp
|
||||||
|
// Queue 1000 logs
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
_client.QueueLogProxyUsage(new ProxyUsageLogRequest
|
||||||
|
{
|
||||||
|
proxy_id = i,
|
||||||
|
status_code = 200,
|
||||||
|
response_time_ms = 450
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Chỉ gửi ~20 batches (50 logs/batch) thay vì 1000 requests
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kết hợp Fire-and-Forget + Batch:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableFireAndForgetLogging": true,
|
||||||
|
"EnableBatchLogging": true,
|
||||||
|
"LogBatchSize": 50,
|
||||||
|
"LogBatchFlushIntervalMs": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
→ Logging hoàn toàn non-blocking và batch processing
|
||||||
|
|
||||||
|
#### Synchronous Logging Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- Đợi response từ server
|
||||||
|
- Block cho đến khi log complete
|
||||||
|
- Throw exception nếu lỗi
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableFireAndForgetLogging": false,
|
||||||
|
"EnableBatchLogging": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ Khi logging là critical (cần đảm bảo log được gửi)
|
||||||
|
- ✅ Low-throughput scenarios
|
||||||
|
- ✅ Debug/testing
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```csharp
|
||||||
|
// Blocking - đợi response
|
||||||
|
var success = await _client.LogProxyUsageAsync(new ProxyUsageLogRequest
|
||||||
|
{
|
||||||
|
proxy_id = 123,
|
||||||
|
status_code = 200
|
||||||
|
});
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
// Handle failure
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Connection Pooling & Keep-Alive Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
|
||||||
|
**gRPC:**
|
||||||
|
- HTTP/2 connection pooling tự động
|
||||||
|
- Keep-alive ping mỗi `KeepAlivePingDelaySeconds` (60s)
|
||||||
|
- Ping timeout: `KeepAlivePingTimeoutSeconds` (30s)
|
||||||
|
- Connection idle timeout: `PooledConnectionIdleTimeoutMinutes` (5min)
|
||||||
|
- Connection lifetime: `PooledConnectionLifetimeMinutes` (10min)
|
||||||
|
|
||||||
|
**HTTP:**
|
||||||
|
- HttpClient connection pooling
|
||||||
|
- Connection idle timeout: `PooledConnectionIdleTimeoutMinutes` (5min)
|
||||||
|
- Connection lifetime: `PooledConnectionLifetimeMinutes` (10min)
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"KeepAlivePingDelaySeconds": 60,
|
||||||
|
"KeepAlivePingTimeoutSeconds": 30,
|
||||||
|
"PooledConnectionIdleTimeoutMinutes": 5,
|
||||||
|
"PooledConnectionLifetimeMinutes": 10,
|
||||||
|
"EnableMultipleHttp2Connections": true,
|
||||||
|
"ConnectTimeoutSeconds": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lợi ích:**
|
||||||
|
- Giảm latency: Reuse connection thay vì tạo mới (~50-100ms → ~5ms)
|
||||||
|
- Giảm overhead: Không cần handshake mỗi request
|
||||||
|
- Better throughput: Multiple connections cho parallel requests
|
||||||
|
|
||||||
|
**Ví dụ:**
|
||||||
|
```csharp
|
||||||
|
// Request 1: Tạo connection mới (~50ms)
|
||||||
|
var proxy1 = await _client.GetProxyAsync();
|
||||||
|
|
||||||
|
// Request 2-100: Reuse connection (~5ms mỗi request)
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
var proxy = await _client.GetProxyAsync();
|
||||||
|
}
|
||||||
|
// Total time: ~50ms + (99 * 5ms) = ~545ms
|
||||||
|
// Without pooling: ~100 * 50ms = ~5000ms
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Compression Mode
|
||||||
|
|
||||||
|
**Cách hoạt động:**
|
||||||
|
- gRPC: Compression tự động với gzip/deflate
|
||||||
|
- HTTP: Accept-Encoding header
|
||||||
|
- Server compress response nếu client support
|
||||||
|
|
||||||
|
**Cấu hình:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SmartPool": {
|
||||||
|
"EnableCompression": true,
|
||||||
|
"CompressionAlgorithm": "gzip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lợi ích:**
|
||||||
|
- Giảm payload size ~30-50%
|
||||||
|
- Giảm bandwidth usage
|
||||||
|
- Faster transfer trên slow networks
|
||||||
|
|
||||||
|
**Khi nào dùng:**
|
||||||
|
- ✅ Khi bandwidth là bottleneck
|
||||||
|
- ✅ Mobile networks
|
||||||
|
- ✅ High-latency networks
|
||||||
|
- ✅ Khi response size lớn
|
||||||
|
|
||||||
|
**Trade-off:**
|
||||||
|
- CPU overhead cho compression/decompression
|
||||||
|
- Thường không đáng kể với modern CPUs
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Protocol Selection
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// High-performance internal service → gRPC
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.Protocol = SmartPoolProtocol.Grpc;
|
||||||
|
});
|
||||||
|
|
||||||
|
// External client → HTTP
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.Protocol = SmartPoolProtocol.Http;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Caching Strategy
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Random/Round-robin → Enable cache
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.EnableClientCache = true;
|
||||||
|
options.ClientCacheDurationSeconds = 30;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Least Delay/Adaptive Ranking → Disable cache
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.EnableClientCache = false; // Cần real-time data
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Logging Strategy
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// High-throughput → Fire-and-forget + Batch
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.EnableFireAndForgetLogging = true;
|
||||||
|
options.EnableBatchLogging = true;
|
||||||
|
options.LogBatchSize = 50;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Critical logging → Synchronous
|
||||||
|
services.AddSmartPoolClient(options =>
|
||||||
|
{
|
||||||
|
options.EnableFireAndForgetLogging = false;
|
||||||
|
options.EnableBatchLogging = false;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Error Handling Pattern
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Detailed error handling
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var proxy = await _client.GetRawProxyAsync(request);
|
||||||
|
}
|
||||||
|
catch (NoAvailableProxiesException)
|
||||||
|
{
|
||||||
|
// Retry với different token hoặc wait
|
||||||
|
}
|
||||||
|
catch (NoMatchingProxiesException)
|
||||||
|
{
|
||||||
|
// Relax filters hoặc use different strategy
|
||||||
|
}
|
||||||
|
catch (SmartPoolException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "SmartPool error: {ErrorCode}", ex.ErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Silent failure
|
||||||
|
var proxy = await _client.TryGetRawProxyAsync(request);
|
||||||
|
if (proxy == null)
|
||||||
|
{
|
||||||
|
// Fallback logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Retry Pattern với Alternative Strategy
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
SmartProxyServer? currentProxy = null;
|
||||||
|
for (int retry = 0; retry < 3; retry++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = retry == 0
|
||||||
|
? new GetProxyRequest { strategy = "least_delay", target_domain = "facebook.com" }
|
||||||
|
: new GetProxyRequest
|
||||||
|
{
|
||||||
|
strategy = "alternative",
|
||||||
|
referer_proxy = currentProxy
|
||||||
|
};
|
||||||
|
|
||||||
|
currentProxy = await _client.GetRawProxyAsync(request);
|
||||||
|
|
||||||
|
// Use proxy...
|
||||||
|
break; // Success
|
||||||
|
}
|
||||||
|
catch (NoMatchingProxiesException)
|
||||||
|
{
|
||||||
|
if (retry == 2) throw; // Last retry failed
|
||||||
|
// Continue to next retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- .NET 10.0
|
||||||
|
- MagicOnion.Client 6.1.7
|
||||||
|
- Microsoft.Extensions.* 10.0.2
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- 📖 **[SDK Documentation](./SDK_DOCUMENTATION.md)** - Tài liệu chi tiết đầy đủ
|
||||||
|
- 📖 **[System README](../../SMARTPOOL_README.md)** - Tổng quan hệ thống
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Internal use only.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# SmartPool Tests
|
||||||
|
|
||||||
|
This project contains unit tests for the SmartPool system.
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
- **Strategies/**: Tests for proxy selection strategies
|
||||||
|
- `RandomStrategyTests.cs`: Tests for random selection
|
||||||
|
- `AlternativeStrategyTests.cs`: Tests for alternative proxy selection
|
||||||
|
- `ProxyStrategyFactoryTests.cs`: Tests for strategy factory
|
||||||
|
|
||||||
|
- **Client/**: Tests for SmartPool client SDK
|
||||||
|
- `SmartPoolClientTests.cs`: Tests for client initialization and configuration
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
dotnet test
|
||||||
|
|
||||||
|
# Run with detailed output
|
||||||
|
dotnet test --logger "console;verbosity=detailed"
|
||||||
|
|
||||||
|
# Run specific test class
|
||||||
|
dotnet test --filter "FullyQualifiedName~RandomStrategyTests"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Coverage
|
||||||
|
|
||||||
|
The tests cover:
|
||||||
|
- Strategy selection logic
|
||||||
|
- Fallback behaviors
|
||||||
|
- Filter application
|
||||||
|
- Client initialization
|
||||||
|
- Configuration validation
|
||||||
|
|
||||||
|
## Adding New Tests
|
||||||
|
|
||||||
|
When adding new strategies or features:
|
||||||
|
1. Create a new test class in the appropriate folder
|
||||||
|
2. Follow the existing naming convention: `{ClassName}Tests.cs`
|
||||||
|
3. Use FluentAssertions for readable assertions
|
||||||
|
4. Mock dependencies using Moq
|
||||||
Reference in New Issue
Block a user