These steps cover writing and running your application:
- From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter10/pool and navigate to it.
- Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter10/pool
You should see a file called go.mod that contains the following content:
module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter10/pool
- Copy the tests from ~/projects/go-programming-cookbook-original/chapter10/pool, or use this as an opportunity to write some of your own code!
- Create a file called worker.go with the following content:
package pool
import (
"context"
"fmt"
)
// Dispatch creates numWorker workers, returns a cancel
// function channels for adding work and responses,
// cancel must be called
func Dispatch(numWorker int) (context.CancelFunc, chan
WorkRequest, chan WorkResponse) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
in := make(chan WorkRequest, 10)
out := make(chan WorkResponse, 10)
for i := 0; i < numWorker; i++ {
go Worker(ctx, i, in, out)
}
return cancel, in, out
}
// Worker loops forever and is part of the worker pool
func Worker(ctx context.Context, id int, in chan WorkRequest,
out chan WorkResponse) {
for {
select {
case <-ctx.Done():
return
case wr := <-in:
fmt.Printf("worker id: %d, performing %s
workn", id, wr.Op)
out <- Process(wr)
}
}
}
- Create a file called work.go with the following content:
package pool
import "errors"
type op string
const (
// Hash is the bcrypt work type
Hash op = "encrypt"
// Compare is bcrypt compare work
Compare = "decrypt"
)
// WorkRequest is a worker req
type WorkRequest struct {
Op op
Text []byte
Compare []byte // optional
}
// WorkResponse is a worker resp
type WorkResponse struct {
Wr WorkRequest
Result []byte
Matched bool
Err error
}
// Process dispatches work to the worker pool channel
func Process(wr WorkRequest) WorkResponse {
switch wr.Op {
case Hash:
return hashWork(wr)
case Compare:
return compareWork(wr)
default:
return WorkResponse{Err: errors.New("unsupported
operation")}
}
}
- Create a file called crypto.go with the following content:
package pool
import "golang.org/x/crypto/bcrypt"
func hashWork(wr WorkRequest) WorkResponse {
val, err := bcrypt.GenerateFromPassword(wr.Text,
bcrypt.DefaultCost)
return WorkResponse{
Result: val,
Err: err,
Wr: wr,
}
}
func compareWork(wr WorkRequest) WorkResponse {
var matched bool
err := bcrypt.CompareHashAndPassword(wr.Compare, wr.Text)
if err == nil {
matched = true
}
return WorkResponse{
Matched: matched,
Err: err,
Wr: wr,
}
}
- Create a new directory named example and navigate to it.
- Create a file named main.go with the following content:
package main
import (
"fmt"
"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter10/pool"
)
func main() {
cancel, in, out := pool.Dispatch(10)
defer cancel()
for i := 0; i < 10; i++ {
in <- pool.WorkRequest{Op: pool.Hash, Text:
[]byte(fmt.Sprintf("messages %d", i))}
}
for i := 0; i < 10; i++ {
res := <-out
if res.Err != nil {
panic(res.Err)
}
in <- pool.WorkRequest{Op: pool.Compare, Text:
res.Wr.Text, Compare: res.Result}
}
for i := 0; i < 10; i++ {
res := <-out
if res.Err != nil {
panic(res.Err)
}
fmt.Printf("string: "%s"; matched: %vn",
string(res.Wr.Text), res.Matched)
}
}
- Run go run main.go.
- You may also run the following commands:
$ go build
$ ./example
You should now see the following output:
$ go run main.go
worker id: 9, performing encrypt work
worker id: 5, performing encrypt work
worker id: 2, performing encrypt work
worker id: 8, performing encrypt work
worker id: 6, performing encrypt work
worker id: 1, performing encrypt work
worker id: 0, performing encrypt work
worker id: 4, performing encrypt work
worker id: 3, performing encrypt work
worker id: 7, performing encrypt work
worker id: 2, performing decrypt work
worker id: 6, performing decrypt work
worker id: 8, performing decrypt work
worker id: 1, performing decrypt work
worker id: 0, performing decrypt work
worker id: 9, performing decrypt work
worker id: 3, performing decrypt work
worker id: 4, performing decrypt work
worker id: 7, performing decrypt work
worker id: 5, performing decrypt work
string: "messages 9"; matched: true
string: "messages 3"; matched: true
string: "messages 4"; matched: true
string: "messages 0"; matched: true
string: "messages 1"; matched: true
string: "messages 8"; matched: true
string: "messages 5"; matched: true
string: "messages 7"; matched: true
string: "messages 2"; matched: true
string: "messages 6"; matched: true
- The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.
- If you copied or wrote your own tests, go up one directory and run go test. Ensure that all the tests pass.