# distill-base **Repository Path**: banyanhouse/distill-base ## Basic Information - **Project Name**: distill-base - **Description**: 一个基础项目结构 - **Primary Language**: Go - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-22 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: webframework **Tags**: None ## README # 建立第一个到注册中心的REST服务 #### 在go.mod文件中引入distill-infra框架,目前是v0.0.8的版本 ``` require ( gitee.com/banyanhouse/distill-infra v0.0.8 github.com/kataras/iris v11.1.1+incompatible ) ``` #### 在项目中创建brun目录,创建文件main.go ```go package main import ( _ "gitee.com/banyanhouse/distill-base" conf "gitee.com/banyanhouse/distill-infra/config" "log" "os" //_ "gitee.com/Hu-Lyndon/distill-resk" infra "gitee.com/banyanhouse/distill-infra" "gitee.com/banyanhouse/distill-infra/utils" ) var ( Version string = "0.0.1" //h bool //v bool configFilePath string //modelFilePath string //wwwDir string //swaggerDir string //port int ) func main() { // 获取程序运行,所需配置文件的路径 if configFilePath == "" { configFilePath = utils.GetCurrentPath("./config/") + "/config.toml" } extensionMap := make(map[string]interface{}) extensionMap["gatewayConfig"] = configFilePath // 读取到config.toml后真实的存储对象 appConfig := &conf.TomlConfig{} //utils.ConfigFromFile(configFilePath, config) configFiles := []string{configFilePath, utils.GetCurrentPath("../config/") + "/config.toml"} utils.ConfigFromSlice(configFiles, appConfig) // 启动框架 app := infra.New(appConfig) app.Start() // 之前是因为没有阻塞的starter,有了Iris就不用下面的了 //c := make(chan int) //<- c } func ErrHandler(err error) { if err != nil { log.Fatal(err.Error()) os.Exit(-1) } } ``` #### 在项目中创建config目录,创建文件config.toml文件 ```toml # 应用配置 [app] httpPort = 18185 name = "hello_world" enabled = true time = "10s" testing = false # 日志配置 [log] enableLineLog = true dir = "/logs" testDir = "/logs/test" fileName = "hello_world" # 文件最大保存时间 maxAge = "24h" # 日志切割时间间隔 rotationTime = "1h" level = "debug" # 注册中心配置 [ext.registry.etcd] Urls = ["192.168.100.1:23791", "192.168.100.1:23792", "192.168.100.1:23793"] # iris配置 [ext.gateway] addr = "192.168.100.1:9091" loglevel = "debug" [ext.gateway.irisconfig] DisablePathCorrection = false EnablePathEscape = false FireMethodNotAllowed = false DisableBodyConsumptionOnUnmarshal = false TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT" Charset = "UTF-8" #自定义配置 [ext.gateway.other] Port = 8088 IgnoreURLs = ["/", "/home", "/user/login", "/user/registe", "/login_pwd", "/micro/http"] JWTTimeout = 3600 #second LogLevel = "debug" Secret = "xxx-Secret" ``` #### 编写一个app.go文件,用于全局注册和控制 ```go package distill_micro_http import ( "fmt" _ "gitee.com/banyanhouse/distill-base/apis/web" infra "gitee.com/banyanhouse/distill-infra" "gitee.com/banyanhouse/distill-infra/api" "gitee.com/banyanhouse/distill-infra/base" "gitee.com/banyanhouse/distill-infra/hook" "gitee.com/banyanhouse/distill-infra/log" "gitee.com/banyanhouse/distill-infra/register" "gitee.com/banyanhouse/distill-infra/validator" "gitee.com/banyanhouse/distill-infra/web" ) // distill-infra的boot.go文件是整个框架设施启动器的基本结构,所有基础设施的启动过程是按照app.go中配置的顺序来加载, // 依次加载和运行所有Starter的Init()方法,再依次运行所有Starter的Setup()方法,最后依次运行所有Starter的Start()方法, // 而最后一个配置的Starter是HookStarter,用于在独立开辟的goroutine中,监听接受终端和终止信号,以依次运行所有Starter的Stop()方法, // 以达到回收资源的目的 func init() { fmt.Println("start app...") // 用于读取toml的配置文件 infra.Register(&base.TomlPropsStarter{}) // 这里启动一个Logger的封装,加入了一些日志滚动的功能 infra.Register(&log.LoggerStarter{}) // 如果需要输出日志到文件中,则可以打开这里,去初始化文件日志 // 这里可以对Handler接受的数据进行验证,后续详细描述 infra.Register(&validator.ValidatorStarter{}) // 默认集成了Iris框架 infra.Register(&web.IrisServerStarter{}) // 启动web框架后,就可以注册我们自己编写的Handler infra.Register(&api.WebApiStarter{}) // 集成了go-micro框架,实际的Iris等web框架,最终也是提交给go-micro中进行注册和启动的 infra.Register(®ister.Etcd3Starter{}) // 这里开辟一个goroutine,用于结束程序,回收资源 infra.Register(&hook.HookStarter{}) fmt.Println("load end ...") } ``` #### 在项目目录中创建一个apis/web目录,创建文件hello_handler.go ```go package web import ( "gitee.com/banyanhouse/distill-infra/api" "gitee.com/banyanhouse/distill-infra/web" "github.com/kataras/iris/context" ) // 这里方便全局注册器app.go用来将handler注册到框架的web注册器中 func init() { api.SetupApi(new(ProdsHandler)) } // 创建一个结构体,用于绑定handler type ProdsHandler struct { } // 这里可以查看我在app.go中的备注 func (c *ProdsHandler) Init() { } // 这里我们手动编写一个handler func (c *ProdsHandler) Setup() { groupRouter := web.IrisMainParty().Party("/micro/http") groupRouter.Get("/hello", func(context context.Context) { r := web.Res{ Code: web.ResCodeOk, Message: "hello world api", } context.JSON(&r) }) } ``` ## 在本机docker desktop中启动一个etcd集群 为求的机器性能和开发便捷性,我们舍弃vmware,还是用docker来搭建相关开发环境 ```yaml version: '2' services: etcd0: image: quay.io/coreos/etcd:v3.4.7 ports: - 23791:2379 volumes: - etcd0:/etcd_data command: - /usr/local/bin/etcd - -name - etcd0 - --data-dir - /etcd_data - -advertise-client-urls - http://etcd0:2379 - -listen-client-urls - http://0.0.0.0:2379 - -initial-advertise-peer-urls - http://etcd0:2380 - -listen-peer-urls - http://0.0.0.0:2380 - -initial-cluster - etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380 etcd1: image: quay.io/coreos/etcd:v3.4.7 ports: - 23792:2379 volumes: - etcd1:/etcd_data command: - /usr/local/bin/etcd - -name - etcd1 - --data-dir - /etcd_data - -advertise-client-urls - http://etcd1:2379 - -listen-client-urls - http://0.0.0.0:2379 - -initial-advertise-peer-urls - http://etcd1:2380 - -listen-peer-urls - http://0.0.0.0:2380 - -initial-cluster - etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380 etcd2: image: quay.io/coreos/etcd:v3.4.7 ports: - 23793:2379 volumes: - etcd2:/etcd_data command: - /usr/local/bin/etcd - -name - etcd2 - --data-dir - /etcd_data - -advertise-client-urls - http://etcd2:2379 - -listen-client-urls - http://0.0.0.0:2379 - -initial-advertise-peer-urls - http://etcd2:2380 - -listen-peer-urls - http://0.0.0.0:2380 - -initial-cluster - etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380 volumes: etcd0: etcd1: etcd2: ``` OK,写到这里,基本上你就可以启动main方法来访问刚刚编写好的API了。 ## 为以后的便捷性,先来把程序放容器里面玩一把,后续开发也有个基础 先给项目编写一个Dockerfile,用于我们将代码可以打成镜像和容器来。 ```dockerfile # Compile stage # 这一步是为了使用go容器对我们的代码进行编译 FROM golang:1.13.8 AS build-env # 以上面的镜像为基础,把代码搞里头 ADD . /distill-base # 进入工作目录 WORKDIR /distill-base # 设定参数 ENV GOOS linux ENV GOARCH amd64 ENV CGO_ENABLED=0 ENV GOFLAGS "-ldflags=-w -ldflags=-s" ENV GO111MODULE=on # 为避免编译时再down一遍相关依赖包,我们启动了vendor ENV GOFLAGS=" -mod=vendor" # 这里设置的是代理,建议大家还是不要用我的,自己去goproxy.io上面申请自己的玩一下 ENV GOPROXY=https://yz271544:UbFWoA20KLRShpM@goproxy.io,direct # 这一行是真正的编译 RUN go build -o /brun brun/main.go # Final stage # 下面才是真正把编译后的程序打成一个新的镜像的过程,为尽量小一些,我们采用了各方建议的debian,其他的也行,看个人爱好 FROM debian:buster #FROM centos:centos8 # 暴露端口 EXPOSE 18185 # 设定工作目录 WORKDIR / # 为我们调试方便,安装一下下面的内容,方便我们可以exec到容器后,可以使用基本的linux操作命令 #RUN dnf -y install coreutils --allowerasing RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \ && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 RUN apt-get install -y coreutils # 好了,从编译容器中,将配置目录复制过来 COPY --from=build-env /distill-base/config /config # 再从编译容器中,将编译完的程序复制过来 COPY --from=build-env /brun / # 设定权限 RUN chmod +x /brun # 设定启动命令 CMD ["/brun"] ``` # 如果你也喜欢用goland,右上角配置一个docker运行配置 1. 点击Edit Configurations... ![image-20200522103104670](images/image-20200522103104670.png) 2. 点击+ -> Docker -> Dockerfile ![image-20200522103240393](images/image-20200522103240393.png) 3. 如下图,给我们的Config填写一下参数,基本上和我一样35岁以上老眼昏花的应该能看清楚。 ![image-20200522103557043](images/image-20200522103557043.png) 点击运行 ![image-20200522103741948](images/image-20200522103741948.png) 开始编译了 ![image-20200522094045843](images/image-20200522094045843.png) 在log标签页中,我们可以看到程序的输出日志,其他标签页点点看,愉快的发现了些什么呢? ![image-20200522103817316](images/image-20200522103817316.png) 因为我们安装了coreutils的包,所以在Files标签页中,就可以浏览容器中的目录情况 ![image-20200522094356386](images/image-20200522094356386.png) http://localhost:18185/micro/http/hello ```json { code: "0", message: "hello world api", data: null } ```