网关
在微服务的场景下,服务因为分布在不同的服务器上,但是用户在访问的时候,不可能去维护这成百上千的入口,希望有统一的入口来进行访问,这就是网关的作用。
网关最重要的一个功能就是路由功能,能将请求转发到具体的业务服务器上
1. 简单实现
httputil.ReverseProxy 定义了一组方法让使用者去实现,主要有这几个
-
Director
最核心的方法, 我们可以在这里对请求进行相应的修改,比如设置请求目标的地址,对原有请求头进行增删改,以及对请求体进行处理等等操作。 -
ModifyResponse
可以让我们对响应的结果进行处理,比如修改、读取响应头和响应体。 -
ErrorHandler
请求出错或者ModifyResponse返回error时会回调该方法,比如目标服务器无法连接,请求超时等等
package mainimport ("github.com/mszlu521/msgo""github.com/mszlu521/msgo/gateway"
)func main() {engine := msgo.Default()engine.OpenGateWay = truevar configs []gateway.GWConfigconfigs = append(configs, gateway.GWConfig{Name: "order",Path: "/order/**",Host: "127.0.0.1",Port: 9003,}, gateway.GWConfig{Name: "goods",Path: "/goods/**",Host: "127.0.0.1",Port: 9002,})engine.SetGateConfigs(configs)engine.Run(":80")
}
package gatewaytype GWConfig struct {Name stringPath stringHost stringPort int
}
package gatewayimport "strings"type TreeNode struct {Name stringChildren []*TreeNodeIsEnd boolGwName string
}func (t *TreeNode) Put(path string, gwName string) {root := tstrs := strings.Split(path, "/")for index, v := range strs {if index == 0 {continue}children := t.ChildrenisMatch := falsefor _, node := range children {if node.Name == v {isMatch = truet = nodebreak}}if !isMatch {isEnd := falseif index == len(strs)-1 {isEnd = true}treeNode := &TreeNode{Name: v, Children: make([]*TreeNode, 0), IsEnd: isEnd, GwName: gwName}children = append(children, treeNode)t.Children = childrent = treeNode}}t = root
}//返回最后匹配的节点
func (t *TreeNode) Get(path string) *TreeNode {strs := strings.Split(path, "/")for index, v := range strs {if index == 0 {continue}children := t.ChildrenisMatch := falsefor _, node := range children {if node.Name == v ||node.Name == "*" ||strings.Contains(node.Name, ":") {isMatch = truet = nodeif index == len(strs)-1 {return node}break}}if !isMatch {//没匹配的情况下 检查是否有**for _, node := range children {if node.Name == "**" {return node}}return nil}}return nil
}
type Engine struct {*routerfuncMap template.FuncMapHTMLRender render.HTMLRenderpool sync.PoolLogger *msLog.LoggerglobalMiddles []MiddlewareFuncerrorHandler ErrorHandlerOpenGateWay boolgatewayTreeNode *gateway.TreeNodegatewayConfigMap map[string]gateway.GWConfig
}
func (e *Engine) handleHttpRequest(ctx *Context) {if e.OpenGateWay {//网关业务处理uri := ctx.R.URL.Pathnode := e.gatewayTreeNodematchNode := node.Get(uri)if matchNode == nil {ctx.W.WriteHeader(http.StatusNotFound)fmt.Fprintln(ctx.W, ctx.R.RequestURI+" not found")return}gwConfig := e.gatewayConfigMap[matchNode.GwName]target, _ := url.Parse(fmt.Sprintf("http://%s:%d%s", gwConfig.Host, gwConfig.Port, uri))director := func(req *http.Request) {req.Host = target.Hostreq.URL.Host = target.Hostreq.URL.Path = target.Pathreq.URL.Scheme = target.Schemeif _, ok := req.Header["User-Agent"]; !ok {req.Header.Set("User-Agent", "")}}response := func(response *http.Response) error {log.Println("改变返回值")return nil}handler := func(writer http.ResponseWriter, request *http.Request, err error) {log.Println("错误处理", err)}proxy := httputil.ReverseProxy{Director: director, ModifyResponse: response, ErrorHandler: handler}proxy.ServeHTTP(ctx.W, ctx.R)return}//....
}
2. 支持Header设置
if gwConfig.Header != nil {gwConfig.Header(req)}
用户在配置中指定其要设置的请求Header即可
3. 支持注册中心
有过前面的经验,这里就比较简单,设置注册中心名称,然后获取host和port的时候去注册中心获取即可
func (e *Engine) handleHttpRequest(ctx *Context) {if e.OpenGateWay {//网关业务处理uri := ctx.R.URL.Pathnode := e.gatewayTreeNodematchNode := node.Get(uri)if matchNode == nil {ctx.W.WriteHeader(http.StatusNotFound)fmt.Fprintln(ctx.W, ctx.R.RequestURI+" not found")return}gwConfig := e.gatewayConfigMap[matchNode.GwName]if gwConfig.OpenRegister {if e.register == "nacos" {client := e.registerClient.(naming_client.INamingClient)instance, err := client.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{ServiceName: gwConfig.Name,})if err != nil {panic(err)}gwConfig.Host = instance.IpgwConfig.Port = int(instance.Port)}}target, _ := url.Parse(fmt.Sprintf("http://%s:%d%s", gwConfig.Host, gwConfig.Port, uri))director := func(req *http.Request) {req.Host = target.Hostreq.URL.Host = target.Hostreq.URL.Path = target.Pathreq.URL.Scheme = target.Schemeif _, ok := req.Header["User-Agent"]; !ok {req.Header.Set("User-Agent", "")}if gwConfig.Header != nil {gwConfig.Header(req)}}response := func(response *http.Response) error {log.Println("改变返回值")return nil}handler := func(writer http.ResponseWriter, request *http.Request, err error) {log.Println("错误处理", err)}proxy := httputil.ReverseProxy{Director: director, ModifyResponse: response, ErrorHandler: handler}proxy.ServeHTTP(ctx.W, ctx.R)return}
}
type Engine struct {*routerfuncMap template.FuncMapHTMLRender render.HTMLRenderpool sync.PoolLogger *msLog.LoggerglobalMiddles []MiddlewareFuncerrorHandler ErrorHandlerOpenGateWay boolgatewayTreeNode *gateway.TreeNodegatewayConfigMap map[string]gateway.GWConfignacosConfig register.NacosConfigregisterClient anyregister string
}
func (e *Engine) SetNacosConfig(config register.NacosConfig) {e.nacosConfig = configclient, err := register.CreateNameClient(e.nacosConfig)if err != nil {panic(err)}e.registerClient = cliente.register = "nacos"
}
package mainimport ("github.com/mszlu521/msgo""github.com/mszlu521/msgo/gateway""github.com/mszlu521/msgo/register""net/http"
)func main() {engine := msgo.Default()engine.OpenGateWay = truevar configs []gateway.GWConfigconfigs = append(configs, gateway.GWConfig{Name: "order",Path: "/order/**",Host: "127.0.0.1",Port: 9003,Header: func(req *http.Request) {req.Header.Set("my", "mszlu")},}, gateway.GWConfig{Name: "goodsCenter",Path: "/goods/**",OpenRegister: true,Header: func(req *http.Request) {req.Header.Set("my", "mszlu")},})engine.SetNacosConfig(register.DefaultNacosConfig)engine.SetGateConfigs(configs)engine.Run(":80")
}
记得注册服务:
//注册服务client, _ := register.CreateNameClient(register.DefaultNacosConfig)config := register.NacosServiceConfig{Port: 9002, ServiceName: "goodsCenter", Ip: "127.0.0.1"}register.RegisterService(client, config)
acosConfig(register.DefaultNacosConfig)
engine.SetGateConfigs(configs)
engine.Run(“:80”)
}
记得注册服务:~~~go
//注册服务client, _ := register.CreateNameClient(register.DefaultNacosConfig)config := register.NacosServiceConfig{Port: 9002, ServiceName: "goodsCenter", Ip: "127.0.0.1"}register.RegisterService(client, config)