init
This commit is contained in:
parent
511ffa1109
commit
868d533c8f
62
engine/firstengine.go
Normal file
62
engine/firstengine.go
Normal file
@ -0,0 +1,62 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FirstEngine struct {
|
||||
Scheduler Scheduler
|
||||
WorkCount int
|
||||
Work func(request Request) (Response, error)
|
||||
}
|
||||
|
||||
type Scheduler interface {
|
||||
Submit(request Request)
|
||||
ConfigChan() chan Request
|
||||
Run()
|
||||
WorkReady(chan Request)
|
||||
}
|
||||
|
||||
func (e *FirstEngine) CreateWorker(in chan Request, out chan Response) {
|
||||
go func() {
|
||||
for {
|
||||
e.Scheduler.WorkReady(in)
|
||||
req := <-in
|
||||
work, err := e.Work(req)
|
||||
if err != nil {
|
||||
log.Printf("work err: %v", err)
|
||||
}
|
||||
out <- work
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (e *FirstEngine) Run(num int) {
|
||||
out := make(chan Response)
|
||||
|
||||
e.Scheduler.Run()
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < e.WorkCount; i++ {
|
||||
e.CreateWorker(e.Scheduler.ConfigChan(), out)
|
||||
}
|
||||
|
||||
log.Printf("time slice 1: %d", time.Since(start)/time.Millisecond)
|
||||
for i := 0; i < num; i++ {
|
||||
e.Scheduler.Submit(Request{Id: strconv.Itoa(i), Dlock: "redis_lock"})
|
||||
}
|
||||
|
||||
log.Printf("time slice 2: %d", time.Since(start)/time.Millisecond)
|
||||
i := 0
|
||||
for id := range out {
|
||||
i++
|
||||
log.Println(id.Id)
|
||||
if i >= num {
|
||||
close(out)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("time slice 3: %d", time.Since(start)/time.Millisecond)
|
||||
}
|
10
engine/type.go
Normal file
10
engine/type.go
Normal file
@ -0,0 +1,10 @@
|
||||
package engine
|
||||
|
||||
type Request struct {
|
||||
Id string
|
||||
Dlock string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Id string
|
||||
}
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
||||
module goRedisDLM
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
)
|
8
go.sum
Normal file
8
go.sum
Normal file
@ -0,0 +1,8 @@
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
60
main.go
Normal file
60
main.go
Normal file
@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"goRedisDLM/engine"
|
||||
"goRedisDLM/rpcSupport"
|
||||
"goRedisDLM/scheduler"
|
||||
"goRedisDLM/worker/client"
|
||||
"log"
|
||||
"net/rpc"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var host = flag.String("host", "", "host")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *host == "" {
|
||||
flag.Usage()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
e := engine.FirstEngine{
|
||||
Scheduler: &scheduler.FirstScheduler{},
|
||||
WorkCount: 10,
|
||||
Work: client.CreateRpcWorker(createRpcPoll(*host)),
|
||||
}
|
||||
|
||||
e.Run(5000)
|
||||
}
|
||||
|
||||
func createRpcPoll(port string) chan *rpc.Client {
|
||||
out := make(chan *rpc.Client)
|
||||
|
||||
var clients []*rpc.Client
|
||||
|
||||
split := strings.Split(port, ",")
|
||||
for i, ele := range split {
|
||||
cli, err := rpcSupport.RpcClient(ele)
|
||||
if err != nil {
|
||||
log.Printf("host: %s,%d create err: %v", ele, i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("rpc cli create ok: %s", ele)
|
||||
|
||||
clients = append(clients, cli)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
for _, ele := range clients {
|
||||
out <- ele
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
61
redisLock/redis.go
Normal file
61
redisLock/redis.go
Normal file
@ -0,0 +1,61 @@
|
||||
package redisLock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
type redisCommInfo struct {
|
||||
key string
|
||||
value string
|
||||
ttl int
|
||||
}
|
||||
|
||||
type RedisLock struct {
|
||||
client *redis.Client
|
||||
ctx context.Context
|
||||
kvt redisCommInfo
|
||||
}
|
||||
|
||||
func CreateRedisLock(c *redis.Client, ctx context.Context, k, v string, ttl int) *RedisLock {
|
||||
return &RedisLock{c, ctx, redisCommInfo{k, v, ttl}}
|
||||
}
|
||||
|
||||
func (r *RedisLock) Lock() {
|
||||
//for r.client.SetNX(r.ctx, r.kvt.key, r.kvt.value, time.Duration(r.kvt.ttl)*time.Second).Val() == false {
|
||||
// time.Sleep(time.Millisecond * 20)
|
||||
//}
|
||||
|
||||
script := `if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then
|
||||
redis.call('hincrby',KEYS[1],ARGV[1],1)
|
||||
redis.call('expire',KEYS[1],ARGV[2])
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end`
|
||||
|
||||
for r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value, r.kvt.ttl).Val().(int64) == 0 {
|
||||
time.Sleep(time.Millisecond * 20)
|
||||
}
|
||||
|
||||
log.Printf("lock ok: %s,%s,%d\n", r.kvt.key, r.kvt.value, r.kvt.ttl)
|
||||
}
|
||||
|
||||
func (r *RedisLock) Unlock() {
|
||||
//if r.client.Get(r.ctx, r.kvt.key).Val() == r.kvt.value {
|
||||
// r.client.Del(r.ctx, r.kvt.key)
|
||||
//}
|
||||
|
||||
//script := "if (redis.call('get',KEYS[1]) == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end"
|
||||
//r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value)
|
||||
|
||||
script := `if redis.call('hexists',KEYS[1],ARGV[1]) == 0 then return nil
|
||||
elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 then return redis.call('del',KEYS[1])
|
||||
else return 0 end`
|
||||
r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value)
|
||||
|
||||
log.Printf("unlock ok: %s,%s,%d\n", r.kvt.key, r.kvt.value, r.kvt.ttl)
|
||||
}
|
39
rpcSupport/jsonrpc.go
Normal file
39
rpcSupport/jsonrpc.go
Normal file
@ -0,0 +1,39 @@
|
||||
package rpcSupport
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
)
|
||||
|
||||
func RpcServer(host string, service interface{}) error {
|
||||
err := rpc.Register(service)
|
||||
if err != nil {
|
||||
log.Println("rpc register error")
|
||||
return err
|
||||
}
|
||||
listen, err := net.Listen("tcp", host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("rpc server listen ok: %s", host)
|
||||
|
||||
for {
|
||||
accept, err := listen.Accept()
|
||||
if err != nil {
|
||||
log.Println("accept err: ", err)
|
||||
}
|
||||
jsonrpc.ServeConn(accept)
|
||||
}
|
||||
}
|
||||
|
||||
func RpcClient(host string) (*rpc.Client, error) {
|
||||
dial, err := net.Dial("tcp", host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jsonrpc.NewClient(dial), nil
|
||||
}
|
56
scheduler/firstscheduler.go
Normal file
56
scheduler/firstscheduler.go
Normal file
@ -0,0 +1,56 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"goRedisDLM/engine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FirstScheduler struct {
|
||||
Request chan engine.Request
|
||||
Worker chan chan engine.Request
|
||||
}
|
||||
|
||||
func (f *FirstScheduler) Submit(request engine.Request) {
|
||||
f.Request <- request
|
||||
}
|
||||
|
||||
func (f *FirstScheduler) ConfigChan() chan engine.Request {
|
||||
return make(chan engine.Request)
|
||||
}
|
||||
|
||||
func (f *FirstScheduler) Run() {
|
||||
f.Request = make(chan engine.Request)
|
||||
f.Worker = make(chan chan engine.Request)
|
||||
|
||||
go func() {
|
||||
var requests []engine.Request
|
||||
var workers []chan engine.Request
|
||||
|
||||
for {
|
||||
var req engine.Request
|
||||
var work chan engine.Request
|
||||
|
||||
if len(requests) > 0 && len(workers) > 0 {
|
||||
req = requests[0]
|
||||
work = workers[0]
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-f.Request:
|
||||
requests = append(requests, r)
|
||||
case w := <-f.Worker:
|
||||
workers = append(workers, w)
|
||||
case work <- req:
|
||||
requests = requests[1:]
|
||||
workers = workers[1:]
|
||||
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (f *FirstScheduler) WorkReady(requests chan engine.Request) {
|
||||
f.Worker <- requests
|
||||
}
|
24
worker/client/rpcworker.go
Normal file
24
worker/client/rpcworker.go
Normal file
@ -0,0 +1,24 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"goRedisDLM/engine"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
func CreateRpcWorker(clients chan *rpc.Client) func(request engine.Request) (engine.Response, error) {
|
||||
return func(request engine.Request) (engine.Response, error) {
|
||||
client := <-clients
|
||||
return RpcWorker(client, request)
|
||||
}
|
||||
}
|
||||
|
||||
func RpcWorker(client *rpc.Client, request engine.Request) (engine.Response, error) {
|
||||
out := engine.Response{}
|
||||
|
||||
err := client.Call("Inventory.Work", request, &out)
|
||||
if err != nil {
|
||||
return engine.Response{Id: "err"}, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
53
worker/rpc.go
Normal file
53
worker/rpc.go
Normal file
@ -0,0 +1,53 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"goRedisDLM/engine"
|
||||
"goRedisDLM/redisLock"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Inventory struct {
|
||||
Client *redis.Client
|
||||
}
|
||||
|
||||
func (i *Inventory) Work(request engine.Request, response *engine.Response) error {
|
||||
value := uuid.New()
|
||||
|
||||
log.Printf("uuid: %s, lock key: %s\n", value.String(), request.Dlock)
|
||||
|
||||
lock := redisLock.CreateRedisLock(i.Client, context.Background(), request.Dlock, value.String(), 30)
|
||||
lock.Lock()
|
||||
|
||||
log.Printf("uuid: %s get dlock\n", value.String())
|
||||
get := i.Client.Get(context.Background(), "inventory")
|
||||
log.Printf("%s\n", get)
|
||||
|
||||
inventory, err := strconv.Atoi(get.Val())
|
||||
if err != nil {
|
||||
goto Error
|
||||
}
|
||||
inventory = inventory - 1
|
||||
if inventory >= 0 {
|
||||
i.Client.Set(context.Background(), "inventory", fmt.Sprintf("%d", inventory), redis.KeepTTL)
|
||||
} else {
|
||||
log.Printf("error DLock error :%d\n", inventory)
|
||||
err = errors.New("inventory err")
|
||||
goto Error
|
||||
}
|
||||
lock.Unlock()
|
||||
|
||||
response.Id = request.Id
|
||||
|
||||
return nil
|
||||
|
||||
Error:
|
||||
lock.Unlock()
|
||||
return err
|
||||
}
|
30
worker/service/main.go
Normal file
30
worker/service/main.go
Normal file
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"goRedisDLM/rpcSupport"
|
||||
"goRedisDLM/worker"
|
||||
"log"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var port = flag.String("port", "", "port")
|
||||
var redisHost = flag.String("redis", "", "redis host")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *port == "" || *redisHost == "" {
|
||||
log.Println("port err")
|
||||
flag.Usage()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: *redisHost,
|
||||
Password: "redhat",
|
||||
})
|
||||
|
||||
log.Fatal(rpcSupport.RpcServer(*port, &worker.Inventory{Client: redisClient}))
|
||||
}
|
Loading…
Reference in New Issue
Block a user