目录
第1讲:为什么说云原生重构了互联网产品开发模式?
第2讲:云原生基础架构的组成以及云原生应用的特征
第3讲:微服务架构是如何演进的?
- 通过垂直分层结构,可以让逻辑请求,也可以为不同层提供不同性能的硬件支持,比如用户界面层需要分发带宽,而业务逻辑层需要算力
- SOA有服务提供者与服务使用者
第4讲:DDD 领域场景分析的战略模式
第5讲:为什么说 Service Meh 是下一代微服务架构?
- 微服务拆分是复杂的,如下图拆分的微服务
第6讲: Go 语言开发快速回顾:语法、数据结构和流程控制
- go 命令
- go的指针应用
- go的struct类型,也是go的类写法
- 在struct定义的变量,大写是包外可以访问,小写是包外部能访问
- go数组使用,声明时,需要固定数组大小
- go的切片slice,即动态数组
- appand切片
- 也可以通过不指定数组大小,来声明切片
- go的map类型
- map的获取
- 判断某键是否存在map中
- go 的for循环语句
- go 中的switch不需要写case
- go defer延迟执行函数,defer是先进后出(解析是历届解析)
第7讲: 如何使用 Go 更好地开发并发程序?
- 以上总共生成了5个一组,共3组,15个(0-14)的生产;再收到queue的消息后,消费也会是15个,顺序是不确定的
go
package main
import (
"fmt"
"time"
)
func send(ch chan int, begin int) {
for i := begin; i < begin+10; i++ {
ch <- i
}
}
func receive(ch <-chan int) {
val := <-ch
fmt.Println("receive:", val)
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go send(ch1, 0)
go receive(ch2)
time.Sleep(time.Second)
for {
select {
case val := <-ch1:
fmt.Println("get value from ch1:", val)
case ch2 <- 2:
fmt.Println("send value by ch2")
case <-time.After(time.Second):
fmt.Println("timeout")
return
}
}
}
go
package main
import (
"context"
"fmt"
"time"
)
const DB_ADDRESS = "db_address"
const CALCULATE_VALUE = "calculate_value"
func readDB(ctx context.Context, cost time.Duration) {
fmt.Println("db address is", ctx.Value(DB_ADDRESS))
select {
case <-time.After(cost):
fmt.Println("read data from db")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
func calculate(ctx context.Context, cost time.Duration) {
fmt.Println("calculate value is", ctx.Value(CALCULATE_VALUE))
select {
case <-time.After(cost):
fmt.Println("calculate finish")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, DB_ADDRESS, "localhost:10086")
ctx = context.WithValue(ctx, CALCULATE_VALUE, 1234)
ctx, cancel := context.WithTimeout(ctx, time.Second*2) //6秒则输出finish
defer cancel()
go readDB(ctx, time.Second*4)
go calculate(ctx, time.Second*4)
time.Sleep(time.Second * 5)
}
第8讲: 如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
-
配置代理
-
安装插件
-
项目需要引入的包
redis的安装:
https://github.com/microsoftarchive/redis/releases (微软的Redis版)
https://gitee.com/qishibo/AnotherRedisDesktopManager/releases/tag/v1.7.1(Redis工具)
cmd输入,redis-server.exe --service-install redis.windows-service.conf 完成服务添加
cmd输入,redis-cli.exe ,输入keys * 查看缓存情况,也可以用redis工具查看
go
package main
import (
"context"
"flag"
"fmt"
"github.com/longjoy/micro-go-course/section08/user/dao"
"github.com/longjoy/micro-go-course/section08/user/endpoint"
"github.com/longjoy/micro-go-course/section08/user/redis"
"github.com/longjoy/micro-go-course/section08/user/service"
"github.com/longjoy/micro-go-course/section08/user/transport"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
)
func main() {
//* 使用了flag定义了带有更多信息的变量
var (
// 服务地址和服务名
servicePort = flag.Int("service.port", 10086, "service port")
)
flag.Parse()
//* 空的 Context,用作 Context 树的根节点,是其他 Context 的父级,一般用于初始化 Context 链
ctx := context.Background()
errChan := make(chan error)
//* 使用dao包,用于数据库操作,打开数据库,初始化db (Gorm)
err := dao.InitMysql("127.0.0.1", "3306", "root", "***", "user")
if err != nil {
log.Fatal(err)
}
//* 打开redis
err = redis.InitRedis("127.0.0.1", "6379", "")
if err != nil {
log.Fatal(err)
}
//* 把UserDAOImpl对象(包含SelectByEmail与Save)给到service,初始化userService,见代码1
userService := service.MakeUserServiceImpl(&dao.UserDAOImpl{})
//* UserEndpoints是2个endpoint.Endpoint类型的函数,如下
//* type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
//* 并make初始化,见代码2
userEndpoints := &endpoint.UserEndpoints{
endpoint.MakeRegisterEndpoint(userService),
endpoint.MakeLoginEndpoint(userService),
}
//* 把路由封装起来,之后启动服务器,见代码3
r := transport.MakeHttpHandler(ctx, userEndpoints)
//* 首先,定义了一个 servicePort 并创建了一个错误通道 errChan。
//* 然后,使用 http.NewServeMux() 创建一个路由处理程序 r,并为根路由添加了一个简单的处理函数。
//* 启动了两个 goroutine:一个用于启动 HTTP 服务器,另一个用于监听系统信号。
//* 最后,从 errChan 接收错误信息,当按下 Ctrl+C 时,会接收到 SIGINT 信号,程序打印错误信息并退出。
go func() {
errChan <- http.ListenAndServe(":"+strconv.Itoa(*servicePort), r)
}()
go func() {
// 监控系统信号,等待 ctrl + c 系统信号通知服务关闭
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errChan <- fmt.Errorf("%s", <-c)
}()
error := <-errChan
log.Println(error)
}
- 代码1:MakeUserServiceImpl操作
go
//* 定义了UserService接口
type UserService interface {
// 登录接口
Login(ctx context.Context, email, password string) (*UserInfoDTO, error)
// 注册接口
Register(ctx context.Context, vo *RegisterUserVO) (*UserInfoDTO, error)
}
//* UserServiceImpl为接口的一个实现,并有一个userDAO的对象值
type UserServiceImpl struct {
userDAO dao.UserDAO
}
func MakeUserServiceImpl(userDAO dao.UserDAO) UserService {
return &UserServiceImpl{
userDAO: userDAO,
}
}
//* 以下函数实现了userService接口的一个具体实现UserServiceImpl
//* 通过userService取到userDAO,符合依赖倒置原则
func (userService *UserServiceImpl) Login(ctx context.Context, email, password string) (*UserInfoDTO, error){
user, err := userService.userDAO.SelectByEmail(email)
......
}
func (userService UserServiceImpl) Register(ctx context.Context, vo *RegisterUserVO) (*UserInfoDTO, error){
......
}
- 代码2:MakeLoginEndpoint:Endpoint的初始化
go
type UserEndpoints struct {
RegisterEndpoint endpoint.Endpoint
LoginEndpoint endpoint.Endpoint
}
//* 发送数据格式
type LoginRequest struct {
Email string
Password string
}
//* 接受数据格式
type LoginResponse struct {
//* 转换json时,这个字段名为user_info
UserInfo *service.UserInfoDTO `json:"user_info"`
}
//* 返回endpoint.Endpoint函数,接受LoginRequest格式,返回LoginResponse格式
func MakeLoginEndpoint(userService service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
//* 通过指针,断言了request的类型,后面的req.Email, req.Password就可以成立
req := request.(*LoginRequest)
userInfo, err := userService.Login(ctx, req.Email, req.Password)
//* 通过userService返回需要的userInfo查询数据,并返回
return &LoginResponse{UserInfo:userInfo}, err
}
}
- 代码3
go
package transport
import (
"context"
"encoding/json"
"errors"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/transport"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/longjoy/micro-go-course/section08/user/endpoint"
"net/http"
"os"
)
var (
ErrorBadRequest = errors.New("invalid request parameter")
)
// MakeHttpHandler make http handler use mux
func MakeHttpHandler(ctx context.Context, endpoints *endpoint.UserEndpoints) http.Handler {
//* mux 是一个强大的 HTTP 路由和分发库
r := mux.NewRouter()
//* 日志打印
kitLog := log.NewLogfmtLogger(os.Stderr)
kitLog = log.With(kitLog, "ts", log.DefaultTimestampUTC)
kitLog = log.With(kitLog, "caller", log.DefaultCaller)
//* 配置服务器的错误处理和错误编码
options := []kithttp.ServerOption{
kithttp.ServerErrorHandler(transport.NewLogErrorHandler(kitLog)),
kithttp.ServerErrorEncoder(encodeError),
}
//* 建立一个路由
r.Methods("POST").Path("/register").Handler(kithttp.NewServer(
endpoints.RegisterEndpoint, //绑定处理的endpoint
decodeRegisterRequest, //数据获取解析,并交给endpoint处理
encodeJSONResponse, //把结果转成Json,发送给客户端,是发送前的处理
options...,
))
r.Methods("POST").Path("/login").Handler(kithttp.NewServer(
endpoints.LoginEndpoint,
decodeLoginRequest,
encodeJSONResponse,
options...,
))
return r
}
func decodeRegisterRequest(_ context.Context, r *http.Request) (interface{}, error) {
username := r.FormValue("username")
password := r.FormValue("password")
email := r.FormValue("email")
if username == "" || password == "" || email == ""{
return nil, ErrorBadRequest
}
return &endpoint.RegisterRequest{
Username:username,
Password:password,
Email:email,
},nil
}
func decodeLoginRequest(_ context.Context, r *http.Request) (interface{}, error) {
email := r.FormValue("email")
password := r.FormValue("password")
if email == "" || password == "" {
return nil, ErrorBadRequest
}
return &endpoint.LoginRequest{
Email:email,
Password:password,
},nil
}
func encodeJSONResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json;charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
switch err {
default:
w.WriteHeader(http.StatusInternalServerError)
}
json.NewEncoder(w).Encode(map[string]interface{}{
"error": err.Error(),
})
}
第9讲: 货运平台应用的微服务划分
https://https://www.bilibili.com/video/BV1Gg4y1u77D/?spm_id_from=333.788.videopod.episodes&vd_source=707ec8983cc32e6e065d5496a7f79ee6&p=10
在这里插入图片描述
第10讲: 微服务 Docker 容器化部署和 Kubernete 容器编排
第11讲: 如何结合 Jenkin 完成持续化集成和自动化测试?
第12讲: 服务注册与发现如何满足服务治理?
第13讲: 如何基于 Conul 给微服务添加服务注册与发现?
第14讲: 如何在 Go-kit 和 Service Meh 中进行服务注册与发现?
第15讲: 微服务间如何进行远程方法调用?
第16讲: Go RPC 如何实现服务间通信?
第17讲: Go-kit 如何集成 gRPC?
结束:
作为想了解下golang的微服务部分,就先看到这里,此教程给到众多惊喜;
云原生与微服务是当下开发大系统有力的解决方案,这篇教程更像是一场淋漓尽致的讲座,介绍了当下的云原生的各个构成,非常值得一学。