作者:薛港-移动云

原文来源: https://tidb.net/blog/df7a1570

PD提供两类API,一类是restful api ,一例是grpc 服务。这两类服务都依赖于ETCD。也就是依赖ETCD实例提供restful以及grpc服务。我们要做的就是启动ETCD的时候,参数配置里设置以下两类参数:

// Config holds the arguments for configuring an etcd
// server.
type Config struct {
// UserHandlers is for registering users handlers and only used
// for
// embedding etcd into other applications.
// The map key is the route path for the handler, and
// you must ensure it can’t be conflicted with etcd’s.
UserHandlers map[string]http.Handler json:"-"
// ServiceRegister is for registering users’ gRPC services. A
// simple usage example:
// cfg := embed.NewConfig()
// cfg.ServerRegister = func(s *grpc.Server) {
// pb.RegisterFooServer(s, &fooServer{})
// pb.RegisterBarServer(s, &barServer{})
// }
// embed.StartEtcd(cfg)
ServiceRegister func(*grpc.Server) json:"-"
}

ETCD里这两个参数初始代码如下:

// CreateServer creates the UNINITIALIZED pd server with
// given configuration.
func CreateServer(ctx context.Context, cfg *config.Config,
serviceBuilders …HandlerBuilder) (*Server, error) {
s := &Server{
cfg: cfg,
persistOptions: config.NewPersistOptions(cfg),
member: &member.Member{},
ctx: ctx,
startTimestamp: time.Now().Unix(),
}
// Adjust etcd config.
etcdCfg, err := s.cfg.GenEmbedEtcdConfig()
if len(serviceBuilders) != 0 {
userHandlers, err := combineBuilderServerHTTPService(ctx,
s, serviceBuilders…)
etcdCfg.UserHandlers = userHandlers
}
etcdCfg.ServiceRegister = func(gs *grpc.Server) {
pdpb.RegisterPDServer(gs, s)
diagnosticspb.RegisterDiagnosticsServer(gs, s)
}
}

先分析 combineBuilderServerHTTPService(ctx, s, serviceBuilders…),这个函数生成UserHanders 对象,用于提供restful api 服务
这个函数接受HandlerBuilder参数,返回一个map结构,key对应api path, value对应http.handle 用于处理restful 对应请求的处理逻辑。

这个函数本身没什么特别的,就是基于ServiceGroup信息,给一类API引入异常恢复中间件,依赖于第三方库( github.com/urfave/negroni)

func combineBuilderServerHTTPService(ctx context.Context, svr *Server, serviceBuilders …HandlerBuilder) (map[string]http.Handler, error) {
}

combineBuilderServerHTTPService的重点是参数serviceBuilders ,这是一个slice,元素类型是函数定义,ServiceGroup 用于生成对应的api的path,http.Handle用于restful 函数请求处理
// HandlerBuilder builds a server HTTP handler.
type HandlerBuilder func(context.Context, *Server) (http.Handler, ServiceGroup, error)

下面我们分析serviceBuilders参数实例化代码,
serviceBuilders := []server.HandlerBuilder{api.NewHandler,
swaggerserver.NewHandler, autoscaling.NewHandler}
serviceBuilders = append(serviceBuilders,
dashboard.GetServiceBuilders()…)
svr, err := server.CreateServer(ctx, cfg, serviceBuilders…)
serviceBuilders 是一个slice,每一个元素都是一个函数定义,返回api 处理handle,以及用于决定api path的数据结构serviceGroup.

我们重点分析slice元素api.NewHandler,然后一通都通,其它api定义也是类似框架:
// NewHandler creates a HTTP handler for API.
func NewHandler(ctx context.Context, svr *server.Server) (http.Handler, server.ServiceGroup, error) {
group := server.ServiceGroup{
Name: “core”,
IsCore: true,
}
router := mux.NewRouter()
r := createRouter(ctx, apiPrefix, svr)
router.PathPrefix(apiPrefix).Handler(negroni.New(
serverapi.NewRuntimeServiceValidator(svr, group),
serverapi.NewRedirector(svr),
negroni.Wrap®),
)

    return router, group, nil
}

重点分析createRouter(ctx, apiPrefix, svr),这个函数生成一个route,包含restful api的path以及处理函数

func createRouter(ctx context.Context, prefix string, svr *server.Server) *mux.Router {
operatorHandler := newOperatorHandler(handler, rd)
apiRouter.HandleFunc("/operators", operatorHandler.List).Methods(“GET”)
apiRouter.HandleFunc("/operators", operatorHandler.Post).Methods(“POST”)
apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Get).Methods(“GET”)
apiRouter.HandleFunc("/operators/{region_id}", operatorHandler.Delete).Methods(“DELETE”)

schedulerHandler := newSchedulerHandler(svr, rd)
apiRouter.HandleFunc("/schedulers", schedulerHandler.List).Methods("GET")
apiRouter.HandleFunc("/schedulers", schedulerHandler.Post).Methods("POST")
apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.Delete).Methods("DELETE")
apiRouter.HandleFunc("/schedulers/{name}", schedulerHandler.PauseOrResume).Methods("POST")


}

有意思的是如下代码

router.PathPrefix(apiPrefix).Handler(negroni.New(
serverapi.NewRuntimeServiceValidator(svr,
group),
serverapi.NewRedirector(svr),
negroni.Wrap®),
)
上面代码hander接受一个negroni创建的handle对象,这个对象可以理解服务中间件拦截器,也就是任何一个api请求会依次经过前面两个hanlde拦截,才会被业务route 处理。我们重点关注第二拦截器:
serverapi.NewRedirector(svr)。这个拦截器也很简单,主要检察当前接受请求的pd 实例,是否是leader,如果不是,那么这个pd作为服务转发方,给pd leader转发restful api 请求

func (h *redirector) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
r.Header.Set(RedirectorHeader, h.s.Name())

    leader := h.s.GetMember().GetLeader()if leader == nil {http.Error(w, "no leader", http.StatusServiceUnavailable)return}urls, err := config.ParseUrls(strings.Join(leader.GetClientUrls(), ","))client := h.s.GetHTTPClient()NewCustomReverseProxies(client, urls).ServeHTTP(w, r)
}

下面分析grpc服务,主要通过ETCD 参数注册grpc服务,下面的代码表明Server对象提供grpc服务

etcdCfg.ServiceRegister = func(gs *grpc.Server) {
pdpb.RegisterPDServer(gs, s)
diagnosticspb.RegisterDiagnosticsServer(gs, s)
}

我们先关注一下grpc提供服务api,然后后面分析每个模块细节时,一个个api开展深入分析

type PDServer interface {
// GetMembers get the member list of this cluster. It does not require
// the cluster_id in request matchs the id of this cluster.
GetMembers(context.Context, *GetMembersRequest) (*GetMembersResponse, error)
Tso(PD_TsoServer) error
Bootstrap(context.Context, *BootstrapRequest) (*BootstrapResponse, error)
IsBootstrapped(context.Context, *IsBootstrappedRequest) (*IsBootstrappedResponse, error)
AllocID(context.Context, *AllocIDRequest) (*AllocIDResponse, error)
GetStore(context.Context, *GetStoreRequest) (*GetStoreResponse, error)
PutStore(context.Context, *PutStoreRequest) (*PutStoreResponse, error)
GetAllStores(context.Context, *GetAllStoresRequest) (*GetAllStoresResponse, error)
StoreHeartbeat(context.Context, *StoreHeartbeatRequest) (*StoreHeartbeatResponse, error)
RegionHeartbeat(PD_RegionHeartbeatServer) error
GetRegion(context.Context, *GetRegionRequest) (*GetRegionResponse, error)
GetPrevRegion(context.Context, *GetRegionRequest) (*GetRegionResponse, error)
GetRegionByID(context.Context, *GetRegionByIDRequest) (*GetRegionResponse, error)
ScanRegions(context.Context, *ScanRegionsRequest) (*ScanRegionsResponse, error)
AskSplit(context.Context, *AskSplitRequest) (*AskSplitResponse, error)
ReportSplit(context.Context, *ReportSplitRequest) (*ReportSplitResponse, error)
AskBatchSplit(context.Context, *AskBatchSplitRequest) (*AskBatchSplitResponse, error)
ReportBatchSplit(context.Context, *ReportBatchSplitRequest) (*ReportBatchSplitResponse, error)
GetClusterConfig(context.Context, *GetClusterConfigRequest) (*GetClusterConfigResponse, error)
PutClusterConfig(context.Context, *PutClusterConfigRequest) (*PutClusterConfigResponse, error)
ScatterRegion(context.Context, *ScatterRegionRequest) (*ScatterRegionResponse, error)
GetGCSafePoint(context.Context, *GetGCSafePointRequest) (*GetGCSafePointResponse, error)
UpdateGCSafePoint(context.Context, *UpdateGCSafePointRequest) (*UpdateGCSafePointResponse, error)
UpdateServiceGCSafePoint(context.Context, *UpdateServiceGCSafePointRequest) (*UpdateServiceGCSafePointResponse, error)
SyncRegions(PD_SyncRegionsServer) error
GetOperator(context.Context, *GetOperatorRequest) (*GetOperatorResponse, error)
SyncMaxTS(context.Context, *SyncMaxTSRequest) (*SyncMaxTSResponse, error)
SplitRegions(context.Context, *SplitRegionsRequest) (*SplitRegionsResponse, error)
GetDCLocationInfo(context.Context, *GetDCLocationInfoRequest) (*GetDCLocationInfoResponse, error)
}

PD api基础框架源码分析相关推荐

  1. SPI驱动框架源码分析

     SPI驱动框架源码分析 2013-04-12 16:13:08 分类: LINUX SPI驱动框架源码分析 SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设 ...

  2. Linux驱动修炼之道-SPI驱动框架源码分析(上)

    Linux驱动修炼之道-SPI驱动框架源码分析(上)   SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设备可工作于m/s模式.主设备发起数据帧,允许多个从设 ...

  3. S3C24XX DMA框架源码分析

    基于S3C2440 的DMA 框架源码分析 基于S3C2440 的DMA 框架源码分析 二寻根溯源 1 设备类的注册 2 s3c2410_dma_init 3 s3c24xx_dma_order_se ...

  4. Java集合类框架源码分析 之 LinkedList源码解析 【4】

    上一篇介绍了ArrayList的源码分析[点击看文章],既然ArrayList都已经做了介绍,那么作为他同胞兄弟的LinkedList,当然必须也配拥有姓名! Talk is cheap,show m ...

  5. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  6. windows C++ Opengl基础框架源码

    windows C++ Opengl基础框架源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SD ...

  7. Android框架源码分析——从设计模式角度看 Retrofit 核心源码

    Android框架源码分析--从设计模式角度看 Retrofit 核心源码 Retrofit中用到了许多常见的设计模式:代理模式.外观模式.构建者模式等.我们将从这三种设计模式入手,分析 Retrof ...

  8. php+yii框架,yii框架源码分析(一)

    yii框架源码分析(一) 本文将对yii中的mvc,路由器,filter,组件机制等最主要的部分进行自己的一点浅析,力求说明自己做一个php mvc不是那么的遥不可及,其实是很简单的. 源码基于yii ...

  9. golang gin框架源码分析(二)---- 渐入佳境 摸索Engine ServeHTTP访问前缀树真正原理

    文章目录 全系列总结博客链接 前引 golang gin框架源码分析(二)---- 渐入佳境 摸索Engine ServeHTTP访问前缀树真正远原理 1.再列示例代码 从示例代码入手 2.r.Run ...

最新文章

  1. 一份很不错的敏捷产品需要文档模板
  2. 圆柱属于能滚动的物体吗_中班科学活动教案:滚动的物体教案(附教学反思)
  3. IE6.0,ie7.0与Firefox的CSS兼容性问题
  4. 【软件开发底层知识修炼】二十五 ABI之函数调用约定二之函数返回值为结构体时的约定
  5. 拓扑排序 codevs 4040 cojs 438
  6. atitit.hbnt orm db 新新增更新最佳实践o7
  7. asp.net中实现登陆的时候用SSL
  8. BuildTools下载地址
  9. STC15单片机使用AT命令的C语言编程
  10. git从本地添加项目到远程仓库
  11. (转)一些个人感悟(2008)
  12. 计算机专硕可以考事业编吗,专硕考事业编制遇冷
  13. python代码实现卷积示意图快速制作
  14. python的cfg是什么模块_python操作cfg配置文件方式
  15. 基于FPGA的VGA时序控制器
  16. 15本经典金融投资著作
  17. 2020北大软微复试知识整理
  18. RecyclerView中播放视频
  19. WPARAM 与 LPARAM 参数的解析 [C#、WinAPI]
  20. 【王喆-推荐系统】评估篇-(task2)推荐模型评估指标

热门文章

  1. WSL2 中 docker volume 的位置
  2. Charles的基本使用及教程
  3. 微电网经济调度(风、光、柴油机、蓄电池、电网交互)(Matlab代码实现)
  4. 20172319 《程序设计与数据结构》第11周学习总结
  5. 中国天气预报城市代码
  6. 2019年秋冬季读书笔记
  7. 云南原乡|坐拥腾冲旅游绝佳位置 是旅居养生度假投资首选
  8. 关于COM中变体类型VARIANT
  9. 分享一道面试题:10ml试管10ml的水,一个7ml,一个3ml,将10ml分成两个5ml的
  10. 策划的权限、视野与产品的最终高度