Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Terokai cara melaksanakan penggunaan panas dalam aplikasi Golang

Terokai cara melaksanakan penggunaan panas dalam aplikasi Golang

PHPz
PHPzasal
2023-04-05 13:50:02944semak imbas

Dalam pembangunan perisian, penggunaan panas adalah teknologi yang sangat penting, yang membolehkan kami mengemas kini aplikasi pada masa jalan tanpa mengganggu operasi. Semasa proses ini, kami boleh mengekalkan keadaan aplikasi dan menggunakan versi baharu aplikasi yang dikemas kini ke pelayan kami tanpa sebarang masa henti.

Dalam beberapa tahun kebelakangan ini, bahasa pengaturcaraan yang dipanggil Golang telah menjadi semakin popular. Golang ialah projek yang dimulakan oleh Google Matlamat reka bentuknya adalah untuk menjadikannya lebih mudah dan lebih cekap untuk menulis aplikasi berprestasi tinggi dan kebolehpercayaan tinggi. Ciri penggunaan panas Golang ialah topik yang menarik Mari kita terokai cara melaksanakan penggunaan panas dalam aplikasi Golang.

Salah satu kekuatan Golang ialah kebolehskalaan semula jadi dan toleransi kesalahan. Kelebihan ini menjadikannya ideal untuk membina aplikasi yang sangat tersedia. Walaupun Golang tidak dapat mengendalikan semua ralat dalam kod dengan anggun seperti beberapa bahasa yang ditaip secara dinamik, ia membolehkan kami menggunakan hot-deploy dengan mudah.

Kami boleh menggunakan beberapa alatan dalam perpustakaan standard yang disediakan oleh Golang untuk melaksanakan penggunaan panas, termasuk isyarat, pantulan, selinux, dsb. Pakej isyarat boleh digunakan untuk menangkap isyarat sistem pengendalian, manakala pakej pantulan membolehkan kami memeriksa dan mengubah suai kod pada masa jalan. Pakej selinux boleh digunakan untuk mengurus keselamatan aplikasi.

Kita boleh menggunakan pakej reflect untuk menulis contoh aplikasi yang sangat kecil. Dalam aplikasi ini, kita boleh menggunakan pakej reflect untuk memuatkan kod daripada fungsi lain. Ini membolehkan kami mengubah suai kod pada masa jalan dan mengemas kini aplikasi.

Berikut ialah contoh kod:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "reflect"
    "sync"
    "syscall"
    "time"
)

func main() {
    done := make(chan bool)
    wg := sync.WaitGroup{}

    wg.Add(1)
    go Monitor(done, &wg)

    for n := 0; n < 100; n++ {
        fmt.Println(n)
        time.Sleep(1 * time.Second)
    }

    done <- true
    wg.Wait()
}

func Monitor(done chan bool, wg *sync.WaitGroup) {
    defer wg.Done()

    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGHUP)

    for {
        select {
        case <-done:
            return
        case <-c:
            fmt.Println("Reloading...")
            Reload()
            fmt.Println("Reloaded!")
        }
    }
}

func Reload() {
    fmt.Println("Before reload")

    f, err := os.Open("test.go")
    if err != nil {
        fmt.Println(err)
        return
    }

    buf := make([]byte, 1024)
    incompleteLine := ""
    for {
        n, _ := f.Read(buf)
        if n == 0 {
            break
        }
        incompleteLine, _ = checkLines(buf[:n], incompleteLine)
    }

    fmt.Println("Loading new code")
    code := fmt.Sprintf(`package main

import (
    "fmt"
)

func run() {
    fmt.Println("New code is running")
}
`)
    Set("run", code)
}

func checkLines(buf []byte, incompleteLine string) (string, error) {
    line := incompleteLine + string(buf)

    complete := true
    for j := 0; j < len(line); j++ {
        if line[j] == '\n' {
            //run complete line...
            fmt.Println(line[:j])
            complete = false
        }
    }
    if complete {
        return "", nil
    }
    return line, nil
}

func Set(funcName string, code string) {
    codeVal := reflect.ValueOf(&code).Elem()
    ptrToCode := codeVal.Addr().Interface().(*string)

    // use function name as package name
    fn := funcName + ": "

    b := []byte(*ptrToCode)
    _, err := Compile(fn, b)
    if err != nil {
        fmt.Println(err)
    }

    // create a new function value of the same type as Run
    v := reflect.MakeFunc(reflect.TypeOf(Run), Compile(fn, b))

    // copy it in
    f := reflect.ValueOf(Run).Elem()
    f.Set(v)
}

func Compile(fn string, src []byte) (func(), error) {
    // no optimization means no inlining, etc, which means func values are inherently invalid
    f, err := CompileWithOpt(fn, src, 0)
    if err != nil {
        return nil, err
    }
    return f, nil
}

func CompileWithOpt(fn string, src []byte, opt int) (func(), error) {
    // we'll prepend some code to show the function name on panics
    src = append([]byte("func "+fn+"() {\n"), src...)
    src = append(src, '\n', '}')

    parsed, err := parser.ParseFile(token.NewFileSet(), "", src, parser.AllErrors)

    if err != nil {
        return nil, err
    }

    conf := types.Config{}
    info := &types.Info{}

    pkgs, err := conf.Check("", token.NewFileSet(), []*ast.File{parsed}, info)
    if err != nil {
        return nil, err
    }
    pkg := pkgs

    for _, n := range parsed.Decls {
        fn, ok := n.(*ast.FuncDecl)
        if !ok {
            continue
        }
        if fn.Name.Name != "run" {
            continue
        }
        var buf bytes.Buffer
        if err := printer.Fprint(&buf, token.NewFileSet(), fn); err != nil {
            return nil, err
        }

        fmt.Println("Compile", buf.String())
    }

    code := string(src)
    fn := func() {
        fmt.Println("Before run")

        err = eval(code, pkg, info)
        if err != nil {
            fmt.Println(err)
            return
        }

        fmt.Println("After run")
    }
    return fn, nil
}

func eval(code string, pkg *types.Package, info *types.Info) error {
    fset := token.NewFileSet()
    file, err := parser.ParseFile(fset, "", code, 0)
    if err != nil {
        fmt.Println(err)
        return err
    }

    conf := types.Config{
        Importer: importer.From("gc", nil, types.GcImport),
    }
    checker := types.NewChecker(&conf, fset, pkg, info)

    if _, err := checker.Files([]*ast.File{file}); err != nil {
        fmt.Println(err)
        return err
    }

    // compile/run, like in the previous example
    var buf bytes.Buffer
    if err := printer.Fprint(&buf, fset, file); err != nil {
        return err
    }

    fmt.Println(buf.String())

    return nil
}

func Run() {
    fmt.Println("Current code is running")
}

Dalam contoh ini, kita dapat melihat bahawa fungsi Reload() akan dipanggil apabila aplikasi membuat perubahan. Fungsi Reload() membaca kod baharu daripada fail yang dipanggil "test.go" dan menambahkannya pada aplikasi menggunakan pakej reflect.

Perlu ambil perhatian bahawa memuatkan kod baharu ke dalam aplikasi mungkin melibatkan penyusunan beberapa kod, yang akan memberi kesan tertentu pada prestasi aplikasi. Walau bagaimanapun, kelebihan penggunaan panas jauh melebihi penalti prestasi.

Sebelum tamat, perlu diingatkan bahawa ini hanyalah contoh program yang mudah. Dalam amalan, penggunaan panas perlu mengambil kira banyak aspek, seperti kerumitan aplikasi, bilangan fail yang perlu dikemas kini dan bahagian aplikasi yang berbeza.

Ringkasnya, dengan menggunakan pakej pantulan dan isyarat Golang, kami boleh melaksanakan penggunaan panas dengan mudah. Walaupun ia mungkin mempunyai sedikit kesan ke atas prestasi aplikasi, teknik ini membolehkan kami mengemas kini kod dengan mudah tanpa menutup aplikasi.

Atas ialah kandungan terperinci Terokai cara melaksanakan penggunaan panas dalam aplikasi Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Tutorial memasang pakej golangArtikel seterusnya:Tutorial memasang pakej golang