Well, Go Design Pattern Combat series, a golang series of design pattern business real use.
preface
This series focuses on how to use design patterns in our real business scenarios.
This series of articles mainly adopts the following structure:
- What is “XX design pattern”?
- What real business scenarios can use the “XX design pattern”?
- How to use “XX design pattern”?
This article focuses on how “composite pattern” can be used in real business scenarios combined with the natural concurrency features of the Go language.
Previous articles of the code components | Go design patterns of actual combat, have introduced the concept of “portfolio model”, and its use in business. Today we are upgrading the “composite mode” to “concurrent composite mode” in combination with the natural concurrency features of the Go language.
Let’s review the simple “combination mode” of knowledge, can view the article in detail the code components | Go design patterns of actual combat”
What is “concurrent composition mode”?
The concept of composite patterns:
A hierarchical object consists of a series of objects that have parent-child relationships through a tree structure.
Concepts of concurrent composition patterns:
A hierarchical object consists of a series of objects that have a parent-child relationship through a tree structure. The children can be executed sequentially or concurrently
Advantages of concurrent composite mode:
- Serial services (blocking parts, such as network IO) can be executed concurrently to take advantage of multiple cores to improve performance.
What real business scenarios can use the “concurrent composition pattern”?
Using the “order settlement page” in “Combination Mode” as an example, let’s continue to look at the order settlement page of a certain east:
From the display form of the page, we can see:
- A page consists of several modules, such as:
- Address module: obtain user address data
- Payment method module: Get the list of payment methods
- Store module: obtain information about stores, shopping cart selected goods and so on
- Invoice module: Get the list of invoice types
- Coupon module: get user coupon list
- A bean module: obtain user integral information
- Gift card module: Obtain the gift card list
- Order detail amount module: Obtain order amount information
- A single module can consist of multiple sub-modules
- The store module is composed of the following modules:
- Commodity module: get the information of the goods selected in the shopping cart
- After-sale module: obtain after-sale information of goods
- Preferential module: obtain the information of preferential activities in which the product participates
- Logistics module: get the list of distribution methods supported by goods
- The store module is composed of the following modules:
Execute the process according to the business logic of “composite mode” :
However, it is clear that some modules do not depend on each other and involve blocking operations such as remote service calls, such as:
- When the address module calls the address service to obtain the user’s address data.
- Payment mode module can also read Redis to get payment mode list data and so on.
So: some modules can actually be executed concurrently.
If we change the above non-dependent module to concurrent execution, we get the following execution flow:
How to use “concurrent combination mode”?
Modeling process of “concurrent combination model” entirely can refer to previous articles of the code components | Go design patterns of actual combat “, we just need to focus on here.
The core of the concurrent composite mode is the Component interface. Let’s look at the Component interface of the composite mode (optimized in the previous article to further encapsulate the BusinessLogicDo method) :
// Component interface
type Component interface {
// Add a child componentMount(c Component, components ... Component) error// Remove a child component
Remove(c Component) error
// Execute the current component business and execute the child components
// CTX business context
// currentConponent Indicates the current component
Do(ctx *Context, currentConponent Component) error
// Execute the current component business logic
BusinessLogicDo(ctx *Context) error
// Execute the child component
ChildsDo(ctx *Context) error
}
Copy the code
Take a look at the Component interface for concurrent composite mode, which looks like this:
// Component interface
type Component interface {
// Add a child componentMount(c Component, components ... Component) error// Remove a child component
Remove(c Component) error
// Execute the current component business: 'BusinessLogicDo' and the child component: 'ChildsDo'
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
// Difference 1: The WaitGroup object parameter is added to wait for the child component's execution to complete.
Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) error
// Execute the current component business logic
// resChan writes back to the channel of the current component business execution result
// Difference 2: a channel parameter is added. The purpose is to introduce a timeout mechanism when the concurrent component executes logic. A channel is required to receive the execution result of the component
BusinessLogicDo(resChan chan interface{}) error
// Execute the child component
ChildsDo(ctx *Context) error
}
Copy the code
Let’s take a closer look at the following points when introducing concurrency as opposed to “composite mode” :
- And send child components need to set timeout: prevent child components execution time is too long, solution keyword
context.WithTimeout
- Distinguish between common components and concurrent components: composite base components, packaged as concurrent base components
- A parent component that owns and issues child components needs to wait for and issues child components to complete execution (including timeouts), solution keyword
sync.WaitGroup
- If the sub-components are sent to execute their own service logic, timeout detection is required: Prevents the sub-components from executing their own service logic for a long time
select
and<-ctx.Done()
The first point is that the timeout is required to send the child component
// Context Business Context
type Context struct {
// context.WithTimeout Derived subcontext
TimeoutCtx context.Context
// The timeout function
context.CancelFunc
}
Copy the code
Second point: Distinguish between normal components and concurrent components
Add a new concurrency BaseComponent structure BaseConcurrencyComponent and synthesize and reuse the BaseComponent BaseComponent in composite mode as follows:
BaseConcurrencyComponent Concurrency base component
type BaseConcurrencyComponent struct {
// Composite base components
BaseComponent
// Whether the current component has and sends child components
HasChildConcurrencyComponents bool
// And send a list of child components
ChildConcurrencyComponents []Component
/ / wg object
*sync.WaitGroup
// Current component business execution result channel
logicResChan chan interface{}
// Error message during current component execution
Err error
}
Copy the code
Third: A parent that owns and issues child components needs to wait and issue child components to complete execution (including timeouts)
Modify the ChildsDo method in “composite mode” to support concurrent execution of subcomponents. The main changes and implementation are as follows:
- through
go
Keyword execution subcomponent - through
*WaitGroup.Wait()
Wait for the results of child component execution
// ChildsDo executes the child component
func (bc *BaseConcurrencyComponent) ChildsDo(ctx *Context) (err error) {
if bc.WaitGroup == nil {
bc.WaitGroup = &sync.WaitGroup{}
}
// Execute and send the child component
for _, childComponent := range bc.ChildConcurrencyComponents {
bc.WaitGroup.Add(1)
go childComponent.Do(ctx, childComponent, bc.WaitGroup)
}
// Execute the child component
for _, childComponent := range bc.ChildComponents {
if err = childComponent.Do(ctx, childComponent, nil); err ! =nil {
return err
}
}
if bc.HasChildConcurrencyComponents {
// Wait for the result of concurrent component execution
bc.WaitGroup.Wait()
}
return
}
Copy the code
Fourth point: sending a child component to execute its own business logic requires timeout detection
The channel returned by the Done() subcontext derived from the select context.withTimeout () keyword, which will be closed if timeout occurs. The specific implementation code is as follows:
// Do executes the child component
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
func (bc *BaseConcurrencyComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
defer wg.Done()
// Initialize and issue the child component channel
if bc.logicResChan == nil {
bc.logicResChan = make(chan interface{}, 1)}go currentConponent.BusinessLogicDo(bc.logicResChan)
select {
// Wait for the service execution result
case <-bc.logicResChan:
// Service execution result
fmt.Println(runFuncName(), "bc.BusinessLogicDo wait.done...")
break
// Wait for timeout
case <-ctx.TimeoutCtx.Done():
// Exit due to timeout
fmt.Println(runFuncName(), "bc.BusinessLogicDo timeout...")
bc.Err = ErrConcurrencyComponentTimeout
break
}
// Execute the child component
err = currentConponent.ChildsDo(ctx)
return
}
Copy the code
The demo code
package main
import (
"context"
"errors"
"fmt"
"net/http"
"reflect"
"sync"
"time"
)
//------------------------------------------------------------
//Go design mode combat series
// Combination mode
//@auhtor TIGERB<https://github.com/TIGERB>
//------------------------------------------------------------
//example:
// Create a root component
// If the child component has a concurrent component, the parent component must be a concurrent component
// type RootComponent struct {
// BaseConcurrencyComponent
// }
//
// func (bc *RootComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // do nothing
// return
// }
//
// Create a concurrent component
// type DemoConcurrenyComponent struct {
// BaseConcurrencyComponent
// }
//
// func (bc *DemoConcurrenyComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // Concurrent component business logic is populated here
// return
// }
//
// Create a normal component
// type DemoComponent struct {
// BaseComponent
// }
//
// func (bc *DemoComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // Common component business logic to populate here
// return
// }
//
// // Common Components
// root.Mount(
// &DemoComponent{},
// )
//
// // Concurrent Component
// root := &RootComponent{}
// root.MountConcurrency(
// &DemoConcurrenyComponent{},
// )
//
// // Initializes the service context and sets the timeout period
// ctx := GetContext(5 * time.Second)
// defer ctx.CancelFunc()
// // Starts executing child components
// root.ChildsDo(ctx)
var (
/ / ErrConcurrencyComponentTimeout concurrent component business timeout
ErrConcurrencyComponentTimeout = errors.New("Concurrency Component Timeout"))// Context Business Context
type Context struct {
// context.WithTimeout Derived subcontext
TimeoutCtx context.Context
// The timeout function
context.CancelFunc
}
// GetContext gets the business context instance
// d Timeout period
func GetContext(d time.Duration) *Context {
c := &Context{}
c.TimeoutCtx, c.CancelFunc = context.WithTimeout(context.Background(), d)
return c
}
// Component interface
type Component interface {
// Add a child componentMount(c Component, components ... Component) error// Remove a child component
Remove(c Component) error
// Execute the current component business: 'BusinessLogicDo' and the child component: 'ChildsDo'
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) error
// Execute the current component business logic
// resChan writes back to the channel of the current component business execution result
BusinessLogicDo(resChan chan interface{}) error
// Execute the child component
ChildsDo(ctx *Context) error
}
// BaseComponent BaseComponent
// Implement Add: Add a child component
// Implement Remove: Removes a child component
type BaseComponent struct {
// List of child components
ChildComponents []Component
}
// Mount mounts a child component
func (bc *BaseComponent) Mount(c Component, components ... Component) (err error) {
bc.ChildComponents = append(bc.ChildComponents, c)
if len(components) == 0 {
return
}
bc.ChildComponents = append(bc.ChildComponents, components...)
return
}
// Remove Removes a child component
func (bc *BaseComponent) Remove(c Component) (err error) {
if len(bc.ChildComponents) == 0 {
return
}
for k, childComponent := range bc.ChildComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
return
}
// Do executes the child component
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
func (bc *BaseComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
Execute the current component business code
err = currentConponent.BusinessLogicDo(nil)
iferr ! =nil {
return err
}
// Execute the child component
return currentConponent.ChildsDo(ctx)
}
// BusinessLogicDo where the current component business logic code is populated
func (bc *BaseComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// do nothing
return
}
// ChildsDo executes the child component
func (bc *BaseComponent) ChildsDo(ctx *Context) (err error) {
// Execute the child component
for _, childComponent := range bc.ChildComponents {
if err = childComponent.Do(ctx, childComponent, nil); err ! =nil {
return err
}
}
return
}
BaseConcurrencyComponent Concurrency base component
type BaseConcurrencyComponent struct {
// Composite base components
BaseComponent
// Whether the current component has and sends child components
HasChildConcurrencyComponents bool
// And send a list of child components
ChildConcurrencyComponents []Component
/ / wg object
*sync.WaitGroup
// Current component business execution result channel
logicResChan chan interface{}
// Error message during current component execution
Err error
}
// Remove Removes a child component
func (bc *BaseConcurrencyComponent) Remove(c Component) (err error) {
if len(bc.ChildComponents) == 0 {
return
}
for k, childComponent := range bc.ChildComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
for k, childComponent := range bc.ChildConcurrencyComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildConcurrencyComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
return
}
// MountConcurrency mounts and sends a child component
func (bc *BaseConcurrencyComponent) MountConcurrency(c Component, components ... Component) (err error) {
bc.HasChildConcurrencyComponents = true
bc.ChildConcurrencyComponents = append(bc.ChildConcurrencyComponents, c)
if len(components) == 0 {
return
}
bc.ChildConcurrencyComponents = append(bc.ChildConcurrencyComponents, components...)
return
}
// ChildsDo executes the child component
func (bc *BaseConcurrencyComponent) ChildsDo(ctx *Context) (err error) {
if bc.WaitGroup == nil {
bc.WaitGroup = &sync.WaitGroup{}
}
// Execute and send the child component
for _, childComponent := range bc.ChildConcurrencyComponents {
bc.WaitGroup.Add(1)
go childComponent.Do(ctx, childComponent, bc.WaitGroup)
}
// Execute the child component
for _, childComponent := range bc.ChildComponents {
if err = childComponent.Do(ctx, childComponent, nil); err ! =nil {
return err
}
}
if bc.HasChildConcurrencyComponents {
// Wait for the result of concurrent component execution
bc.WaitGroup.Wait()
}
return
}
// Do executes the child component
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
func (bc *BaseConcurrencyComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
defer wg.Done()
// Initialize and issue the child component channel
if bc.logicResChan == nil {
bc.logicResChan = make(chan interface{}, 1)}go currentConponent.BusinessLogicDo(bc.logicResChan)
select {
// Wait for the service execution result
case <-bc.logicResChan:
// Service execution result
fmt.Println(runFuncName(), "bc.BusinessLogicDo wait.done...")
break
// Wait for timeout
case <-ctx.TimeoutCtx.Done():
// Exit due to timeout
fmt.Println(runFuncName(), "bc.BusinessLogicDo timeout...")
bc.Err = ErrConcurrencyComponentTimeout
break
}
// Execute the child component
err = currentConponent.ChildsDo(ctx)
return
}
// CheckoutPageComponent Order settlement page component
type CheckoutPageComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// BusinessLogicDo where the current component business logic code is populated
func (bc *CheckoutPageComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Order settlement page component...")
return
}
// AddressComponent AddressComponent
type AddressComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *AddressComponent) BusinessLogicDo(resChan chan interface{}) error {
fmt.Println(runFuncName(), "Address component...")
fmt.Println(runFuncName(), "Get address info ing...")
// Simulate a remote invocation of the address service
http.Get("http://example.com/")
resChan <- struct{} {}// Write the service execution result
fmt.Println(runFuncName(), "Get address info done...")
return nil
}
// PayMethodComponent Payment method component
type PayMethodComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *PayMethodComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Payment method component...")
fmt.Println(runFuncName(), "Get payment method ing...")
// Simulate remote invocation of address service omitted
resChan <- struct{}{}
fmt.Println(runFuncName(), "Get payment method done...")
return nil
}
// StoreComponent Store components
type StoreComponent struct {
// Composite base components
BaseComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *StoreComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Store components...")
return
}
// SkuComponent Commodity component
type SkuComponent struct {
// Composite base components
BaseComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *SkuComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Commodity components...")
return
}
// PromotionComponent Promo component
type PromotionComponent struct {
// Composite base components
BaseComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *PromotionComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Promo component...")
return
}
// ExpressComponent Indicates the logistics component
type ExpressComponent struct {
// Composite base components
BaseComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *ExpressComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Logistics components...")
return
}
// AftersaleComponent
type AftersaleComponent struct {
// Composite base components
BaseComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *AftersaleComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Aftermarket components...")
return
}
// InvoiceComponent invoicing component
type InvoiceComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *InvoiceComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Invoice component...")
fmt.Println(runFuncName(), "Get invoice information ing...")
// Simulate remote invocation of address service omitted
resChan <- struct{} {}// Write the service execution result
fmt.Println(runFuncName(), "Get invoice information done...")
return
}
// CouponComponent CouponComponent
type CouponComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *CouponComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Coupon kit...")
fmt.Println(runFuncName(), "Get the best coupon ing...")
// Simulate a remote call to the coupon service
http.Get("http://example.com/")
// Write the service execution result
resChan <- struct{}{}
fmt.Println(runFuncName(), Get the best coupon done...)
return
}
// GiftCardComponent GiftCardComponent
type GiftCardComponent struct {
// Composite base components
BaseConcurrencyComponent
}
// Where BusinessLogicDo concurrency components actually populate the business logic
func (bc *GiftCardComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Gift card components...")
fmt.Println(runFuncName(), "Get gift card info ing...")
// Simulate remote invocation of address service omitted
resChan <- struct{} {}// Write the service execution result
fmt.Println(runFuncName(), "Get gift card info done...")
return
}
// OrderComponent Order amount details component
type OrderComponent struct {
// Composite base components
BaseComponent
}
// BusinessLogicDo where the current component business logic code is populated
func (bc *OrderComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// The business logic of the current component is written here
fmt.Println(runFuncName(), "Order amount details component...")
return
}
/ / the Demo sample
func Demo(a) {
// Initialize the large component of the order settlement page
checkoutPage := &CheckoutPageComponent{}
// Mount child components
storeComponent := &StoreComponent{}
skuComponent := &SkuComponent{}
skuComponent.Mount(
&PromotionComponent{},
&AftersaleComponent{},
)
storeComponent.Mount(
skuComponent,
&ExpressComponent{},
)
// Mount components --
// Common components
checkoutPage.Mount(
storeComponent,
&OrderComponent{},
)
// Concurrent components
checkoutPage.MountConcurrency(
&AddressComponent{},
&PayMethodComponent{},
&InvoiceComponent{},
&CouponComponent{},
&GiftCardComponent{},
)
// Initialize the business context and set the timeout
ctx := GetContext(5 * time.Second)
defer ctx.CancelFunc()
// Start building page component data
checkoutPage.ChildsDo(ctx)
}
func main(a) {
runtime.GOMAXPROCS(runtime.NumCPU() - 1)
Demo()
}
// Get the name of the running function
func runFuncName(a) string {
pc := make([]uintptr.1)
runtime.Callers(2, pc)
f := runtime.FuncForPC(pc[0])
return f.Name()
return ""
}
Copy the code
Code running results:
Running] go run ".. / easy - tips/go/patterns/composite/concurrency/composite - concurrency. Go "main. (* StoreComponent). BusinessLogicDo store component... Main.(*SkuComponent).BusinessLogicDo commodity component... Main.(*PromotionComponent).BusinessLogicDo offers component... Main.(*AftersaleComponent).BusinessLogicDo aftermarket components... Main.(*ExpressComponent).BusinessLogicDo Logistics component... Main.(*OrderComponent).BusinessLogicDo Order Amount details component... Main.(*PayMethodComponent).BusinessLogicDo Payment method component... Main.(*PayMethodComponent).BusinessLogicDo Gets the payment method ing... Main.(*InvoiceComponent).BusinessLogicDo Invoicing component... Main.(*InvoiceComponent).BusinessLogicDo get invoice information ing... Main.(*GiftCardComponent).BusinessLogicDo GiftCardComponent... Main.(*GiftCardComponent).BusinessLogicDo Get gift card information ing... Main.(*CouponComponent).BusinessLogicDo CouponComponent... Main.(*CouponComponent).BusinessLogicDo Get invoice information ing... Main.(*AddressComponent).BusinessLogicDo AddressComponent... Main.(*AddressComponent).BusinessLogicDo Get address information ing... Main.(*InvoiceComponent).BusinessLogicDo... main.(*BaseConcurrencyComponent).Do bc.BusinessLogicDo wait.done... main.(*BaseConcurrencyComponent).Do bc.BusinessLogicDo wait.done... Main.(*PayMethodComponent).BusinessLogicDo Get paid method done... Main.(*AddressComponent).BusinessLogicDo Get address done... main.(*BaseConcurrencyComponent).Do bc.BusinessLogicDo wait.done... Main.(*CouponComponent).BusinessLogicDo Get invoice info done... main.(*BaseConcurrencyComponent).Do bc.BusinessLogicDo wait.done... Main.(*GiftCardComponent).BusinessLogicDo Get gift card info done... main.(*BaseConcurrencyComponent).Do bc.BusinessLogicDo wait.done...Copy the code
Benchmark comparison of Composite mode and Concurrent composite mode
Benchmark code:
package composite
import (
"easy-tips/go/patterns/composite/concurrency"
"easy-tips/go/patterns/composite/normal"
"runtime"
"testing"
)
// go test -benchmem -run=^$ easy-tips/go/patterns/composite -bench . -v -count=1 --benchtime 20s
func Benchmark_Normal(b *testing.B) {
b.SetParallelism(runtime.NumCPU())
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
normal.Demo()
}
})
}
func Benchmark_Concurrency(b *testing.B) {
b.SetParallelism(runtime.NumCPU())
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
concurrency.Demo()
}
})
}
Copy the code
Local machine Benchmark comparison test results:
(TIGERB) 🤔 ➜ composite git: (master) ✗ go test - benchmem - run = ^ $easy - tips/go/patterns/composite - bench. - v - count = 1 --benchtime 20s goos: darwin goarch: amd64 pkg: easy-tips/go/patterns/composite Benchmark_Normal-4 376 56666895 ns/op 35339 B/op 286 allocs/op Benchmark_Concurrency-4 32669301 ns/op 36445 B / 715 op 299 allocs/op PASS ok easy tips/go/patterns/composite 68.835 sCopy the code
Benchmark_Concurrency-4 has an average run time of 32669301 ns compared to Benchmark_Normal’s 56666895 ns.
conclusion
The “Concurrent composite pattern” is a “new pattern” formed by appropriate encapsulation of a specific design pattern combined with the inherent concurrency characteristics of the Go language.
Appendix basic code templates and usage instructions for concurrent composition patterns
//------------------------------------------------------------
//Go design mode combat series
// Combination mode
//@auhtor TIGERB<https://github.com/TIGERB>
//------------------------------------------------------------
//example:
// Create a root component
// If the child component has a concurrent component, the parent component must be a concurrent component
// type RootComponent struct {
// BaseConcurrencyComponent
// }
//
// func (bc *RootComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // do nothing
// return
// }
//
// Create a concurrent component
// type DemoConcurrenyComponent struct {
// BaseConcurrencyComponent
// }
//
// func (bc *DemoConcurrenyComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // Concurrent component business logic is populated here
// return
// }
//
// Create a normal component
// type DemoComponent struct {
// BaseComponent
// }
//
// func (bc *DemoComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// // Common component business logic to populate here
// return
// }
//
// // Common Components
// root.Mount(
// &DemoComponent{},
// )
//
// // Concurrent Component
// root := &RootComponent{}
// root.MountConcurrency(
// &DemoConcurrenyComponent{},
// )
//
// // Initializes the service context and sets the timeout period
// ctx := GetContext(5 * time.Second)
// defer ctx.CancelFunc()
// // Starts executing child components
// root.ChildsDo(ctx)
var (
/ / ErrConcurrencyComponentTimeout concurrent component business timeout
ErrConcurrencyComponentTimeout = errors.New("Concurrency Component Timeout"))// Context Business Context
type Context struct {
// context.WithTimeout Derived subcontext
TimeoutCtx context.Context
// The timeout function
context.CancelFunc
}
// GetContext gets the business context instance
// d Timeout period
func GetContext(d time.Duration) *Context {
c := &Context{}
c.TimeoutCtx, c.CancelFunc = context.WithTimeout(context.Background(), d)
return c
}
// Component interface
type Component interface {
// Add a child componentMount(c Component, components ... Component) error// Remove a child component
Remove(c Component) error
// Execute the current component business: 'BusinessLogicDo' and the child component: 'ChildsDo'
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) error
// Execute the current component business logic
// resChan writes back to the channel of the current component business execution result
BusinessLogicDo(resChan chan interface{}) error
// Execute the child component
ChildsDo(ctx *Context) error
}
// BaseComponent BaseComponent
// Implement Add: Add a child component
// Implement Remove: Removes a child component
type BaseComponent struct {
// List of child components
ChildComponents []Component
}
// Mount mounts a child component
func (bc *BaseComponent) Mount(c Component, components ... Component) (err error) {
bc.ChildComponents = append(bc.ChildComponents, c)
if len(components) == 0 {
return
}
bc.ChildComponents = append(bc.ChildComponents, components...)
return
}
// Remove Removes a child component
func (bc *BaseComponent) Remove(c Component) (err error) {
if len(bc.ChildComponents) == 0 {
return
}
for k, childComponent := range bc.ChildComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
return
}
// Do executes the child component
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
func (bc *BaseComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
Execute the current component business code
err = currentConponent.BusinessLogicDo(nil)
iferr ! =nil {
return err
}
// Execute the child component
return currentConponent.ChildsDo(ctx)
}
// BusinessLogicDo where the current component business logic code is populated
func (bc *BaseComponent) BusinessLogicDo(resChan chan interface{}) (err error) {
// do nothing
return
}
// ChildsDo executes the child component
func (bc *BaseComponent) ChildsDo(ctx *Context) (err error) {
// Execute the child component
for _, childComponent := range bc.ChildComponents {
if err = childComponent.Do(ctx, childComponent, nil); err ! =nil {
return err
}
}
return
}
BaseConcurrencyComponent Concurrency base component
type BaseConcurrencyComponent struct {
// Composite base components
BaseComponent
// Whether the current component has and sends child components
HasChildConcurrencyComponents bool
// And send a list of child components
ChildConcurrencyComponents []Component
/ / wg object
*sync.WaitGroup
// Current component business execution result channel
logicResChan chan interface{}
// Error message during current component execution
Err error
}
// Remove Removes a child component
func (bc *BaseConcurrencyComponent) Remove(c Component) (err error) {
if len(bc.ChildComponents) == 0 {
return
}
for k, childComponent := range bc.ChildComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
for k, childComponent := range bc.ChildConcurrencyComponents {
if c == childComponent {
fmt.Println(runFuncName(), "Remove.", reflect.TypeOf(childComponent))
bc.ChildConcurrencyComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...)
}
}
return
}
// MountConcurrency mounts and sends a child component
func (bc *BaseConcurrencyComponent) MountConcurrency(c Component, components ... Component) (err error) {
bc.HasChildConcurrencyComponents = true
bc.ChildConcurrencyComponents = append(bc.ChildConcurrencyComponents, c)
if len(components) == 0 {
return
}
bc.ChildConcurrencyComponents = append(bc.ChildConcurrencyComponents, components...)
return
}
// ChildsDo executes the child component
func (bc *BaseConcurrencyComponent) ChildsDo(ctx *Context) (err error) {
if bc.WaitGroup == nil {
bc.WaitGroup = &sync.WaitGroup{}
}
// Execute and send the child component
for _, childComponent := range bc.ChildConcurrencyComponents {
bc.WaitGroup.Add(1)
go childComponent.Do(ctx, childComponent, bc.WaitGroup)
}
// Execute the child component
for _, childComponent := range bc.ChildComponents {
if err = childComponent.Do(ctx, childComponent, nil); err ! =nil {
return err
}
}
if bc.HasChildConcurrencyComponents {
// Wait for the result of concurrent component execution
bc.WaitGroup.Wait()
}
return
}
// Do executes the child component
// CTX business context
// currentConponent Indicates the current component
// The WaitGroup object of the WG parent component
func (bc *BaseConcurrencyComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
defer wg.Done()
// Initialize and issue the child component channel
if bc.logicResChan == nil {
bc.logicResChan = make(chan interface{}, 1)}go currentConponent.BusinessLogicDo(bc.logicResChan)
select {
// Wait for the service execution result
case <-bc.logicResChan:
// Service execution result
fmt.Println(runFuncName(), "bc.BusinessLogicDo wait.done...")
break
// Wait for timeout
case <-ctx.TimeoutCtx.Done():
// Exit due to timeout
fmt.Println(runFuncName(), "bc.BusinessLogicDo timeout...")
bc.Err = ErrConcurrencyComponentTimeout
break
}
// Execute the child component
err = currentConponent.ChildsDo(ctx)
return
}
Copy the code
Special note: the concept of some design patterns in this series may be different from the original concept, because it will be used in combination with the actual situation, take its essence, appropriate change, flexible use.Copy the code
The article lists
- The code template | Go design patterns of actual combat
- Chain called | Go design patterns of actual combat
- Code components | Go design patterns of actual combat
- Subscription notice | Go design patterns of actual combat
- Customer decision | Go design patterns of actual combat
- State transform | Go design patterns of actual combat
Go design pattern practice series for more articles click here to view