288 lines
11 KiB
Markdown
288 lines
11 KiB
Markdown
---
|
||
# Fengling Project Rewrite - Conversation Summary
|
||
## Project Information
|
||
**Project Name**: Fengling (风灵) - QR Code Marketing Management Platform Rewrite
|
||
**Project Locations**:
|
||
- **Old Project**: `/Users/movingsam/Fengling.Refactory/`
|
||
- `Fengling.Backend.Web/` - Old monolithic backend
|
||
- `Yarp.Gateway/` - Old gateway (no longer relevant for reference)
|
||
|
||
- **New Project**: `/Users/movingsam/Fengling.Refactory.Buiding/`
|
||
- `src/YarpGateway/` - New independent gateway service
|
||
- `src/YarpGateway.Admin/` - Vue3 admin UI
|
||
## What We Did
|
||
### Phase 1: Initial Gateway Setup
|
||
1. Created YARP Gateway backend with:
|
||
- YARP 2.2.0 (reverse proxy framework)
|
||
- EF Core 9.0.0 + PostgreSQL (192.168.100.10:5432)
|
||
- StackExchange.Redis (192.168.100.10:6379) for distributed locks
|
||
- Serilog for logging
|
||
2. Created Vue3 Admin frontend with:
|
||
- Vue 3 + TypeScript + Vite + Element Plus + Pinia
|
||
- Running on http://localhost:5173
|
||
### Phase 2: Core Architecture Implementation
|
||
#### 1. Route Priority Design (99% Global + 1% Tenant-Specific)
|
||
**User's requirement**:
|
||
> "普通情况(租户走特定的实例(pod)只是特殊场景)所以是要对 前缀匹配到不同的服务这块的ui呢"
|
||
**Implementation**:
|
||
- Added `IsGlobal` boolean field to `GwTenantRoute` table
|
||
- Route priority: Tenant-specific routes > Global routes
|
||
- Database migration: `20260201133826_AddIsGlobalToTenantRoute`
|
||
**Benefit**:
|
||
- Before: 100 tenants × 10 services = 1000 route configurations
|
||
- After: 10 global routes + few tenant-specific routes
|
||
#### 2. In-Memory Route Caching
|
||
**File**: `src/YarpGateway/Services/RouteCache.cs`
|
||
- Loads routes from database at startup
|
||
- Priority-based lookup: tenant route → global route → 404
|
||
- Hot reload support via `ReloadAsync()`
|
||
- Avoids database queries per request
|
||
#### 3. Redis Distributed Load Balancing
|
||
**File**: `src/YarpGateway/LoadBalancing/DistributedWeightedRoundRobinPolicy.cs`
|
||
- Implements weighted round-robin algorithm
|
||
- Uses Redis for distributed locks: `lock:{instanceName}:{clusterId}`
|
||
- Stores load balancing state in Redis: `lb:{instanceName}:{clusterId}:state`
|
||
- Supports multiple gateway instances
|
||
#### 4. Dynamic Proxy Configuration
|
||
**File**: `src/YarpGateway/DynamicProxy/DynamicProxyConfigProvider.cs`
|
||
- Implements `IProxyConfigProvider`
|
||
- Loads routes and clusters from database
|
||
- Provides configuration to YARP
|
||
#### 5. Tenant Routing Middleware
|
||
**File**: `src/YarpGateway/Middleware/TenantRoutingMiddleware.cs`
|
||
- Extracts tenant ID from JWT headers (`X-Tenant-Id`)
|
||
- Uses `RouteCache` to get route cluster
|
||
- Sets `context.Items["DynamicClusterId"]` for YARP
|
||
### Phase 3: Frontend Development
|
||
Created Vue3 admin pages:
|
||
1. **Dashboard.vue** - Statistics dashboard
|
||
2. **TenantList.vue** - Tenant management
|
||
3. **TenantRoutes.vue** - Tenant-specific routes
|
||
4. **GlobalRoutes.vue** - Global routes management (NEW)
|
||
5. **ClusterInstances.vue** - Service instance management
|
||
### Phase 4: Bug Fixes
|
||
1. Fixed 4K screen width constraints (removed max-width, added 100% width/height)
|
||
2. Fixed route `/tenants` not displaying until page refresh
|
||
3. Fixed CORS configuration (allowed origins: localhost:5173, 127.0.0.1:5173)
|
||
4. Fixed multiple compilation errors in `DistributedWeightedRoundRobinPolicy.cs`:
|
||
- Missing using statements
|
||
- JsonSerializer ambiguity (used full namespace)
|
||
- HashCode.Combine signature errors
|
||
- Typo: `M.achineName` → `MachineName`
|
||
- Changed `await using var` to `using var`
|
||
### Phase 5: Database Configuration
|
||
Applied database migration:
|
||
```bash
|
||
dotnet ef database update
|
||
```
|
||
## Current Status
|
||
### ✅ Completed
|
||
1. Backend compiles successfully
|
||
2. Frontend runs on http://localhost:5173
|
||
3. Database migrations applied
|
||
4. Global routes management UI created
|
||
5. Redis distributed load balancing implemented
|
||
6. In-memory route caching implemented
|
||
### ⚠️ Known Issue: YARP Dynamic Routing
|
||
**Problem**:
|
||
- Accessing `/api/product/test` returns 404
|
||
- Logs show: "Request reached end of middleware pipeline"
|
||
- `DynamicProxyConfigProvider` is registered but not being used by YARP
|
||
**User's comment**:
|
||
> "你等下 下游的应用还没建立 肯定404吧"
|
||
> "你先把网关的ui更新一下功能呀。这个你先别急着测试吧"
|
||
**Conclusion**: The 404 is expected because downstream microservices don't exist yet. User wants to focus on UI updates and move to microservices analysis.
|
||
## Database Schema
|
||
### Tables
|
||
#### Tenants
|
||
```sql
|
||
Id: bigint (PK)
|
||
TenantCode: varchar(50) (unique)
|
||
TenantName: varchar(100)
|
||
Status: int (1=enabled, 0=disabled)
|
||
IsDeleted: boolean
|
||
CreatedTime, UpdatedTime, Version
|
||
```
|
||
#### TenantRoutes
|
||
```sql
|
||
Id: bigint (PK)
|
||
TenantCode: varchar(50) (empty string = global route)
|
||
ServiceName: varchar(100)
|
||
ClusterId: varchar(100)
|
||
PathPattern: varchar(200)
|
||
Priority: int (0=global, 10=tenant)
|
||
Status: int
|
||
IsGlobal: boolean (NEW)
|
||
IsDeleted: boolean
|
||
CreatedTime, UpdatedTime, Version
|
||
```
|
||
#### ServiceInstances
|
||
```sql
|
||
Id: bigint (PK)
|
||
ClusterId: varchar(100)
|
||
DestinationId: varchar(100)
|
||
Address: varchar(200)
|
||
Health: int (1=healthy)
|
||
Weight: int
|
||
Status: int
|
||
IsDeleted: boolean
|
||
CreatedTime, UpdatedTime, Version
|
||
```
|
||
## Configuration
|
||
### Backend (appsettings.json)
|
||
```json
|
||
{
|
||
"ConnectionStrings": {
|
||
"DefaultConnection": "Host=192.168.100.10;Port=5432;Database=fengling_gateway;Username=movingsam;Password=sl52788542"
|
||
},
|
||
"Redis": {
|
||
"ConnectionString": "192.168.100.10:6379",
|
||
"Database": 0,
|
||
"InstanceName": "YarpGateway"
|
||
},
|
||
"Cors": {
|
||
"AllowedOrigins": ["http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:5174"],
|
||
"AllowAnyOrigin": false
|
||
},
|
||
"ReverseProxy": {
|
||
"Routes": {},
|
||
"Clusters": {}
|
||
}
|
||
}
|
||
```
|
||
### Ports
|
||
- Frontend: http://localhost:5173
|
||
- Backend: http://0.0.0.0:8080
|
||
## API Endpoints
|
||
### Tenant Management
|
||
- `GET /api/gateway/tenants` - List tenants
|
||
- `POST /api/gateway/tenants` - Create tenant
|
||
- `DELETE /api/gateway/tenants/{id}` - Delete tenant
|
||
### Tenant Routes
|
||
- `GET /api/gateway/tenants/{tenantCode}/routes` - List tenant routes
|
||
- `POST /api/gateway/tenants/{tenantCode}/routes` - Create tenant route
|
||
### Global Routes
|
||
- `GET /api/gateway/routes/global` - List global routes
|
||
- `POST /api/gateway/routes/global` - Create global route
|
||
- `DELETE /api/gateway/routes/{id}` - Delete route
|
||
### Cluster Instances
|
||
- `GET /api/gateway/clusters/{clusterId}/instances` - List instances
|
||
- `POST /api/gateway/clusters/{clusterId}/instances` - Add instance
|
||
- `DELETE /api/gateway/instances/{id}` - Delete instance
|
||
### Configuration
|
||
- `POST /api/gateway/reload` - Reload configuration
|
||
## File Structure
|
||
```
|
||
/Users/movingsam/Fengling.Refactory.Buiding/src/
|
||
├── YarpGateway/
|
||
│ ├── Config/
|
||
│ │ ├── DatabaseRouteConfigProvider.cs
|
||
│ │ ├── DatabaseClusterConfigProvider.cs
|
||
│ │ ├── JwtConfig.cs
|
||
│ │ └── RedisConfig.cs
|
||
│ ├── Controllers/
|
||
│ │ └── GatewayConfigController.cs
|
||
│ ├── Data/
|
||
│ │ ├── GatewayDbContext.cs
|
||
│ │ └── GatewayDbContextFactory.cs
|
||
│ ├── DynamicProxy/
|
||
│ │ └── DynamicProxyConfigProvider.cs
|
||
│ ├── LoadBalancing/
|
||
│ │ └── DistributedWeightedRoundRobinPolicy.cs
|
||
│ ├── Middleware/
|
||
│ │ ├── JwtTransformMiddleware.cs
|
||
│ │ └── TenantRoutingMiddleware.cs
|
||
│ ├── Models/
|
||
│ │ ├── GwTenant.cs
|
||
│ │ ├── GwTenantRoute.cs
|
||
│ │ └── GwServiceInstance.cs
|
||
│ ├── Services/
|
||
│ │ ├── RouteCache.cs
|
||
│ │ └── RedisConnectionManager.cs
|
||
│ ├── Migrations/
|
||
│ │ ├── 20260201120312_InitialCreate.cs
|
||
│ │ └── 20260201133826_AddIsGlobalToTenantRoute.cs
|
||
│ ├── sql/
|
||
│ │ └── init.sql
|
||
│ ├── Program.cs
|
||
│ └── appsettings.json
|
||
│
|
||
└── YarpGateway.Admin/
|
||
├── src/
|
||
│ ├── api/
|
||
│ │ └── index.ts
|
||
│ ├── components/
|
||
│ │ └── Layout.vue
|
||
│ ├── stores/
|
||
│ │ └── tenant.ts
|
||
│ ├── views/
|
||
│ │ ├── Dashboard.vue
|
||
│ │ ├── TenantList.vue
|
||
│ │ ├── TenantRoutes.vue
|
||
│ │ ├── GlobalRoutes.vue
|
||
│ │ └── ClusterInstances.vue
|
||
│ ├── router/
|
||
│ │ └── index.ts
|
||
│ └── main.ts
|
||
└── package.json
|
||
```
|
||
## What We're Doing Now
|
||
The user wants to stop working on the gateway routing issue and instead focus on:
|
||
1. Updating the gateway UI functionality (COMPLETED - GlobalRoutes.vue added)
|
||
2. Analyzing the old backend for microservices split
|
||
User's exact words:
|
||
> "你先把网关的ui更新一下功能呀。这个你先别急着测试吧"
|
||
> "我准备开始分析业务的微服务拆分了"
|
||
## Next Steps (For New Conversation)
|
||
### Immediate Priority: Microservices Analysis
|
||
**Task**: Analyze the old backend (`/Users/movingsam/Fengling.Refactory/Fengling.Backend.Web/`) to determine how to split it into microservices
|
||
**Known Old Backend Structure**:
|
||
```
|
||
/Users/movingsam/Fengling.Refactory/Fengling.Backend.Web/src/src/
|
||
├── account/ # Account module
|
||
├── activityplan/ # Activity planning
|
||
├── basis/ # Basic configuration
|
||
├── channel/ # Channel management
|
||
├── company/ # Company management
|
||
├── coupon/ # Coupon management
|
||
├── fieldConfig/ # Field configuration
|
||
├── flow/ # Workflow
|
||
├── gift/ # Gift management
|
||
├── integralConfig/ # Points configuration
|
||
├── member/ # Member management
|
||
├── promoter/ # Promoter management
|
||
├── qipei/ # Service matching
|
||
├── reports/ # Reports
|
||
├── riskManage/ # Risk management
|
||
└── [many more modules...]
|
||
```
|
||
### Analysis Goals
|
||
1. Identify business domain boundaries
|
||
2. Determine which modules should become independent microservices
|
||
3. Design inter-service communication patterns
|
||
4. Plan database splitting strategy
|
||
5. Consider shared services (auth, configuration, etc.)
|
||
### Deferred Tasks (Lower Priority)
|
||
1. Fix YARP `DynamicProxyConfigProvider` to properly integrate with YARP
|
||
2. Test dynamic routing with actual downstream services
|
||
3. Complete deployment architecture (Docker, Kubernetes)
|
||
## Key Technical Decisions
|
||
### 1. Why Global Routes + Tenant-Specific Routes?
|
||
**Reason**: 99% of tenants share the same services, only 1% need dedicated instances
|
||
**Benefit**: Drastically reduces configuration complexity
|
||
### 2. Why YARP?
|
||
- Microsoft official support
|
||
- High performance (based on Kestrel)
|
||
- Extensible (custom load balancing policies)
|
||
- Dynamic configuration support
|
||
### 3. Why Redis?
|
||
- Distributed locks for multi-instance scenarios
|
||
- Persistent load balancing state
|
||
- High performance (millisecond response)
|
||
## Important Notes for Continuation
|
||
1. **Database Access**: PostgreSQL at 192.168.100.10:5432, Database: fengling_gateway
|
||
2. **Redis Access**: 192.168.100.10:6379
|
||
3. **Project Location**: `/Users/movingsam/Fengling.Refactory.Buiding/`
|
||
4. **User Preference**: Manually handles database migrations (user applies SQL manually)
|
||
5. **Old Gateway**: User confirmed the old gateway at `/Users/movingsam/Fengling.Refactory/Yarp.Gateway/` is no longer relevant for reference
|
||
--- |