Iris微服务框架_golang web框架_完整示例Demo

Iris简介

Iris是一款Go语言中用来开发web应用的框架,该框架支持编写一次并在任何地方以最小的机器功率运行,如Android、ios、Linux和Windows等。该框架只需要一个可执行的服务就可以在平台上运行了。

Iris框架以简单而强大的api而被开发者所熟悉。iris除了为开发者提供非常简单的访问方式外,还同样支持MVC。另外,用iris构建微服务也很容易。

在iris框架的官方网站上,被称为速度最快的Go后端开发框架。在Iris的网站文档上,列出了该框架具备的一些特点和框架特性,列举如下:

Iris特性
  • 专注于高性能
  • 简单流畅的API
  • 高扩展性
  • 强大的路由和中间件生态系统
  • 使用iris独特的表达主义路径解释器构建RESTful API
  • 动态路径参数化或通配符路由与静态路由不冲突
  • 使用重定向选项从URL中删除尾部斜杠
  • 使用虚拟主机和子域名变得容易
  • 分组API和静态或甚至动态子域名
  • net / http和negroni-like处理程序通过iris.FromStd兼容
  • 针对任意Http请求错误 定义处理函数
  • 支持事务和回滚
  • 支持响应缓存
  • 使用简单的函数嵌入资源并与go-bindata 保持兼容
  • mvc
  • 上下文
  • 高度可扩展的试图渲染(目前支持markdown,json,xml,jsonp等等)
  • 正文绑定器和发送HTTP响应的便捷功能
  • 限制请求正文
  • 提供静态资源或嵌入式资产
  • 本地化i18N
  • 压缩(Gzip是内置的)
  • 身份验证
  • Basic Authentication
  • OAuth, OAuth2 (支持27个以上的热门网站)
  • JWT *服务器
  • 通过TLS提供服务时,自动安装和提供来自https://letsencrypt.org的证书
  • 默认为关闭状态
  • 在关闭,错误或中断事件时注册
  • 连接多个服务器,完全兼容 net/http#Server
  • 视图系统.支持五种模板隐隐 完全兼容 html/template
  • Websocket库,其API类似于http://socket.io [如果你愿意,你仍然可以使用你最喜欢的]
  • 热重启
  • Typescript集成 + Web IDE

官方源代码地址: https://github.com/kataras/iris

Iris web微服务框架示例

总体功能

  • 集成aiwuTech.fileLogger日志按天切割,按级别输出到不同日志文件
  • 集成redis
  • 集成mysql
  • 读取etcd配置
  • 异常处理,全局异常处理
  • 拦截器打印请求和响应参数
  • 前端html集成
  • 等等

Iris微服务框架_golang web框架_完整示例Demo_第1张图片

下载iris依赖包
go get -u github.com/kataras/iris
main.go项目入口

init方法: 初始化相关配置,都有注释说明
main方法: 启动Iris服务

package main

import (
	. "web-demo/config"
	. "web-demo/log"
	. "web-demo/redis"
	. "web-demo/db"
	_ "web-demo/handler"
	"web-demo/web"
	"flag"
	"fmt"
	"time"
)

var showVersion = flag.Bool("v", true, "print version")

func init(){
	//调用 flag.Parse() 进行解析
	flag.Parse()

	//初始化配置文件
	ConfigRead()

	//初始化log日志
	LogInit()

	//初始化redis
	RedisInit(Cfg.RedisAddr,0 , Cfg.RedisPassword, Cfg.RedisMaxConn)

	//初始化mysql
	SqlDBInit(&SqlDBParam{Cfg.MysqlHost, Cfg.MysqlPort, Cfg.MysqlUser, Cfg.MysqlPwd,
	Cfg.MysqlDb})
}

func main() {
	if *showVersion {
		//这个日期就是写死的一个日期,不是这个日期就不认识,就不能正确的格式化
		//据说是go诞生之日
		version := fmt.Sprintf("%s %s@%s", "web-demo", "1.0", time.Now().Format("2006-01-02 15:04:05"))
		fmt.Println(version)
	}

	Log.Info("start server...")

	//监听端口
	Log.Info("listen on :%s", Cfg.ListenPort)
	web.RunIris(Cfg.ListenPort)
}


AccessLogMiddleware.go 中间件拦截器

在拦截器中添加请求头
把请求参数和响应参数打印到日志文件

package web

import (
	. "web-demo/util/threadlocal"
	. "web-demo/log"
	"github.com/kataras/iris"
	. "github.com/jtolds/gls"
	"strconv"
	"time"
)

func init() {
	RegisterPreMiddleware(accessLogMiddleware{})
}

type accessLogMiddleware struct {
	// your 'stateless' fields here
}

func (m accessLogMiddleware) Serve(ctx *iris.Context) {
	//check header
	//request id
	requestId := ctx.RequestHeader("X-Web-Demo-RequestId")
	if requestId == "" {
		requestId = strconv.FormatInt(time.Now().UnixNano(), 10)
	}

	//access log
	AccessLog.Info(requestId + "\t" + ctx.RequestIP() + "\t" + string(ctx.RequestURI()))

	//response requestId
	ctx.Response.Header.Add("X-Web-Demo-RequestId", requestId)

	//do chian
	Mgr.SetValues(Values{Rid: requestId}, func() {
		ctx.Next()
	})

	//rrlog
	RRLog.Info(requestId + "\t" + "-RequestIP:==" + ctx.RequestIP())
	RRLog.Info(requestId + "\t" + "-RequestP:==" + string(ctx.RequestPath(true)))
	RRLog.Info(requestId + "\t" + "-RequestD:==" + string(ctx.Request.Body()))
	RRLog.Info(requestId + "\t" + "-Response:==" + string(ctx.Response.Body()))
}


logger.go 日志定义和配置
package log

import (
	"flag"
	"github.com/aiwuTech/fileLogger"
	"os"
	. "web-demo/util/threadlocal"
	"web-demo/config"
)

var Log Logger
var ErrorLog Logger
var AccessLog Logger
var RRLog Logger
var InnerRRLog Logger

type Logger interface {
	Trace(format string, params ...interface{})
	Info(format string, params ...interface{})
	Warn(format string, params ...interface{})
	Error(format string, params ...interface{})
}

type CustomedLogger struct{
	MyLogger Logger
}
func (cl CustomedLogger)Trace(format string, params ...interface{}){
	cl.MyLogger.Trace(cl.resetFormat(format), params)
}
func (cl CustomedLogger)Info(format string, params ...interface{}){
	cl.MyLogger.Info(cl.resetFormat(format), params)
}
func (cl CustomedLogger)Warn(format string, params ...interface{}){
	cl.MyLogger.Warn(cl.resetFormat(format), params)
}
func (cl CustomedLogger)Error(format string, params ...interface{}){
	cl.MyLogger.Error(cl.resetFormat(format), params)
}
func (cl CustomedLogger)resetFormat(format string) string{
	logstr := format
	if rid, ok := Mgr.GetValue(Rid); ok {
		logstr = rid.(string) + " - " + logstr
	}
	return logstr
}

func LogInit() {
	logPath := config.Cfg.LogPath
	if (len(logPath) == 0) {
		logPath = "/Users/liang/ideaWorkspace/go/src/web-demo/logs/web-demo"
	}

	flag.Parse()
	if !isExist(logPath) {
		os.Mkdir(logPath, 0755)
	}

	logger := fileLogger.NewDailyLogger(logPath, "root.log", "", fileLogger.DEFAULT_LOG_SCAN, fileLogger.DEFAULT_LOG_SEQ)
	Log = CustomedLogger{MyLogger: logger}

	errorLog := fileLogger.NewDailyLogger(logPath, "error.log", "", fileLogger.DEFAULT_LOG_SCAN, fileLogger.DEFAULT_LOG_SEQ)
	ErrorLog = CustomedLogger{MyLogger: errorLog}

	accessLog := fileLogger.NewDailyLogger(logPath, "access.log", "", fileLogger.DEFAULT_LOG_SCAN, fileLogger.DEFAULT_LOG_SEQ)
	AccessLog = CustomedLogger{MyLogger: accessLog}

	rRLog := fileLogger.NewDailyLogger(logPath, "rr.log", "", fileLogger.DEFAULT_LOG_SCAN, fileLogger.DEFAULT_LOG_SEQ)
	RRLog = CustomedLogger{MyLogger: rRLog}

	innerRRLog := fileLogger.NewDailyLogger(logPath, "inner_rr.log", "", fileLogger.DEFAULT_LOG_SCAN, fileLogger.DEFAULT_LOG_SEQ)
	InnerRRLog = CustomedLogger{MyLogger: innerRRLog}
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}


mysql.go mysql初始化连接
package db

import (
	. "web-demo/log"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	. "web-demo/config"
)

var Mysql *gorm.DB

type SqlDBParam struct {
	Ip       string
	Port     int
	User     string
	Pw       string
	Database string
}
// mysql初始化文件
func SqlDBInit(param *SqlDBParam) {
	Log.Info("init mysql...")
	param_s := fmt.Sprintf(
		"%v:%v@tcp(%v:%v)/%v?parseTime=True&loc=Local",
		param.User,
		param.Pw,
		param.Ip,
		param.Port,
		param.Database,
	)
	Log.Info("mysql param: %s", param_s)

	db, err := gorm.Open("mysql", param_s)
	if err != nil {
		Log.Error("open mysql error: %v", err)
		panic(err)
	}
	db.DB().SetMaxIdleConns(Cfg.MysqlMaxConn)
	db.SingularTable(true)
	db.LogMode(true)

	Mysql = db
	Log.Info("init mysql end.")
}

redis.go redis初始化和常用操作方法
package redis

import (
	. "web-demo/log"
	"github.com/garyburd/redigo/redis"
	"time"
)

var (
	redisPool *redis.Pool
)

const (
	maxIdle     = 500
	idleTimeout = 0 * time.Second
	wait        = true
)

//Init
//eg: RedisInit("127.0.0.1:6379", 0, "pwd", 8)
func RedisInit(server string, db int, password string, maxConn int) {
	maxActive := maxConn

	//Make a pool object
	redisPool = &redis.Pool{
		// Maximum number of idle connections in the pool.
		MaxIdle: maxIdle,

		// Maximum number of connections allocated by the pool at a given time.
		// When zero, there is no limit on the number of connections in the pool.
		MaxActive: maxActive,

		// Close connections after remaining idle for this duration. If the value
		// is zero, then idle connections are not closed. Applications should set
		// the timeout to a value less than the server's timeout.
		IdleTimeout: idleTimeout,

		// If Wait is true and the pool is at the MaxActive limit, then Get() waits
		// for a connection to be returned to the pool before returning.
		Wait: wait,

		// Dial is an application supplied function for creating and configuring a
		// connection.
		//
		// The connection returned from Dial must not be in a special state
		// (subscribed to pubsub channel, transaction started, ...).
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			if password != "" {
				if _, err := c.Do("AUTH", password); err != nil {
					c.Close()
					return nil, err
				}
			}
			if _, err := c.Do("SELECT", db); err != nil {
				c.Close()
				return nil, err
			}
			return c, nil
		},
		// TestOnBorrow is an optional application supplied function for checking
		// the health of an idle connection before the connection is used again by
		// the application. Argument t is the time that the connection was returned
		// to the pool. If the function returns an error, then the connection is
		// closed.
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
	Log.Info("redis[%v %v] init ok", server, db)
}

......

handler 注意Iris会自动扫描handler包名

如果分了多个包,像demo里分了user和html,这时iris扫描不到.
需要在handler包中添加一个公共的文件把这2个包导入,如下所示

package handler

import (
	_ "web-demo/handler/html"
	_ "web-demo/handler/user"
)

其他功能就不详细介绍了,请看源代码

web-demo运行
#启动项目
go run main.go 
#打印如下,表示成功启动8080端口
listen on :8080

浏览器访问:
http://127.0.0.1:8080/webdemo/html/v1/passenger/login.md
显示login.md文件内容

http://127.0.0.1:8080/webdemo/html/v1/index
显示index.html内容

Demo源代码地址:https://github.com/golang-example/web-demo

你可能感兴趣的:(golang,微服务,golang,iris)