【微服务网关——https与http2代理实现】

news2024/11/13 11:12:39

1.https与http2代理

1.1 重新认识https与http2

  • https是http安全版本
  • http2是一种传输协议
  • 两者并没有本质联系

1.1.1 https与http的区别

HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)是用于在网络上交换数据的两种协议。HTTPS 是 HTTP 的扩展,它在 HTTP 的基础上增加了加密层SSL/TLS ,以保护数据的安全。

  • HTTPS
    • 加密: HTTPS 使用 SSL/TLS 协议来加密传输的数据,保护数据免受窃听。
    • 验证身份: HTTPS 通过数字证书验证服务器的身份,确保数据被发送到正确的服务器。
    • 数据完整性: HTTPS 通过加密和校验机制,确保传输的数据不会被篡改。
    • 端口:443
https请求流程

在这里插入图片描述

1.1.2 http1.1与http2区别

  • HTTP/1.1
    • 文本协议:数据以明文格式传输,HTTP/1.1 请求和响应是人类可读的文本。
    • 单一请求-响应通道:每次请求和响应通过单独的 TCP 连接传输,连接复用有限。
    • 队头阻塞:由于请求是按顺序处理的,前一个请求处理未完成时,后续请求会被阻塞。
    • 连接管理:需要为每个请求创建一个新的连接,或者使用连接保持(keep-alive)来重用连接。
  • HTTP/2
    • 二进制协议:数据以二进制格式传输,增加了解析和处理的效率。
    • 多路复用:一个 TCP 连接可以同时承载多个请求和响应,消除了队头阻塞(Head-of-Line Blocking)的问题。
    • 头部压缩:使用 HPACK 算法对 HTTP 头部进行压缩,减少了冗余数据的传输。
    • 优先级控制:可以设置请求的优先级,优化资源的分配和加载顺序。

1.2 http2与https的关系

  • http2代表多路复用的传输协议
  • https代表http服务器使用了加密协议
  • 一个启用https的服务器不一定使用http2
  • 但是使用http2的服务器必须启用https(浏览器强制)

1.3 http2设计目标

  • 感知延迟有实质上改进
  • 解决HTTP1.1中的“队首阻塞”问题
  • 并行操作无需与服务器建立多个连接
  • 保持HTTP1.1语义,只是标准拓展并非替代
    在这里插入图片描述

2.https与http2代理实现

2.1 创建下游测试服务器

2.1.1 代码实现

package main

import (
	"fmt"
	"github.com/e421083458/gateway_demo/demo/proxy/reverse_proxy_https/testdata"
	"golang.org/x/net/http2"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)
func main() {
	rs1 := &RealServer{Addr: "127.0.0.1:3003"}
	rs1.Run()
	rs2 := &RealServer{Addr: "127.0.0.1:3004"}
	rs2.Run()

	//监听关闭信号
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
}

type RealServer struct {
	Addr string
}

func (r *RealServer) Run() {
	log.Println("Starting httpserver at " + r.Addr)
	mux := http.NewServeMux()
	mux.HandleFunc("/", r.HelloHandler)
	mux.HandleFunc("/base/error", r.ErrorHandler)
	server := &http.Server{
		Addr:         r.Addr,
		WriteTimeout: time.Second * 3,
		Handler:      mux,
	}
	go func() {
		// 开启http2
		http2.ConfigureServer(server, &http2.Server{})
		// 开启https
		log.Fatal(server.ListenAndServeTLS(testdata.Path("server.crt"), testdata.Path("server.key")))
	}()
}

func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {
	upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)
	io.WriteString(w, upath)
}

func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {
	upath := "error handler"
	w.WriteHeader(500)
	io.WriteString(w, upath)
}

2.1.2 证书签名生成方式

/*
//CA私钥
openssl genrsa -out ca.key 2048
//CA数据证书
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example1.com" -days 5000 -out ca.crt

//服务器私钥(默认由CA签发)
openssl genrsa -out server.key 2048
//服务器证书签名请求:Certificate Sign Request,简称csr(example1.com代表你的域名)
openssl req -new -key server.key -subj "/CN=example1.com" -out server.csr
//上面2个文件生成服务器证书(days代表有效期)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
*/

2.1.3 如何开启http2

goLang服务器支持https与http

  • ListenAndServer调整成ListenAndServerTLS即可支持https
  • https服务器设置http2.ConfigureServer支持http2
    • 设置http2对应的handler回调
    • http.Server中设置了对http2的回调
    • http2回调方法负责从帧数据流中转换数据为http请求体
// ConfigureServer 为 net/http 的 Server 添加 HTTP/2 支持。
//
// 参数 conf 可以为 nil。
//
// ConfigureServer 必须在 s 开始服务之前调用。
func ConfigureServer(s *http.Server, conf *Server) error {
    if s == nil {
        panic("nil *http.Server") // 如果传入的 *http.Server 是 nil,程序会崩溃
    }
    if conf == nil {
        conf = new(Server) // 如果传入的 Server 配置是 nil,则新建一个默认的 Server 配置
    }
    // 初始化 conf 的内部状态
    conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})}
    
    // 如果 conf 中的 IdleTimeout 没有设置,则使用 s 的 IdleTimeout 或 ReadTimeout
    if h1, h2 := s, conf; h2.IdleTimeout == 0 {
        if h1.IdleTimeout != 0 {
            h2.IdleTimeout = h1.IdleTimeout
        } else {
            h2.IdleTimeout = h1.ReadTimeout
        }
    }
    // 注册优雅关机的回调函数
    s.RegisterOnShutdown(conf.state.startGracefulShutdown)

    // 如果没有 TLS 配置,创建一个默认的 TLS 配置
    if s.TLSConfig == nil {
        s.TLSConfig = new(tls.Config)
    } else if s.TLSConfig.CipherSuites != nil {
        // 如果已提供了 CipherSuite 列表,检查它的顺序或是否缺少必要的 CipherSuite
        haveRequired := false
        sawBad := false
        for i, cs := range s.TLSConfig.CipherSuites {
            switch cs {
            case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                // 另一种 MTI 密码套件,防止只支持 ECDSA 的服务器。
                // 详情请参见 http://golang.org/cl/30721。
                tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
                haveRequired = true
            }
            if isBadCipher(cs) {
                sawBad = true
            } else if sawBad {
                return fmt.Errorf("http2: TLSConfig.CipherSuites 索引 %d 包含 HTTP/2 批准的密码套件 (%#04x),但它在未批准的密码套件之后。这样配置,可能会导致不支持先前批准的密码套件的客户端被分配一个未批准的套件并拒绝连接。", i, cs)
            }
        }
        if !haveRequired {
            return fmt.Errorf("http2: TLSConfig.CipherSuites 缺少 HTTP/2 所需的 AES_128_GCM_SHA256 密码(至少需要 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 或 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 之一)。")
        }
    }

    // 注意:这里没有设置 MinVersion 为 tls.VersionTLS12,
    // 因为我们不想干扰用户服务器上的 HTTP/1.1 流量。我们稍后在接受连接时强制执行 TLS 1.2。
    // 理想情况下,这应该在下一步协议选择中完成,但使用 TLS <1.2 与 HTTP/2 仍然是客户端的错误。

    s.TLSConfig.PreferServerCipherSuites = true // 服务器优先选择密码套件

    haveNPN := false
    for _, p := range s.TLSConfig.NextProtos {
        if p == NextProtoTLS {
            haveNPN = true
            break
        }
    }
    if !haveNPN {
        s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS) // 添加 HTTP/2 协议
    }

    if s.TLSNextProto == nil {
        s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} // 初始化 TLSNextProto 映射
    }
    // 定义 protoHandler,用于处理新连接
    protoHandler := func(hs *http.Server, c *tls.Conn, h http.Handler) {
        if testHookOnConn != nil {
            testHookOnConn() // 测试钩子,如果存在,则调用
        }
        // TLSNextProto 接口早于 context 出现,因此 net/http 包通过 Handler 上一个未公开的方法传递每个连接的基础 context。
        // 这仅供内部 net/http<=>http2 使用。
        var ctx context.Context
        type baseContexter interface {
            BaseContext() context.Context
        }
        if bc, ok := h.(baseContexter); ok {
            ctx = bc.BaseContext() // 获取 base context
        }
        // 服务连接
        conf.ServeConn(c, &ServeConnOpts{
            Context:    ctx,
            Handler:    h,
            BaseConfig: hs,
        })
    }
    s.TLSNextProto[NextProtoTLS] = protoHandler // 设置 protoHandler
    return nil // 成功返回 nil
}

2.2 https代理整合网关

package public

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"github.com/e421083458/gateway_demo/demo/proxy/reverse_proxy_https/testdata"
	"golang.org/x/net/http2"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

var transport = &http.Transport{
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second, //连接超时
		KeepAlive: 30 * time.Second, //长连接超时时间
	}).DialContext,
	//TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
	TLSClientConfig: func() *tls.Config {
		pool := x509.NewCertPool()
		caCertPath := testdata.Path("ca.crt")
		caCrt, _ := ioutil.ReadFile(caCertPath)
		pool.AppendCertsFromPEM(caCrt)
		return &tls.Config{RootCAs: pool}
	}(),
	MaxIdleConns:          100,              //最大空闲连接
	IdleConnTimeout:       90 * time.Second, //空闲超时时间
	TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间
	ExpectContinueTimeout: 1 * time.Second,  //100-continue 超时时间
}

func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
	//请求协调者
	director := func(req *http.Request) {
		targetIndex := rand.Intn(len(targets))
		target := targets[targetIndex]
		targetQuery := target.RawQuery
		fmt.Println("target.Scheme")
		fmt.Println(target.Scheme)
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
		if _, ok := req.Header["User-Agent"]; !ok {
			req.Header.Set("User-Agent", "user-agent")
		}
	}
	http2.ConfigureTransport(transport)
	return &httputil.ReverseProxy{Director: director, Transport: transport,}
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

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

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

相关文章

基于DPU的云原生计算资源共池管理解决方案

1. 方案背景和挑战 在传统的云环境中&#xff0c;通常存在着不同的技术栈&#xff0c;支撑多样化的计算服务&#xff0c;具体如下&#xff1a; ① OpenStack环境与虚拟化云主机及裸金属服务 OpenStack是一个开源的云计算管理平台项目&#xff0c;它提供了部署和管理大规模计…

JAVA 代码块介绍

一、基本介绍 代码化块又称为初始化块&#xff0c;属于类中的成员[即 是类的一部分]&#xff0c;类似于方法&#xff0c;将逻辑语句封装在方法体中&#xff0c;通过包围起来。 但和方法不同&#xff0c;没有方法名&#xff0c;没有返回&#xff0c;没有参数&#xff0c;只有方…

人工智能的新时代:从模型到应用的转变

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

NLP 面试八股:“Transformers / LLM 的词表应该选多大?“ 学姐这么告诉我答案

NLP 面试八股&#xff1a;“Transformers / LLM 的词表应该选多大?" 学姐这么告诉我答案 原创 看图学 看图学 2024年07月03日 07:55 北京 题目&#xff1a; Transformers/大模型的 token vocabulary 应该选多大&#xff1f; 答案 先说一下结论&#xff1a; 数据量够大…

南航秋招指南,线上测评和线下考试

南航秋招简介 南航作为国内一流的航空公司&#xff0c;对人才的需求量非常旺盛&#xff0c;每年也有很多专业对口的工作提供给应届毕业生&#xff0c;对于应届毕业生而言&#xff0c;一定要抓住任何一个应聘机会&#xff0c;并且在规定的范围内进行简历的提交&#xff0c;以便…

为什么需要重写equals和如何重写equals

首先先看Java中的 &#xff0c;比较的两个对象的地址值。 如果是基本数据类型&#xff0c;那么就是比较的是值。 如果是引用数据类型&#xff0c;比较的就是地址. object类中的equals方法也是用的&#xff1b; 所以要比较两个对象的大小&#xff0c;去调用默认的equals方法…

鼠标宏怎么设置?6款鼠标自动点击器强推,游戏玩家专用!(2024全)

随着电子游戏和日常应用的不断发展&#xff0c;我们经常会遇到一些重复性的任务或操作。而在这种情况下&#xff0c;鼠标宏以其自动化的特点成为了许多玩家和使用者的利器之一。如果你正在寻找如何设置鼠标宏来简化操作并提高效率&#xff0c;那么你来对地方了。在本文中&#…

2024最新版若依-RuoYi-Vue3-PostgreSQL前后端分离项目部署手册教程

项目简介: RuoYi-Vue3-PostgreSQL 是一个基于 RuoYi-Vue3 框架并集成 PostgreSQL 数据库的项目。该项目提供了一套高效的前后端分离的开发解决方案&#xff0c;适用于中小型企业快速构建现代化的企业级应用。此项目结合了 RuoYi-Vue-Postgresql 和 RuoYi-Vue3 的优点&#xff0…

WEB06JavaScriptAjax

基础语法 引入方式 引入方式 内部脚本&#xff1a;将JS代码定义在HTML页面中 JavaScript代码必须位于<script></script>标签之间 在HTML文档中&#xff0c;可以在任意地方&#xff0c;放置任意数量的<script> 一般会把脚本置于<body>元素的底部&a…

matlab:对带参数a关于x的方程求解

题目 讲解 简洁对各个式子的内部含义用浅显易懂的话语总结出来了&#xff0c;耐心体会 f(a) (x)exp(x)x^ax^(sqrt(x))-100;%因为下面的fzero的第一个数需要一个fun&#xff0c;所以这里有两个句柄&#xff0c;第一个a是输入的&#xff0c;第二个x是需要被解出的 A0:0.1:2;%创…

提高生产效率和质量的六西格玛方法(优思学院)

想象一下&#xff0c;你被公司指派了一个重要项目&#xff0c;这家公司生产豪华汽车。当前&#xff0c;公司的生产数量正在下降&#xff0c;每天生产的汽车数量明显减少。此外&#xff0c;还存在安装在这些汽车上的雨刮器质量问题。你面临的挑战是找到解决方案&#xff0c;将生…

【CW32F030CxTx StartKit开发板】利用超声波传感器实现智能灯控

目录 1、超声波传感器 2、硬件连线 3. 程序开发 3.1 超声波测距 3.2 LED控制 4. 演示视频 本文首发于21ic。 感谢21ic和武汉芯源提供的测试机会。 在上一篇帖子中介绍了CW32F030CxTxStartKit 评估板的环境构建。本次介绍如何利用超声波传感器实现人来灯亮&#xff0c;人…

前端开发过程中经常遇到的问题以及对应解决方法 (持续更新)

我的朋友已经工作了 3 年&#xff0c;他过去一直担任前端工程师。 不幸的是&#xff0c;他被老板批评了&#xff0c;因为他在工作中犯了一个错误&#xff0c;这是一个非常简单但容易忽视的问题&#xff0c;我想也是很多朋友容易忽视的一个问题。 今天我把它分享出来&#xff…

【PCIe】P2P DMA

PCIe P2P (peer-to-peer communication)是PCIe的一种特性&#xff0c;它使两个PCIe设备之间可以直接传输数据&#xff0c;而不需要使用主机RAM作为临时存储。如下图3的走向 比如EP1要发送和数据给EP2,操作流程如下&#xff1a; 1. 打开EP1的dma控制器&#xff1b;--client侧 …

微调LLMs : 介绍,方法和最佳实践

来自Turing的LLMs的Fine-Tuning的最佳实践。 大型语言模型&#xff08;LLMs&#xff09;凭借其先进的功能和高度精密的解决方案&#xff0c;已经彻底改变了自然语言处理领域。这些模型在海量文本数据集上接受训练&#xff0c;执行诸如文本生成、翻译、摘要和问题回答等多种任务…

Linux安装Jmeter及简单使用教程

Linux安装Jmeter 首先需要java环境 java --version官网 下载二进制包 #创建文件夹 sudo mkdir /usr/local/jmeter #解压 sudo tar zxvf apache-jmeter-5.6.3.tgz -C /usr/local/jmeter编辑配置文件 sudo vim /etc/profile&#xff0c;添加以下内容 export JMETER_HOME/usr/l…

【Python_GUI】tkinter常用组件——文本类组件

文本时窗口中必不可少的一部分&#xff0c;tkinter模块中&#xff0c;有3种常用的文本类组件&#xff0c;通过这3种组件&#xff0c;可以在窗口中显示以及输入单行文本、多行文本、图片等。 Label标签组件 Label组件的基本使用 Label组件是窗口中比较常用的组件&#xff0c;…

spring mvc学习

第四章 Spring MVC 第一节 Spring MVC 简介 1. Spring MVC SpringMVC是一个Java 开源框架&#xff0c; 是Spring Framework生态中的一个独立模块&#xff0c;它基于 Spring 实现了Web MVC&#xff08;数据、业务与展现&#xff09;设计模式的请求驱动类型的轻量级Web框架&am…

Java面试八股之MySQL和postgresql的区别有哪些

MySQL和postgresql的区别有哪些 MySQL和PostgreSQL是两种非常流行的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它们各自拥有独特的特性和优势&#xff0c;适用于不同的场景。以下是它们之间的一些主要区别&#xff1a; SQL标准遵守程度 PostgreSQL 更严…

IDEA启动tomcat之后控制台出现中文乱码问题

方法1&#xff1a; 第一步&#xff1a;file--setting--Editor--File Encodings 注意页面中全部改为UTF-8&#xff0c;然后apply再ok 第二步&#xff1a;Run--Edit Configuration&#xff0c;将VM options输入以下值&#xff1a; -Dfile.encodingUTF-8 还是一样先apply再ok …