11 KiB
11 KiB
# Fengling Project Rewrite - Conversation Summary
## Project Information
Project Name: Fengling (风灵) - QR Code Marketing Management Platform Rewrite
Project Locations:
- Old Project:
- New 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