以下是对 Go Micro 中涉及的一些设计模式的代码示例及详细解释:
**一、微服务架构模式(服务注册与发现)
**1. 定义服务:
package main
import (
"context"
"log"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *HelloRequest, rsp *HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
type HelloRequest struct {
Name string
}
type HelloResponse struct {
Greeting string
}
func main() {
// 创建内存注册中心
reg := memory.NewRegistry()
// 创建服务
service := micro.NewService(
micro.Name("greeter"),
micro.Registry(reg),
)
// 初始化服务
service.Init()
// 注册服务
if err := micro.RegisterHandler(service.Server(), new(Greeter)); err!= nil {
log.Fatal(err)
}
// 启动服务
if err := service.Run(); err!= nil {
log.Fatal(err)
}
}
解释:
- **微服务架构模式**:将一个大型的应用程序拆分为多个小型的、独立的服务,每个服务专注于特定的业务功能。在这个例子中,我们定义了一个名为`Greeter`的服务,它提供了一个`Hello`方法用于向用户打招呼。
- **服务注册与发现**:服务提供者将自己的服务信息注册到注册中心,服务消费者从注册中心获取服务提供者的地址信息,然后进行调用。这有助于实现服务的动态发现和负载均衡。在这个例子中,我们创建了一个内存注册中心`memory.NewRegistry()`,并在创建服务时通过`micro.Registry(reg)`将其传入。这样,当服务启动时,它会自动注册到注册中心,其他服务可以通过注册中心发现这个服务并进行调用。
**二、代理模式(RPC 代理)**
在 Go Micro 中,当客户端发起 RPC 请求时,实际上是通过一个代理对象进行的。以下是一个简单的客户端代码示例:
解释:
- **代理模式**:为其他对象提供一种代理以控制对这个对象的访问。在 Go Micro 中,客户端发起的 RPC 请求实际上是通过一个代理对象发送到服务端。代理对象负责处理网络通信、序列化和反序列化等操作,对客户端隐藏了底层的通信细节。
- 在这个例子中,`greeterClient := greeter.NewGreeterService("greeter", service.Client())`创建了一个代理对象,它代表了远程的`Greeter`服务。当调用`greeterClient.Hello`时,实际上是通过这个代理对象将请求发送到远程服务,并接收响应。代理对象负责处理网络通信、序列化和反序列化等操作,使得客户端可以像调用本地方法一样调用远程服务。
**三、门面模式(服务接口封装)**
package main
import (
"context"
"log"
"github.com/micro/go-micro/v2"
greeter "github.com/micro/examples/greeter/srv/proto/greeter"
)
func main() {
// 创建新的服务
service := micro.NewService(micro.Name("greeter-client"))
service.Init()
// 创建 Greeter 服务的客户端
greeterClient := greeter.NewGreeterService("greeter", service.Client())
// 调用远程服务
rsp, err := greeterClient.Hello(context.Background(), &greeter.HelloRequest{Name: "John"})
if err!= nil {
log.Fatal(err)
}
log.Println(rsp.Greeting)
}
以服务端为例:
package main
import (
"log"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/server"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *HelloRequest, rsp *HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
type HelloRequest struct {
Name string
}
type HelloResponse struct {
Greeting string
}
func main() {
service := micro.NewService(
micro.Name("greeter"),
)
service.Init()
// 注册服务到 Micro 框架
if err := micro.RegisterHandler(service.Server(), new(Greeter)); err!= nil {
log.Fatal(err)
}
if err := service.Run(); err!= nil {
log.Fatal(err)
}
}
解释:
- **门面模式**:为子系统中的一组接口提供一个一致的界面,定义了一个高层接口,使得子系统更容易使用。在 Go Micro 中,为开发者提供了简洁的接口来定义服务。例如,通过实现一个结构体的方法来定义服务的处理逻辑,然后使用`micro.RegisterHandler`将服务注册到框架中。这封装了复杂的网络通信和服务管理逻辑,让开发者专注于业务逻辑的实现。
- 在这个例子中,我们定义了一个`Greeter`结构体,实现了`Hello`方法来处理请求。然后,通过`micro.RegisterHandler`将这个服务注册到 Micro 框架中。框架会自动处理网络通信、请求路由等复杂操作,开发者只需要关注业务逻辑的实现。
**四、插件模式(可插拔组件)**
以使用不同的注册中心插件为例:
package main
import (
"log"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/etcd"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *HelloRequest, rsp *HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
type HelloRequest struct {
Name string
}
type HelloResponse struct {
Greeting string
}
func main() {
// 创建 etcd 注册中心
reg := etcd.NewRegistry(
registry.Addrs("localhost:2379"),
)
service := micro.NewService(
micro.Name("greeter"),
micro.Registry(reg),
)
service.Init()
if err := micro.RegisterHandler(service.Server(), new(Greeter)); err!= nil {
log.Fatal(err)
}
if err := service.Run(); err!= nil {
log.Fatal(err)
}
}
解释:
- **插件模式**:允许软件的某些部分可以被动态地替换或扩展,而不影响整体的功能。在 Go Micro 中,支持各种插件,如注册中心插件、传输插件、编解码插件等。这种设计允许开发者根据具体需求选择不同的插件,实现了高度的可扩展性和灵活性。
- 在这个例子中,我们使用了`etcd.NewRegistry`创建了一个 etcd 注册中心插件,并在创建服务时通过`micro.Registry(reg)`将其传入。这样,我们可以轻松地切换注册中心插件,而不需要修改服务的核心代码。例如,如果我们想要使用 Consul 注册中心,只需要引入 Consul 的插件包,并使用`consul.NewRegistry`创建注册中心即可。
**五、装饰器模式(中间件)**
package main
import (
"context"
"log"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/server"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *HelloRequest, rsp *HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
type HelloRequest struct {
Name string
}
type HelloResponse struct {
Greeting string
}
func logMiddleware(fn server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
log.Println("Before handling request")
err := fn(ctx, req, rsp)
log.Println("After handling request")
return err
}
}
func main() {
service := micro.NewService(
micro.Name("greeter"),
)
service.Init()
// 使用中间件装饰服务处理函数
service.Server().Handle(
service.Server().NewHandler(&Greeter{}, logMiddleware),
)
if err := service.Run(); err!= nil {
log.Fatal(err)
}
}
解释:
- **装饰器模式**:动态地给一个对象添加一些额外的职责,而不改变其结构。在 Go Micro 中,允许使用中间件来装饰服务的处理函数。中间件可以在请求处理的前后执行一些额外的操作,如日志记录、身份验证、性能监控等。这类似于装饰器模式,为服务添加额外的功能而不修改服务的核心逻辑。
- 在这个例子中,我们定义了一个中间件函数`logMiddleware`,它在服务处理函数执行前后打印日志。然后,在注册服务处理函数时,使用`service.Server().NewHandler(&Greeter{}, logMiddleware)`将中间件应用到服务处理函数上。这样,每次请求到达服务时,中间件会先打印日志,然后调用服务处理函数,最后再打印日志。这种方式可以在不修改服务核心逻辑的情况下,为服务添加日志记录功能。如果需要添加其他功能,如身份验证、性能监控等,只需要定义相应的中间件函数,并将其应用到服务处理函数上即可。