Service Weaver
Introduction
Taipei KAI CHU CHUNG
I/T Specialist, IBM
KAI CHU CHUNG
Cloud GDE
Gopher
I/T Specialist, IBM
@CageChung
https://kaichu.io
https://www.facebook.com/groups/GCPUG.TW
Agenda
01 Why Service Weaver
02 Go-kit
03 Step by Step Tutorial
04 FUNDAMENTALS
Service
Weaver
Why Service
Weaver
Service Weaver is an
open-source programming
framework that decreases
the overhead of developing
and managing applications.
Modern app Developers have more operational overhead
that reduces their ability to create differentiated value
Developers spend
at least 25% more
time planning &
designing for
cloud-native
environments.
47% of Go
Developers report
that testing &
debugging
microservice code
is their biggest
challenge.
App portability
(29%) between
local, staging
prod environments
is cited as a top
app development
challenge.
39% of Developers
lack sufficient
observability
tools resulting
in an average of
54 outages per
Enterprise
annually.
Longer upfront lead
time
Greater testing
responsibilities
More operational
complexities
Greater long-tail risks
Service Weaver targets key Developer feedback loops
that negatively impact feature velocity
Focus on business
logic and
minimize
cognitive load
for microservice
design decisions
upfront.
Conduct more
comprehensive
tests locally and
reduce the need
to manage remote
test
infrastructure.
Generate
prod-ready apps
and simplify
deployments and
safe rollouts
with just a few
commands.
Monitor logs,
metrics & traces
out of the box to
identify & triage
production
incidents faster.
Design & write code Experiment, test &
debug
Deployment & rollouts Observability & response
4.1K starts
Service Weaver OSS community
interest since inception
(Dec 2023)
201 forks
Go-kit
https://github.com/go-kit/kit
Go kit is a programming
toolkit for building
microservices (or elegant
monoliths) in Go.
How is a Go-kit microservice modeled?
Transport
Endpoint
Service
https://gokit.io/faq/#design-mdash-how-is-a-go-kit-microservice-modeled
Go-kit Endpoint
Go + microservices = Go kit - Speaker Deck - https://speakerdeck.com/peterbourgon/go-plus-microservices-equals-go-kit?slide=78
.
├── auth
├── circuitbreaker
├── cmd
├── endpoint
├── examples
├── log
├── metrics
├── ratelimit
├── sd
├── tracing
├── transport
├── util
├── .build.yml
├── .gitignore
├── .travis.yml
Go-kit components
Authentication: Basic, casbin, JWT.
Circuit Breaker: Hystrix, GoBreaker, and HandyBreaker.
Logging: Provide an interface for structured logging. Recognizes that logs
are data. They need context and semantics to be useful for analysis.
Supported formats are logfmt and JSON.
Metrics: Provides uniform interfaces for service instrumentation. Comes
with counters, gauges, and histograms. Has adapters for CloudWatch,
Prometheus, Graphite, DogStatsD, StatsD, expvar, and more.
Rate Limit: Uses Go's token bucket implementation.
Service Discovery: Consul, DNS SRV, etcd, Eureka, ZooKeeper, and more.
Tracing: OpenCensus, OpenTracing, and Zipkin.
Transport: AMQP, AWS Lambda, gRPC, HTTP, NATS, Thrift.
https://github.com/cage1016/gokit-workshop
go mod init github.com/cage1016/square
gk n s square
sed -i "" 's|Foo(ctx context.Context, s string) (res string, err error)|Square(ctx context.Context, s int64)
(res int64, err error)|g' internal/app/square/service/service.go
gk init square
sed -i "" 's/return res, err/return s * s, err/g' internal/app/square/service/service.go
gk add grpc square
cd pb/square && compile.sh
gk init grpc square
gk new cmd square
https://github.com/cage1016/gokit-workshop
curl -X POST -d '{"s": 5}' localhost:8180/square
$ go run cmd/square/main.go
level=info ts=2023-12-06T15:16:47.99649Z caller=main.go:74 service=square version= commitHash= buildTimeStamp=
level=info ts=2023-12-06T15:16:47.999764Z caller=main.go:153 service=square protocol=HTTP exposed=8180
level=info ts=2023-12-06T15:16:48.000035Z caller=main.go:185 service=square protocol=GRPC exposed=8181
level=info ts=2023-12-06T15:17:04.260239Z caller=logging.go:25 service=square method=Square s=5 err=null
level=info ts=2023-12-06T15:17:04.260547Z caller=middleware.go:19 service=square method=square
transport_error=null took=333.25µs
25.7K starts
Go-kit OSS community interest since
inception
(Dec 2023)
2.5k forks
Step by Step
Tutorial
Step 1: Split Your Application Into Components
Split your application into
components written as regular
Go interfaces. Don't fuss with
any networking or serialization
code. Focus on your business
logic.
type Adder interface {
Add(context.Context, int, int) (int, error)
}
type adder struct{
weaver.Implements[Adder]
}
func (adder) Add(_ context.Context, x, y int) (int, error)
return x + y, nil
}
Step 2: Call Your Components
Call your components using
regular Go method calls. No
need for RPCs or HTTP
requests. Forget about
versioning issues. The type
system guarantees
components are compatible.
var adder Adder = ... // See documentation
sum, err := adder.Add(ctx, 1, 2)
Step 3: Deploy Your Components
Test your app locally and
deploy it to the cloud. Service
Weaver lets you think about
what your code does without
worrying about where it's
running.
$ go test . # Test locally.
$ go run . # Run locally.
$ weaver gke deploy weaver.toml # Run on Google Cloud.
$ weaver kube deploy weaver.toml # Run on Kubernetes.
Step 4: Place Your Components
Run your components
wherever you want: in the
same process or on different
machines. Run as many
replicas as you want; scale up
or down to match load.
Components
func main() {
if err := weaver.Run(context.Background(), serve); err != nil {
log.Fatal(err)
}
}
// app is the main component of the application. weaver.Run creates
// it and passes it to serve.
type app struct{
weaver.Implements[weaver.Main]
}
// serve is called by weaver.Run and contains the body of the application.
func serve(context.Context, *app) error {
fmt.Println("Hello")
return nil
}
go mod tidy
weaver generate .
go run .
Hello
Multiple Components - Square
// Squarer component.
type Squarer interface {
Square(context.Context, int) (int, error)
}
// Implementation of the Squarer component.
type squarer struct {
weaver.Implements[Squarer]
}
func (s *squarer) Square(_ context.Context, n int) (int, error) {
return n * n, nil
}
Multiple Components
type app struct {
weaver.Implements[weaver.Main]
squarer weaver.Ref[Squarer]
}
func serve(ctx context.Context, app *app) error {
// Call the Square method.
var s Squarer = app.squarer.Get()
res, err := s.Square(ctx, 5)
if err != nil {
return err
}
fmt.Println(res)
return nil
}
go mod tidy
weaver generate .
go run .
╭───────────────────────────────────────────────────╮
│ app : devfest-2023 │
│ deployment : fcbd2574-48f1-413e-8098-4342b3098b3b │
╰───────────────────────────────────────────────────╯
25
Listeners
func serve(ctx context.Context, app *app) error {
http.HandleFunc("/square", func(w http.ResponseWriter, r *http.Request) {
valueStr := r.URL.Query().Get("value")
var input int
if valueStr == "" {
http.Error(w, "missing value", http.StatusBadRequest)
return
}
input, _ = strconv.Atoi(valueStr)
res, err := app.squarer.Get().Square(ctx, input)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Square %d, %d!n", input, res)
})
return http.Serve(app.square, nil)
}
[single]
listeners.hello = {address ="localhost:1234
$ SERVICEWEAVER_CONFIG=weaver.toml go run .
╭──────────────────────────────────────────
│ app : devfest-2023
│ deployment : 382c49e4-8c41-4862-9424-2f97
╰──────────────────────────────────────────
square listener available on 127.0.0.1:1234
$ curl localhost:12345/square?value=99
Square 99, 9801!
FUNDAMENTALS
Components: Components are Service Weaver's core abstraction. A component is a long-lived, possibly replicated
entity that exposes a set of methods
Logging: Service Weaver provides a logging API, weaver.Logger
Metrics: A histogram samples observations (usually things like request durations or response sizes) and counts
them in configurable buckets. It also provides a sum of all observed values.
Tracing: Service Weaver relies on OpenTelemetry to trace your application
Profiling: Service Weaver allows you to profile an entire Service Weaver application, even one that is deployed in
multiple processes across multiple machines
Routing: By default, when a client invokes a remote component's method, this method call will be performed by
one of possibly many component replicas, selected arbitrarily
Storage: By default, Service Weaver leaves the storage and retrieval of application data up to the developer
Testing: Service Weaver includes a weavertest package that you can use to test your Service Weaver applications
Versioning: Serving systems evolve over time. Whether you're fixing bugs or adding new features, it is inevitable
that you will have to roll out a new version of your system to replace the currently running version
Components
// Interfaces
a(context.Context) error
b(context.Context, int) error
c(context.Context) (int, error)
d(context.Context, int) (int, error)
// Implementation
type foo struct{
weaver.Implements[Foo]
// ...
}
// Listeners
type impl struct{
weaver.Implements[MyComponent]
foo weaver.Listener
Bar weaver.Listener
}
Logging
type Adder interface {
Add(context.Context, int, int) (int, error)
}
type adder struct {
weaver.Implements[Adder]
}
func (a *adder) Add(ctx context.Context, x, y int) (int, error) {
// adder embeds weaver.Implements[Adder] which provides the Logger method.
logger := a.Logger(ctx)
logger.Debug("A debug log.")
logger.Info("An info log.")
logger.Error("An error log.", fmt.Errorf("an error"))
return x + y, nil
}
D1103 08:55:15.650138 main.Adder 73ddcd04 adder.go:12 │ A debug log.
I1103 08:55:15.650149 main.Adder 73ddcd04 adder.go:13 │ An info log.
E1103 08:55:15.650158 main.Adder 73ddcd04 adder.go:14 │ An error log. err="an error"
Metrics
var (
addCount = metrics.NewCounter(
"add_count",
"The number of times Adder.Add has been called",
)
addConcurrent = metrics.NewGauge(
"add_concurrent",
"The number of concurrent Adder.Add calls",
)
addSum = metrics.NewHistogram(
"add_sum",
"The sums returned by Adder.Add",
[]float64{1, 10, 100, 1000, 10000},
)
)
type Adder interface {
Add(context.Context, int, int) (int, error)
}
type adder struct {
weaver.Implements[Adder]
}
func (*adder) Add(_ context.Context, x, y int) (int, e
addCount.Add(1.0)
addConcurrent.Add(1.0)
defer addConcurrent.Sub(1.0)
addSum.Put(float64(x + y))
return x + y, nil
}
Tracing
import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"github.com/ServiceWeaver/weaver"
)
func serve(ctx context.Context, app *app) error {
fmt.Printf("hello listener available on %vn", app.lis)
// Serve the /hello endpoint.
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!n", r.URL.Query().Get("name"))
})
// Create an otel handler to manually enable tracing.
otelHandler := otelhttp.NewHandler(http.DefaultServeMux, "http")
return http.Serve(lis, otelHandler)
}
Codelab
https://github.com/ServiceW
eaver/workshops
Workshop
1. Hello, World!
2. A Search Component
3. Unit Tests
4. An HTTP Server
5. Logging
6. Multiprocess Execution
7. A Cache Component
8. Routing the Cache
9. Metrics
10. ChatGPT
Resources
Service Weaver
● https://serviceweaver.dev
● https://github.com/ServiceW
eaver/weaver
QA

Devfest 2023 - Service Weaver Introduction - Taipei.pdf

  • 1.
    Service Weaver Introduction Taipei KAICHU CHUNG I/T Specialist, IBM
  • 2.
    KAI CHU CHUNG CloudGDE Gopher I/T Specialist, IBM @CageChung https://kaichu.io
  • 3.
  • 5.
    Agenda 01 Why ServiceWeaver 02 Go-kit 03 Step by Step Tutorial 04 FUNDAMENTALS Service Weaver
  • 6.
  • 7.
    Service Weaver isan open-source programming framework that decreases the overhead of developing and managing applications.
  • 8.
    Modern app Developershave more operational overhead that reduces their ability to create differentiated value Developers spend at least 25% more time planning & designing for cloud-native environments. 47% of Go Developers report that testing & debugging microservice code is their biggest challenge. App portability (29%) between local, staging prod environments is cited as a top app development challenge. 39% of Developers lack sufficient observability tools resulting in an average of 54 outages per Enterprise annually. Longer upfront lead time Greater testing responsibilities More operational complexities Greater long-tail risks
  • 9.
    Service Weaver targetskey Developer feedback loops that negatively impact feature velocity Focus on business logic and minimize cognitive load for microservice design decisions upfront. Conduct more comprehensive tests locally and reduce the need to manage remote test infrastructure. Generate prod-ready apps and simplify deployments and safe rollouts with just a few commands. Monitor logs, metrics & traces out of the box to identify & triage production incidents faster. Design & write code Experiment, test & debug Deployment & rollouts Observability & response
  • 10.
    4.1K starts Service WeaverOSS community interest since inception (Dec 2023) 201 forks
  • 11.
  • 12.
    Go kit isa programming toolkit for building microservices (or elegant monoliths) in Go.
  • 13.
    How is aGo-kit microservice modeled? Transport Endpoint Service https://gokit.io/faq/#design-mdash-how-is-a-go-kit-microservice-modeled
  • 14.
    Go-kit Endpoint Go +microservices = Go kit - Speaker Deck - https://speakerdeck.com/peterbourgon/go-plus-microservices-equals-go-kit?slide=78
  • 15.
    . ├── auth ├── circuitbreaker ├──cmd ├── endpoint ├── examples ├── log ├── metrics ├── ratelimit ├── sd ├── tracing ├── transport ├── util ├── .build.yml ├── .gitignore ├── .travis.yml Go-kit components Authentication: Basic, casbin, JWT. Circuit Breaker: Hystrix, GoBreaker, and HandyBreaker. Logging: Provide an interface for structured logging. Recognizes that logs are data. They need context and semantics to be useful for analysis. Supported formats are logfmt and JSON. Metrics: Provides uniform interfaces for service instrumentation. Comes with counters, gauges, and histograms. Has adapters for CloudWatch, Prometheus, Graphite, DogStatsD, StatsD, expvar, and more. Rate Limit: Uses Go's token bucket implementation. Service Discovery: Consul, DNS SRV, etcd, Eureka, ZooKeeper, and more. Tracing: OpenCensus, OpenTracing, and Zipkin. Transport: AMQP, AWS Lambda, gRPC, HTTP, NATS, Thrift.
  • 16.
    https://github.com/cage1016/gokit-workshop go mod initgithub.com/cage1016/square gk n s square sed -i "" 's|Foo(ctx context.Context, s string) (res string, err error)|Square(ctx context.Context, s int64) (res int64, err error)|g' internal/app/square/service/service.go gk init square sed -i "" 's/return res, err/return s * s, err/g' internal/app/square/service/service.go gk add grpc square cd pb/square && compile.sh gk init grpc square gk new cmd square
  • 17.
    https://github.com/cage1016/gokit-workshop curl -X POST-d '{"s": 5}' localhost:8180/square $ go run cmd/square/main.go level=info ts=2023-12-06T15:16:47.99649Z caller=main.go:74 service=square version= commitHash= buildTimeStamp= level=info ts=2023-12-06T15:16:47.999764Z caller=main.go:153 service=square protocol=HTTP exposed=8180 level=info ts=2023-12-06T15:16:48.000035Z caller=main.go:185 service=square protocol=GRPC exposed=8181 level=info ts=2023-12-06T15:17:04.260239Z caller=logging.go:25 service=square method=Square s=5 err=null level=info ts=2023-12-06T15:17:04.260547Z caller=middleware.go:19 service=square method=square transport_error=null took=333.25µs
  • 18.
    25.7K starts Go-kit OSScommunity interest since inception (Dec 2023) 2.5k forks
  • 19.
  • 20.
    Step 1: SplitYour Application Into Components Split your application into components written as regular Go interfaces. Don't fuss with any networking or serialization code. Focus on your business logic. type Adder interface { Add(context.Context, int, int) (int, error) } type adder struct{ weaver.Implements[Adder] } func (adder) Add(_ context.Context, x, y int) (int, error) return x + y, nil }
  • 21.
    Step 2: CallYour Components Call your components using regular Go method calls. No need for RPCs or HTTP requests. Forget about versioning issues. The type system guarantees components are compatible. var adder Adder = ... // See documentation sum, err := adder.Add(ctx, 1, 2)
  • 22.
    Step 3: DeployYour Components Test your app locally and deploy it to the cloud. Service Weaver lets you think about what your code does without worrying about where it's running. $ go test . # Test locally. $ go run . # Run locally. $ weaver gke deploy weaver.toml # Run on Google Cloud. $ weaver kube deploy weaver.toml # Run on Kubernetes.
  • 23.
    Step 4: PlaceYour Components Run your components wherever you want: in the same process or on different machines. Run as many replicas as you want; scale up or down to match load.
  • 24.
    Components func main() { iferr := weaver.Run(context.Background(), serve); err != nil { log.Fatal(err) } } // app is the main component of the application. weaver.Run creates // it and passes it to serve. type app struct{ weaver.Implements[weaver.Main] } // serve is called by weaver.Run and contains the body of the application. func serve(context.Context, *app) error { fmt.Println("Hello") return nil } go mod tidy weaver generate . go run . Hello
  • 25.
    Multiple Components -Square // Squarer component. type Squarer interface { Square(context.Context, int) (int, error) } // Implementation of the Squarer component. type squarer struct { weaver.Implements[Squarer] } func (s *squarer) Square(_ context.Context, n int) (int, error) { return n * n, nil }
  • 26.
    Multiple Components type appstruct { weaver.Implements[weaver.Main] squarer weaver.Ref[Squarer] } func serve(ctx context.Context, app *app) error { // Call the Square method. var s Squarer = app.squarer.Get() res, err := s.Square(ctx, 5) if err != nil { return err } fmt.Println(res) return nil } go mod tidy weaver generate . go run . ╭───────────────────────────────────────────────────╮ │ app : devfest-2023 │ │ deployment : fcbd2574-48f1-413e-8098-4342b3098b3b │ ╰───────────────────────────────────────────────────╯ 25
  • 27.
    Listeners func serve(ctx context.Context,app *app) error { http.HandleFunc("/square", func(w http.ResponseWriter, r *http.Request) { valueStr := r.URL.Query().Get("value") var input int if valueStr == "" { http.Error(w, "missing value", http.StatusBadRequest) return } input, _ = strconv.Atoi(valueStr) res, err := app.squarer.Get().Square(ctx, input) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintf(w, "Square %d, %d!n", input, res) }) return http.Serve(app.square, nil) } [single] listeners.hello = {address ="localhost:1234 $ SERVICEWEAVER_CONFIG=weaver.toml go run . ╭────────────────────────────────────────── │ app : devfest-2023 │ deployment : 382c49e4-8c41-4862-9424-2f97 ╰────────────────────────────────────────── square listener available on 127.0.0.1:1234 $ curl localhost:12345/square?value=99 Square 99, 9801!
  • 28.
  • 29.
    Components: Components areService Weaver's core abstraction. A component is a long-lived, possibly replicated entity that exposes a set of methods Logging: Service Weaver provides a logging API, weaver.Logger Metrics: A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a sum of all observed values. Tracing: Service Weaver relies on OpenTelemetry to trace your application Profiling: Service Weaver allows you to profile an entire Service Weaver application, even one that is deployed in multiple processes across multiple machines Routing: By default, when a client invokes a remote component's method, this method call will be performed by one of possibly many component replicas, selected arbitrarily Storage: By default, Service Weaver leaves the storage and retrieval of application data up to the developer Testing: Service Weaver includes a weavertest package that you can use to test your Service Weaver applications Versioning: Serving systems evolve over time. Whether you're fixing bugs or adding new features, it is inevitable that you will have to roll out a new version of your system to replace the currently running version
  • 30.
    Components // Interfaces a(context.Context) error b(context.Context,int) error c(context.Context) (int, error) d(context.Context, int) (int, error) // Implementation type foo struct{ weaver.Implements[Foo] // ... } // Listeners type impl struct{ weaver.Implements[MyComponent] foo weaver.Listener Bar weaver.Listener }
  • 31.
    Logging type Adder interface{ Add(context.Context, int, int) (int, error) } type adder struct { weaver.Implements[Adder] } func (a *adder) Add(ctx context.Context, x, y int) (int, error) { // adder embeds weaver.Implements[Adder] which provides the Logger method. logger := a.Logger(ctx) logger.Debug("A debug log.") logger.Info("An info log.") logger.Error("An error log.", fmt.Errorf("an error")) return x + y, nil } D1103 08:55:15.650138 main.Adder 73ddcd04 adder.go:12 │ A debug log. I1103 08:55:15.650149 main.Adder 73ddcd04 adder.go:13 │ An info log. E1103 08:55:15.650158 main.Adder 73ddcd04 adder.go:14 │ An error log. err="an error"
  • 32.
    Metrics var ( addCount =metrics.NewCounter( "add_count", "The number of times Adder.Add has been called", ) addConcurrent = metrics.NewGauge( "add_concurrent", "The number of concurrent Adder.Add calls", ) addSum = metrics.NewHistogram( "add_sum", "The sums returned by Adder.Add", []float64{1, 10, 100, 1000, 10000}, ) ) type Adder interface { Add(context.Context, int, int) (int, error) } type adder struct { weaver.Implements[Adder] } func (*adder) Add(_ context.Context, x, y int) (int, e addCount.Add(1.0) addConcurrent.Add(1.0) defer addConcurrent.Sub(1.0) addSum.Put(float64(x + y)) return x + y, nil }
  • 33.
    Tracing import ( "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "github.com/ServiceWeaver/weaver" ) func serve(ctxcontext.Context, app *app) error { fmt.Printf("hello listener available on %vn", app.lis) // Serve the /hello endpoint. http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s!n", r.URL.Query().Get("name")) }) // Create an otel handler to manually enable tracing. otelHandler := otelhttp.NewHandler(http.DefaultServeMux, "http") return http.Serve(lis, otelHandler) }
  • 34.
  • 35.
    Workshop 1. Hello, World! 2.A Search Component 3. Unit Tests 4. An HTTP Server 5. Logging 6. Multiprocess Execution 7. A Cache Component 8. Routing the Cache 9. Metrics 10. ChatGPT
  • 36.
  • 37.
    Service Weaver ● https://serviceweaver.dev ●https://github.com/ServiceW eaver/weaver
  • 38.