API サーバー ソースコード分析: エントリ ポイント分析

API サーバー ソースコード分析: エントリ ポイント分析


Kubernetes (K8s) クラスターで最も重要なコンポーネントの 1 つは、すべてのクラスター管理アクティビティのエントリ ポイントである API サーバーです。この記事から、K8s API サーバーのコードを詳細に分析し、アプリケーションのエントリ ポイント、フレームワーク、etcd との通信について説明します。

アプリケーションのエントリポイント

K8s API サーバーのメイン エントリ ポイントは、cmd/kube-apiserver/apiserver.go ファイルにあります。

 // cmd/kube-apiserver/apiserver.go

// apiserver はクラスターのメイン API サーバーおよびマスターです。
// クラスター管理 API を提供する役割を担います。
パッケージメイン

輸入
「オス」

「k8s.io/コンポーネントベース/cli」
_ "k8s.io/component-base/logs/json/register" // JSON ログ形式の登録に使用
_ "k8s.io/component-base/metrics/prometheus/clientgo" // すべてのprometheus client-goプラグインをロードします
_ "k8s.io/component-base/metrics/prometheus/version" // バージョンインジケーターの登録に使用
「k8s.io/kubernetes/cmd/kube-apiserver/app」


関数main (){
コマンド: = app新しいAPIサーバーコマンド()
コード: = cli実行(コマンド)
os終了(コード)
}

app.NewAPIServerCommand() は cobra コマンド オブジェクトを構築し、cli.Run はコマンドを実行するため、NewAPIServerCommand 関数が cobra.Command オブジェクトを構築する方法を直接確認できます。

 // cmd/kube-apiserver/app/server.go

// NewAPIServerCommand はデフォルトのパラメータを持つ *cobra.Command オブジェクトを作成します
func NewAPIServerCommand () *コブラ指示{
// NewServerRunOptions はデフォルトのパラメータを使用して新しい ServerRunOptions オブジェクトを作成します。
// apiserver を実行するには ServerRunOption オブジェクトが必要です
s : =オプション新しいサーバー実行オプション()
cmd : = &コブラ指示{
使用方法: "kube-apiserver"
Long : ` Kubernetes APIサーバーはデータを検証し構成します
ポッドサービスレプリケーションコントローラおよび
その他APIサーバーREST操作提供
他のすべてのコンポーネントが相互作用するクラスターの共有状態。`,

// ......
RunE : func ( cmd * cobra .コマンド, args []文字列)エラー{
verflag.PrintAndExitIfRequested ( )
fs : = cmdです。フラグ()

err : = sの場合ログ検証して適用します();エラー!=ゼロ{
エラーを返す
}
クリフラグ印刷フラグ( fs )

エラー: = checkNonZeroInsecurePort ( fs )
err != nil の場合{
エラーを返す
}
// デフォルトオプションを設定する
完了オプションエラー: =完了( s )
err != nil の場合{
エラーを返す
}
// オプションを確認する
エラーの場合: = completedOptions検証します(); len (エラー) != 0 {
utilerrors.NewAggregate ( errs )を返す
}
Run ( completedOptionsgenericapiserver.SetupSignalHandler ())返します
},
}

// ......

戻りコマンド
}

この関数の中心的な機能は、Complete(s) 関数を使用して、apiserver の起動に必要なデフォルトのパラメータを生成し、起動のためにそのデフォルトのパラメータを Run 関数に渡すことです。

 // cmd/kube-apiserver/app/server.go
// 実行は指定された APIServer を実行し、終了できません。
func Run ( completeOptionscompletedServerRunOptions , stopCh <- chan struct {} ) error {

// サービス チェーンを作成する (3 つのサーバー コンポーネントを含む)
サーバーerr : = CreateServerChain ( completeOptionsstopCh )


// ヘルスチェック、生存チェック、OpenAPI ルート登録など、サービスを開始する前の準備。
準備完了エラー: =サーバー準備実行()

// 正式に実行を開始する
返却準備完了実行( stopCh )
}

Run 関数では、CreateServerChain 関数を使用して、委任を通じて接続された APIServer オブジェクトを作成します。

 // cmd/kube-apiserver/app/server.go

// CreateServerChain は委任を通じて接続された APIServer を作成します
func CreateServerChain ( completedOptionscompletedServerRunOptions , stopCh <- chan struct { } ) ( * aggregatorapiserver.APIAggregator , error ) {
// CreateKubeAPIServerConfig は、APIServer を実行するためのすべての構成リソースを作成しますが、リソースは実行しません。
kubeAPIServerConfigserviceResolverpluginInitializererr : = CreateKubeAPIServerConfig ( completeOptions ) を作成します。

// // APIExtensionsServer 構成を作成する
apiExtensionsConfig err : = createAPIExtensionsConfig ( * kubeAPIServerConfig.GenericConfigkubeAPIServerConfig.ExtraConfig.VersionedInformers pluginInitializer completedOptions.ServerRunOptions completedOptions.MasterCount
serviceResolverwebhookNewDefaultAuthenticationInfoResolverWrapper ( kubeAPIServerConfig.ExtraConfig.ProxyTransport kubeAPIServerConfig.GenericConfig.EgressSelector kubeAPIServerConfig.GenericConfig.LoopbackClientConfig kubeAPIServerConfig.GenericConfig.TracerProvider ) )

// APIExtensionsServerを作成し、ルートを登録する
apiExtensionsServererr : = createAPIExtensionsServer ( apiExtensionsConfiggenericapiserver.NewEmptyDelegateWithCustomHandler ( notFoundHandler ) )

// KubeAPIServerを作成し、ルートを登録する
kubeAPIServer エラー: = CreateKubeAPIServer ( kubeAPIServerConfigapiExtensionsServer。GenericAPIServer )

// // aggregatorServer 構成を作成する
aggregatorConfig err : = createAggregatorConfig ( * kubeAPIServerConfig.GenericConfigcompletedOptions.ServerRunOptionskubeAPIServerConfig.ExtraConfig.VersionedInformersserviceResolver kubeAPIServerConfig.ExtraConfig.ProxyTransport pluginInitializer )

// aggregatorServer を作成し、ルートを登録する
aggregatorServererr : = createAggregatorServer ( aggregatorConfigkubeAPIServer.GenericAPIServerapiExtensionsServer.Informers )

aggregatorServerを返すnil
}

上記の関数では、CreateServerChain によって APIExtensionServer、KubeAPIServer、AggregatorServer の 3 つのサーバーが作成されることがわかります。 APIServer は、さまざまな種類のリクエストを処理するために、次の 3 つのコンポーネントに依存しています。

  • APIExtensionServer: 主に CustomResourceDefinition (CRD) リクエストの処理を担当します。
  • KubeAPIServer: 一般的な処理、認証、承認に加えて、K8s 組み込みリソースに対するリクエストの処理を主に担当します。
  • AggregatorServer: 主にアグリゲーター処理を担当し、集約された K8s サービスにリクエストを転送するプロキシ サーバーとして機能します。

各サーバーには対応する構成があります。上記の関数の apiExtensionServer と aggregatorServer の構成は kubeAPIServerConfig に依存する必要があり、これらの ServerConfig はすべて GenericConfig に依存する必要があることがわかります。 CreateKubeAPIServerConfig 関数は kubeAPIServerConfig を作成します。この関数では、次のコードに示すように、buildGenericConfig を呼び出すことによって GenericConfig オブジェクトが作成されます。

 // cmd/kube-apiserver/app/server.go
// CreateKubeAPIServerConfig は、APIServer を実行するためのすべての構成リソースを作成します。
func CreateKubeAPIServerConfig ( s完了したServerRunOptions ) (
*コントロールプレーン.Config
aggregatorapiserver.ServiceResolver
[]入場料プラグイン初期化子
エラー
){
proxyTransport : = CreateProxyTransport ()
// 共通構成を構築する
genericConfigversionedInformersserviceResolverpluginInitializersadmissionPostStartHookstorageFactoryerr : = buildGenericConfig ( s.ServerRunOptionsproxyTransport )

// ......

config : = &コントロールプレーン.設定{
汎用設定:汎用設定
追加構成:コントロールプレーン追加構成{
APIリソース構成ソース:ストレージファクトリーAPIリソース構成ソース
ストレージファクトリー:ストレージファクトリー
イベントTTL : sイベントTTL
KubeletClientConfig : sKubeletConfig
EnableLogsSupport : sEnableLogsHandler
プロキシトランスポート:プロキシトランスポート

サービスIP範囲: sプライマリサービスクラスタIP範囲
APIServerServiceIP : s.APIServerServiceIP
セカンダリサービスIP範囲: sセカンダリサービスクラスタIP範囲

APIサーバーサービスポート: 443

ServiceNodePortRange : sサービスノードポート範囲
KubernetesServiceNodePort : sKubernetesServiceNodePort

EndpointReconcilerType :リコンサイラータイプ( s . EndpointReconcilerType )、
マスターカウント: sマスターカウント

サービスアカウント発行者: sサービスアカウント発行者
ServiceAccountMaxExpiration : sサービスアカウントトークン最大有効期限
有効期限を延長: s認証サービス アカウント有効期限を延長

バージョン管理された情報提供者:バージョン管理された情報提供者

IdentityLeaseDurationSeconds : sアイデンティティリース期間秒数
IdentityLeaseRenewIntervalSeconds : sアイデンティティリース更新間隔秒数
},
}

// ......

configserviceResolverpluginInitializersnil を返します
}

関数buildGenericConfig (
s *オプションサーバー実行オプション
プロキシトランスポート* http輸送
)(...){
// 一般的な構成オブジェクトを作成する
genericConfig = genericapiserverNewConfig (レガシースキーム.コーデック)

// ......

//認証インスタンスを作成する
lastErr = sの場合認証ApplyTo ( & genericConfig.Authentication genericConfig.SecureServinggenericConfig.EgressSelectorgenericConfig.OpenAPIConfigclientgoExternalClientversionedInformers ) ; lastErr != nil {
戻る
}

// ...
// openapi/swagger 構成、OpenAPIConfig は OpenAPI 仕様を生成するために使用されます
getOpenAPIDefinitions : = openapi ですGetOpenAPIDefinitionsWithoutDisabledFeatures (生成されたopenapiGetOpenAPIDefinitions )
一般的な設定OpenAPIConfig = genericapiserverDefaultOpenAPIConfig ( getOpenAPIDefinitions openapinamer.NewDefinitionNamer ( legacyscheme.Scheme extensionsapiserver.Schemeaggregatorscheme.Scheme ))
一般的な設定OpenAPIConfig情報タイトル= 「Kubernetes」
一般的な設定LongRunningFunc =フィルター基本的な長時間実行リクエストチェック (
セットしますNewString ( "watch""proxy" )、
セットしますNewString ( "attach""exec""proxy""log""portforward" )、


// storageFactoryConfig オブジェクトは、etcd 認証、アドレス、ストレージ プレフィックスなど、kube-apiserver が etcd と対話する方法を定義します。
// このオブジェクトは、リソース情報、リソース エンコーディング情報、リソース ステータスなどのリソースの保存方法も定義します。
storageFactoryConfig : = kubeapiserver新しいストレージファクトリー構成()
ストレージファクトリ構成。 APIリソース構成= genericConfigマージされたリソース構成
StorageFactoryConfig が完了しましたエラー: = storageFactoryConfigです。完了( s . Etcd )

storageFactorylastErr =完了したStorageFactoryConfig新しい()

lastErr = sの場合などなどストレージファクトリーストレージファクトリーを適用しますlastErr != nil {
戻る
}

// ......

// SharedInformerFactory を初期化する
kubeClientConfig : = genericConfig ですループバッククライアント構成
clientgoExternalClientエラー: = clientgoclientset新しいConfig ( kubeClientConfig )
versionedInformers = clientgoinformers ですNewSharedInformerFactory ( clientgoExternalClient10 *時間.)

// 認証構成。内部的に authenticatorConfig.New() を呼び出します。
// K8s は 9 つの認証メカニズムを提供しており、それぞれがインスタンス化されると認証子になります。
lastErr = sの場合認証ApplyTo ( & genericConfig.Authentication genericConfig.SecureServing genericConfig.EgressSelectorgenericConfig.OpenAPIConfigclientgoExternalClientversionedInformers ) ; lastErr != nil {
戻る
}

// 認証インスタンスを作成します。 K8s は 6 つの認証メカニズムも提供します。各承認メカニズムは、インスタンス化された後に承認者になります。
一般的な設定承認承認者genericConfigRuleResolvererr = BuildAuthorizer ( sgenericConfig . EgressSelectorversionedInformers )

// ...
//監査
lastErr = sです。監査適用先( genericConfig )

// アドミッションコントローラ
// k8s リソースは、認証と承認が通過した後、etcd に永続化される前に、アドミッション コントロール ロジックに入ります。
// アドミッション制御には、要求されたリソースに対するカスタム操作(検証、変更、拒否)が含まれます。
// アドミッションコントローラは、プラグインデータ構造を通じて一様に登録、保存、管理されます
admissionConfig : = & kubeapiserveradmission設定{
外部情報提供者:バージョン管理情報提供者
LoopbackClientConfig : genericConfigループバッククライアント構成
CloudConfigFile : sクラウドプロバイダーCloudConfigFile
}
serviceResolver = buildServiceResolver ( s.EnableAggregatorRouting genericConfig.LoopbackClientConfig.HostversionedInformers )
pluginInitializersadmissionPostStartHookerr = admissionConfig新規( proxyTransportgenericConfig . EgressSelectorserviceResolvergenericConfig . TracerProvider )

エラー= s入場料適用先(
ジェネリック構成
バージョン化された情報提供者
kubeClientConfig
機能.DefaultFeatureGate
プラグイン初期化子...)

// ...

}

それでは、これら 3 つのサーバーがどのように構築されているかを見てみましょう。

go-restfulフレームワーク

ここでは、APIServer がこのフレームワークを使用するため、まず go-restful フレームワークを理解する必要があります。次のコードは go-restful の公式例です。このデモを理解すると、基本的に go-restful フレームワークの使い方がわかります。

パッケージメイン

輸入
"ログ"
「ネット/http」

RESTfulスペック「github.com/emicklei/go-restful-openapi/v2」
レストフル「github.com/emicklei/go-restful/v3」
「github.com/go-openapi/spec」


// UserResource はユーザードメインへの REST レイヤーです
タイプUserResource構造体{
// 通常はDAO(データアクセスオブジェクト)を使用します
ユーザーマップ[文字列]ユーザー
}

// WebService は、ユーザー リソースに対する REST 要求を処理できる新しいサービスを作成します。
func ( u UserResource ) WebService () * restfulWebサービス{
ws : = new ( restful.WebService )追加します。
ws
パス( "/users" )。
( restful . MIME_XMLrestful . MIME_JSON )を消費します
( restful . MIME_JSONrestful . MIME_XML )を生成します// ルートごとにこれを指定することもできます

tags : = []文字列{ "users" }

wsルート( ws . GET ( "/" )。To ( u . findAllUsers )。
// ドキュメント
Doc ( 「すべてのユーザーを取得」 )。
メタデータ( restfulspec . KeyOpenAPITagstags )。
書き込みます([]ユーザー{})。
( 200"OK" 、[]ユーザー{})を返します

wsルート( ws . GET ( "/{user-id}" )。To ( u . findUser )。
// ドキュメント
Doc ( 「ユーザーを取得する」 )。
Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "integer" ). DefaultValue ( "1" )).
メタデータ( restfulspec . KeyOpenAPITagstags )。
書き込みますユーザー{})。 // レスポンスで
( 200"OK"User {} )を返します
( 404"見つかりません"nil )を返します)

wsルート( ws . PUT ( "/{user-id}" )。To ( u . updateUser )。
// ドキュメント
ドキュメント( 「ユーザーの更新」 )。
Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "string" )).
メタデータ( restfulspec . KeyOpenAPITagstags )。
リクエストから( User {})) //を読み取ります

wsルート( ws . PUT ( "" ). To ( u . createUser ).
// ドキュメント
Doc ( 「ユーザーを作成する」 )。
メタデータ( restfulspec . KeyOpenAPITagstags )。
リクエストから( User {})) //を読み取ります

wsルート( ws . DELETE ( "/{user-id}" )。To ( u . removeUser )。
// ドキュメント
Doc ( 「ユーザーを削除する」 )。
メタデータ( restfulspec . KeyOpenAPITagstags )。
Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "string" )))

WSを返す
}

// http://localhost:8080/users を取得します
//
func ( u UserResource ) findAllUsers ( request * restful . Requestresponse * restful . Response ) {
リスト: = []ユーザー{}
_についてそれぞれ: =範囲uユーザー{
list = append (リスト)
}
response.WriteEntity (リスト)
}

// http://localhost:8080/users/1 を取得します
//
func ( u UserResource ) findUser ( request * restful.Request , response * restful.Response ) {
id : =リクエストPathParameter ( "ユーザーID" )
usr : = uです。ユーザー[ ID ]
len ( usr.ID ) == 0の場合{
応答WriteErrorString ( http . StatusNotFound「ユーザーが見つかりませんでした。」 )
}それ以外{
レスポンス.WriteEntity (usr )
}
}

// http://localhost:8080/users/1 を配置します
// <User><Id>1</Id><Name>メリッサ ラズベリー</Name></User>
//
func ( u * UserResource ) updateUser ( request * restful . Requestresponse * restful . Response ) {
usr : = new (ユーザー)
エラー: =リクエストエンティティの読み取り( & usr )
エラー== nilの場合{
あなたユーザー[ usr . ID ] = * usr
レスポンス.WriteEntity (usr )
}それ以外{
応答書き込みエラー( http . StatusInternalServerErrorerr )
}
}

// http://localhost:8080/users/1 を配置します
// <User><Id>1</Id><Name>メリッサ</Name></User>
//
func ( u * UserResource ) createUser ( request * restful . Requestresponse * restful . Response ) {
usr : =ユーザー{ ID :リクエスト. PathParameter ( "ユーザーID" ) }
エラー: =リクエストエンティティの読み取り( & usr )
エラー== nilの場合{
あなたユーザー[ usr . ID ] = usr
応答WriteHeaderAndEntity ( http . StatusCreatedusr )
}それ以外{
応答書き込みエラー( http . StatusInternalServerErrorerr )
}
}

// http://localhost:8080/users/1 を削除
//
func ( u * UserResource ) removeUser ( request * restful.Request , response * restful.Response ) {
id : =リクエストPathParameter ( "ユーザーID" )
削除( u .ユーザーid )
}

関数main (){
u : = UserResource { map [ string ] User {}}
安らかなデフォルトコンテナ。 ( u.WebService ())追加します

config : = restfulspec です設定{
Web サービス: RESTfulRegisteredWebServices (), // 表示されるサービスを制御します
APIパス: "/apidocs.json"
PostBuildSwaggerObjectHandler :強化SwaggerObject }
安らかなデフォルトコンテナ。 ( restfulspec.NewOpenAPIService ( config ))追加します

// オプションとして、REST API に優れた Web UI を提供する Swagger サービスをインストールできます。
// Swagger HTML5 アセットをダウンロードし、以下の構成で FilePath の場所を変更する必要があります。
// http://localhost:8080/apidocs/?url=http://localhost:8080/apidocs.json を開く
http://www.youtube.com/watch?v=vUyQyYyxcハンドル( "/apidocs/"http . StripPrefix ( "/apidocs/"http . FileServer ( http . Dir ( "/Users/emicklei/Projects/swagger-ui/dist" ))))

ログPrintf ( "localhost:8080 でリッスンを開始します" )
ログ致命的( http .ListenAndServe ( ":8080"nil ))
}

関数enrichSwaggerObject ( swo * spec.Swagger ){
スウォ情報= &仕様.情報{
InfoProps :仕様情報プロパティ{
タイトル: 「UserService」
説明: 「ユーザーを管理するためのリソース」
お問い合わせ先: & spec .連絡先情報{
ContactInfoProps :仕様連絡先情報プロパティ{
名前: 「ジョン」
メールアドレス: "[email protected]"
URL : "http://johndoe.org"
},
},
ライセンス: & spec .ライセンス{
ライセンスプロパティ: specライセンスプロパティ{
名前「MIT」
URL : "http://mit.org"
},
},
バージョン: "1.0.0"
},
}
スウォタグ= [] specタグ{ spec .タグ{ TagProps : spec .タグプロパティ{
名前: 「ユーザー」
説明: 「ユーザーの管理」 }}}
}

// ユーザーは単なるサンプルタイプです
タイプUser構造体{
ID文字列`json : "id"説明: "ユーザーの識別子" `
名前文字列`json : "name"説明: "ユーザー名"デフォルト: "john" `
年齢int `json : "年齢"説明: "ユーザーの年齢"デフォルト: "21" `
}

このサンプル コードでは、go-restful のコア機能を使用して、ユーザーの追加、削除、クエリ、変更を実装するシンプルな RESTful API を実装します。コンテナ、Web サービス、ルートといういくつかのコア概念があります。

  • コンテナー: 複数の Web サービスと http.ServerMux を含むサーバー コンテナー。
  • WebService: サービスは複数のルートから構成されます。 WebService は実際には、上記の例の /users など、特定のオブジェクトに関連するサービスを表します。 /users の RESTful API を実装するには、追加、削除、確認、変更を行うルート (Route のコレクション) を追加する必要があります。
  • ルート: URL、http メソッド、受信および応答メディア タイプ、処理関数を含むルーティング。各ルートは、メソッドとパスに従って対応するメソッドにマッピングされます。これは、メソッド/パスから関数へのマッピング関係を抽象化したものです。たとえば、上記の例の ws.Route(ws.GET("/{user-id}").To(u.findUser)) は、パス /users/{user-id} に対する GET リクエストであり、findUser メソッドにルーティングされて処理されます。
  • コンテナは Web サービスのコレクションです。コンテナに複数の Web サービスを追加できます。コンテナーは ServeHTTP() メソッドを実装しているため、本質的には http ハンドラーであり、http サーバーで直接使用できます。

Kubernetes での go-restful の使用は比較的基本的なものであり、最も基本的なルーティング機能が使用されます。 K8s には、CRD などのカスタム リソース オブジェクトを含む多くの組み込みリソース オブジェクトがあるため、これらのリソースに対応するインターフェースは最初から直接ハードコードされているのではなく、一連のコードを通じて動的に登録されます。したがって、次の分析は、APIServer が次のルーティング処理を提供できるようにする方法を見つけることです。

 GET / apis / apps / v1 / namespaces / { namespace } /デプロイメント/ { name }
POST / apis / apps / v1 / namespaces / { namespace } /デプロイメント

GET / apis / apps / v1 / namespaces / { namespace } / daemonsets / { name }
POST / apis / apps / v1 /名前空間/ {名前空間} /デーモンセット

go-restful の基本を理解した後、これら 3 つのサーバーがどのようにインスタンス化されるかを学習できます。

<<:  チップの革新に10年を費やしたAmazon Web Servicesは、デジタル変革のためのクラウドハードウェア「アクセラレータ」を提供します

>>:  クラウド ストレージのセキュリティ: データ暗号化メカニズムとセキュリティ レベルの簡単な分析

推薦する

新しいサイトが追加された経験から、私はBaiduの検索アルゴリズムの力を過小評価しなくなりました。

人々は常に百度と Google を比較することを好み、百度の検索アルゴリズムは Google よりは...

Webmaster Network からの毎日のレポート: JD.com が新しいドメイン名を開始、WeChat の課金は当然の結論になる可能性

1.2345 航行海賊事件: 会長と他の被告8人に執行猶予付きの判決記者は消息筋から、2011年に公...

pumpcloud-高帯域幅香港直接接続VPSレビュー/NetflixとTVBを視聴可能/Windowsで

ここで、一部のハイエンドユーザーにpumpcloudの香港VPSをお勧めする必要があります。本当にハ...

インターネット大手がクラウドコンピューティングをめぐって競争

戦いは雲の上、そこでは孤独だ。 11月22日、アマゾンは国防総省との100億ドルの契約をめぐって米国...

クラウド間の移行に必要な 7 つの重要なステップ

企業のビジネスがクラウド プラットフォーム間で移行する場合、少なくとも短期的には、製品やテクノロジー...

ウェブサイトがブロックされた場合の一般的な予防策

多くの友人が、なぜウェブサイトが K-ed されたのか、K-ed された場合はどうすればよいのか、ウ...

Vultr クラウド ホスティング: 月額 2.5 ドル - 15 のデータ センター / 自由に勝てる / Alipay + WeChat / トップアップで 2 倍の金額

世界的に有名なクラウド ホスティング プロバイダーである Vultr の 11 月の最新アクティビテ...

ウェブサイト構築におけるSEO最適化の考慮方法

SEO 最適化は、ウェブサイト構築の秘訣となっています。今日、顧客はオンライン マーケティングをます...

Baidu の SEO における手動介入から SEO 担当者の将来の最適化の道筋について議論する

2012 年 2 月 15 日、SEO コミュニティで非常にエキサイティングな出来事が起こりました。...

検索エンジンのスパイダーにウェブサイトをクロールさせる方法

SEO 最適化に携わる人なら誰でも、ウェブサイトが検索エンジンで上位にランクインするには、スパイダー...

良いサービスを提供することがプロモーションの効果的な手段である理由についての簡単な分析

ウェブマスターの間で一般的に誰もが気にする問題は、ウェブサイトのランキング、含まれるアイテムの数、収...

データサイエンティストと開発者向けの新しいツールであるAmazon SageMakerが中国で利用可能になりました

【51CTO.comオリジナル記事】機械学習は誕生以来、多くの分野で応用されてきましたが、現時点では...

コミュニティ解体|新規ユーザー獲得からユーザー転換までのプロセスをゲームグループ視点で見る

コミュニティとは何ですか?コミュニティをどのように定義しますか?コミュニティを形成するにはどうすれば...

evlgaming-1 メモリ KVM/6.5 USD/月/サポート Windows/カンザス

Evlgaming には、2010 年に登録され、カンザス州にオフィスを構える、flamevps と...

新しいサイトのスナップショットとソリューションの組み込みに影響する要因の解釈

新しいウェブサイトがオンラインになるたびに、ウェブマスターはこのウェブサイトに非常に失望しています。...