16-网关-自研微服务框

news2025/1/16 13:59:13

网关

在这里插入图片描述

在微服务的场景下,服务因为分布在不同的服务器上,但是用户在访问的时候,不可能去维护这成百上千的入口,希望有统一的入口来进行访问,这就是网关的作用。

网关最重要的一个功能就是路由功能,能将请求转发到具体的业务服务器上

1. 简单实现

httputil.ReverseProxy 定义了一组方法让使用者去实现,主要有这几个

  • Director
    最核心的方法, 我们可以在这里对请求进行相应的修改,比如设置请求目标的地址,对原有请求头进行增删改,以及对请求体进行处理等等操作。

  • ModifyResponse
    可以让我们对响应的结果进行处理,比如修改、读取响应头和响应体。

  • ErrorHandler
    请求出错或者ModifyResponse返回error时会回调该方法,比如目标服务器无法连接,请求超时等等

package main

import (
	"github.com/mszlu521/msgo"
	"github.com/mszlu521/msgo/gateway"
)

func main() {
	engine := msgo.Default()
	engine.OpenGateWay = true
	var configs []gateway.GWConfig
	configs = 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 gateway

type GWConfig struct {
	Name string
	Path string
	Host string
	Port int
}

package gateway

import "strings"

type TreeNode struct {
	Name     string
	Children []*TreeNode
	IsEnd    bool
	GwName   string
}

func (t *TreeNode) Put(path string, gwName string) {
	root := t
	strs := strings.Split(path, "/")
	for index, v := range strs {
		if index == 0 {
			continue
		}
		children := t.Children
		isMatch := false
		for _, node := range children {
			if node.Name == v {
				isMatch = true
				t = node
				break
			}
		}
		if !isMatch {
			isEnd := false
			if index == len(strs)-1 {
				isEnd = true
			}
			treeNode := &TreeNode{Name: v, Children: make([]*TreeNode, 0), IsEnd: isEnd, GwName: gwName}
			children = append(children, treeNode)
			t.Children = children
			t = 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.Children
		isMatch := false
		for _, node := range children {
			if node.Name == v ||
				node.Name == "*" ||
				strings.Contains(node.Name, ":") {
				isMatch = true
				t = node
				if 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 {
	*router
	funcMap          template.FuncMap
	HTMLRender       render.HTMLRender
	pool             sync.Pool
	Logger           *msLog.Logger
	globalMiddles    []MiddlewareFunc
	errorHandler     ErrorHandler
	OpenGateWay      bool
	gatewayTreeNode  *gateway.TreeNode
	gatewayConfigMap map[string]gateway.GWConfig
}

func (e *Engine) handleHttpRequest(ctx *Context) {
	if e.OpenGateWay {
		//网关业务处理
		uri := ctx.R.URL.Path
		node := e.gatewayTreeNode
		matchNode := 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.Host
			req.URL.Host = target.Host
			req.URL.Path = target.Path
			req.URL.Scheme = target.Scheme
			if _, 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.Path
		node := e.gatewayTreeNode
		matchNode := 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.Ip
				gwConfig.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.Host
			req.URL.Host = target.Host
			req.URL.Path = target.Path
			req.URL.Scheme = target.Scheme
			if _, 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 {
	*router
	funcMap          template.FuncMap
	HTMLRender       render.HTMLRender
	pool             sync.Pool
	Logger           *msLog.Logger
	globalMiddles    []MiddlewareFunc
	errorHandler     ErrorHandler
	OpenGateWay      bool
	gatewayTreeNode  *gateway.TreeNode
	gatewayConfigMap map[string]gateway.GWConfig
	nacosConfig      register.NacosConfig
	registerClient   any
	register         string
}

func (e *Engine) SetNacosConfig(config register.NacosConfig) {
	e.nacosConfig = config
	client, err := register.CreateNameClient(e.nacosConfig)
	if err != nil {
		panic(err)
	}
	e.registerClient = client
	e.register = "nacos"
}

package main

import (
	"github.com/mszlu521/msgo"
	"github.com/mszlu521/msgo/gateway"
	"github.com/mszlu521/msgo/register"
	"net/http"
)

func main() {
	engine := msgo.Default()
	engine.OpenGateWay = true
	var configs []gateway.GWConfig
	configs = 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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1492628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb03-HTTP协议,Tomcat,Servlet

目录 一、HTTP协议 1.概述 2.特点 3.请求数据格式 (1)请求行 (2)请求头 (3)请求体 (4)常见请求头 (5)GET和POST请求区别 4.响应数据格式 &#xf…

uniapp 手写 简易 时间轴 组件

一、案例如图 该案例设计条件: 左侧时间 和竖线、点、内容都是居中对其的,上下时间点中间要有一段距离 二、编写逻辑 1. 布局结构:一共三个元素,左侧是时间和黑点,中间是线条,右侧是内容 2. 样式难点&#…

MATLAB知识点:循环语句中的break 和 continue 关键字

​讲解视频:可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇(数学建模清风主讲,适合零基础同学观看)_哔哩哔哩_bilibili 节选自​第4章:MATLAB程序流程控制 break 和 con…

Pytorch学习 day03(Tensorboard、Transforms)

Tensorboard Tensorboard能够可视化loss的变化过程,便于我们查看模型的训练状态,也能查看模型当前的输入和输出结果 在Pycharm中,可以通过按住ctrl,并左键点击某个库来进入源文件查看该库的使用方法SummaryWriter是用来向log_dir…

六、软考-系统架构设计师笔记-软件工程基础知识

1、软件工程 软件工程是将系统化的、严格约束的、可量化的方法应用于软件的开发、运行和维护,即将工程化应用于软件并对上述方法的研究。 软件要经历从需求分析、软件设计、软件开发、运行维护,直至被淘汰这样的全过程,这个过程称为软件的生…

【Android】位置修改相关

获取位置服务总开关状态 //获取LOCATION_MODE值,但adb状态下无法获取 //0为关闭,1 gps、2 network、3 高精度等 int state Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.LOCATION_MODE,Settings.Secure.LOCATION_MODE_HIGH_…

嵌入式硬件设计实例:基于STM32的流水灯原理图和PCB设计

最近本来在忙别的事,大约忙到五一以后吧,没有时间研究一些深入的知识。本来想停更的,但是刚刚申请到博客专家,想想还是得更新点文章。这里就把一些以前我做过的一些简单的硬件设计拿来出来复习一下。 本篇文章就来介绍一下&#…

【论文翻译】结构化状态空间模型

文章目录 3.2 对角结构化状态空间模型3.2.1 S4D:对角SSM算法3.2.2 完整应用实例 3.3 对角化加低秩(DPLR)参数化3.3.1 DPLR 状态空间核算法3.3.2 S4-DPLR 算法和计算复杂度3.3.3赫尔维兹(稳定)DPLR形式 这篇文章是Mamba作者博士论文…

重量的定义、质量和重量之间的区别

一、简述 物体的重量取决于该物体所在空间点的引力场。重量是一种力,因此它是一个矢量,这意味着它有方向和大小。通过自由体图来表示物体重量产生的力通常很方便。 重量总是从物体的质心向下作用到地球中心。(如果你在不同的天体上&#xff0…

万物皆可模块化分解

引言 为何要模块化,这里的主体是人,客体是事物。当事物很小时,人可以很轻松的解决;但是当事物远大于人能处理的范围时,我们就可以考虑对它进行模块化分解。模块化是一种解决复杂问题的方式,放之四海而皆可…

LVS集群(Linux Virtual server)相关介绍及LVS的NAT模式部署

群集的含义 ●Cluster,集群、群集由多台主机构成,但对外只表现为一个整体,只提供访问入口(域名或IP地址),相当于一台大型计算机 问题: 互联网应用中,随着站点对硬件性能、响应速度、服务稳定性、数据可靠…

Nvm下载安装和基本使用

下载与安装 github地址:Releases coreybutler/nvm-windows (github.com) 默认安装:安装nvm时候,全默认即可(如果自定义目录,切记 nvm的安装路径 :不要有汉字,不要有空格,不然后面会…

day04-SpringBootWeb入门

一、SpringBootWeb快速入门 1 需求 需求:基于 SpringBoot 的方式开发一个 web 应用,浏览器发起请求 /hello后,给浏览器返回字符串“Hello World ~”。 2 开发步骤 第1步:创建 SpringBoot 工程项目 第2步:定义 HelloC…

在 Rust 中实现 TCP : 3. TCP连接四元组

连接四元组 我们的项目已经取得了很大的进展——接下来能够开始解决 TCP 协议的实现问题。下面将讨论 TCP 的一些行为及其各种状态。 在多任务操作系统中,各种应用程序(例如 Web 服务器、电子邮件客户端等)需要同时进行网络访问。为了区分这…

使用GitOps自动化推动AI/ML工作流程

作为一名深耕自动化和人工智能领域的开发人员,我们逐渐认识到尖端工具和方法之间的显着协同作用,这些协同作用突破了可能性的界限。在这次探索中,我们想分享一个概念,它不仅彻底改变了我们的软件开发和基础设施管理方法&#xff0…

华为智慧教室3.0的晨光,点亮教育智能化变革

“教室外有更大的世界,但世界上没有比教室更伟大的地方。” 我们在求学阶段,都听说过这句话,但往往是在走出校园之后,才真正理解了这句话。为了让走出校园的孩子能够有能力,有勇气探索广阔的世界。我们应该准备最好的教…

JProfiler详解 JVM性能监测内存泄露分析工具

JProfiler详解 JProfiler简介主要功能特点使用场景注意事项使用案例使用步骤Could not verify ssh-ed25519 host key with fingerprint 问题解决内存泄露分析 JProfiler简介 JProfiler是一款业界领先的Java性能分析工具,由ej-technologies公司开发,专门…

Elasticsearch:使用 Streamlit、语义搜索和命名实体提取开发 Elastic Search 应用程序

作者:Camille Corti-Georgiou 介绍 一切都是一个搜索问题。 我在 Elastic 工作的第一周就听到有人说过这句话,从那时起,这句话就永久地印在了我的脑海中。 这篇博客的目的并不是我出色的同事对我所做的相关陈述进行分析,但我首先…

dolphinescheduler调用API

(作者:陈玓玏) 1. 打开api文档 api文档地址:http://{api server ip}:12345/dolphinscheduler/swagger-ui/index.html?languagezh_CN&langcn,我是用k8s部署的,所以ip和端口是由service决定的&#xf…

前端学习之HTML(第二天)--多媒体标签和表格标签

注&#xff1a;里面的注释是对各个标签的解释 多媒体标签 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title> </head> <body> <!-- audio是音频可以填写绝对路径也可填写相对路径 --> &l…