preface
Hi, my little asong is back. I didn’t update it for two weeks. I’m busy recently, plus I’m lazy. So, uh huh, you know. However, the sharing that I brought today is absolutely dry goods, which is also needed in the development of actual projects, so in order to be able to explain clearly, I specially wrote a sample for reference only. This article will focus on the sample learning, which has been uploaded to GitHub and can be downloaded by yourself. OK, stop talking nonsense, I know you can’t wait, let’s get started!!!
wire
Dependency injection
Before introducing wire, let’s take a look at dependency injection. Students who have used spring should be familiar with this. The most common way of inversion of control (IOC) is called dependency injection. A class that places a dependent class as a row parameter in a dependency is called dependency injection. Maybe you don’t understand. In my opinion, the way to construct an object with a large parameter is to accept only one parameter. The construction of his control operation is also handed over to a third party, namely the inversion of control. For example: there is no concept of class in go, which is embodied in the form of structure. Suppose we have a class of oars, and we want to set the ship to have 12 oars, then we can write the following code:
package main
import (
"fmt"
)
type ship struct {
pulp *pulp
}
func NewShip(pulp *pulp) *ship{
return &ship{
pulp: pulp,
}
}
type pulp struct {
count int
}
func Newpulp(count int) *pulp{
return &pulp{
count: count,
}
}
func main(){
p:= Newpulp(12)
s := NewShip(p)
fmt.Println(s.pulp.count)
}
I believe you can see the problem at a glance. Whenever the demand changes, we have to create an object to specify the oar. This code is not easy to maintain. Let’s make a change.
package main
import (
"fmt"
)
type ship struct {
pulp *pulp
}
func NewShip(pulp *pulp) *ship{
return &ship{
pulp: pulp,
}
}
type pulp struct {
count int
}
func Newpulp() *pulp{
return &pulp{
}
}
func (c *pulp)set(count int) {
c.count = count
}
func (c *pulp)get() int {
return c.count
}
func main(){
p:= Newpulp()
s := NewShip(p)
s.pulp.set(12)
fmt.Println(s.pulp.get())
}
The advantage of this code is that it is loosely coupled, easy to maintain, and easy to test. If we change the demand now, we need 20 oars directlys.pulp.set(20)
That’s fine.
Use of wire
wire
There are two basic concepts,Provider
(constructor) andInjector
(injector).Provider
In fact, it is to create a function. Above usInitializeCron
namelyInjector
。 Each injector is actually an object creation and initialization function. In this function, we just need to tellwire
What type of object to create, the dependency of this type,wire
We will initialize and create a function for the tool to complete.
The purpose of pulling this long is to lead to wire. Although the code above implements dependency injection, it is no problem for us to implement dependency ourselves when the amount of code is small and the structure is not complex. When the relationship between structures becomes very complex, it will be extremely cumbersome and error prone to manually create dependencies and then assemble them 。 So the role of wire comes. Let’s install wire before using it.
$ go get github.com/google/wire/cmd/wire
Execution of this command will result in$GOPATH/bin
To generate an executable programwire
This is the code generator. Don’t forget it$GOPATH/bin
Add system environment variables$PATH
Medium.
Based on the simple example above, let’s first see how to use wire. Let’s first create a wire file with the following contents:
//+build wireinject
package main
import (
"github.com/google/wire"
)
type Ship struct {
Pulp *Pulp
}
func NewShip(pulp *Pulp) *Ship {
return &Ship{
pulp: pulp,
}
}
type Pulp struct {
Count int
}
func NewPulp() *Pulp {
return &Pulp{
}
}
func (c *Pulp)set(count int) {
c.count = count
}
func (c *Pulp)get() int {
return c.count
}
func InitShip() *Ship {
wire.Build(
NewPulp,
NewShip,
)
return &Ship{}
}
func main(){
}
amongInitShip
The return value of this function is the type of object we need to create,wire
Just know the type, it doesn’t matter what you return. In the function, we callwire.Build()
Will be createdship
The dependent type constructor is passed in. So we write it, and now we need to go to the console to execute itwire
。
$ wire
wire: asong.cloud/Golang_Dream/wire_cron_example/ship: wrote /Users/asong/go/src/asong.cloud/Golang_Dream/wire_cron_example/ship/wire_gen.go
We see wire generated_ gen.go This document:
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from mian.go:
func InitShip() *Ship {
pulp := NewPulp()
ship := NewShip(pulp)
return ship
}
// mian.go:
type Ship struct {
pulp *Pulp
}
func NewShip(pulp *Pulp) *Ship {
return &Ship{
pulp: pulp,
}
}
type Pulp struct {
count int
}
func NewPulp() *Pulp {
return &Pulp{}
}
func (c *Pulp) set(count int) {
c.count = count
}
func (c *Pulp) get() int {
return c.count
}
func main() {
}
You can see that the generated file is generated according to the definitionInitShip()
This function and dependency binding are also implemented. We can call this function directly, which saves a lot of code to implement dependency binding relationship by ourselves.
be careful:If you are using wire for the first time, you will encounter a problem. The generated code will conflict with the original code because they both define the same functionfunc InitShip() *Ship
, so you need to add it in the first line of the original file//+build wireinject
, and also have a blank line with the package name to resolve the conflict.
The above example is still simple. Let’s take a look at a little more examples. When we develop the web background in daily life, the code is hierarchical and familiardao
、service
、controller
、model
wait. actuallydao
、service
、controller
There is a calling sequence.controller
callservice
Layer,service
Layer calldao
Layer, which forms the dependency relationship. In the actual development, we use hierarchical dependency injection to make the code more hierarchical and easy to maintain. So, I’ve written an example, let’s learn how to use it. This adoptioncron
Timing task replacementcontroller
Insteadcontroller
,cron
I will explain the timing task later.
//+build wireinject
package wire
import (
"github.com/google/wire"
"asong.cloud/Golang_Dream/wire_cron_example/config"
"asong.cloud/Golang_Dream/wire_cron_example/cron"
"asong.cloud/Golang_Dream/wire_cron_example/cron/task"
"asong.cloud/Golang_Dream/wire_cron_example/dao"
"asong.cloud/Golang_Dream/wire_cron_example/service"
)
func InitializeCron(mysql *config.Mysql) *cron.Cron{
wire.Build(
dao.NewClientDB,
dao.NewUserDB,
service.NewUserService,
task.NewScanner,
cron.NewCron,
)
return &cron.Cron{}
}
Let’s take a look at this code,dao.NewClientDB
Create a*sql.DB
Object, depending onmysql
Configuration file for,dao.NewUserDB
Create a*UserDB
Object, he depends on*sql.DB
,service.NewUserService
Create aUserService
Object, depending on*UserDB
Object,task.NewScanner
Create a*Scanner
Object, he depends on*UserService
Object,cron。NewCron
Create a*Cron
Object, he depends on*Scanner
Object, in fact, there are layers of binding relationship, layer by layer, hierarchical, and easy to maintain code.
Well, here’s the basic usage. Let’s learn about itcron
。
cron
Basic learning
In our daily development or operation and maintenance, we often encounter some periodic tasks or requirements, such as executing a script every period of time and performing an operation every month. Linux provides us with a convenient way — crontab timing task; crontab is a custom timer, we can use crontab command to execute specified system instructions or shell script scripts at fixed intervals. This time interval is written in a similar way to the cron expression we usually use. The operation is performed periodically by setting the character
Now that we know the basic concepts, let’s introduce cron expressions. There are two commonly used cron formats: one is the “standard” cron format, which consists ofcron linux
System program use, there is another kind isQuartz Scheduler
Use cron format. One of the differences between the two is supportseconds
Field, one is not supported, but the gap is not very big, we will bring it in the following explanationseconds
This field has no effect.
cronAn expression is a string that consists of6
Space divided into7
Each domain represents a time meaning. The format is as follows:
[Second] [minute] [hour] [day] [month] [week] [year]
The part of [year] is usually omitted, and actually consists of the first six parts.
The definition of each part is presented in the form of a table
field | Is it required | Values and ranges | wildcard |
---|---|---|---|
second | yes | 0-59 | , – * / |
branch | yes | 0-59 | , – * / |
Time | yes | 0-23 | , – * / |
day | yes | 1-31 | , – * ? / L W |
month | yes | 1-12 or Jan-Dec | , – * / |
week | yes | 1-7 or sun-sat | , – * ? / L # |
year | no | 1970-2099 | , – * / |
Looking at the range of this value, it is easy to understand. The most difficult to understand is the wildcard. Let’s focus on the wildcard.
,
This means that it is executed at more than two time points, if we define it as5,10,15
It means that the timing task is executed at the 5th, 10th and 15th points respectively.-
This is a better understanding of this is to specify a continuous range in a certain domain, if we define it in the “time” field6-12
It is triggered every hour between 6 and 12 o’clock,
express6,7,8,9,10,11,12
*
Represents all values and can be interpreted as “per”. If you set it in the “day” field*
, which means that it is triggered every day.?
Indicates that no value is specified. The scenario used is that you don’t need to care about the value of this field currently set. For example: to trigger an operation on the 8th day of each month, but we don’t care about the day of the week, we can set it like this0 0 0 8 * ?
/
When a field is triggered periodically, the symbol divides the expression in its domain into two parts. The first part is the starting value, which will be reduced by one unit except the second, for example, it is defined on the “second”5/10
It means to execute every 10 seconds from the 5th second, while “Min” means to execute every 10 minutes from the 5th second.L
In EnglishLASTIt can only be used in “day” and “week”. It is set in “day” to indicate the last day of the current month (based on the current month, if it is February, it will also be based on whether it is a Runnian); on “week”, it means Saturday, equivalent to “7” or “SAT”. If you add a number before “L”, it means the last one in the data. For example, setting “7L” on “week” means “the last Saturday of the month”W
Indicates the closest working day (Monday to Friday) from the specified date. It can only be used in “day” and can only be used after specific numbers. If “15W” is set on the “day”, it means that it is triggered on the working day nearest to the 15th of each month. If the 15th happens to be a Saturday, it will be triggered on the nearest Friday (14th). If the 15th is a weekend, it will be triggered on the nearest Monday (16th). If the 15th happens to be on a weekday (Monday to Friday), it will be triggered on that day. If it is “1W”, it can only be pushed to the next most recent working day of this month, not to the previous month.#
Represents the day of the week of a month. It can only be used on the week. For example, “2 × 3” means on the third Tuesday of each month.
After learning about wildcards, let’s look at a few examples:
- Once a day at 10:00:
0 0 10 * * *
- Every 10 minutes:
0 */10 * * *
- Once a month at 3:00 a.m. on the 1st of every month:
0 0 3 1 * ?
- At 23:30 on the last day of each month:
0 30 23 L * ?
- Once a week at 3 am on Saturdays:
0 0 3 ? * L
- Once at 30 and 50:
0 30,50 * * * ?
Cron in go
We learned the basics. Now we want to use timed tasks in go projects. What should we do?github
There’s a star on it, the higher onecron
Library, we can use itrobfig/cron
This library develops our timed tasks.
Before learning, let’s install itcron
$ go get -u github.com/robfig/cron/v3
This is a relatively stable version at present. Now this version adopts the standard specification, and the default is Noseconds
If we want to bring fields, we need to create cron objects to specify. Show it later. Let’s start with a simple use:
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
func main() {
c := cron.New()
c.AddFunc("@every 1s", func() {
fmt.Println("task start in 1 seconds")
})
c.Start()
select{}
}
Here we usecron.New
Create a cron object to manage scheduled tasks. callcron
Object’sAddFunc()
Method to add a scheduled task to the manager.AddFunc()
Two parameters are accepted. Parameter 1 specifies the trigger time rule in the form of string. Parameter 2 is a function without parameters, which is called every time it is triggered.@every 1s
It is triggered once per second,@every
Add a time interval after it to indicate how often it is triggered. for example@every 1h
It is triggered every hour,@every 1m2s
It means to trigger every 1 minute and 2 seconds.time.ParseDuration()
All the supported formats can be used here. callc.Start()
Start timing cycle.
Pay attention, becausec.Start()
Start a new goroutine for loop detection, and we add a line at the end of the codeselect{}
Prevent the main goroutine from exiting.
When we define time above, we usecron
Pre defined time rules: let’s learn about the predefined time rules that he has:
@yearly
You can also write@annually
, representing 0:00 on the first day of each year. Equivalent to0 0 1 1 *
;@monthly
: represents 0:00 on the first day of each month. Equivalent to0 0 1 * *
;@weekly
: refers to 0:00 on the first day of a week. Note that the first day is Sunday, that is, the 0:00 that ends on Saturday and starts on Sunday. Equivalent to0 0 * * 0
;@daily
You can also write@midnight
, which means 0 o’clock every day. Equivalent to0 0 * * *
;@hourly
: indicates the beginning of the hour. Equivalent to0 * * * *
。
cron
It also supports fixed time interval. The format is as follows:
@every <duration>
It means everyduration
Trigger once.<duration>
Will calltime.ParseDuration()
Function parsing, soParseDuration
All supported formats are OK.
Project usage
Since my own project is to add scheduled tasks through the implementation of the job interface, let’s introduce the use of the job interface. In addition to directly using the nonparametric function as a callback,cron
Also supportedjob
Interface:
type Job interface{
Run()
}
We need to implement this interface. Here, I will use the example I wrote to demonstrate it. Now, my timing task is to periodically scan the data in the DB table. The implementation tasks are as follows:
package task
import (
"fmt"
"asong.cloud/Golang_Dream/wire_cron_example/service"
)
type Scanner struct {
lastID uint64
user *service.UserService
}
const (
ScannerSize = 10
)
func NewScanner(user *service.UserService) *Scanner{
return &Scanner{
user: user,
}
}
func (s *Scanner)Run() {
err := s.scannerDB()
if err != nil{
fmt.Errorf(err.Error())
}
}
func (s *Scanner)scannerDB() error{
s.reset()
flag := false
for {
users,err:=s.user.MGet(s.lastID,ScannerSize)
if err != nil{
return err
}
if len(users) < ScannerSize{
flag = true
}
s.lastID = users[len(users) - 1].ID
for k,v := range users{
fmt.Println(k,v)
}
if flag{
return nil
}
}
}
func (s *Scanner)reset() {
s.lastID = 0
}
Above is the implementationRun
Method, and then we need to call thecron
The addjob method of theScanner
Object is added to the timing manager.
package cron
import (
"github.com/robfig/cron/v3"
"asong.cloud/Golang_Dream/wire_cron_example/cron/task"
)
type Cron struct {
Scanner *task.Scanner
Schedule *cron.Cron
}
func NewCron(scanner *task.Scanner) *Cron {
return &Cron{
Scanner: scanner,
Schedule: cron.New(),
}
}
func (s *Cron)Start() error{
_,err := s.Schedule.AddJob("*/1 * * * *",s.Scanner)
if err != nil{
return err
}
s.Schedule.Start()
return nil
}
actuallyAddFunc()
Method is also calledAddJob()
method. first,cron
be based onfunc()
Type defines a new typeFuncJob
:
// cron.go
type FuncJob func()
Then let’sFuncJob
realizationJob
Interface:
// cron.go
func (f FuncJob) Run() {
f()
}
stayAddFunc()
Method, convert the incoming callback toFuncJob
Type and then callAddJob()
method:
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
return c.AddJob(spec, FuncJob(cmd))
}
Well, we’ll explain the basic use here. Finally, we’ll add the last knowledge point, that is, on the issue of time specification, defaultv3
The version does not come withseconds
Field, you need to use it in this way
cron.New(cron.WithSeconds())
Create the object’s passed in parameter.
All right. I want to explain the end of the code will not run, has been uploaded to GitHub, you can download to learn: https://github.com/asong2020/…
summary
That’s all for today’s article. The summary of this article is not complete. It’s just an entry-level effect. If you want to continue to deepen, you still need to read the document and learn by yourself. Learn to read official documents, in order to make more progress. For example, if I don’t look at the documents, I won’t know what the time specifications are now. So I still need to form a good habit of reading documents. Make a preview, the next issue is the go elastic tutorial, you can pay attention to it if you need it.
At the end, I’d like to send you a little welfare. Recently, I was reading the book “micro service architecture design mode”. I talked about it very well. I also collected a PDF. If you need it, you can download it by yourself. Access: public official account: DreamWorks [Golang], background reply: [micro service], you can get it.
I have translated a Chinese document of gin, which will be maintained regularly. If necessary, you can download it by replying to [gin] in the background.
I am asong, an ordinary program ape, let me slowly become stronger. Welcome to your attention. See you next time~~~
Recommended articles in the past:
- It’s said that you don’t know JWT and swagger yet. I’m not going to eat any more. I’ll come with the practice project
- You will master these two language levels
- Go to realize the multi person chat room, where you want to chat anything you can!!!
- Grpc practice – learning grpc is that simple
- RPC practice of go standard library
- In 2020, the latest gin framework Chinese document asong picked up English again and translated it with heart
- Several hot loading methods based on gin
- Boss: this kid can’t use the validator library to verify the data. It has opened ~~~