Maison > développement back-end > Golang > le corps du texte

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Libérer: 2023-07-21 09:53:55
avant
740 Les gens l'ont consulté
Obtenir les métriques d'exécution d'une application nous donne une meilleure idée de son fonctionnement réel. En connectant ces indicateurs à des systèmes de surveillance tels que prometheus et zabbix, les applications peuvent être détectées en continu et les anomalies peuvent être alertées et traitées en temps opportun.

Pull et Push

Il existe deux façons d'interfacer avec le système de surveillance, l'une est Pull et l'autre est Push.

Prenons Prometheus comme exemple. L'application expose l'interface HTTP et permet à Prometheus de capturer périodiquement des indicateurs via l'interface. Dans Push, l'application transmet activement les indicateurs à PushGateway et Prometheus accède à PushGateway pour capturer les données.

Il existe un package appelé expvar dans la bibliothèque standard Go. Son nom est une combinaison de exp et var, ce qui signifie exporter des variables.

expvar fournit une interface standardisée pour les variables publiques et expose ces variables au format Json via HTTP, ce qui est très approprié pour se connecter au système de surveillance à l'aide de Pull.

Utilisation de la bibliothèque expvar

expvar est une bibliothèque standard, ce qui signifie que nous n'avons pas besoin de dépendances supplémentaires, et elle fournit également des indicateurs prêts à l'emploi. Apprenons à utiliser cette bibliothèque.

Lorsque la bibliothèque expvar est référencée (<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">import "expvar"</span>),以下 init 函数将被自动调用。

func init() {
 http.HandleFunc("/debug/vars", expvarHandler)
 Publish("cmdline", Func(cmdline))
 Publish("memstats", Func(memstats))
}
Copier après la connexion

该函数为我们注册了 /debug/vars 路径的 HTTP 服务,访问该路径将得到 Json 格式的指标。

因此,我们还需要调用 ListenAndServe 绑定端口,并开始服务 HTTP 请求。

http.ListenAndServe(":8080", nil)
Copier après la connexion

完整代码如下

package main

import (
 _ "expvar"
 "net/http"
)

func main() {
 http.ListenAndServe(":8080", nil)
}
Copier après la connexion

将程序运行起来后,通过 curl 请求,得到以下结果

$ curl localhost:8080/debug/vars
{
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build1657217338/b001/exe/main"],
"memstats": {"Alloc":278880,"TotalAlloc":278880,"Sys":8735760,"Lookups":0,"Mallocs":1169,"Frees":87,"HeapAlloc":278880,"HeapSys":3866624,"HeapIdle":2949120,"HeapInuse":917504,"HeapReleased":2899968,"HeapObjects":1082,"StackInuse":327680,"StackSys":327680,"MSpanInuse":28696,"MSpanSys":32640,"MCacheInuse":9600,"MCacheSys":15600,"BuckHashSys":3875,"GCSys":3826448,"OtherSys":662893,"NextGC":4194304,"LastGC":0,"PauseTotalNs":0,"PauseNs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":0,"NumForcedGC":0,"GCCPUFraction":0,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":41,"Frees":0},{"Size":16,"Mallocs":496,"Frees":0},{"Size":24,"Mallocs":63,"Frees":0},{"Size":32,"Mallocs":28,"Frees":0},{"Size":48,"Mallocs":134,"Frees":0},{"Size":64,"Mallocs":50,"Frees":0},{"Size":80,"Mallocs":17,"Frees":0},{"Size":96,"Mallocs":17,"Frees":0},{"Size":112,"Mallocs":6,"Frees":0},{"Size":128,"Mallocs":9,"Frees":0},{"Size":144,"Mallocs":9,"Frees":0},{"Size":160,"Mallocs":18,"Frees":0},{"Size":176,"Mallocs":6,"Frees":0},{"Size":192,"Mallocs":0,"Frees":0},{"Size":208,"Mallocs":37,"Frees":0},{"Size":224,"Mallocs":6,"Frees":0},{"Size":240,"Mallocs":0,"Frees":0},{"Size":256,"Mallocs":12,"Frees":0},{"Size":288,"Mallocs":7,"Frees":0},{"Size":320,"Mallocs":2,"Frees":0},{"Size":352,"Mallocs":13,"Frees":0},{"Size":384,"Mallocs":1,"Frees":0},{"Size":416,"Mallocs":30,"Frees":0},{"Size":448,"Mallocs":1,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":0,"Frees":0},{"Size":576,"Mallocs":5,"Frees":0},{"Size":640,"Mallocs":5,"Frees":0},{"Size":704,"Mallocs":3,"Frees":0},{"Size":768,"Mallocs":0,"Frees":0},{"Size":896,"Mallocs":6,"Frees":0},{"Size":1024,"Mallocs":8,"Frees":0},{"Size":1152,"Mallocs":10,"Frees":0},{"Size":1280,"Mallocs":3,"Frees":0},{"Size":1408,"Mallocs":1,"Frees":0},{"Size":1536,"Mallocs":0,"Frees":0},{"Size":1792,"Mallocs":7,"Frees":0},{"Size":2048,"Mallocs":2,"Frees":0},{"Size":2304,"Mallocs":3,"Frees":0},{"Size":2688,"Mallocs":2,"Frees":0},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3200,"Mallocs":0,"Frees":0},{"Size":3456,"Mallocs":0,"Frees":0},{"Size":4096,"Mallocs":8,"Frees":0},{"Size":4864,"Mallocs":1,"Frees":0},{"Size":5376,"Mallocs":1,"Frees":0},{"Size":6144,"Mallocs":2,"Frees":0},{"Size":6528,"Mallocs":0,"Frees":0},{"Size":6784,"Mallocs":0,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":2,"Frees":0},{"Size":9472,"Mallocs":8,"Frees":0},{"Size":9728,"Mallocs":0,"Frees":0},{"Size":10240,"Mallocs":0,"Frees":0},{"Size":10880,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":0,"Frees":0},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14336,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":18432,"Mallocs":0,"Frees":0}]}
}
Copier après la connexion

可以看到,expvar 默认已提供了两项指标,分别是程序执行命令(os.Args)和运行时内存分配(runtime.Memstats)信息。

expvar 库重点内容

expvar 中最重要的是 Publish 函数和 Var 接口。

func Publish(name string, v Var) {}
Copier après la connexion

Publish 函数签名中需要两个参数,name 是我们指定的指标名。例如在上文 expvar 的 init 函数下 <span style="font-size: 15px;">Publish("cmdline", Func(cmdline))</span>代码行,其中<span style="font-size: 15px;">cmdline</span>就是指标名,而<span style="font-size: 15px;">Func(cmdline)</span>import "expvar"), la fonction init suivante sera appelée automatiquement.

// Var is an abstract type for all exported variables.
type Var interface {
 // String returns a valid JSON value for the variable.
 // Types with String methods that do not return valid JSON
 // (such as time.Time) must not be used as a Var.
 String() string
}
Copier après la connexion
Copier après la connexion
Cette fonction enregistre pour nous le service HTTP du chemin /debug/vars. L'accès à ce chemin obtiendra des indicateurs au format Json.

🎜🎜🎜 Par conséquent, nous devons également appeler ListenAndServe pour lier le port et commencer à traiter les requêtes HTTP. 🎜🎜
type Int struct {
 i int64
}

type Float struct {
 f uint64
}

type Map struct {
 m      sync.Map // map[string]Var
 keysMu sync.RWMutex
 keys   []string // sorted
}

type String struct {
 s atomic.Value // string
}

type Func func() any
Copier après la connexion
Copier après la connexion
🎜🎜Le code complet est le suivant🎜🎜
intVar = expvar.NewInt(“metricName”)
floatVar = expvar.NewFloat(“metricName”)
mapVar = expvar.NewMap(“metricName”)
stringVar = expvar.NewString(“metricName”)
Copier après la connexion
Copier après la connexion
🎜🎜Après avoir exécuté le programme, via la requête curl, les résultats suivants sont obtenus🎜🎜
func NewInt(name string) *Int {
 v := new(Int)
 Publish(name, v)
 return v
}
Copier après la connexion
Copier après la connexion
🎜🎜Comme vous pouvez le voir, expvar a fourni deux indicateurs par défaut, qui sont commandes d'exécution du programme (os.Args) et informations sur l'allocation de mémoire d'exécution (runtime.Memstats). 🎜🎜

Contenu clé de la bibliothèque expvar🎜

🎜🎜Les éléments les plus importants dans expvar sont la fonction Publier et l'interface Var. 🎜🎜
type MyStruct struct {
 Field1 string
 Field2 int
 Field3 float64
}
Copier après la connexion
Copier après la connexion
🎜🎜La signature de la fonction de publication nécessite deux paramètres, name est le nom de l'indicateur que nous avons spécifié. Par exemple, sous la fonction init de expvar ci-dessus 🎜🎜Publish("cmdline", Func(cmdline))🎜🎜ligne de code, où🎜🎜cmdline🎜🎜 est le nom de l'indicateur, et 🎜🎜Func(cmdline)🎜🎜 est expvar.Func qui implémente le Variable de type d'interface Var. 🎜🎜🎜🎜Interface Var, qui définit uniquement une méthode String. Il convient de noter que cette méthode doit renvoyer une chaîne Json valide. 🎜🎜
// Var is an abstract type for all exported variables.
type Var interface {
 // String returns a valid JSON value for the variable.
 // Types with String methods that do not return valid JSON
 // (such as time.Time) must not be used as a Var.
 String() string
}
Copier après la connexion
Copier après la connexion

为了方便使用,expvar 库中提供了五种导出变量类型,它们均实现了 Var 接口。

type Int struct {
 i int64
}

type Float struct {
 f uint64
}

type Map struct {
 m      sync.Map // map[string]Var
 keysMu sync.RWMutex
 keys   []string // sorted
}

type String struct {
 s atomic.Value // string
}

type Func func() any
Copier après la connexion
Copier après la connexion

我们分别通过调用 expvar.NewXXX 函数即可完成前四种类型的变量创建与指标注册。

intVar = expvar.NewInt(“metricName”)
floatVar = expvar.NewFloat(“metricName”)
mapVar = expvar.NewMap(“metricName”)
stringVar = expvar.NewString(“metricName”)
Copier après la connexion
Copier après la connexion

例如 expvar.NewInt 函数,它会内部调用 Publish 方法完成指标名与 expvar.Int 类型变量的绑定。

func NewInt(name string) *Int {
 v := new(Int)
 Publish(name, v)
 return v
}
Copier après la connexion
Copier après la connexion

而 expvar.Func 类型,其实是为了让我们可以自定义导出类型。

例如,假如我们想要暴露以下定义的结构体

type MyStruct struct {
 Field1 string
 Field2 int
 Field3 float64
}
Copier après la connexion
Copier après la connexion

首先需要创建一个数据生成函数。它用于在每次调用 HTTP 服务路径时,通过该函数导出这里面的数据。

func MyStructData() interface{} {
 return MyStruct{
  Field1: "Gopher",
  Field2: 22,
  Field3: 19.99,
 }
}
Copier après la connexion

最后,通过 Publish 函数注册指标名即可。

expvar.Publish("metricName", expvar.Func(MyStructData))
Copier après la connexion

完整示例

下面,我们给出一个覆盖五种导出变量类型的完整示例。

package main

import (
 "expvar"
 "github.com/shirou/gopsutil/v3/host"
 "github.com/shirou/gopsutil/v3/load"
 "github.com/shirou/gopsutil/v3/mem"
 "net/http"
 "time"
)

type Load struct {
 Load1  float64
 Load5  float64
 Load15 float64
}

func AllLoadAvg() interface{} {
 return Load{
  Load1:  LoadAvg(1),
  Load5:  LoadAvg(5),
  Load15: LoadAvg(15),
 }
}

func LoadAvg(loadNumber int) float64 {
 avg, _ := load.Avg()
 switch loadNumber {
 case 5:
  return avg.Load5
 case 15:
  return avg.Load15
 default:
  return avg.Load1
 }
}

func main() {
 var (
  aliveOfSeconds = expvar.NewInt("aliveOfSeconds")
  hostID         = expvar.NewString("hostID")
  lastLoad       = expvar.NewFloat("lastLoad")
  virtualMemory  = expvar.NewMap("virtualMemory")
 )
 expvar.Publish("allLoadAvg", expvar.Func(AllLoadAvg))
 h, _ := host.HostID()
 hostID.Set(h)

 go http.ListenAndServe(":8080", nil)

 for {
  aliveOfSeconds.Add(1)
  vm, _ := mem.VirtualMemory()
  lastLoad.Set(LoadAvg(1))
  virtualMemory.Add("active", int64(vm.Active))
  virtualMemory.Add("buffer", int64(vm.Buffers))
  time.Sleep(1 * time.Second)
 }
}
Copier après la connexion

在上述示例中,我们通过 gopsutil 库(介绍可见还在自己写 Go 系统监控函数吗一文)获取了一些系统信息,并展示了如何通过 expvar 中的各种变量类型将这些信息进行导出。

curl 访问 localhost:8080/debug/vars 结果如下

$ curl localhost:8080/debug/vars
{
"aliveOfSeconds": 1,
"allLoadAvg": {"Load1":1.69580078125,"Load5":1.97412109375,"Load15":1.90283203125},
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build3566019824/b001/exe/main"],
"hostID": "7a1a74f2-30fc-5bc1-b439-6b7aef22e58d",
"lastLoad": 1.69580078125,
"memstats": {"Alloc":256208,"TotalAlloc":256208,"Sys":8735760,"Lookups":0,"Mallocs":1089,"Frees":48,"HeapAlloc":256208,"HeapSys":3866624,"HeapIdle":2891776,"HeapInuse":974848,"HeapReleased":2859008,"HeapObjects":1041,"StackInuse":327680,"StackSys":327680,"MSpanInuse":19992,"MSpanSys":32640,"MCacheInuse":9600,"MCacheSys":15600,"BuckHashSys":3905,"GCSys":3851120,"OtherSys":638191,"NextGC":4194304,"LastGC":0,"PauseTotalNs":0,"PauseNs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":0,"NumForcedGC":0,"GCCPUFraction":0,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":35,"Frees":0},{"Size":16,"Mallocs":415,"Frees":0},{"Size":24,"Mallocs":71,"Frees":0},{"Size":32,"Mallocs":37,"Frees":0},{"Size":48,"Mallocs":141,"Frees":0},{"Size":64,"Mallocs":52,"Frees":0},{"Size":80,"Mallocs":20,"Frees":0},{"Size":96,"Mallocs":23,"Frees":0},{"Size":112,"Mallocs":14,"Frees":0},{"Size":128,"Mallocs":7,"Frees":0},{"Size":144,"Mallocs":7,"Frees":0},{"Size":160,"Mallocs":18,"Frees":0},{"Size":176,"Mallocs":6,"Frees":0},{"Size":192,"Mallocs":1,"Frees":0},{"Size":208,"Mallocs":42,"Frees":0},{"Size":224,"Mallocs":3,"Frees":0},{"Size":240,"Mallocs":0,"Frees":0},{"Size":256,"Mallocs":9,"Frees":0},{"Size":288,"Mallocs":8,"Frees":0},{"Size":320,"Mallocs":5,"Frees":0},{"Size":352,"Mallocs":13,"Frees":0},{"Size":384,"Mallocs":3,"Frees":0},{"Size":416,"Mallocs":33,"Frees":0},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":1,"Frees":0},{"Size":576,"Mallocs":4,"Frees":0},{"Size":640,"Mallocs":8,"Frees":0},{"Size":704,"Mallocs":3,"Frees":0},{"Size":768,"Mallocs":1,"Frees":0},{"Size":896,"Mallocs":6,"Frees":0},{"Size":1024,"Mallocs":8,"Frees":0},{"Size":1152,"Mallocs":9,"Frees":0},{"Size":1280,"Mallocs":3,"Frees":0},{"Size":1408,"Mallocs":1,"Frees":0},{"Size":1536,"Mallocs":1,"Frees":0},{"Size":1792,"Mallocs":9,"Frees":0},{"Size":2048,"Mallocs":1,"Frees":0},{"Size":2304,"Mallocs":2,"Frees":0},{"Size":2688,"Mallocs":2,"Frees":0},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3200,"Mallocs":1,"Frees":0},{"Size":3456,"Mallocs":0,"Frees":0},{"Size":4096,"Mallocs":5,"Frees":0},{"Size":4864,"Mallocs":0,"Frees":0},{"Size":5376,"Mallocs":1,"Frees":0},{"Size":6144,"Mallocs":1,"Frees":0},{"Size":6528,"Mallocs":0,"Frees":0},{"Size":6784,"Mallocs":0,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":1,"Frees":0},{"Size":9472,"Mallocs":8,"Frees":0},{"Size":9728,"Mallocs":0,"Frees":0},{"Size":10240,"Mallocs":0,"Frees":0},{"Size":10880,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":0,"Frees":0},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14336,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":18432,"Mallocs":0,"Frees":0}]},
"virtualMemory": {"active": 1957449728, "buffer": 0}
}
Copier après la connexion

总结

标准库 expvar 为需要导出的公共变量提供了一个标准化的接口,使用比较简单。

expvar 包内部定义的几种基础类型都相应给出了并发安全的操作方法,我们不需要去重复实现一遍,能够开箱即用。

Cependant, selon les statistiques des projets publics sur https://go.dev/, le nombre d'importations de cette bibliothèque est inférieur à 10 000.

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Par rapport au nombre d'importations dans d'autres bibliothèques standards, la présence d'expvar est trop faible.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:Go语言进阶学习
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal