Skip to content

نظام Instance والميزات

core.Instance هو الكائن المركزي الذي يحتفظ بجميع الميزات (DNS، التوجيه، مدراء الوكلاء، إلخ.) ويدير دورة حياتها.

هيكل Instance

go
// core/xray.go
type Instance struct {
    statusLock                 sync.Mutex
    features                   []features.Feature
    pendingResolutions         []resolution
    pendingOptionalResolutions []resolution
    running                    bool
    resolveLock                sync.Mutex
    ctx                        context.Context
}

النسخة هي في جوهرها حاوية حقن اعتماديات. تُسجِّل الميزات نفسها، ويمكن لميزات أخرى أن تُعلن عن اعتماديات يتم حلها عندما تصبح جميع الميزات المطلوبة متوفرة.

واجهة Feature

كل مكون رئيسي ينفذ واجهة features.Feature:

go
// features/feature.go
type Feature interface {
    common.HasType   // Type() interface{}
    common.Runnable  // Start() error, Close() error
}

يتم تحديد أنواع الميزات بقيم مؤشرات حارسة:

go
func ManagerType() interface{} { return (*Manager)(nil) }
func ClientType() interface{}  { return (*Client)(nil) }

تسلسل التهيئة

mermaid
sequenceDiagram
    participant Config
    participant Instance
    participant Feature
    participant InboundMgr
    participant OutboundMgr

    Config->>Instance: New(config)
    loop لكل إعداد تطبيق
        Instance->>Instance: CreateObject(settings)
        Instance->>Feature: AddFeature(feature)
        Feature-->>Instance: حل الاعتماديات المعلقة
    end
    Note over Instance: تسجيل القيم الافتراضية تلقائياً<br/>(DNS، Policy، Router، Stats)
    Instance->>Instance: InitSystemDialer(dns, obm)
    Instance->>Instance: التحقق: هل جميع الاعتماديات محلولة؟
    loop لكل إعداد وارد
        Instance->>InboundMgr: AddHandler(handler)
    end
    loop لكل إعداد صادر
        Instance->>OutboundMgr: AddHandler(handler)
    end
    Note over Instance: النسخة جاهزة (لم تبدأ بعد)
    Instance->>Instance: Start()
    loop لكل ميزة
        Instance->>Feature: Start()
    end

الخطوات الرئيسية في initInstanceWithConfig()

  1. ميزات التطبيق: التكرار على config.App (المُوزِّع، المُوجِّه، DNS، السياسات، الإحصائيات، إلخ.)، إنشاء الكائنات عبر CreateObject()، وتسجيلها كميزات.

  2. القيم الافتراضية الأساسية: إذا لم يتم تهيئة DNS أو Policy أو Router أو Stats، يتم تسجيل تنفيذات افتراضية (بدون عمليات):

    go
    essentialFeatures := []struct{ Type, Instance }{
        {dns.ClientType(), localdns.New()},
        {policy.ManagerType(), policy.DefaultManager{}},
        {routing.RouterType(), routing.DefaultRouter{}},
        {stats.ManagerType(), stats.NoopManager{}},
    }
  3. تهيئة مُتصل النظام: تهيئة مُتصل النظام العام بعميل DNS ومدير الصادر (لحل DNS في الصادر freedom).

  4. فحص الاعتماديات: إذا بقيت أي pendingResolutions دون حل، يفشل التهيئة مع خطأ.

  5. المعالجات: إضافة جميع معالجات الوارد والصادر.

حل الاعتماديات

آلية RequireFeatures() تُمكِّن حقن الاعتماديات المُؤجَّل:

go
// مكون يُعلن عما يحتاجه:
core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router,
    pm policy.Manager, sm stats.Manager, dc dns.Client) error {
    return d.Init(config, om, router, pm, sm)
})

عند استدعاء RequireFeatures():

  1. التحقق مما إذا كانت جميع أنواع المعاملات مسجلة مسبقاً
  2. إذا نعم، يتم استدعاء رد النداء فوراً
  3. إذا لا، يتم تخزينها كـ resolution في pendingResolutions
  4. في كل مرة يتم فيها استدعاء AddFeature()، تتم إعادة فحص جميع الحلول المعلقة

يستخدم الحل الانعكاس (reflection) لمطابقة أنواع المعاملات:

go
type resolution struct {
    deps     []reflect.Type      // أنواع الميزات المطلوبة
    callback interface{}         // func(features...) error
}

إنشاء الكائنات

CreateObject() هي دالة المصنع التي تحول إعدادات protobuf إلى كائنات وقت التشغيل:

go
// يُستدعى من core/config.go
func CreateObject(server *Instance, config interface{}) (interface{}, error) {
    ctx := context.WithValue(server.ctx, xrayKey, server)
    return common.CreateObject(ctx, config)
}

يستدعي هذا سجل الإعدادات حيث يرتبط كل نوع protobuf بدالة مُنشئ مسجلة عبر common.RegisterConfig().

نمط سجل الإعدادات

كل مكون يتبع هذا النمط:

go
func init() {
    common.Must(common.RegisterConfig((*Config)(nil),
        func(ctx context.Context, config interface{}) (interface{}, error) {
            c := config.(*Config)
            // بناء كائن وقت التشغيل من الإعدادات
            return NewHandler(ctx, c)
        }))
}

نوع Config في protobuf هو المفتاح، ودالة المصنع تنشئ كائن وقت التشغيل. المُغلِّف TypedMessage (protobuf Any) يوفر إلغاء تسلسل آمن النوع.

ملاحظات التنفيذ

لإعادة تنفيذ نظام Instance:

  1. تحتاج إلى سجل ميزات يربط أنواع الميزات بالنسخ
  2. مُحلل اعتماديات يُطلق ردود النداء عندما تتوفر جميع الاعتماديات
  3. مُفكك تسلسل للإعدادات يربط أنواع الإعدادات بالمُنشئات
  4. دورة حياة الميزة: التسجيل ← حل الاعتماديات ← البدء ← الإغلاق

يمكن استبدال النهج المبني على الانعكاس بتسجيل صريح في اللغات ذات الأنواع الثابتة:

instance.addFeature(dispatcherFeature)
instance.addFeature(routerFeature)
// المُوجِّه يرى أن المُوزِّع متوفر، فيحل اعتماديته

تحليل تقني لأغراض إعادة التنفيذ.