1653883320
Golax is the official go implementation for the Lax framework.
Lax wants to be the best "user experience" for developers making REST APIs.
The design principles for Lax are:
my_api := golax.NewApi()
my_api.Root.
Interceptor(golax.InterceptorError).
Interceptor(myLogingInterceptor)
my_api.Root.Node("hello").
Method("GET", func(c *golax.Context) {
// At this point, Root interceptors has been already executed
fmt.Fprintln(c.Response, "Hello world!")
})
my_api.Serve()
Routing is based on nodes.
There are three types: static
, regex
and parameter
.
(
and )
, if the regex match.{
and }
, always matches.The performance compared with the most popular alternative is very similar (actually golax performs slightly better) however code readability and maintainability is far better with golax implementation.
Tests has been executed in a Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
.
Learn more about this https://github.com/fulldump/golax-performance.
If I want to handle a GET /users/1234/stats
request, all interceptors in nodes from <root>
to .../stats
are executed:
To abort the execution, call to c.Error(404, "Resource not found")
:
my_api := golax.NewApi()
my_api.Root.
Node("users").
Node("{user_id}").
Method("GET", func (c *golax.Context) {
fmt.Fprintln(c.Response, "You are looking for user " + c.Parameter)
})
my_api.Serve()
It is also possible get all parameters:
func (c *golax.Context) {
fmt.Fprintln(c.Response, "All parameters:", c.Parameters)
}
According to Google's API design guidelines to map RPC services to REST HTTP, it describes custom methods as extra operations that can not be easyly mapped to HTTP verbs. More info about custom methods
For example, this URL has a custom method :activate
:
https://my.service.com/v1/users/31231231231:activate
Golax support custom methods as operations:
my_api.Root.
Node("v1").
Node("users").
Node("{user_id}").
Operation("activate").
Method("POST", func(c *golax.Context) {
user_id := c.Parameters["{user_id}"]"
fmt.Fprintln(c.Response, "Here is custom method ':activate' for user "+user_id)
})
TODO: put here some examples to cover cool things:
Related docs:
Author: Fulldump
Source Code: https://github.com/fulldump/golax
License: MIT license
1599854400
Go announced Go 1.15 version on 11 Aug 2020. Highlighted updates and features include Substantial improvements to the Go linker, Improved allocation for small objects at high core counts, X.509 CommonName deprecation, GOPROXY supports skipping proxies that return errors, New embedded tzdata package, Several Core Library improvements and more.
As Go promise for maintaining backward compatibility. After upgrading to the latest Go 1.15 version, almost all existing Golang applications or programs continue to compile and run as older Golang version.
#go #golang #go 1.15 #go features #go improvement #go package #go new features
1653883320
Golax is the official go implementation for the Lax framework.
Lax wants to be the best "user experience" for developers making REST APIs.
The design principles for Lax are:
my_api := golax.NewApi()
my_api.Root.
Interceptor(golax.InterceptorError).
Interceptor(myLogingInterceptor)
my_api.Root.Node("hello").
Method("GET", func(c *golax.Context) {
// At this point, Root interceptors has been already executed
fmt.Fprintln(c.Response, "Hello world!")
})
my_api.Serve()
Routing is based on nodes.
There are three types: static
, regex
and parameter
.
(
and )
, if the regex match.{
and }
, always matches.The performance compared with the most popular alternative is very similar (actually golax performs slightly better) however code readability and maintainability is far better with golax implementation.
Tests has been executed in a Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
.
Learn more about this https://github.com/fulldump/golax-performance.
If I want to handle a GET /users/1234/stats
request, all interceptors in nodes from <root>
to .../stats
are executed:
To abort the execution, call to c.Error(404, "Resource not found")
:
my_api := golax.NewApi()
my_api.Root.
Node("users").
Node("{user_id}").
Method("GET", func (c *golax.Context) {
fmt.Fprintln(c.Response, "You are looking for user " + c.Parameter)
})
my_api.Serve()
It is also possible get all parameters:
func (c *golax.Context) {
fmt.Fprintln(c.Response, "All parameters:", c.Parameters)
}
According to Google's API design guidelines to map RPC services to REST HTTP, it describes custom methods as extra operations that can not be easyly mapped to HTTP verbs. More info about custom methods
For example, this URL has a custom method :activate
:
https://my.service.com/v1/users/31231231231:activate
Golax support custom methods as operations:
my_api.Root.
Node("v1").
Node("users").
Node("{user_id}").
Operation("activate").
Method("POST", func(c *golax.Context) {
user_id := c.Parameters["{user_id}"]"
fmt.Fprintln(c.Response, "Here is custom method ':activate' for user "+user_id)
})
TODO: put here some examples to cover cool things:
Related docs:
Author: Fulldump
Source Code: https://github.com/fulldump/golax
License: MIT license
1618480618
Are you looking for the best Android app development frameworks? Get the best Android app development frameworks that help to build the top-notch Android mobile app.
For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910
#best android mobile app development frameworks #top mobile app development frameworks #android app development frameworks #top frameworks for android app development #most popular android app development frameworks #app development frameworks
1655225700
go-web-framework-benchmark
This benchmark suite aims to compare the performance of Go web frameworks. It is inspired by Go HTTP Router Benchmark but this benchmark suite is different with that. Go HTTP Router Benchmark suit aims to compare the performance of routers but this Benchmark suit aims to compare whole HTTP request processing.
Last Test Updated: 2020-05
test environment
When I investigated performance of Go web frameworks, I found Go HTTP Router Benchmark, created by Julien Schmidt. He also developed a high performance http router: httprouter. I had thought I got the performance result until I created a piece of codes to mock the real business logics:
api.Get("/rest/hello", func(c *XXXXX.Context) {
sleepTime := strconv.Atoi(os.Args[1]) //10ms
if sleepTime > 0 {
time.Sleep(time.Duration(sleepTime) * time.Millisecond)
}
c.Text("Hello world")
})
When I use the above codes to test those web frameworks, the token time of route selection is not so important in the whole http request processing, although performance of route selection of web frameworks are very different.
So I create this project to compare performance of web frameworks including connection, route selection, handler processing. It mocks business logics and can set a special processing time.
The you can get some interesting results if you use it to test.
When you test a web framework, this test suit will starts a simple http server implemented by this web framework. It is a real http server and only contains GET url: "/hello".
When this server processes this url, it will sleep n milliseconds in this handler. It mocks the business logics such as:
It contains a test.sh that can do those tests automatically.
It uses wrk to test.
The first test case is to mock 0 ms, 10 ms, 100 ms, 500 ms processing time in handlers.
the concurrency clients are 5000.
Latency is the time of real processing time by web servers. The smaller is the better.
Allocs is the heap allocations by web servers when test is running. The unit is MB. The smaller is the better.
If we enable http pipelining, test result as below:
In 30 ms processing time, the test result for 100, 1000, 5000 clients is:
If we enable http pipelining, test result as below:
You should install this package first if you want to run this test.
go get github.com/smallnest/go-web-framework-benchmark
It takes a while to install a large number of dependencies that need to be downloaded. Once that command completes, you can run:
cd $GOPATH/src/github.com/smallnest/go-web-framework-benchmark
go build -o gowebbenchmark *.go
./test.sh
It will generate test results in processtime.csv and concurrency.csv. You can modify test.sh to execute your customized test cases.
./test-latency.sh
./test-latency-nonkeepalive.sh
./test-pipelining.sh
……
web_frameworks=( "default" "ace" "beego" "bone" "denco" "echov1" "echov2standard" "echov2fasthttp" "fasthttp-raw" "fasthttprouter" "fasthttp-routing" "gin" "gocraftWeb" "goji" "gojiv2" "gojsonrest" "gorestful" "gorilla" "httprouter" "httptreemux" "lars" "lion" "macaron" "martini" "pat" "r2router" "tango" "tiger" "traffic" "violetear" "vulcan")
……
./test-all.sh
you can run the shell script plot.sh
in testresults directory and it can generate all images in its parent directory.
Welcome to add new Go web frameworks. You can follow the below steps and send me a pull request.
Please add your web framework alphabetically.
Only test those webframeworks which are stable
some libs have not been maintained and the test code has removed them
Author: Smallnest
Source Code: https://github.com/smallnest/go-web-framework-benchmark
License: Apache-2.0 license
1651179240
cacheme - Redis Caching Framework For Go
🌀 Read this first: Caches, Promises and Locks. This is how caching part works in cacheme.
🌀 Real world example with Echo and Ent: https://github.com/Yiling-J/echo-ent-cacheme-example
// old
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
comment, err := ent.Comment.Get(context.Background(), int(id))
// new
comment, err := cacheme.CommentCacheStore.Get(c.Request().Context(), c.Param("id"))
go get github.com/Yiling-J/cacheme-go/cmd
After installing cacheme-go
codegen, go to the root directory(or the directory you think cacheme should stay) of your project, and run:
go run github.com/Yiling-J/cacheme-go/cmd init
The command above will generate cacheme
directory under current directory:
└── cacheme
├── fetcher
│ └── fetcher.go
└── schema
└── schema.go
It's up to you where the cacheme directory should be, just remember to use the right directory in Store Generation step.
Edit schema.go
and add some schemas:
package schema
import (
"time"
cacheme "github.com/Yiling-J/cacheme-go"
)
var (
// default prefix for redis keys
Prefix = "cacheme"
// store schemas
Stores = []*cacheme.StoreSchema{
{
Name: "Simple",
Key: "simple:{{.ID}}",
To: "",
Version: 1,
TTL: 5 * time.Minute,
Singleflight: false,
MetaData: false,
},
}
)
More details here
Run code generation from the root directory of the project as follows:
# this will use default schema path ./cacheme/schema
go run github.com/Yiling-J/cacheme-go/cmd generate
Or you can use custom schema path:
go run github.com/Yiling-J/cacheme-go/cmd generate ./yours/cacheme/schema
This produces the following files:
└── cacheme
├── fetcher
│ └── fetcher.go
├── schema
│ └── schema.go
├── store
│ ├── base.go
│ └── simple.go
└── store.go
If you update schema, just run generate
again.
Each cache store can provide a fetch function in fetcher.go
, you should call this Setup
function before create client:
import "your/cacheme/store"
func Setup() {
store.SimpleCacheStore.Fetch = func(ctx context.Context, ID string) (string, error) {
return ID, nil
}
}
You can setup fetch functions in any place, using any pattern, not restricted to this file. Just make sure you have a fetch function when using store.
import (
"your_project/cacheme"
"your_project/cacheme/fetcher"
)
func main() {
// setup fetcher
fetcher.Setup()
// create client
client := cacheme.New(
redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
}),
)
// or cluster client
client := cacheme.NewCluster(
redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{
":7000",
":7001",
":7002"},
}),
)
}
Get
Get cached result. If not in cache, call fetch function and store data to Redis.
// "foo" is the {{.ID}} part of the schema
result, err := client.SimpleCacheStore.Get(ctx, "foo")
GetP
Get multiple keys from multiple stores using pipeline. For each key, if not in cache, call fetch function and store data to Redis.
pipeline := client.NewPipeline()
ids := []string{"1", "2", "3", "4"}
var ps []*store.SimplePromise
for _, i := range ids {
promise, err := client.SimpleCacheStore.GetP(ctx, pipeline, i)
ps = append(ps, promise)
}
err = pipeline.Execute(ctx)
fmt.Println(err)
for _, promise := range ps {
r, err := promise.Result()
fmt.Println(r, err)
}
Consider using GetM
API for single store, see GetM
example below.
// same pipeline for different stores
pipeline := client.NewPipeline()
ids := []string{"1", "2", "3", "4"}
var ps []*store.SimplePromise // cache string
var psf []*store.FooPromise // cache model.Foo struct
for _, i := range ids {
promise, err := client.SimpleCacheStore.GetP(ctx, pipeline, i)
ps = append(ps, promise)
}
for _, i := range ids {
promise, err := client.FooCacheStore.GetP(ctx, pipeline, i)
psf = append(psf, promise)
}
// execute only once
err = pipeline.Execute(ctx)
// simple store results
for _, promise := range ps {
r, err := promise.Result()
fmt.Println(r, err)
}
// foo store results
for _, promise := range psf {
r, err := promise.Result()
fmt.Println(r, err)
}
GetM
Get multiple keys from same store, also using Redis pipeline. For each key, if not in cache, call fetch function and store data to Redis.
qs, err := client.SimpleCacheStore.GetM("foo").GetM("bar").GetM("xyz").Do(ctx)
// qs is a queryset struct, support two methods: GetSlice and Get
// GetSlice return ordered results slice
r, err := qs.GetSlice() // r: {foo_result, bar_result, xyz_result}
// Get return result of given param
r, err := qs.Get("foo") // r: foo_result
r, err := qs.Get("bar") // r: bar_result
r, err := qs.Get("fake") // error, because "fake" not in queryset
You can also initialize a getter using MGetter
getter := client.SimpleCacheStore.MGetter()
for _, id := range ids {
getter.GetM(id)
}
qs, err := getter.Do(c.Request().Context())
Invalid
err := client.SimpleCacheStore.Invalid(ctx, "foo")
Update
err := client.SimpleCacheStore.Update(ctx, "foo")
InvalidAll
Only works when you enable MetaData
option in schema.
// invalid all version 1 simple cache
client.SimpleCacheStore.InvalidAll(ctx, "1")
Each schema has 5 fields:
""
1
model.Foo{}
&model.Foo{}
[]model.Foo{}
map[model.Foo]model.Bar{}
string
, int
, or callable func() string
.true
, concurrent requests to same key on same executable will call Redis only oncetrue
, each store will save all generated keys to a Redis Set, so InvalidAll
method can work.Singleflight
option:Get
: support.GetM
: support. singleflight key will be the combination of all keys, order by alphabetical.GetP
: not support.Version
callable can help you managing version better. Example: // models.go
const FooCacheVersion = "1"
type Foo struct {}
const BarCacheVersion = "1"
type Bar struct {Foo: Foo}
// schema.go
// version has 3 parts: foo version & bar version & global version number
// if you change struct, update FooCacheVersion or BarCacheVersion
// if you change fetcher function or ttl or something else, change global version number
{
Name: "Bar",
Key: "bar:{{.ID}}:info",
To: model.Bar{},
Version: func() string {return model.FooCacheVersion + model.BarCacheVersion + "1"},
TTL: 5 * time.Minute,
},
Singleflight
to true
, Cacheme Get
command will be wrapped in a singleflight, so concurrent requests to same key will call Redis
only once. Let's use some example to explain this:product:1:info
may be hit 100000 times per second. Now you should turn on singleflight, and the actually redis hit may reduce to 5000.user:123:cart
, only the user himself can see that. Now no need to use singleflight, becauese there shouldn't be concurrent requests to that key.category:{{.categoryID}}:book:{{.bookID}}
with prefix cacheme
, version 1 will generate key: cacheme:category:1:book:3:v1
categoryID
and bookID
in generated code, as fetch func params.You can use custom logger with cacheme, your logger should implement cacheme logger interface:
type Logger interface {
Log(store string, key string, op string)
}
Here store
is the store tag, key
is cache key without prefix, op
is operation type. Default logger is NOPLogger
, just return and do nothing.
logger := &YourCustomLogger{}
client.SetLogger(logger)
Parallel benchmarks of Cacheme
SetParallelism(100)
, singleflight oncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkCachemeGetParallel-12 10000 198082 ns/op
BenchmarkCachemeGetParallel-12 1000000 9501 ns/op
Author: Yiling-J
Source Code: https://github.com/Yiling-J/cacheme-go
License: Apache-2.0 License