当前位置:网站首页>Microservices: how to solve the problem of link tracing

Microservices: how to solve the problem of link tracing

2020-11-06 01:32:15 itread01

### One 、 Link tracking ​ Microservice architecture is to divide a single application into a variety of small, wired Services , Each service completes a single business function , They are independent and decoupled from each other , Each service can evolve independently . Compared with the traditional single service , Microservices are isolated 、 Technology heterogeneity 、 Extensible suite and simplified deployment . ​ The same , Microservice architecture brings many benefits at the same time , It also adds a lot of complexity to the system . It's a decentralized service , Usually deployed in different data centers 、 On clusters of different servers . and , The same microservice system may be made up of different teams 、 Developed in different languages . Usually, an application consists of multiple microservices , The data interaction between micro services needs to be completed by remote call , So in a system of many micro Services , Requests need to flow between services , The call link is complex , Once there is a problem , It is very difficult to locate problems and trace anomalies . ​ Link tracking system is to solve the above problems , It is used to track the full call link for each request , Record the task name of the call from the start of the request to the end of the request 、 Time consuming 、 Tag data and log information , And through visual interface analysis and display , To help technicians locate abnormal services accurately 、 Find performance bottlenecks 、 Sort out the call links and estimate the system capacity . ​ The theoretical models of link tracking system are almost all used for reference Google A paper on ”Dapper, a Large-Scale Distributed Systems Tracing Infrastructure”, Typical products are Uber jaeger、Twitter zipkin、 Taobao eagle eye, etc . These products are implemented in different ways , But there are generally three core steps :** Data collection 、 Data storage and query display **. ​ Link tracking system first step , And the most basic work is data collection . In the process , Link tracking system needs to intrude user code for embedding , Used to collect tracking data . But because of different link tracking systems API Not compatible with each other , So there are different ways to write embedded code , It leads to users need to make great changes when switching different link tracking products . In order to solve such problems , So it was born OpenTracing Regulate , Designed to unify link tracking systems API. ### Two 、OpenTracing Regulate ​ OpenTracing It's a decentralized tracking protocol , It's not about the platform or the language , With a unified interface specification , Easy access to different distributed tracking systems . ​ OpenTracing The semantic specification can be found in :https://github.com/opentracing/specification/blob/master/specification.md #### 2.1 Data model (Data Model) ​ OpenTracing The data models defined in the semantic specification are Trace、Sapn as well as Reference. ##### 2.1.1 Trace ​ Trace Represents a complete tracking link , for example : The execution of a transaction or process . One Trace It's made up of one or more Span A directed acyclic graph (DAG). The figure below shows an example by 8 One Span Composed of Trace: ``` [Span A] ←←←(the root span) | +------+------+ | | [Span B] [Span C] ←←←(Span C is a `ChildOf` Span A) | | [Span D] +---+-------+ | | [Span E] [Span F] >>> [Span G] >>> [Span H] ↑ ↑ ↑ (Span G `FollowsFrom` Span F) ``` According to the time axis way, it is more intuitive to show the Trace: ``` ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time [Span A···················································] [Span B··············································] [Span D··········································] [Span C········································] [Span E·······] [Span F··] [Span G··] [Span H··] ``` ##### 2.1.2 Span ​ Span Represents an independent unit of work , It's the basic component of a tracking link . for example : once RPC call 、 A function call or Http Ask for . Every Span Encapsulates the following states : - Operation name Used to indicate that Span Task name of . for example : One RPC Method name , A function name , Or the name of a subtask in a large task . - Start timestamp Task start time . - End timestamp . Mission end time . Through Span End timestamp and start timestamp of , You can figure out what to do Span The overall time taken . - A set of Span Label Every one of them Span A tag is a key value pair . The key must be a string , Values can be strings 、 Brin or numeric type . Common label keys can refer to :https://github.com/opentracing/specification/blob/master/semantic_conventions.md - A set of Span The Journal Each one Span The log consists of a key value pair and a corresponding timestamp . The key must be a string , Values can be of any type . Common log key reference :https://github.com/opentracing/specification/blob/master/semantic_conventions.md ##### 2.1.3 Reference ​ One Span Can be associated with one or more Span There is a causal relationship , This relationship is called Reference.OpenTracing Two relationships are currently defined :ChildOf( Father and son ) Relationship and FollowsFrom( Follow ) Relationship . - ChildOf Relationship Father Span According to laizi Span The results of the implementation of , Now Span To the father Span Of Reference The relationship is ChildOf. For example, for once RPC call , Server side Span( Son Span) Calling with the client Span( Father Span) Namely ChildOf Relationship . - FollowsFrom Relationship Father Span The implementation of the law is not dependent on laizi Span The results of the implementation of , Now Span To the father Span Of Reference The relationship is FollowFrom.FollowFrom Often used to indicate an asynchronous call , For example, in the message queue Consumer Span And Producer Span Relationship between . #### 2.2 Application interface (API) ##### 2.2.1 Tracer ​ Tracer Interfaces are used to create Span、 Cross program injection and extraction of data . It usually has the following functions : - Start a new span Create and launch a new Span. - Inject Will SpanContext Injection carrier (Carrier). - Extract From the carrier (Carrier) Extract from SpanContext. ##### 2.2.2 Span - Retrieve a SpanContext return Span Corresponding SpanContext. - Overwrite the operation name Update operation name . - Set a span tag Set Span Label information . - Log structured data Record structured data . - Set a baggage item baggage item It's a string type key value pair , It corresponds to something Span, Follow me Trace Spread together . Since each key value is copied to each local and remote child Span, This can lead to huge networks and CPU Spending . - Get a baggage item Get baggage item Value . - Finish End with a Span. ##### 2.2.3 Span Context ​ Used to carry data across service boundaries , Include trace ID、Span ID And need to spread downstream Span Of baggage Information . stay OpenTracing in , Mandatory requirements SpanContext The example is immutable , To avoid being in Span Complex lifecycle issues occur when completing and referencing . ##### 2.2.4 NoopTracer ​ All right OpenTracing API The realization of , Some form of NoopTracer, Used for marking control OpenTracing Or inject something harmless to the test . ## 3、 ... and 、Jaeger ​ Jaeger yes Uber Open source distributed tracking system , Its application interface follows exactly OpenTracing Regulate .jaeger It uses go Language writing , It has the characteristics of cross platform and cross language , Provides a variety of language client call interface , for example c++、java、go、python、ruby、php、nodejs etc. . Project address :https://github.com/jaegertracing #### 3.1 Jaeger Components [ Failed to save the image in the external chain , Source station may have anti-theft chain mechanism , It is recommended to save the pictures and upload them directly (img-miLIEWHv-1604561903414)(https://i.loli.net/2020/04/13/bvTxdUkBRuawY1F.png)] - **jaeger-client** jaeger Client code library for , It realizes OpenTracing Agreement . When our application assembles it , Responsible for collecting information , And send it to jaeger-agent.** This is the only place where we need to write code **. - **jaeger-agent** Responsible for receiving from jaeger-client From Trace/Span Information , And upload them to jaeger-collector. - **jaeger-collector** Responsible for receiving from jaeger-agent From Trace/Span Information , And after verification 、 Index and so on , Then write it to the back-end storage . - **data store** Responsible for data storage .Jaeger Data storage is a pluggable component , Currently support Cassandra、ElasticSearch and Kafka. - **jaeger-query & ui** Responsible for data inquiry , And display the query results through the front-end interface . [ Failed to save the image in the external chain , Source station may have anti-theft chain mechanism , It is recommended to save the pictures and upload them directly (img-ogkrm3Hb-1604561903417)(https://i.loli.net/2020/04/13/UMoHYtlX1ydsx5Q.jpg)] #### 3.2 Quick start ​ Jaeger The official provided all-in-one The image , Easy and fast testing : ``` # Pull the image $docker pull jaegertracing/all-in-one:latest # Execute image $docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ -p 5775:5775/udp \ -p 6831:6831/udp \ -p 6832:6832/udp \ -p 5778:5778 \ -p 14268:14268 \ -p 9411:9411 \ -p 16686:16686 \ jaegertracing/all-in-one:latest ``` ​ Through all-in-one The image starts , We found that Jaeger Occupied a lot of ports . Here are the port instructions : | Port | Agreement | Module | function | | ------ | ----- | --------- | ------ | | 5775 | UDP | agent | Receive compressed format Zipkin thrift Information | | 6831 | UDP | agent | Receive compressed format Jaeger thrift Information | | 6832 | UDP | agent | Receiving binary format Jaeger thrift Information | | 5778 | HTTP | agent | Service configuration 、 Sampling strategy port | | 14268 | HTTP | collector | To receive directly from the client Jaeger thrift Information | | 9411 | HTTP | collector | receive Zipkin Transmitted json perhaps thrift Information | | 16686 | HTTP | query | Browser display port | ​ After starting , We can visit [http://localhost:16686](http://localhost:16686) , View and query the collected data in the browser . ​ As approved all-in-one The data collected by the mapping method are stored in docker in , Can't persist , So it can only be used in development or test environment , Cannot be used in production environment . According to the actual situation in the production environment , Deploy each component separately . ## Four 、Jaeger Application in business code ​ The system uses Jaeger Very simple , Just insert a small amount of code into the original program . The following code simulates a query of user account balances , The business scenario of executing the deduction : #### 4.1 initialization jaeger Function ​ The arguments are mainly arranged according to actual needs , For example, the service name 、 Sampling mode 、 Sampling ratio, etc . ``` func initJaeger() (tracer opentracing.Tracer, closer io.Closer, err error) { // Construct configuration information cfg := &config.Configuration{ // Set the service name ServiceName: "ServiceAmount", // Set sampling arguments Sampler: &config.SamplerConfig{ Type: "const", // Full sampling mode Param: 1, // Open state }, } // Generate a new tracer tracer, closer, err = cfg.NewTracer() if err == nil { // Set tracer Is a global singleton opentracing.SetGlobalTracer(tracer) } return } ``` #### 4.2 Check user balance function ​ Used to detect user balance , Simulate a subtask Span. ``` func CheckBalance(request string, ctx context.Context) { // Set up the son span span, _ := opentracing.StartSpanFromContext(ctx, "CheckBalance") // The simulation system performs a series of operations , Time consuming 1/3 second time.Sleep(time.Second / 3) // Example : Put the information that needs to be tracked into tag span.SetTag("request", request) span.SetTag("reply", "CheckBalance reply") // End the present span span.Finish() log.Println("CheckBalance is done") } ``` #### 4.3 Debit function from user account ​ Deduct money from a user's account , Simulate a subtask span. ``` func Reduction(request string, ctx context.Context) { // Set up the son span span, _ := opentracing.StartSpanFromContext(ctx, "Reduction") // The simulation system performs a series of operations , Time consuming 1/2 second time.Sleep(time.Second / 2) // Example : Put the information that needs to be tracked into tag span.SetTag("request", request) span.SetTag("reply", "Reduction reply") // End the present span span.Finish() log.Println("Reduction is done") } ``` #### 4.4 The main function ​ initialization jaeger The environment , Generate tracer, Establish a father span, And call to query balance and deduction two sub tasks span. ``` package main import ( "context" "fmt" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go/config" "io" "log" "time" ) func main() { // initialization jaeger, Create a new tracer tracer, closer, err := initJaeger() if err != nil { panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) } defer closer.Close() // Create a new span, As a father span, Start the billing process span := tracer.StartSpan("CalculateFee") // Generative father span Of context ctx := opentracing.ContextWithSpan(context.Background(), span) // Example : Set a span Label information span.SetTag("db.instance", "customers") // Example : Output a span Log information span.LogKV("event", "timed out") // Will father span Of context As an argument , Call detection user balance function CheckBalance("CheckBalance request", ctx) // Will father span Of context As an argument , Call the deduction function Reduction("Reduction request", ctx) // The father ends span span.Finish() } ``` ## 5、 ... and 、Jaeger stay gRPC Applications in microservices ​ We still simulated a query of user account balances , The business scenario of executing the deduction , And the query user account balance and the implementation of the deduction function into gRPC Microservices : #### 5.1 gRPC Server End code main.go: ​ The code uses a third-party dependency Library github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing, The dependency library will OpenTracing Package as generic gRPC Intermediary software , And through gRPC Interceptors are embedded seamlessly gRPC In service . ``` package main import ( "fmt" "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go/config" "google.golang.org/grpc" "google.golang.org/grpc/reflection" "grpc-jaeger-server/account" "io" "log" "net" ) // initialization jaeger func initJaeger() (tracer opentracing.Tracer, closer io.Closer, err error) { // Construct configuration information cfg := &config.Configuration{ // Set the service name ServiceName: "ServiceAmount", // Set sampling arguments Sampler: &config.SamplerConfig{ Type: "const", // Full sampling mode Param: 1, // Turn on full sampling mode }, } // Generate a new tracer tracer, closer, err = cfg.NewTracer() if err == nil { // Set tracer Is a global singleton opentracing.SetGlobalTracer(tracer) } return } func main() { // initialization jaeger, Create a new tracer tracer, closer, err := initJaeger() if err != nil { panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) } defer closer.Close() log.Println("succeed to init jaeger") // Register gRPC account Service server := grpc.NewServer(grpc.UnaryInterceptor(grpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(tracer)))) account.RegisterAccountServer(server, &AccountServer{}) reflection.Register(server) log.Println("succeed to register account service") // Monitoring gRPC account Service port listener, err := net.Listen("tcp", ":8080") if err != nil { log.Println(err) return } log.Println("starting register account service") // Turn on gRpc account Service if err := server.Serve(listener); err != nil { log.Println(err) return } } ``` Billing microservices accountsever.go: ``` package main import ( "github.com/opentracing/opentracing-go" "golang.org/x/net/context" "grpc-jaeger-server/account" "time" ) // Billing services type AccountServer struct{} // Detect user balance microservices , The simulator span Mission func (s *AccountServer) CheckBalance(ctx context.Context, request *account.CheckBalanceRequest) (response *account.CheckBalanceResponse, err error) { response = &account.CheckBalanceResponse{ Reply: "CheckBalance Reply", // The result of the treatment } // Set up the son span span, _ := opentracing.StartSpanFromContext(ctx, "CheckBalance") // The simulation system performs a series of operations , Time consuming 1/3 second time.Sleep(time.Second / 3) // Put the information that needs to be tracked into tag span.SetTag("request", request) span.SetTag("reply", response) // End the present span span.Finish() return response, err } // The micro service is deducted from the user's account , The simulator span Mission func (s *AccountServer) Reduction(ctx context.Context, request *account.ReductionRequest) (response *account.ReductionResponse, err error) { response = &account.ReductionResponse{ Reply: "Reduction Reply", // The result of the treatment } // Set up the son span span, _ := opentracing.StartSpanFromContext(ctx, "Reduction") // The simulation system performs a series of operations , Time consuming 1/3 second time.Sleep(time.Second / 3) // Put the information that needs to be tracked into tag span.SetTag("request", request) span.SetTag("reply", response) // End the present span span.Finish() return response, err } ``` #### 5.2 gRPC Client End code main.go: ``` package main import ( "context" "fmt" "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go/config" "google.golang.org/grpc" "grpc-jaeger-client/account" "io" "log" ) // initialization jaeger func initJaeger() (tracer opentracing.Tracer, closer io.Closer, err error) { // Construct configuration information cfg := &config.Configuration{ // Set the service name ServiceName: "ServiceAmount", // Set sampling arguments Sampler: &config.SamplerConfig{ Type: "const", // Full sampling mode Param: 1, // Turn on full sampling mode }, } // Generate a new tracer tracer, closer, err = cfg.NewTracer() if err == nil { // Set tracer Is a global singleton opentracing.SetGlobalTracer(tracer) } return } func main() { // initialization jaeger, Create a new tracer tracer, closer, err := initJaeger() if err != nil { panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) } defer closer.Close() log.Println("succeed to init jaeger") // Create a new span, As a father span span := tracer.StartSpan("CalculateFee") // Close when function returns span defer span.Finish() // Generate span Of context ctx := opentracing.ContextWithSpan(context.Background(), span) // Connect gRPC server conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure(), grpc.WithUnaryInterceptor(grpc_opentracing.UnaryClientInterceptor(grpc_opentracing.WithTracer(tracer), ))) if err != nil { log.Println(err) return } // establish gRPC Billing service client client := account.NewAccountClient(conn) // Will father span Of context As an argument , Call to check the balance of the user gRPC Microservices checkBalanceResponse, err := client.CheckBalance(ctx, &account.CheckBalanceRequest{ Account: "user account", }) if err != nil { log.Println(err) return } log.Println(checkBalanceResponse) // Will father span Of context As an argument , Call the withholding person gRPC Microservices reductionResponse, err := client.Reduction(ctx, &account.ReductionRequest{ Account: "user account", Amount: 1, }) if err != nil { log.Println(err) return } log.Println(reductionResponse) } ``` notes : All source codes of this paper are located in :https://github.com/wangshizebin/micro-service The development tool used in this paper is :[goland](http://www.sousou88.com/software/2072802.html) From [ Whoosh download ](http://www.sousou8

版权声明
本文为[itread01]所创,转载请带上原文链接,感谢