1. はじめに
インターネットの発展に伴い、アプリケーションの同時パフォーマンスに対する関心がますます高まっています。 golang は、同時実行性の高いプログラミング言語として、開発者の間でますます人気が高まっています。 GC メカニズム、コルーチン、チャネルのサポートが付属しているため、プログラムの複雑さと開発の難易度が大幅に軽減されます。
この記事では、golang を使用して単純なクラスターを構築し、同時リクエストを適切に割り当て、プログラムのパフォーマンスと信頼性を向上させる方法を紹介します。
2. クラスター構築の原理
具体的な操作を紹介する前に、まずクラスター構築の原理を理解しましょう。一般に、クラスターは、同じまたは異なるアプリケーションを実行する複数のサーバーとして単純に理解できます。複数のサーバーがネットワークを通じて通信し、負荷分散やデータ共有などの機能を実行します。
golang には、http サーバーを簡単に構築できる net/http というパッケージがあります。 http サーバーに加えて、サーバー サービスの検出、負荷分散、およびクラスター内のその他の機能もサポートする必要があります。現時点では、zookeeper などのサードパーティ コンポーネントを使用して実装できます。
この記事では、クラスター内のサービス登録センターとして etcd を使用し、負荷分散とサービス検出の機能を完了します。
3. 環境の準備
設定を開始する前に、対応するツールと環境をインストールする必要があります。
公式 Web サイトから golang 環境をダウンロードして設定し、対応するインストール パッケージを「https://golang.org/dl/」からダウンロードできます。 「。」
etcd は、coreos 社がオープンソース化した分散キーバリューストレージシステムで、ロードバランシングやサービス登録などの機能を簡単に実現できます。対応バージョンは「https://github.com/etcd-io/etcd/releases」からダウンロードできます。
4. 特定の操作
まず、クライアントのリクエストを処理する http サービス プログラムを作成する必要があります。ここでは、golang システムに組み込まれている net/http パッケージを使用できます。最も基本的な操作は、http サービスを開始するために使用される ListenAndServe 関数です。
次に、ローカルの http リクエストをリッスンし、クライアントに文を返すプログラムを作成します。
コードは次のとおりです:
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello World") }) http.ListenAndServe(":8080", nil) }
etcd では、まずサービスを登録するためのキーと値のペアを作成する必要があります。 。この例では、サービス パスとして /services/httpServer を使用し、ノード値としてローカルホストの IP アドレスとポート番号 8080 を使用します。
etcd クライアントで次のコマンドを実行して登録を完了します。
curl -L http://127.0.0.1:2379/v2/keys/services/httpServer -XPUT -d value='{"host":"localhost", "port":"8080"}'
負荷分散と高可用性を実現するために etcd で複数のサービス ノードを構成します。
etcd クラスターでは、より安全なサービス アクセスと負荷分散を実現する必要があります。ここでは etcd_sdk パッケージを使用します。これは、etcd 登録センターに接続してサービス ノードを読み取るのに便利です。
サービス プログラムを作成するときは、etcd 登録情報を読み取り、登録の変更を常に監視して、クラスター登録センターとの同期を維持することをお勧めします。
コードは次のとおりです:
package main import ( "context" "fmt" "github.com/coreos/etcd/clientv3" "net/http" "strings" "sync" "time" ) var ( endpoints []string currentConfig clientv3.Config etcdConfigLocker sync.Mutex ) func getConfig()(clientv3.Config, error) { etcdConfigLocker.Lock() defer etcdConfigLocker.Unlock() if endpoints == nil { return clientv3.Config{}, fmt.Errorf("no endpoints available") } return clientv3.Config{ Endpoints: endpoints, DialTimeout: 5 * time.Second, }, nil } type ServiceInfo struct { Key string `json:"key"` Value string `json:"value"` } func main() { endpoints = append(endpoints, "127.0.0.1:2379") readServices() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { url := fmt.Sprintf("http://%s%s", getService(), r.URL.Path) fmt.Printf("Forward %s => %s\n", r.URL.Path, url) http.Redirect(w, r, url, 307) }) err := http.ListenAndServe(":8080", nil) if err != nil { panic(err) } readServices() } func getService() string { config, err := getConfig() if err != nil { panic(err) } client, err := clientv3.New(config) if err != nil { panic(err) } defer client.Close() prefix := "services/httpServer" ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) resp, err := client.Get(ctx, prefix, clientv3.WithPrefix()) cancel() if err != nil { panic(err) } services := make([]*ServiceInfo, 0) for _, kv := range resp.Kvs { services = append(services, &ServiceInfo{ Key: string(kv.Key), Value: string(kv.Value), }) } if len(services) == 0 { panic(fmt.Errorf("no endpoint available")) } return strings.Replace(services[0].Value, "\"", "", -1) } func readServices() { go func() { for { getConfigFromEtcd() time.Sleep(5 * time.Second) } }() } func getConfigFromEtcd() { client, err := clientv3.New(currentConfig) if err != nil { fmt.Printf("ERROR: create etcd client failed: %s\n", err.Error()) return } defer client.Close() key := "services/httpServer" ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second) resp, err := client.Get(ctx, key, clientv3.WithPrefix()) cancel() if err != nil { fmt.Printf("ERROR: get etcd key(%s) failed: %s\n", key, err.Error()) return } tempEndpoints := make([]string, 0, len(resp.Kvs)) for _, itm := range resp.Kvs { value := string(itm.Value) tempEndpoints = append(tempEndpoints, value) } fmt.Printf("INFO: get endpoints from etcd(%s) success: %v\n", currentConfig.Endpoints, tempEndpoints) currentConfig = clientv3.Config{ Endpoints: tempEndpoints, DialTimeout: 5 * time.Second, } }
コードでは、etcd SDK の clientv3 パッケージを使用して etcd 登録センターに接続し、そこからサービス ノード情報を取得します。 getConfig() 関数と getConfigFromEtcd() 関数は、etcd レジストリ情報を読み取るために使用されます。
上記の手順を構成した後、プログラムを実行できます。ターミナルを開き、プロジェクト ディレクトリに切り替え、次のコマンドを実行します:
go run main.go
操作が成功したら、ブラウザを開いて http://127.0.0.1:8080 にアクセスすると、プログラムの出力が表示されます。 Hello World」というメッセージが表示され、プログラムが正常に実行されたことが示されます。
この例では http サービスを使用していますが、実際のプロジェクトでは、プログラムのパフォーマンスを向上させるために grpc などの高性能プロトコルを使用することもできます。
5. まとめ
この記事では、golang を使用してクラスターを構築する原則と具体的な操作を紹介しました。 etcd 登録センターと対応する SDK パッケージを使用することで、サービス ノードの登録、読み取り、動的メンテナンスを実現します。これにより、プログラムのパフォーマンスと信頼性が向上し、ユーザー エクスペリエンスの向上につながります。
実際のアプリケーションでは、プログラムの信頼性を確保するために、プログラムのセキュリティと耐障害性にも注意を払う必要があります。
以上がgolangでクラスターを構築する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。