优化结构
This commit is contained in:
Generated
+2579
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,245 @@
|
||||
syntax = "proto3";
|
||||
package kratos.api;
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
option go_package = "kra/internal/conf;conf";
|
||||
|
||||
message Bootstrap {
|
||||
Server server = 1;
|
||||
Data data = 2;
|
||||
AdminBackend admin = 3;
|
||||
}
|
||||
|
||||
message Server {
|
||||
message HTTP {
|
||||
string network = 1;
|
||||
string addr = 2;
|
||||
google.protobuf.Duration timeout = 3;
|
||||
}
|
||||
HTTP http = 1;
|
||||
}
|
||||
|
||||
message Data {
|
||||
message Database {
|
||||
string driver = 1;
|
||||
string source = 2;
|
||||
string host = 3;
|
||||
string port = 4;
|
||||
string user = 5;
|
||||
string password = 6;
|
||||
string name = 7;
|
||||
string config = 8;
|
||||
string path = 9;
|
||||
string alias_name = 10;
|
||||
bool disable = 11;
|
||||
string prefix = 12;
|
||||
string engine = 13;
|
||||
string log_mode = 14;
|
||||
int32 max_idle_conns = 15;
|
||||
int32 max_open_conns = 16;
|
||||
int32 conn_max_lifetime = 17;
|
||||
bool singular = 18;
|
||||
}
|
||||
message Redis {
|
||||
string network = 1;
|
||||
string addr = 2;
|
||||
google.protobuf.Duration read_timeout = 3;
|
||||
google.protobuf.Duration write_timeout = 4;
|
||||
string name = 5;
|
||||
string password = 6;
|
||||
int32 db = 7;
|
||||
bool use_cluster = 8;
|
||||
repeated string cluster_addrs = 9;
|
||||
}
|
||||
message MongoHost {
|
||||
string host = 1;
|
||||
string port = 2;
|
||||
}
|
||||
message Mongo {
|
||||
string coll = 1;
|
||||
string options = 2;
|
||||
string database = 3;
|
||||
string username = 4;
|
||||
string password = 5;
|
||||
string auth_source = 6;
|
||||
uint64 min_pool_size = 7;
|
||||
uint64 max_pool_size = 8;
|
||||
int64 socket_timeout_ms = 9;
|
||||
int64 connect_timeout_ms = 10;
|
||||
bool is_zap = 11;
|
||||
repeated MongoHost hosts = 12;
|
||||
}
|
||||
Database database = 1;
|
||||
Redis redis = 2;
|
||||
repeated Database database_list = 3;
|
||||
repeated Redis redis_list = 4;
|
||||
Mongo mongo = 5;
|
||||
}
|
||||
|
||||
// AdminBackend contains settings for the administration HTTP transport.
|
||||
message AdminBackend {
|
||||
string router_prefix = 1;
|
||||
JWT jwt = 2;
|
||||
Captcha captcha = 3;
|
||||
Local local = 4;
|
||||
Email email = 5;
|
||||
Storage storage = 6;
|
||||
// ConfigPath is populated by the entrypoint and is not required in YAML.
|
||||
string config_path = 7;
|
||||
Media media = 8;
|
||||
repeated Disk disk_list = 9;
|
||||
System system = 10;
|
||||
Zap zap = 11;
|
||||
CORS cors = 12;
|
||||
App app = 13;
|
||||
WebSocket websocket = 14;
|
||||
MQ mq = 15;
|
||||
|
||||
message JWT {
|
||||
string signing_key = 1;
|
||||
google.protobuf.Duration expires_time = 2;
|
||||
google.protobuf.Duration buffer_time = 3;
|
||||
string issuer = 4;
|
||||
}
|
||||
|
||||
message Captcha {
|
||||
int32 key_long = 1;
|
||||
int32 img_width = 2;
|
||||
int32 img_height = 3;
|
||||
google.protobuf.Duration store_expiration = 4;
|
||||
}
|
||||
|
||||
message Local {
|
||||
string store_path = 1;
|
||||
string path_prefix = 2;
|
||||
}
|
||||
|
||||
message Email {
|
||||
string to = 1;
|
||||
string from = 2;
|
||||
string host = 3;
|
||||
string secret = 4;
|
||||
string nickname = 5;
|
||||
int32 port = 6;
|
||||
bool is_ssl = 7;
|
||||
bool is_login_auth = 8;
|
||||
}
|
||||
|
||||
message Media {
|
||||
int32 session_ttl = 1;
|
||||
int64 max_file_size = 2;
|
||||
string chunk_dir = 3;
|
||||
}
|
||||
|
||||
message Disk {
|
||||
string mount_point = 1;
|
||||
}
|
||||
|
||||
message System {
|
||||
bool use_redis = 1;
|
||||
bool use_multipoint = 2;
|
||||
bool use_strict_auth = 3;
|
||||
bool disable_auto_migrate = 4;
|
||||
bool use_mongo = 5;
|
||||
int32 addr = 6;
|
||||
int32 iplimit_count = 7;
|
||||
int32 iplimit_time = 8;
|
||||
}
|
||||
|
||||
message Zap {
|
||||
string level = 1;
|
||||
string prefix = 2;
|
||||
string format = 3;
|
||||
string director = 4;
|
||||
string encode_level = 5;
|
||||
string stacktrace_key = 6;
|
||||
bool show_line = 7;
|
||||
bool log_in_console = 8;
|
||||
int32 retention_day = 9;
|
||||
bool access_req_body = 10;
|
||||
bool access_resp_data = 11;
|
||||
bool access_req_headers = 12;
|
||||
int32 access_log_max_bytes = 13;
|
||||
repeated string file_only_modules = 14;
|
||||
}
|
||||
|
||||
message CORS {
|
||||
string mode = 1;
|
||||
repeated CORSRule whitelist = 2;
|
||||
}
|
||||
message CORSRule {
|
||||
string allow_origin = 1;
|
||||
string allow_methods = 2;
|
||||
string allow_headers = 3;
|
||||
string expose_headers = 4;
|
||||
bool allow_credentials = 5;
|
||||
}
|
||||
message App {
|
||||
string node = 1;
|
||||
string app_id = 2;
|
||||
string env = 3;
|
||||
}
|
||||
|
||||
message WebSocket {
|
||||
bool enabled = 1;
|
||||
string path = 2;
|
||||
repeated string allow_origins = 3;
|
||||
int64 max_message_size = 4;
|
||||
google.protobuf.Duration write_wait = 5;
|
||||
google.protobuf.Duration pong_wait = 6;
|
||||
google.protobuf.Duration ping_period = 7;
|
||||
int32 message_buffer_size = 8;
|
||||
bool concurrent_message_handling = 9;
|
||||
}
|
||||
|
||||
// MQ config is persisted in sys_integration_configs (kind=mq/provider=emqx).
|
||||
// The bootstrap fields are retained as a one-time migration source.
|
||||
message MQ {
|
||||
bool enabled = 1;
|
||||
string broker = 2;
|
||||
string client_id = 3;
|
||||
string username = 4;
|
||||
string password = 5;
|
||||
int32 keep_alive = 6;
|
||||
bool clean_session = 7;
|
||||
int32 connect_timeout = 8;
|
||||
}
|
||||
|
||||
message Storage {
|
||||
string type = 1;
|
||||
Qiniu qiniu = 2;
|
||||
ObjectStore aliyun_oss = 3;
|
||||
ObjectStore huawei_obs = 4;
|
||||
ObjectStore tencent_cos = 5;
|
||||
ObjectStore aws_s3 = 6;
|
||||
ObjectStore cloudflare_r2 = 7;
|
||||
ObjectStore minio = 8;
|
||||
}
|
||||
|
||||
message Qiniu {
|
||||
string zone = 1;
|
||||
string bucket = 2;
|
||||
string base_url = 3;
|
||||
string access_key = 4;
|
||||
string secret_key = 5;
|
||||
bool use_https = 6;
|
||||
bool use_cdn_domains = 7;
|
||||
}
|
||||
|
||||
// ObjectStore covers S3-compatible configuration used by public cloud
|
||||
// providers and self-hosted MinIO.
|
||||
message ObjectStore {
|
||||
string endpoint = 1;
|
||||
string region = 2;
|
||||
string bucket = 3;
|
||||
string access_key = 4;
|
||||
string secret_key = 5;
|
||||
string base_url = 6;
|
||||
string path_prefix = 7;
|
||||
bool use_ssl = 8;
|
||||
bool force_path_style = 9;
|
||||
string account_id = 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Runtime owns the active immutable configuration snapshot. Callers always
|
||||
// receive clones so a config file reload cannot race with request handling.
|
||||
type Runtime struct {
|
||||
snapshot atomic.Pointer[runtimeSnapshot]
|
||||
mu sync.RWMutex
|
||||
nextID uint64
|
||||
listeners map[uint64]func(*Data, *AdminBackend)
|
||||
}
|
||||
|
||||
type runtimeSnapshot struct {
|
||||
data *Data
|
||||
admin *AdminBackend
|
||||
}
|
||||
|
||||
func NewRuntime(data *Data, admin *AdminBackend) *Runtime {
|
||||
runtime := &Runtime{listeners: make(map[uint64]func(*Data, *AdminBackend))}
|
||||
runtime.Replace(data, admin)
|
||||
return runtime
|
||||
}
|
||||
|
||||
func cloneData(value *Data) *Data {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return proto.Clone(value).(*Data)
|
||||
}
|
||||
|
||||
func cloneAdmin(value *AdminBackend) *AdminBackend {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return proto.Clone(value).(*AdminBackend)
|
||||
}
|
||||
|
||||
func (r *Runtime) Replace(data *Data, admin *AdminBackend) {
|
||||
data = cloneData(data)
|
||||
admin = cloneAdmin(admin)
|
||||
r.snapshot.Store(&runtimeSnapshot{data: data, admin: admin})
|
||||
r.mu.RLock()
|
||||
listeners := make([]func(*Data, *AdminBackend), 0, len(r.listeners))
|
||||
for _, listener := range r.listeners {
|
||||
listeners = append(listeners, listener)
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
for _, listener := range listeners {
|
||||
listener(cloneData(data), cloneAdmin(admin))
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe registers a callback invoked after every successful runtime
|
||||
// configuration replacement. It is used by long-lived clients such as the
|
||||
// logger and other long-lived clients that must follow runtime config changes.
|
||||
func (r *Runtime) Subscribe(listener func(*Data, *AdminBackend)) func() {
|
||||
if listener == nil {
|
||||
return func() {}
|
||||
}
|
||||
r.mu.Lock()
|
||||
r.nextID++
|
||||
id := r.nextID
|
||||
r.listeners[id] = listener
|
||||
r.mu.Unlock()
|
||||
return func() {
|
||||
r.mu.Lock()
|
||||
delete(r.listeners, id)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runtime) Data() *Data {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil
|
||||
}
|
||||
return cloneData(snapshot.data)
|
||||
}
|
||||
|
||||
func (r *Runtime) Admin() *AdminBackend {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil
|
||||
}
|
||||
return cloneAdmin(snapshot.admin)
|
||||
}
|
||||
|
||||
func (r *Runtime) Values() (*Data, *AdminBackend) {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return cloneData(snapshot.data), cloneAdmin(snapshot.admin)
|
||||
}
|
||||
|
||||
func (r *Runtime) ConfigPath() string {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil || snapshot.admin == nil {
|
||||
return ""
|
||||
}
|
||||
return snapshot.admin.ConfigPath
|
||||
}
|
||||
|
||||
func (r *Runtime) UpdateDatabase(database *Data_Database) {
|
||||
for {
|
||||
current := r.snapshot.Load()
|
||||
var data *Data
|
||||
var admin *AdminBackend
|
||||
if current != nil {
|
||||
data = cloneData(current.data)
|
||||
admin = cloneAdmin(current.admin)
|
||||
}
|
||||
if data == nil {
|
||||
data = &Data{}
|
||||
}
|
||||
data.Database = proto.Clone(database).(*Data_Database)
|
||||
next := &runtimeSnapshot{data: data, admin: admin}
|
||||
if r.snapshot.CompareAndSwap(current, next) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuntimeSnapshotsAreImmutable(t *testing.T) {
|
||||
runtime := NewRuntime(&Data{Database: &Data_Database{Name: "first"}}, &AdminBackend{RouterPrefix: "/api", Jwt: &AdminBackend_JWT{SigningKey: "secret"}})
|
||||
admin := runtime.Admin()
|
||||
admin.RouterPrefix = "/changed"
|
||||
admin.Jwt.SigningKey = "changed"
|
||||
data := runtime.Data()
|
||||
data.Database.Name = "changed"
|
||||
|
||||
if got := runtime.Admin(); got.RouterPrefix != "/api" || got.Jwt.SigningKey != "secret" {
|
||||
t.Fatalf("admin snapshot mutated: %+v", got)
|
||||
}
|
||||
if got := runtime.Data().Database.Name; got != "first" {
|
||||
t.Fatalf("data snapshot mutated: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeConcurrentReplaceAndRead(t *testing.T) {
|
||||
runtime := NewRuntime(&Data{Database: &Data_Database{Name: "initial"}}, &AdminBackend{Jwt: &AdminBackend_JWT{SigningKey: "initial"}})
|
||||
var wait sync.WaitGroup
|
||||
for worker := 0; worker < 8; worker++ {
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
defer wait.Done()
|
||||
for index := 0; index < 500; index++ {
|
||||
_ = runtime.Admin().Jwt.SigningKey
|
||||
_ = runtime.Data().Database.Name
|
||||
}
|
||||
}()
|
||||
}
|
||||
for index := 0; index < 500; index++ {
|
||||
runtime.Replace(&Data{Database: &Data_Database{Name: "next"}}, &AdminBackend{Jwt: &AdminBackend_JWT{SigningKey: "next"}})
|
||||
}
|
||||
wait.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user