微服务架构的特性:
微服务带来的好处:
微服务架构面临的问题:交互过多导致的服务间网络通信问题(分布式计算8个谬论)
如何管理和控制服务间的通信:
type animal struct{
name string
Speak string
}
type Horse struct{
animal
sound string
}
一般,不讲包中的struct直接暴露给外部,所以struct的名字会以小写字母开头,不将其导出。
外部如果需要创建struct的实例,调用struct的操作,那么就给这个struct编写一个构造函数。
// abc/abc.go文件内容:
package abc
type animal struct{
name string
Speak string
}
func NewAnimal() *animal{
a := new(animal)
a.name = "ok"
return a
}
// test.go内容:
package main
import (
"fmt"
"./abc"
)
func main() {
t1 := abc.NewAnimal()
// t1.name = "haha" // 无法访问name属性
t1.Speak = "hhhh" // 可以访问Speak属性
fmt.Println(t1.Speak)
}
当内部struct嵌套进外部struct时,内部struct的方法也会被嵌套,也就是说外部struct拥有了内部struct的方法。
但是需要注意方法的首字母大小写问题。由于内、外struct在同一包内,所以直接在该包内构建外部struct实例,外部struct实例是可以直接访问内部struct的所有方法的。但如果在其它包内构建外部struct实例,该实例将无法访问内部struct中首字母小写的方法。
同一个包内:
package main
import (
"fmt"
)
type person struct {
name string
age int
}
// 未导出方法
func (p *person) speak() {
fmt.Println("speak in person")
}
// 导出的方法
func (p *person) Sing() {
fmt.Println("Sing in person")
}
// Admin exported
type Admin struct {
person
salary int
}
func main() {
a := new(Admin)
a.speak() // 正常输出
a.Sing() // 正常输出
}
不同包的struct实例化,不能导出小写方法
package abc
import "fmt"
// 未导出的person
type person struct {
name string
age int
}
// 未导出的方法
func (p *person) speak() {
fmt.Println("speak in person")
}
// 导出的方法
func (p *person) Sing() {
fmt.Println("Sing in person")
}
// Admin exported
type Admin struct {
person
salary int
}
// ----------------------
package main
import "./abc"
func main() {
a := new(abc.Admin)
// 下面报错
// a.speak()
// 下面正常
a.Sing()
}
Fx是一个golang版本的依赖注入框架,在项目中的引用:
import "github.com/uber-go/fx"
Fx应用程序,使用依赖注入来消除全局变量,而无需手动将函数连接在一起。
同时,go.uber.org/fx/fxtest也为Fx应用程序提供了端到端测试的能力。
func NewHandler(logger *log.Logger) (http.Handler, error) {
logger.Print("Executing NewHandler.")
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
logger.Print("Got a request.")
}), nil
}
// 为了实现下面这种长时的http server
// srv, err := NewServer() // some long-running network server
// if err != nil {
// log.Fatalf("failed to construct server: %v", err)
// }
// // Construct other objects as necessary.
// go srv.Start()
// defer srv.Stop()
// 通过Fx实现的方法,在下面这个构造方法中,实现了server的启动并且返回这个
// server的handler
func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {
logger.Print("Executing NewMux.")
// 第一步,构造一个mux和server(mux -- multiplexer:多路器)
// 在所有的handle都注册前,不启动server
mux := http.NewServeMux()
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
// 如果调用newmux,我们知道另一个功能正在使用mux。
// 在这种情况下,我们将使用Lifecycle类型注册启动并停止我们的HTTP服务器的钩子。
//
// Hooks 按照依赖顺序执行. At startup, NewLogger's hooks 在 NewMux的hook之前运行。
// 在结束时,循序按照相反顺序
//
// OnStart产生错误,会返回一个error,并且中断启动过程,然后执行OnStop部分。
//
// OnStop执行错误会日志记录下这个错误,并继续执行其余的Hooks
lc.Append(fx.Hook{
// 为了缓解死锁的影响,Fx在OnStart和OnStop 的Hook增加了一个时间限制.
// 默认的,hooks有总共15秒去执行完. 超时时间会通过 Go's usual context.Context 传递.
OnStart: func(context.Context) error {
logger.Print("Starting HTTP server.")
// In production, we'd want to separate the Listen and Serve phases for
// better error-handling.
go server.ListenAndServe()
return nil
},
OnStop: func(ctx context.Context) error {
logger.Print("Stopping HTTP server.")
return server.Shutdown(ctx)
},
})
return mux
}
// Register在MUX上安装我们的HTTP处理程序。
//
// Register 是典型的顶层应用函数: 它需要一个普通类型,如servemux,
// 它通常来自第三方库,并将其引入包含我们应用程序逻辑的类型.
// 在这个例子中,介绍包括注册HTTP处理程序。
// 其他典型示例包括注册RPC程序和启动队列消费者。
//
// FX调用这些函数调用,并从上面的构造函数不同地对待。
// 它们的参数仍然通过依赖项注入提供,并且它们可能仍然返回错误以指示故障,但忽略任何其他返回值。
//
// Unlike constructors, invocations are called eagerly. See the main function
// below for details.
func Register(mux *http.ServeMux, h http.Handler) {
mux.Handle("/", h)
}
func main() {
app := fx.New(
// Provide 所有我们需要的构造函数, 告诉 Fx 我们想要如何去构造
// *log.Logger, http.Handler, and *http.ServeMux types.
// Remember that constructors are called lazily, so this block doesn't do
// much on its own.
fx.Provide(
NewLogger,
NewHandler,
NewMux,
),
// 由于构造函数懒惰地称为,我们需要一些调用启动我们的应用程序。
// 在这种情况下,我们将使用Register。由于它取决于http.handler和* http.servemux,
// 因此调用它需要fx来使用上面的构造函数构建这些类型。
// 由于我们调用NewMux,我们还注册生命周期挂钩以启动并停止HTTP服务器。
fx.Invoke(Register),
// 这部分是可选的. 使用下面的代码, 你可以控制Fx在哪logs5它的事件
// 在本例中, we're using a NopLogger to keep
// our test silent. Normally, you'll want to use an
// fxevent.ZapLogger or an fxevent.ConsoleLogger.
fx.WithLogger(
func() fxevent.Logger {
return fxevent.NopLogger
},
),
)
// In a typical application, we could just use app.Run() here. Since we
// don't want this example to run forever, we'll use the more-explicit Start
// and Stop.
startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := app.Start(startCtx); err != nil {
log.Fatal(err)
}
// Normally, we'd block here with <-app.Done(). Instead, we'll make an HTTP
// request to demonstrate证明 that our server is running.
if _, err := http.Get("http://localhost:8080/"); err != nil {
log.Fatal(err)
}
stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := app.Stop(stopCtx); err != nil {
log.Fatal(err)
}
}
Output:
Executing NewLogger.
Executing NewMux.
Executing NewHandler.
Starting HTTP server.
Got a request.
Stopping HTTP server.