program – Eternal Night https://www.shuijingwanwq.com There is no problem not worth solving, and no technology not worth learning! Wed, 03 Jun 2026 13:45:27 +0000 en-US hourly 1 https://wordpress.org/?v=7.0 GoRoutine Quick Start: Multi-Scenario Execution Process Analysis (with Code + Flowchart) https://www.shuijingwanwq.com/en/2026/05/01/11062/ https://www.shuijingwanwq.com/en/2026/05/01/11062/#respond Fri, 01 May 2026 02:36:55 +0000 https://www.shuijingwanwq.com/?p=11062 浏览量: 29

As the core feature of the Go language, Goroutine is the key to lightweight concurrency. It is lighter than traditional operating system threads and has lower creation cost. The default initial stack is only 2KB and supports dynamic expansion, which allows developers to easily achieve high concurrency programming. For beginners who are new to Go concurrency, understanding the execution relationship between goroutine and the main thread (can be understood as a process or main coroutine) is the core difficulty of getting started.
This article will start from a basic requirement and extend 4 different scenarios (including the case where Goroutine is not enabled), and dismantle the gorou through complete code and visualization execution flow chart. The concurrent execution logic of tine and the main thread can help you quickly grasp the core usage and scheduling characteristics of goroutine, as well as the essential difference between ‘open coroutines’ and ‘no coroutines’.

A review of basic needs
Core requirements: start a goroutine in the main thread (can be understood as a process or main coroutine), and the coroutine outputs ‘hello every 1 second’ , world’, the main thread outputs ‘hello: golang’ every 1 second, and exits the program after 10 outputs, requiring both to execute at the same time.
First look at the complete code of the basic scenario, and then gradually extend other scenarios, focusing on the comparison scenario of ‘Goroutine is not enabled’:

package main

import (
	"fmt"
	"strconv"
	"time"
)

// test 函数:goroutine执行的逻辑,每隔1秒输出hello,world
func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello,world " + strconv.Itoa(i))
		time.Sleep(time.Second) // 暂停1秒,模拟耗时操作
	}
}

func main() {
	// 开启goroutine:在go关键字后跟上要执行的函数,即可启动一个协程
	go test() 

	// 主线程逻辑:每隔1秒输出hello:golang,执行10次
        for i := 1; i <= 10; i++ {
                fmt.Println("main() hello,golang " + strconv.Itoa(i))
                time.Sleep(time.Second)
        }
}

The core concept
Before analyzing the scenario, first clarify the two key knowledge points to avoid understanding the deviation, especially the key difference between ‘open coroutines’ and ‘unopened coroutines’:

  1. Main thread and coroutines: After the main function is started, a main thread will be created by default; the goroutine is started through the go keyword. The exit rule of the GO program is: after the main thread exits, all the coroutines will be forced to terminate, regardless of whether the execution is completed, this is the core logical basis of the next three ‘enabled coroutines’ scenarios; and When the coroutines are not opened (remove the go keyword), the ‘synchronous call’ will be executed, that is, the test function is executed first, and then the subsequent logic of the main function is executed, and there is no ‘concursion’.
  2. Concurrency execution essence: goroutine is managed by the scheduler (GMP model) at the Go runtime, rather than the operating system directly scheduled. In the GMP model, G (Goroutine) is the task carrier, M (Machine) is the operating system thread, and P (Processor) is a logical processor, which is responsible for coordinating the binding and scheduling of G and M, and steals the machine through work. The system implements load balancing, so that the main thread and the coroutines seem to be ‘execute at the same time’, but it is actually the result of the scheduler’s quick switching of execution rights; when goroutine is not enabled, only the main thread is executing, and the program is executed in ‘sequential execution’ logic, and there is no scheduling switching.
  3. The role of time.sleep: The most intuitive function of time.sleep(time.second) in the code is to slow down the program execution speed and facilitate the naked eye Observe the effect of parallel execution – after all, the scheduling and switching speed of the coroutine scheduling is extremely fast. In addition, its more critical role is to make the current coroutines (whether the main coroutines or sub-coroutines) actively and timely give up the CPU time slice to ensure that other coroutines can quickly obtain the right to execute. It should be noted that not using time.sleep does not necessarily have no other coroutine preemption rights, depending on the go version: go Before 1.14, only collaborative preemption is supported. When there is no blocking operation (such as time.sleep, channel operation), the current coroutines will always occupy the CPU, and other coroutines cannot be preempted; go 1.14 and later introduce asynchronous preemptive scheduling, which will force preemption of long-term operation coroutines, but the effect is not as stable as Time.sleep actively gives up, and cannot achieve the effect of ‘slow output, easy to observe’. When a coroutine is not started, the program is only executed by the main coroutines, and Sleep will only suspend the execution process of the main coroutines, and neither can implement parallel observation, and there is no ‘other coroutine preemption’ situation (because there are no other coroutines).

Detailed explanation of four scenarios (code + flowchart + execution analysis)
The following is divided into four scenarios, one by one analyzes the execution process of goroutine and the main thread, and the focus is to supplement ‘Gorouti is not enabled The scene of NE’ is contrasted with other scenarios that enable coroutines to help you intuitively understand the essential difference between ‘opening coroutines’ and ‘not opening coroutines’. Each scenario contains complete code, execution flow diagrams, and core analysis.

Scenario 1: test() executes 10 times, main() executes 10 times (basic scenario, open goroutine)

  1. complete code
    Consistent with the basic requirements code, the core is that the number of executions of the coroutine and the main thread is the same, both 10 times, and each execution is sleeping for 1 second, and the coroutine is started through go test(). The execution result is as follows
/app/go-atguigu/goroutine-demo # go run main.go
main() hello,golang1
test () hello,world 1
main() hello,golang2
test () hello,world 2
test () hello,world 3
main() hello,golang3
main() hello,golang4
test () hello,world 4
test () hello,world 5
main() hello,golang5
test () hello,world 6
main() hello,golang6
main() hello,golang7
test () hello,world 7
test () hello,world 8
main() hello,golang8
main() hello,golang9
test () hello,world 9
test () hello,world 10
main() hello,golang10
/app/go-atguigu/goroutine-demo # 
  1. execution flowchart
    We use the flowchart to visualize the execution order of the two (the arrows indicate the execution right switching, and the execution right is regained after hibernation):
流程图 TD
    A[程序启动] --> B[主协程启动,执行go test()]
    B --> C[子协程test()启动,进入就绪态]
    B --> D[主协程继续执行自身for循环]
    C --> E[子协程输出hello,world 1]
    D --> F[主协程输出hello:golang 1]
    E --> G[子协程休眠1秒,让出CPU,进入等待态]
    F --> H[主协程休眠1秒,让出CPU,进入等待态]
    G --> I[子协程休眠结束,进入就绪态,等待调度]
    H --> J[主协程休眠结束,进入就绪态,等待调度]
    I --> K[调度器分配执行权,子协程输出hello,world 2]
    J --> L[调度器分配执行权,主协程输出hello:golang 2]
    K --> M[子协程休眠1秒]
    L --> N[主协程休眠1秒]
    M --> O[重复执行,直到两者都执行10次]
    N --> O
    O --> P[主协程执行完毕,退出]
    O --> Q[子协程执行完毕,退出]
    P --> R[程序终止]
    Q --> R
    
  1. perform analysis
  • Starting logic: After the program starts, the main thread executes go test() first, starts the coroutine (at this time, the coroutine enters the ready state, waiting for the schedule), and then the main thread continues to execute its own for loop.
  • Concurrency logic: Since both have time.sleep(time.second), they will actively give up after each output CPU, the scheduler will alternately assign the execution rights to the main thread and coroutines, so the console will alternately output ‘hello, world x’ and ‘hello: golang x’ (the order may be slightly different, determined by the scheduler, which is a normal phenomenon).
  • Exit logic: When both the main thread and the coroutines have executed 10 loops, both of them exit normally, and the program is terminated. Because the number of executions of the two is the same and the sleep time is the same, the high probability will be completed at the same time.
  • Key Note: The output order is not absolutely fixed, because the scheduling of goroutine is controlled by the Go runtime, not our manual control – the scheduler will allocate the execution rights according to the system resources and task conditions. The main thread can output 2 times in a row, and the coroutine outputs 2 times in a row, but the overall rule of ‘alternate output every 1 second’ will be maintained, which is the embodiment of work stealing and preemptive scheduling in the GMP scheduling model.

Scenario 2: test() executes 20 times, main() executes 10 times

  1. complete code
    Only the number of loops to modify the test function is 20 times, the main thread is still 10 times, the rest of the logic remains unchanged, and the coroutines are opened through go test():
package main

import (
	"fmt"
	"strconv"
	"time"
)

// test()执行20次
func test() {
	for i := 1; i <= 20; i++ {
		fmt.Println("test() hello,world " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main() {
	go test() // 开启goroutine

	// main()执行10次
	for i := 1; i <= 10; i++ {
		fmt.Println("main() hello,golang " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
  1. execution flowchart
流程图 TD
    A[程序启动] --> B[主协程启动,执行go test()]
    B --> C[子协程test()启动,就绪态]
    B --> D[主协程执行自身for循环]
    C --> E[子协程输出hello,world 1]
    D --> F[主协程输出hello:golang 1]
    E --> G[子协程休眠1秒,让出CPU]
    F --> H[主协程休眠1秒,让出CPU]
    G --> I[子协程就绪,等待调度]
    H --> J[主协程就绪,等待调度]
    I --> K[子协程输出hello,world 2]
    J --> L[主协程输出hello:golang 2]
    K --> M[子协程休眠]
    L --> N[主协程休眠]
    M --> O[重复执行,直到主协程执行10次]
    N --> O
    O --> P[主协程执行完毕,退出]
    P --> Q[子协程被强制终止(未执行完20次)]
    Q --> R[程序终止]
  1. perform analysis
  • The first 10 executions: exactly the same as scenario 1, the main thread and the coroutines are alternately output, sleep for 1 second after each output, and the scheduler alternately assigns the execution right.
  • Key turning point: When the main thread executes the 10th loop, it will exit directly. At this time, the coroutine has only executed 10 times (the same number of execution times as the main thread, because each sleep time is the same), and there are 10 remaining unexecuted.
  • Exit logic (emphasis): According to Go’s coroutine rules, the main thread is the ‘entry’ of the program. After the main thread exits, no matter whether the coroutine is executed or not, it will be forced to terminate. Therefore, the coroutine cannot continue to execute the remaining 10 outputs, and the program directly terminates. This is also the point that is easy to step on the pit in Go concurrent programming – if the coroutines are to be executed, you need to use synchronization mechanisms such as sync.waitGroup.
  • Phenomenon verification: After running the code, the console will alternately output 10 groups of ‘Hello, World X’ and ‘Hello: Golang X’, and then the program will exit directly, and there will be no output of ‘Hello, World 11’ and later. The execution result is as follows
/app/go-atguigu/goroutine-demo # go run main.go
main() hello,golang1
test () hello,world 1
test () hello,world 2
main() hello,golang2
test () hello,world 3
main() hello,golang3
main() hello,golang4
test () hello,world 4
main() hello,golang5
test () hello,world 5
test () hello,world 6
main() hello,golang6
test () hello,world 7
main() hello,golang7
main() hello,golang8
test () hello,world 8
main() hello,golang9
test () hello,world 9
main() hello,golang10
test () hello,world 10
test () hello,world 11
/app/go-atguigu/goroutine-demo # go run main.go
main() hello,golang1
test () hello,world 1
test () hello,world 2
main() hello,golang2
main() hello,golang3
test () hello,world 3
test () hello,world 4
main() hello,golang4
main() hello,golang5
test () hello,world 5
test () hello,world 6
main() hello,golang6
main() hello,golang7
test () hello,world 7
main() hello,golang8
test () hello,world 8
main() hello,golang9
test () hello,world 9
main() hello,golang10
test () hello,world 10
/app/go-atguigu/goroutine-demo # 

Note: One of the execution results outputs hello, world 11 . Small probability events, or in other words, the execution result will not output the output of ‘Hello, World 12’ and later. Here is a simple execution diagram 1

One of the execution results output hello, world 11 . Small probability events, or in other words, the execution result will not output the output of "Hello, World 12" and later. Here is a simple execution diagram 1

Scenario 3: test() executes 10 times, main() executes 100 times (the main thread executes more times, open goroutine)

  1. complete code
    The number of loops to modify the main function is 100 times, and the coroutine is still 10 times, and the rest of the logic remains unchanged. The coroutines are opened through go test():
package main

import (
	"fmt"
	"strconv"
	"time"
)

// test()执行10次
func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello,world " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main() {
	go test() // 开启goroutine

	// main()执行20次
	for i := 1; i <= 20; i++ {
		fmt.Println("main() hello,golang " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
  1. execution flowchart
流程图 TD
    A[程序启动] --> B[主协程启动,执行go test()]
    B --> C[子协程test()启动,就绪态]
    B --> D[主协程执行自身for循环]
    C --> E[子协程输出hello,world 1]
    D --> F[主协程输出hello:golang 1]
    E --> G[子协程休眠1秒,让出CPU]
    F --> H[主协程休眠1秒,让出CPU]
    G --> I[子协程就绪,等待调度]
    H --> J[主协程就绪,等待调度]
    I --> K[子协程输出hello,world 2]
    J --> L[主协程输出hello:golang 2]
    K --> M[子协程休眠]
    L --> N[主协程休眠]
    M --> O[重复执行,直到子协程执行10次]
    N --> O
    O --> P[子协程执行完毕,正常退出]
    P --> Q[主协程继续执行剩余90次循环]
    Q --> R[主协程执行完毕,退出]
    R --> S[程序终止]
  1. perform analysis
  • The first 10 executions: consistent with scenario 1 and scenario 2, the main thread and the coroutine are alternately output, the scheduler alternately assigns the execution right, and the two execute simultaneously.
  • Key turning point: After the 10th cycle of the coroutine is executed, it will exit normally (after the coroutines are executed, it will be automatically recycled by the Go run and release resources). At this time, the main thread has only been executed 10 times, and the remaining 90 times have not been executed.
  • Follow-up execution: After the coroutine exits, the main thread will continue to execute the remaining 90 loops. At this time, only the main thread is running in the program, and the console will continue to output ‘hello:golang X’ (X from 11 to 100) until the main thread is completed.
  • Core difference: The exit of the coroutines will not affect the execution of the main thread – the main thread will be executed until the end of its own loop, and the program will be terminated. This is in stark contrast to Scenario 2. The core reason is: the main thread is the core of the program, and the coroutine is the ‘subjective task’ started by the main thread. The exit of the process does not affect the main thread, and the exit of the main thread will terminate all the coroutines, which is also determined by the coroutine life cycle management rules of GO. The execution result is as follows
/app/go-atguigu/goroutine-demo # go run main.go
main() hello,golang1
test () hello,world 1
main() hello,golang2
test () hello,world 2
main() hello,golang3
test () hello,world 3
main() hello,golang4
test () hello,world 4
main() hello,golang5
test () hello,world 5
main() hello,golang6
test () hello,world 6
main() hello,golang7
test () hello,world 7
main() hello,golang8
test () hello,world 8
main() hello,golang9
test () hello,world 9
main() hello,golang10
test () hello,world 10
main() hello,golang11
main() hello,golang12
main() hello,golang13
main() hello,golang14
main() hello,golang15
main() hello,golang16
main() hello,golang17
main() hello,golang18
main() hello,golang19
main() hello,golang20
/app/go-atguigu/goroutine-demo # 

Scenario 4: test() executes 10 times, main() executes 10 times (goroutine is not enabled, remove the go keyword)

  1. complete code
    Core modification: remove the go keyword, directly call the test() function, do not open the coroutine, the number of execution times of test() and main() is 10 times, the rest of the logic remains unchanged, and the focus is on the difference between ‘simultaneous execution’ and ‘concurrent execution’:
package main

import (
	"fmt"
	"strconv"
	"time"
)

// test()执行10次,无go关键字,同步执行
func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello,world " + strconv.Itoa(i))
		time.Sleep(time.Second) // 暂停1秒,仅暂停当前执行流程
	}
}

func main() {
	test() // 去掉go关键字,不开启goroutine,同步调用test()

	// 主线程逻辑:只有test()执行完毕后,才会执行此处
	for i := 1; i <= 10; i++ {
		fmt.Println("main() hello,golang " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
  1. execution flowchart
    When the goroutine is not enabled, the program is advanced in the ‘sequential’ logic, and there is no execution right to switch. The flow diagram is as follows:
流程图 TD
    A[程序启动] --> B[主协程启动,执行test()(无go关键字)]
    B --> C[test()输出hello,world 1]
    C --> D[test()休眠1秒,程序暂停]
    D --> E[test()继续输出hello,world 2]
    E --> F[test()休眠1秒]
    F --> G[重复执行,直到test()执行完10次]
    G --> H[test()执行完毕,主协程继续执行自身for循环]
    H --> I[main()输出hello:golang 1]
    I --> J[main()休眠1秒,程序暂停]
    J --> K[main()输出hello:golang 2]
    K --> L[main()休眠1秒]
    L --> M[重复执行,直到main()执行完10次]
    M --> N[主协程执行完毕,退出]
    N --> O[程序终止]

Perform analysis (focus on the scenarios that start the coroutines)

  • Starting logic: After the program starts, the main thread executes the test() directly (there is no go keyword). At this time, no coroutines are created, the program only has the main The thread is executing, and the test() function will be executed completely, and then the for loop of the main() function is executed, which belongs to ‘simultaneous call’.
  • Execution logic (difference from the core of the open coroutine): no concurrency effect, the execution sequence is completely fixed – first output 10 times in a row ‘hello, world x’ (1 second interval), and the output of ‘hello:golang x’ will start to output after the test() function is fully executed, and there will be no ‘alternate output’. Because there is no coroutine, there is no scheduler switching execution right, and the program is executed in sequence in code writing.
  • Exit logic: After the test() is executed 10 times, it exits normally, and the main thread continues to execute its own 10 loops. After the main() execution is completed, the program terminates. Different from the scenario of opening the coroutines, there is no situation where ‘the main thread exits early and the coroutine is terminated’, because there is no coroutine, all the logic is executed sequentially in the main thread.
  • Core comparison summary: enable goroutine (plus go) → concurrent execution (the main thread and the coroutine are alternately executed, the scheduler assigns the execution right); goroutine is not enabled (go go) → Synchronous execution (the main thread executes test() sequentially, and there is no scheduling switch). This is the most basic use difference between goroutine, and it is also the key to understanding ‘concurrency’ and ‘synchronization’. The execution result is as follows
/app/go-atguigu/goroutine-demo # go run main.go
main() hello,golang1
test () hello,world 1
main() hello,golang2
test () hello,world 2
main() hello,golang3
test () hello,world 3
main() hello,golang4
test () hello,world 4
main() hello,golang5
test () hello,world 5
main() hello,golang6
test () hello,world 6
main() hello,golang7
test () hello,world 7
main() hello,golang8
test () hello,world 8
main() hello,golang9
test () hello,world 9
main() hello,golang10
test () hello,world 10
main() hello,golang11
main() hello,golang12
main() hello,golang13
main() hello,golang14
main() hello,golang15
main() hello,golang16
main() hello,golang17
main() hello,golang18
main() hello,golang19
main() hello,golang20
/app/go-atguigu/goroutine-demo # 

4. Core Summary and Pit Avoidance Guide
Through the comparison of the above 4 scenarios (3 kinds of goroutines, 1 type is not enabled), we can summarize the goroutine The core characteristics, the essential difference between ‘opening and not opening’, and the key points of entry-level pitfalls can help you quickly grasp the use logic of goroutine:

  1. The difference between the core characteristics and the opening/unopened
  • Lightweight and efficient: goroutine is dispatched by Go runtime, the initial stack is only 2KB, the creation and destruction cost is much lower than the operating system thread, and supports millions of concurrency;
  • The difference between concurrency and synchronization: enable goroutine (go test()) → the main thread and the coroutine are executed concurrently, the scheduler assigns the execution rights through the work stealing mechanism, and alternate output occurs; goroutine (test()) → is not enabled Synchronous execution, the main thread first executes the test(), and then executes its own logic, the order is fixed, and there is no alternate output.
  • Life cycle rules: When goroutine is turned on, the main thread dominant program exits (the main thread exits → Compulsory termination of the coroutine), the exit of the coroutine does not affect the main thread; when the goroutine is not enabled, there is no main/coroutine distinction, all logic is executed sequentially in the main thread, and the program terminates after execution.
  1. Getting Started Points
  • Avoid ‘exit when the coroutine is not completed’: If the main thread is required to exit after the execution of the coroutine is completed, the ‘execution number of execution’ cannot be relied on, and SYNC needs to be used .WaitGroup (will be explained later), otherwise the main thread exiting early will cause the coroutine to be forced to terminate, resulting in the problem of unfinished tasks or resource leakage.
  • Understand ‘scheduling randomness’: After the goroutine is turned on, the execution sequence is determined by the scheduler, not fixed alternation, even if the sleep time of the two in the code is the same, it is possible This is normal when continuous output occurs (especially when the CPU resources are tight), and the execution sequence is completely fixed and there is no randomness when the goroutine is not turned on.
  • Use the non-blocking coroutine with caution: If there is no blocking operation such as time.sleep in the coroutines, the scheduler may make one coroutine execute the other (go 1.14+ supports preemptive scheduling, but it is still not recommended to rely on dependencies), you can actively make the CPU time slice through runtime.gosched() to ensure the concurrent effect; when goroutine is not turned on, there is no such problem.
  • Distinguish the ‘go keywords’: the Go keyword is the core of opening goroutine, and removing Go will become an ordinary synchronous function call, which will lose the concurrency ability. This is the most confusing point for beginners. Be sure to use code to compare memory.

5. Follow-up learning direction
This article helps you master the basic usage and execution process of goroutine through 4 scenarios, as well as the essential difference between ‘opening coroutines’ and ‘not opening coroutines’, but this is just an entry to Go concurrency. Follow-up can continue to learn:

  1. sync.waitGroup: Implement the main thread to wait for the execution of the coroutines to complete, and solve the problem of ‘the coroutines have not been executed and terminated’.
  2. sync.mutex: Solve the ‘competitive condition’ problem when multiple goroutines operate the same variable at the same time, and avoid data confusion.
  3. Channel: The core mechanism of ‘communication is better than shared memory’ in the Go language, which realizes secure communication and synchronization between goroutines.
  4. In-depth GMP scheduling model: understand the collaborative working mechanism of G, M, and P, as well as the underlying principles of work theft and preemptive scheduling, and improve the performance optimization ability of concurrent programs.
    Goroutine is the soul of the Go language, mastering its basic execution logic, and the difference between ‘on and unopened’, can lay a solid foundation for subsequent high-concurrency programming. It is recommended that you run the code in this article more, observe the output results in different scenarios, and understand the scheduling logic in combination with the flowchart, in order to truly understand the usage of goroutine~
]]>
https://www.shuijingwanwq.com/en/2026/05/01/11062/feed/ 0
Implementation of setting and getting global variables in Yii 2.0 (based on yii::$app->params) https://www.shuijingwanwq.com/en/2021/10/15/14304/ https://www.shuijingwanwq.com/en/2021/10/15/14304/#respond Fri, 15 Oct 2021 10:14:34 +0000 params]]> https://www.shuijingwanwq.com/?p=14304 浏览量: 16

1. At this stage, there is a requirement, and it is necessary to send the corresponding operation logs generated to the log system of the middle platform in an interface call. The log system interface of Zhongtai supports multiple log records at the same time. Therefore, it is decided to plan to first save the generated N operation logs in the interface call, and first store them in the global variable, and then at the end of the interface call, get the log records in the global variable, one HTTP request, and send it to the log system of the middle stage. as shown in Figure 1

现阶段存在一个需求,需要在一次接口调用中,将生成的相应操作日志,发送至中台的日志系统。中台的日志系统接口是支持同时接收多条日志记录的。因此,决定先计划将一次接口调用中,生成的 N 条操作日志,先存储至全局变量中,然后在接口调用结束时,获取全局变量中的日志记录,一次 HTTP 请求,一并发送给中台的日志系统。

Figure 1

2. Reference URL:https://www.yiiframework.com/doc/guide/2.0/zh-cn/structure-applications#params. yii::$app->params. as shown in Figure 2

参考网址:https://www.yiiframework.com/doc/guide/2.0/zh-cn/structure-applications#params 。Yii::$app-&gt;params。

Figure 2

3. Edit the log template message and configure the corresponding template. Edit the corresponding language package file: common/messages/en-cn/application.php


 '新建选题【{plan_title}】',
	302201 => '启用选题【{plan_title}】',
	302202 => '禁用选题【{plan_title}】',
	302203 => '提交选题【{plan_title}】进行审核',
	302204 => '编辑选题【{plan_title}】',
	302205 => '删除选题【{plan_title}】',
	302206 => '通过选题【{plan_title}】',
	302207 => '拒绝选题【{plan_title}】',
	302208 => '指派【{plan_task_title}】{plan_task_type}给【{user_nick}】',
	302209 => '启用【{plan_task_title}】{plan_task_type}',
	302210 => '禁用【{plan_task_title}】{plan_task_type}',
	302211 => '删除【{plan_task_title}】{plan_task_type}',
	302212 => '新建【{plan_task_title}】{plan_task_type}指派给【{user_nick}】',
	302213 => '编辑【{plan_task_title}】{plan_task_type}',
	302214 => '转派【{plan_task_title}】{plan_task_type}给【{user_nick}】',
	302215 => '认领【{plan_task_title}】{plan_task_type}',
];



4. Classify the log messages accordingly, edit: common/config/params.php


 [ //框架服务接口日志操作类型
		[
			'operateKey' => 'plan_management', //操作类型标识
			'operateTitle' => '选题管理', //操作类型名称
			'codes' => [302200, 302201, 302202, 302203, 302204, 302205], //日志详情的返回码
		],
		[
			'operateKey' => 'plan_review', //操作类型标识
			'operateTitle' => '选题审核', //操作类型名称
			'codes' => [302206, 302207], //日志详情的返回码
		],
		[
			'operateKey' => 'plan_task_management', //操作类型标识
			'operateTitle' => '任务管理', //操作类型名称
			'codes' => [302208, 302209, 302210, 302211, 302212, 302213, 302214, 302215], //日志详情的返回码
		]
	],
];



5. Write a method of automatically generating logs global variables


	/**
	 * 自动创建上报日志信息,放入(Yii::$app->params['cmcApiLogs']),为后续 HTTP 请求准备数据
	 * @param int $code 返回码
	 * @param array $data 数据,需要被替换的变量
	 * 格式如下:
	 * [
	 *     'plan_title' => '选题名称',
	 * ]
	 *
	 * @param object $identity 当前用户的身份实例
	 */
	public static function automaticCreate($code, $data, $identity)
	{
		foreach (Yii::$app->params['cmcApiLogOperates'] as $operateValue) {
			foreach ($operateValue['codes'] as $codeValue) {
				if ($codeValue == $code) {
					$operateKey = $operateValue['operateKey'];
					$operateTitle = $operateValue['operateTitle'];
				}
			}
		}
		if (isset($operateKey) && isset($operateTitle)) {
			Yii::$app->params['cmcApiLogs'][] = [
				'user_token' => $identity->user_token,
				'service_key' => Yii::$app->params['cmcApi']['serviceKey'],
				'log_type' => 'operate',
				'operate_key' => $operateKey,
				'operate_title' => $operateTitle,
				'log_detail' => Yii::t('application', Yii::t('application', Yii::t('application', $code), $data)),
				'log_time' => date('Y-m-d H:i:s'),
				'log_ip' => '0.0.0.0',
			];
		}
	}


6. Check whether there exists in the trigger event_after_request event, and print yii::$app->params if it exists[‘cmcApiLogs’]. There are 2 log records, you can get the log records in the global variable in the trigger event_after_request event, one HTTP request, and send it to the log system of the middle station. as shown in Figure 3

在 触发 EVENT_AFTER_REQUEST 事件中判断是否存在,如果存在则打印 Yii::$app-&gt;params['cmcApiLogs']。存在 2 条日志记录,可以在 触发 EVENT_AFTER_REQUEST 事件中 获取全局变量中的日志记录,一次 HTTP 请求,一并发送给中台的日志系统。

Figure 3


Array
(
    [0] => Array
        (
            [user_token] => 3cf1c3d1db4951ad3179e1452580c0c3
            [service_key] => pcs
            [log_type] => operate
            [operate_key] => plan_management
            [operate_title] => 选题管理
            [log_detail] => 新建选题【选题 20211015 0】
            [log_time] => 2021-10-15 14:02:00
            [log_ip] => 0.0.0.0
        )

    [1] => Array
        (
            [user_token] => 3cf1c3d1db4951ad3179e1452580c0c3
            [service_key] => pcs
            [log_type] => operate
            [operate_key] => plan_management
            [operate_title] => 选题管理
            [log_detail] => 提交选题【选题 20211015 0】进行审核
            [log_time] => 2021-10-15 14:02:00
            [log_ip] => 0.0.0.0
        )

)



]]>
https://www.shuijingwanwq.com/en/2021/10/15/14304/feed/ 0
When using the Windows Photo Viewer, it prompts: Windows Photo Viewer cannot display this picture because the available memory on the computer may be insufficient. Please close some programs that are not currently in use or release some hard disk space (if the hard disk is full), and try again. The solution of https://www.shuijingwanwq.com/en/2020/06/18/14852/ https://www.shuijingwanwq.com/en/2020/06/18/14852/#respond Thu, 18 Jun 2020 07:45:20 +0000 https://www.shuijingwanwq.com/?p=14852 浏览量: 14

1. When using the Windows Photo Viewer, it prompts: Windows Photo Viewer cannot display this picture because the available memory on the computer may be insufficient. Please close some programs that are not currently in use or release some hard disk space (if the hard disk is full), and try again. as shown in Figure 1

在使用 Windows 照片查看器时,提示:Windows 照片查看器无法显示此图片,因为计算机上的可用内存可能不足。请关闭一些目前没有使用的程序或者释放部分硬盘空间(如果硬盘已满),然后重试。

Figure 1

2. When viewing the large icon, it can be displayed normally, as shown in Figure 2

查看超大图标时,可以正常显示

Figure 2

3. Check the file size: 88.0 KB, the file is not large. as shown in Figure 3

查看文件大小:88.0 KB,文件并不大。

Figure 3

4. Check the available memory on the computer: 7.7 GB, so eliminate the situation that the available memory may be insufficient. as shown in Figure 4

查看计算机上的可用内存:7.7 GB,因此,排除掉可用内存可能不足的情况。

Figure 4

5. Check the hard disk space on the computer, free space: 144 GB, so eliminate the situation that the hard disk is full. as shown in Figure 5

查看计算机上的硬盘空间,可用空间:144 GB,因此,排除掉硬盘已满的情况。

Figure 5

6. Open the picture in Adobe Photoshop CC 2015, File – Export – Quick Export to PNG. as shown in Figure 6

在 Adobe Photoshop CC 2015 中打开图片,文件 - 导出 - 快速导出为 PNG。

Figure 6

7. Use the Windows Photo Viewer again to display this picture. as shown in Figure 7

再次使用 Windows 照片查看器,可以显示此图片。

Figure 7

]]>
https://www.shuijingwanwq.com/en/2020/06/18/14852/feed/ 0
In Docker, Rancher deployment, execute Yii 2 console commands based on the Supervisor timing https://www.shuijingwanwq.com/en/2018/08/14/15682/ https://www.shuijingwanwq.com/en/2018/08/14/15682/#respond Tue, 14 Aug 2018 05:17:29 +0000 https://www.shuijingwanwq.com/?p=15682 浏览量: 13

1. Supervisor is a small system that can manage and monitor various processes in UNIX-like systems. The configuration file of supervisor is a Windows-ini style (Python ConfigParser) file.

2. The program configuration item of the configuration file of Supervisor


; * 为必选项
;[program:theprogramname]
;command=/bin/cat              ; * 要执行的命令路径(可以是相对与 PATH 的路径)也可带参数
;startsecs=1                   ; 需要考虑进程启动成功的时间, 当 running 状态超过该值时,表明启动成功 (def. 1)
;autorestart=unexpected        ; 当进程退出时是否应该重启,可选值为 false true unexpected ,为 false 时表示不重启,为 true 表示重启,为 unexpected 时,如果退出状态码不是 exitcodes 中之一时进行重启 (def: unexpected)
;stopwaitsecs=10               ; 这个是当我们向子进程发送 stopsignal 信号后,到系统返回信息给 supervisord,所等待的最大时间。超过这个时间,supervisord会向该子进程发送一个强制 kill 的信号。(default 10)
;stdout_logfile=/a/path        ; 进程 stdout 输出的日志文件路径,如果将其设置为NONE,则supervisord将不创建任何日志文件; default AUTO
;stderr_logfile=/a/path        ; 除非redirect_stderr为true,否则将进程 stderr 输出放在此文件中。日志文件路径,如果将其设置为NONE,则supervisord将不创建任何日志文件; default AUTO


3. Create a new \build\c_files\etc\supervisord.d\yii-task-submit.ini, as shown in Figure 1

新建 \build\c_files\etc\supervisord.d\yii-task-submit.ini

Figure 1


[program:yii-task-submit]
command = php /sobey/www/aims-rpc/yii task/submit
autorestart = true
startsecs = 0
stopwaitsecs = 10
stderr_logfile = /data/logs/yii-task-submit-stderr.log
stdout_logfile = /data/logs/yii-task-submit-stdout.log


4. StopWaitSecs, after the program sends a stop signal, wait for the operating system to return SIGCHLD to the number of seconds. If the number of seconds passed before the supervisord receives sigchld from the process, the supervisord will try to terminate it with the final sigkill. The default interval is now 1 second to execute the command, for example: if the script execution time is 20 seconds, the interval is 21 seconds.

5. It is found that the result does not meet the expectations of command execution, enter the container command line, check /data/logs/yii-task-submit-stderr.log


Exception 'yii\httpclient\Exception' with message 'Curl error: #6 - Could not resolve host: AIMS_RPC_CFG_AI_API_HOST_INFOAIMS_RPC_CFG_AI_API_BASE_URL; Name or service not known'

in /sobey/www/aims-rpc/vendor/yiisoft/yii2-httpclient/src/CurlTransport.php:57

Stack trace:
#0 /sobey/www/aims-rpc/vendor/yiisoft/yii2-httpclient/src/Client.php(221): yii\httpclient\CurlTransport->send(Object(yii\httpclient\Request))
#1 /sobey/www/aims-rpc/vendor/yiisoft/yii2-httpclient/src/Request.php(421): yii\httpclient\Client->send(Object(yii\httpclient\Request))
#2 /sobey/www/aims-rpc/common/logics/http/ai_api/Task.php(63): yii\httpclient\Request->send()
#3 /sobey/www/aims-rpc/console/services/TaskService.php(233): common\logics\http\ai_api\Task->getQueryReady()
#4 /sobey/www/aims-rpc/console/services/TaskService.php(33): console\services\TaskService->getAiQueryReady()
#5 /sobey/www/aims-rpc/console/controllers/TaskController.php(43): console\services\TaskService->submit()
#6 [internal function]: console\controllers\TaskController->actionSubmit()
#7 /sobey/www/aims-rpc/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#8 /sobey/www/aims-rpc/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#9 /sobey/www/aims-rpc/vendor/yiisoft/yii2/console/Controller.php(148): yii\base\Controller->runAction('submit', Array)
#10 /sobey/www/aims-rpc/vendor/yiisoft/yii2/base/Module.php(528): yii\console\Controller->runAction('submit', Array)
#11 /sobey/www/aims-rpc/vendor/yiisoft/yii2/console/Application.php(180): yii\base\Module->runAction('task/submit', Array)
#12 /sobey/www/aims-rpc/vendor/yiisoft/yii2/console/Application.php(147): yii\console\Application->runAction('task/submit', Array)
#13 /sobey/www/aims-rpc/vendor/yiisoft/yii2/base/Application.php(386): yii\console\Application->handleRequest(Object(yii\console\Request))
#14 /sobey/www/aims-rpc/yii(23): yii\base\Application->run()
#15 {main}


6. After solving the problem of Rancher environment variable replacement, upgrade again, the result is in line with the expectation of command after execution, but for the output of some string printing information of the command, there is no automatic wrapping, and the readability is poor. /data/logs/yii-task-submit-stderr.log, as shown in Figure 2

解决了 Rancher 环境变量替换的问题后,再次升级,结果符合 command 执行后的预期,不过对于命令的一些字符串打印信息的输出,未自动换行,可读性较差,查看 /data/logs/yii-task-submit-stderr.log

Figure 2


状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无获取状态:1(待提交)的1条任务:无


7. Check /data/logs/yii-task-submit-stdout.log, the default is automatic wrapping


Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii
Could not open input file: /sobey/www/aims-rpc/yii


8. Edit \Console\Controllers\TaskController.php, add a newline when outputting the message


/**
* 调用AI接口:提交任务
* 任务描述:
* 1、查询任务队列是否空闲,如果否,则退出;
* 2、获取1条任务,其条件与排序规则如下:
* (1)任务,状态:1(待提交);
* (2)租户优先级,状态:1(启用);
* (3)排序规则:group_pri.group_pri(顺序),group_pri.group_pri_updated_at(倒序),task.pri(顺序),task.pri_updated_at(倒序),task.created_at(顺序);
* 3、获取1条任务,如果无,则退出;
* 4、获取当前任务的人工智能服务提交日志,如果为空,则提交,否则重做;
* 5、人工智能服务:提交任务/重做任务;
* 6、操作数据(事务)
* (1)创建MySQL模型(人工智能服务提交日志);
* (2)更新任务模型的状态:2(已提交),人工智能服务任务状态:1(处理中);
* (3)创建MySQL模型(人工智能服务任务进度);
*/
public function actionSubmit()
{

$service = new TaskService();
$submitResult = $service->submit();

if ($submitResult['status'] === false) {
$this->stderr($submitResult['message'] . "\n");
return $submitResult['code'];
}

return ExitCode::OK;
}


9. After the upgrade, check /data/logs/yii-task-submit-stderr.log again, and it has been automatically lined, as shown in Figure 3

升级后,再次查看 /data/logs/yii-task-submit-stderr.log,已经自动换行

Figure 3


获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无
获取状态:1(待提交)的1条任务:无


]]>
https://www.shuijingwanwq.com/en/2018/08/14/15682/feed/ 0