正向代理与反向代理

函数是一等公民

// 新建函数类型
type HandlerFunc func(http.ResponseWriter, *http.Request)// 新建函数方法
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {f(w, r)
}// 主函数
func main() {// 实例化函数hf := HandlerFunc(HelloHandler)// 创建response对象resp := httptest.NewRecorder()// 实例化request对象req := httptest.NewRequest("GET", "/", bytes.NewBuffer([]byte("test")))// 在response中写入"hello world"hf.ServeHTTP(resp, req)// 读取response中的内容bts, _ := ioutil.ReadAll(resp.Body)// 打印response中的内容fmt.Println(string(bts))
}// 创建具体的handler函数
func HelloHandler(res http.ResponseWriter, req *http.Request) {res.Write([]byte("Hello world"))
}

http服务端

var (Addr = ":1210"
)func main() {// 创建路由器mux := http.NewServeMux()// 设置路由规则mux.HandleFunc("/bye", sayBye)// 创建服务器server := &http.Server{Addr:         Addr,WriteTimeout: time.Second * 3,Handler:      mux,}// 监听端口并提供服务log.Println("Starting httpserver at " + Addr)log.Fatal(server.ListenAndServe())
}// 新建具体的handler函数
func sayBye(w http.ResponseWriter, r *http.Request) {time.Sleep(1 * time.Second)w.Write([]byte("bye bye ,this is httpServer"))
}

http客户端

func main() {// 创建连接池transport := &http.Transport{DialContext: (&net.Dialer{Timeout:   30 * time.Second, //连接超时KeepAlive: 30 * time.Second, //探活时间}).DialContext,MaxIdleConns:          100,              //最大空闲连接IdleConnTimeout:       90 * time.Second, //空闲超时时间TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间ExpectContinueTimeout: 1 * time.Second,  //100-continue状态码超时时间}// 创建客户端client := &http.Client{Timeout:   time.Second * 30, //请求超时时间Transport: transport,        //连接池设置}// 请求数据resp, err := client.Get("http://127.0.0.1:1210/bye")if err != nil {panic(err)}defer resp.Body.Close()// 读取内容bds, err := ioutil.ReadAll(resp.Body)if err != nil {panic(err)}// 打印返回的内容fmt.Println(string(bds))
}

web浏览器代理

// web浏览器代理type Pxy struct{}func (p *Pxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {// 客户端的ip和端口fmt.Printf("Received request %s %s %s\n", req.Method, req.Host, req.RemoteAddr)// 创建连接池transport := http.DefaultTransport// step 1,浅拷贝对象,然后就再新增属性数据outReq := new(http.Request)*outReq = *req// 获取ip和端口号if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {if prior, ok := outReq.Header["X-Forwarded-For"]; ok {clientIP = strings.Join(prior, ", ") + ", " + clientIP //增加ip}// 设置请求头outReq.Header.Set("X-Forwarded-For", clientIP)}// step 2, 请求下游res, err := transport.RoundTrip(outReq)if err != nil {rw.WriteHeader(http.StatusBadGateway)return}// step 3, 把下游请求内容返回给上游for key, value := range res.Header {for _, v := range value {rw.Header().Add(key, v)}}rw.WriteHeader(res.StatusCode)io.Copy(rw, res.Body)res.Body.Close()
}func main() {fmt.Println("Serve on :8080")http.Handle("/", &Pxy{})http.ListenAndServe("0.0.0.0:8080", nil)
}

反向代理

在这里插入图片描述

建立(反向代理)的下游web服务端

type RealServer struct {Addr string
}// hello handler
func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {//127.0.0.1:8008/abc?sdsdsa=11//r.Addr=127.0.0.1:8008//req.URL.Path=/abc//fmt.Println(req.Host)upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)realIP := fmt.Sprintf("RemoteAddr=%s,X-Forwarded-For=%v,X-Real-Ip=%v\n", req.RemoteAddr, req.Header.Get("X-Forwarded-For"), req.Header.Get("X-Real-Ip"))header := fmt.Sprintf("headers =%v\n", req.Header)io.WriteString(w, upath)io.WriteString(w, realIP)io.WriteString(w, header)}// error handler
func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {upath := "error handler"w.WriteHeader(500)io.WriteString(w, upath)
}// timeout handler
func (r *RealServer) TimeoutHandler(w http.ResponseWriter, req *http.Request) {time.Sleep(6 * time.Second)upath := "timeout handler"w.WriteHeader(200)io.WriteString(w, upath)
}func (r *RealServer) Run() {// 打印web服务端ip地址log.Println("Starting httpserver at " + r.Addr)// 注册路由mux := http.NewServeMux()// 绑定path和逻辑处理函数mux.HandleFunc("/", r.HelloHandler)mux.HandleFunc("/base/error", r.ErrorHandler)mux.HandleFunc("/test_http_string/test_http_string/aaa", r.TimeoutHandler)// 设置服务相关参数server := &http.Server{Addr:         r.Addr,          // ip和端口地址WriteTimeout: time.Second * 3, // 设置超时时间Handler:      mux,             //绑定路由}// 另起一个协程监听端口消息go func() {log.Fatal(server.ListenAndServe())}()
}func main() {rs1 := &RealServer{Addr: "127.0.0.1:2003"}rs1.Run()rs2 := &RealServer{Addr: "127.0.0.1:2004"}rs2.Run()//监听关闭信号quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit
}

创建反向代理

var (proxy_addr = "http://127.0.0.1:2003"port       = "2002"
)func handler(w http.ResponseWriter, r *http.Request) {//step 1 解析(客户端)代理地址,并更改请求体的协议和主机proxy, err := url.Parse(proxy_addr)r.URL.Scheme = proxy.Schemer.URL.Host = proxy.Host//step 2 请求下游(服务端)transport := http.DefaultTransportresp, err := transport.RoundTrip(r)if err != nil {log.Print(err)return}//step 3 把下游(服务端)请求内容返回给上游(客户端)for k, vv := range resp.Header {for _, v := range vv {w.Header().Add(k, v)}}defer resp.Body.Close()bufio.NewReader(resp.Body).WriteTo(w)
}func main() {// 绑定路由,监听客户端端口的信息http.HandleFunc("/", handler)log.Println("Start serving on port " + port)// 监听客户端端口传过来的信息err := http.ListenAndServe(":"+port, nil)if err != nil {log.Fatal(err)}
}

模拟客户端请求,请求客户端IP:端口/path

curl 'http://127.0.0.1:2003/sdadasds?sdsdsdsdsadadas=111'

基于ReverseProxy搭建http代理

  1. 更改内容支持
  2. 错误信息回调
  3. 支持自定义负载均衡
  4. url重写
  5. 连接池功能
  6. 支持websocket服务:独立章节介绍
  7. 支持https协议

ReverseProxy实现的简单的http代理

var addr = "127.0.0.1:2002"func main() {//127.0.0.1:2002/xxx//127.0.0.1:2003/base/xxxrs1 := "http://127.0.0.1:2003/base"url1, err1 := url.Parse(rs1)fmt.Printf("url1=%v\n", url1) //url1=http://127.0.0.1:2003/baseif err1 != nil {log.Println(err1)}// 实例化代理proxy := httputil.NewSingleHostReverseProxy(url1)log.Println("Starting httpserver at " + addr)// 开启监听,地址为addr = "127.0.0.1:2002"向地址url1=http://127.0.0.1:2003/base转发log.Fatal(http.ListenAndServe(addr, proxy))
}

ReverseProxy代理修改客户端请求的内容

// 客户端请求ip:端口号
var addr = "127.0.0.1:2002"// 拼接路径
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
}// 实例化代理
func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {//客户端请求的url:http://127.0.0.1:2002/dir?name=123targetQuery := target.RawQuery //RayQuery: name=123// 请求转发函数director := func(req *http.Request) {//url_rewrite//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abcre, _ := regexp.Compile("^/dir(.*)")req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")req.URL.Scheme = target.Scheme //Scheme: httpreq.URL.Host = target.Host     //Host: 127.0.0.1:2002//拼接路径req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) //target.Path : /base ; req.URL.Path : /dirif targetQuery == "" || req.URL.RawQuery == "" {req.URL.RawQuery = targetQuery + req.URL.RawQuery} else {req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery}// 添加header头// User-Agent,浏览器的用户代理字符串。告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本// 例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)if _, ok := req.Header["User-Agent"]; !ok {req.Header.Set("User-Agent", "")}}// 修改内容的函数modifyFunc := func(res *http.Response) error {// 当状态码异常的时候修改response.body里面的数据if res.StatusCode != 200 {return errors.New("error statusCode")oldPayload, err := ioutil.ReadAll(res.Body)if err != nil {return err}newPayLoad := []byte("hello " + string(oldPayload))res.Body = ioutil.NopCloser(bytes.NewBuffer(newPayLoad))res.ContentLength = int64(len(newPayLoad))res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))}return nil}// 打印错误日志的函数errorHandler := func(res http.ResponseWriter, req *http.Request, err error) {res.Write([]byte(err.Error()))}return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc, ErrorHandler: errorHandler}
}func main() {//127.0.0.1:2002/xxx//127.0.0.1:2003/base/xxx// 代理转发的地址rs1 := "http://127.0.0.1:2003/base"// 解析urlurl1, err1 := url.Parse(rs1) //url1=http://127.0.0.1:2003/basefmt.Printf("url1=%v\n", url1)if err1 != nil {log.Println(err1)}// 创建proxy实例proxy := NewSingleHostReverseProxy(url1)log.Println("Starting httpserver at " + addr)log.Fatal(http.ListenAndServe(addr, proxy))
}

一些特殊的请求头参数

  1. X-Forwarded-For头信息可以有多个,中间用逗号分隔,第一项为真实的客户端IP,剩下的就是曾经经过的代理或负载均衡的IP地址,经过几个就会出现几个。但是由于X-Forwarded-For内容是可以修改的所以并不安全和可信,最好满足IP的格式才读取,这样安全性高点。标准格式如下所示:
    X-Forwarded-For: client1, proxy1, proxy2

在这里插入图片描述

  1. X-Real-IP客户端请求的实际IP

在这里插入图片描述

  1. Connection有两个值:Close和Keep-Alive。当使用Connection:Close时,和HTTP1.0协议是一样的,当read方法读完数据时立即返回;而使用Connection:Keep-Alive时,read方法在读完数据后还要被阻塞一段时间。直接读取数据超时时间过后,还继续往下执行
    Connection: close
  2. TE客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息
    TE: trailers,deflate;q=0.5
  3. Trailer响应头允许发送方包括为了提供可能,而所述消息体被发送,诸如消息完整性检查,数字签名,或后处理状态动态地生成的元数据在分块消息的末尾附加字段,比如超时时间
    Trailer: Expires
  4. User-Agent是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA。它是一个特殊字符串头,是一种向访问网站提供你所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。通过这个标 识,用户所访问的网站可以显示不同的排版从而为用户提供更好的体验或者进行信息统计;例如用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的 UA来判断的
    User-Agent:Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50

End-to-end 端到端头部
此类头部字段会转发给 请求/响应 的最终接收目标,必须保存在由缓存生成的响应头部,必须被转发。
Hop-by-hop 逐跳首部
此类头部字段只对单次转发有效。会因为转发给缓存/代理服务器而失效,在HTTP 1.1 版本之后,如果要使用Hop-by-hop头部字段则需要提供Connection字段。而且除了以下8个字段为逐跳字段,其余均为端到端字段。
Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authenrization、Trailer、TE、Tranfer-Encoding、Upgrade

创建服务端

type RealServer struct {Addr string
}// hello handler
func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {//127.0.0.1:8008/abc?sdsdsa=11//r.Addr=127.0.0.1:8008//req.URL.Path=/abc//fmt.Println(req.Host)upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)realIP := fmt.Sprintf("RemoteAddr=%s,X-Forwarded-For=%v,X-Real-Ip=%v\n", req.RemoteAddr, req.Header.Get("X-Forwarded-For"), req.Header.Get("X-Real-Ip"))header := fmt.Sprintf("headers =%v\n", req.Header)io.WriteString(w, upath)io.WriteString(w, realIP)io.WriteString(w, header)}// error handler
func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {upath := "error handler"w.WriteHeader(500)io.WriteString(w, upath)
}// timeout handler
func (r *RealServer) TimeoutHandler(w http.ResponseWriter, req *http.Request) {time.Sleep(6 * time.Second)upath := "timeout handler"w.WriteHeader(200)io.WriteString(w, upath)
}func (r *RealServer) Run() {// 打印web服务端ip地址log.Println("Starting httpserver at " + r.Addr)// 注册路由mux := http.NewServeMux()// 绑定path和逻辑处理函数mux.HandleFunc("/", r.HelloHandler)mux.HandleFunc("/base/error", r.ErrorHandler)mux.HandleFunc("/test_http_string/test_http_string/aaa", r.TimeoutHandler)// 设置服务相关参数server := &http.Server{Addr:         r.Addr,          // ip和端口地址WriteTimeout: time.Second * 3, // 设置超时时间Handler:      mux,             //绑定路由}// 另起一个协程监听端口消息go func() {log.Fatal(server.ListenAndServe())}()
}func main() {// 根据端口127.0.0.1:2003创建服务rs1 := &RealServer{Addr: "127.0.0.1:2003"}rs1.Run()// 根据端口127.0.0.1:2004创建服务rs2 := &RealServer{Addr: "127.0.0.1:2004"}rs2.Run()//监听关闭信号quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit
}

创建第一级代理

// 客户端请求地址
var addr = "127.0.0.1:2001"// 设置连接池
var transport = &http.Transport{DialContext: (&net.Dialer{Timeout:   30 * time.Second, //连接超时KeepAlive: 30 * time.Second, //长连接超时时间}).DialContext,MaxIdleConns:          100,              //最大空闲连接IdleConnTimeout:       90 * time.Second, //空闲超时时间TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间ExpectContinueTimeout: 1 * time.Second,  //100-continue 超时时间
}// 字符串拼接
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
}// 创建代理实例
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {//	请求协调者director := func(req *http.Request) {//	url_rewrite//	127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??//	127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc//	127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abcre, _ := regexp.Compile("^/dir(.*)")req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")//	随机负载均衡targetIndex := rand.Intn(len(targets))target := targets[targetIndex]targetQuery := target.RawQueryreq.URL.Scheme = target.Schemereq.URL.Host = target.Host//	url地址重写:重写前:/aa 重写后:/base/aareq.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")}//	只在第一代理中设置此header头req.Header.Set("X-Real-Ip", req.RemoteAddr)}//	更改内容modifyFunc := func(resp *http.Response) error {//	请求以下命令:curl 'http://127.0.0.1:2002/error'if resp.StatusCode != 200 {//	获取内容oldPayload, err := ioutil.ReadAll(resp.Body)if err != nil {return err}//追加内容newPayload := []byte("StatusCode error:" + string(oldPayload))resp.Body = ioutil.NopCloser(bytes.NewBuffer(newPayload))resp.ContentLength = int64(len(newPayload))resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPayload)), 10))}return nil}//	错误回调:关闭real_server时测试,错误回调errFunc := func(w http.ResponseWriter, r *http.Request, err error) {http.Error(w, "ErrorHandler error:"+err.Error(), 500)}return &httputil.ReverseProxy{Director:       director,Transport:      transport,ModifyResponse: modifyFunc,ErrorHandler:   errFunc}
}// 主函数
func main() {// 本代理地址rs1 := "http://127.0.0.1:2002"url1, err1 := url.Parse(rs1) //	解析代理地址if err1 != nil {log.Println(err1)}urls := []*url.URL{url1}                    //	创建代理列表proxy := NewMultipleHostsReverseProxy(urls) //	创建代理实例log.Println("Starting httpserver at " + addr)log.Fatal(http.ListenAndServe(addr, proxy)) //	监听客户端信息
}

创建二级代理

// 第一级代理的地址
var addr = "127.0.0.1:2002"// 创建连接池
var transport = &http.Transport{DialContext: (&net.Dialer{Timeout:   30 * time.Second, //连接超时KeepAlive: 30 * time.Second, //长连接超时时间}).DialContext,MaxIdleConns:          100,              //最大空闲连接IdleConnTimeout:       90 * time.Second, //空闲超时时间TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间ExpectContinueTimeout: 1 * time.Second,  //100-continue 超时时间
}// 拼接字符串
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
}// 创建第二级代理实例
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {//请求协调者director := func(req *http.Request) {//url_rewrite//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abcre, _ := regexp.Compile("^/dir(.*)")                   //正则匹配req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1") //替换//随机负载均衡targetIndex := rand.Intn(len(targets)) //根据服务器的序列号产生随机整数target := targets[targetIndex]         //选择随机选取的服务器targetQuery := target.RawQuery         //获取查询语句req.URL.Scheme = target.Scheme         //协议种类,Scheme: httpreq.URL.Host = target.Host             //地址,Host: 127.0.0.1:2003//todo 部分章节补充1//todo 当对域名(非内网)反向代理时需要设置此项。当作后端反向代理时不需要req.Host = target.Host// url地址重写:重写前:/aa 重写后:/base/aareq.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")}//只在第一代理中设置此header头,在二级及其以后的代理中直接转发即可//req.Header.Set("X-Real-Ip", req.RemoteAddr)}//更改内容modifyFunc := func(resp *http.Response) error {//请求以下命令:curl 'http://127.0.0.1:2002/error'//todo 部分章节功能补充2//todo 兼容websocketif strings.Contains(resp.Header.Get("Connection"), "Upgrade") {return nil}var payload []bytevar readErr error//todo 部分章节功能补充3//todo 兼容gzip压缩if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {gr, err := gzip.NewReader(resp.Body)if err != nil {return err}payload, readErr = ioutil.ReadAll(gr)resp.Header.Del("Content-Encoding")} else {payload, readErr = ioutil.ReadAll(resp.Body)}if readErr != nil {return readErr}//异常请求时设置StatusCodeif resp.StatusCode != 200 {payload = []byte("StatusCode error:" + string(payload))}//todo 部分章节功能补充4//todo 因为预读了数据所以内容重新回写resp.Body = ioutil.NopCloser(bytes.NewBuffer(payload))resp.ContentLength = int64(len(payload))resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(payload)), 10))return nil}//错误回调 :关闭real_server时测试,错误回调errFunc := func(w http.ResponseWriter, r *http.Request, err error) {http.Error(w, "ErrorHandler error:"+err.Error(), 500)}return &httputil.ReverseProxy{Director:       director,Transport:      transport,ModifyResponse: modifyFunc,ErrorHandler:   errFunc}
}// 启动服务
func main() {// 请求服务器的地址//rs1 := "http://www.baidu.com"rs1 := "http://127.0.0.1:2003"url1, err1 := url.Parse(rs1)if err1 != nil {log.Println(err1)}// 请求服务器的地址//rs2 := "http://www.baidu.com"rs2 := "http://127.0.0.1:2004"url2, err2 := url.Parse(rs2)if err2 != nil {log.Println(err2)}// 将服务器的两个地址写进代理的请求列表中urls := []*url.URL{url1, url2}proxy := NewMultipleHostsReverseProxy(urls)   //创建代理实例log.Println("Starting httpserver at " + addr) //打印日志log.Fatal(http.ListenAndServe(addr, proxy))   //开启监听
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代理和反向代理

代理是代理的是用户&#xff08;eg&#xff1a;浏览器&#xff09;&#xff1b; 反向代理是代理的是服务器&#xff08;Nginx&#xff09;&#xff1b; 如下图&#xff1a; 常用的代理工具&#xff1a;Fiddler、Whistle、Charles 常用切换代理的工具&#xff1a;SwitchyOme…

反向代理HAproxy

HAProxy 介绍和架构HAProxy 安装HAProxy 基础配置HAProxy 调度算法HAProxy 高级功能 1 Web 架构介绍 2 HAProxy 简介 负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均衡将特定的业务(…

正向代理、反向代理介绍

目录 一、定义 1、正向代理(forward proxy) 2、反向代理(Reverse proxy) 3、透明代理(transparent proxy) 二、生活中代理的例子 1、正向代理 2、反向代理 三、代理的作用 1、正向代理的作用 2、反向代理的作用 目前大家提到的代理技术无外乎三种&#xff1a;正向代理…

代理、正向代理与反向代理

一、代理 1.1 什么是代理 代理也被叫做网络代理&#xff0c;是一种比较特殊的网络服务&#xff0c;允许一个终端&#xff08;通常指客户端&#xff09;通过这个服务与另一个终端&#xff08;通常指服务器端&#xff09;进行非直接的连接。例如&#xff1a;一些网关、路由器等…

如何下载一直播的回放视频

想下载一直播回放视频&#xff0c;然后就可以用播放器倍速观看了 &#xff08;一&#xff09;处理一下想要下载的视频 &#xff08;二&#xff09;使用ffmpeg将.m3u8格式文件转换成.mp4文件 &#xff08;1&#xff09;下载&#xff1a; 下载地址:windows版本 : http://ffmpe…

php对接腾讯云直播,聊天,im,云录制产生回放

首先先在腾讯云中开通这三项 IM中创建项目 云直播 解析推拉流地址&#xff1a; $domain $this->getConfig(anchor_push); //推流地址$domainpull $this->getConfig(anchor_pull); //拉流地址$streamName kangfuyuan.$res; //直播间ID&#xff08;唯一的&am…

心法利器[84] | 最近面试小结

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

Visual Studio 2022 集成虚幻引擎功能

【CSDN 编者按】微软作为全球著名的游戏厂商&#xff0c;其在游戏制作与开发体验上也积极下功夫。在听取了游戏开发人员的反馈后&#xff0c;Visual Studio 2022 正式集成 Unreal Engine &#xff08;UE、虚幻引擎&#xff09; 作者 | David Li 责编 | 梦依丹 出品 | CSD…

苹果:付费才能用 iOS 开发者预览版,网友吐槽:找 Bug,还得先交 99 美元?

整理 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 以后要尝鲜 iOS 开发者预览版&#xff0c;要先付费&#xff0c;才能测试&#xff1f; 苹果的更新说明 近日&#xff0c;苹果最新公布的一项政策引发了不少开发者的争议&#xff0c;其表示&#xff0c;从…

Android 系统 TP 事件处理流程

简单流程 首先注册 i2c 设备&#xff0c;添加 tp 驱动&#xff0c;然后在 tp 驱动上响应中断、获取用户操作的 数据&#xff0c;通过筛选等一系列操作将数据上报。 *中断、线程、工作队列生成 *放大缩小、xy 对调等算法 *get_event 函数使用 1. 系统调用 TP 驱动 TP 驱动的调…

Java网络编程之UDP和TCP套接字

一. 网络编程概述 我们知道在网络通信中, 数据的发送是从应用层开始, 一直封装到物理层然后进行发送的, 应用层要将数据交给传输层进行封装; 而接收方拿到数据后是从物理层到应用层进行分用, 传输层要将拿到的数据再分用给应用层进行使用, 网络编程实际操作中最关键的就是我们所…

工程师“魔改” AirPods Pro 接口,苹果“妥协”将成大势所趋?

整理 | 朱珂欣 出品 | CSDN程序人生&#xff08;ID&#xff1a;coder_life&#xff09; 近年来&#xff0c; USB-C 接口凭借其高效的数据传输、充电速度等优势&#xff0c;促使时下的许多手机品牌大规模使用&#xff0c;可谓实现了统一“半壁江山”的势头&#xff0c;甚至盖…

web开发中的通信协议

websocket def&#xff1a;websocket协议是基于tcp的&#xff0c;实现浏览器与服务器之间全双工通信的一种网络协议 websocket是一种持久协议&#xff0c;多应用在聊天&#xff0c;客服咨询等有实时报送需求的场景下。 早期没有websocket时&#xff0c;通过ajax短时轮询&#x…

GLM联合go-cqhttp实现qq群GLM机器人服务器的本地化部署笔记

GLM qq群服务器的本地化部署笔记 一. 概述1.1 整体结构1.2 目标1.3 需求1.4 流程说明 二. 部署流程2.1 使用GPT转发程序帮助文档2.1.1 使用git安装GLM2.1.2 不使用git安装GLM 2.2 使用Anaconda Navigator 虚拟运行GLM2.2.1 [https://www.anaconda.com/](https://www.anaconda.c…

chatglm-6b:本地手动下载,本地部署

文章目录 模型效果演示操作步骤步骤一步骤二步骤三 ChatGLM-6B是一个由清华大学和智谱AI联合研发的开源对话语言模型。它是一个支持中英双语问答的对话系统&#xff0c;并在中文方面进行了特别的优化。 该模型基于General Language Model (GLM)架构&#xff0c;具有62亿参数。借…

阿里组织变革:设立六大业务集团,成熟一个,上市一个;微软软件工程师最高年薪28.8万美元;iOS 16.4 发布|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

乐视实行四天半工作制,网友:还招人吗,我有个朋友想去!

作者 | 苏宓、出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 新春的第一个工作日&#xff0c;有一个别人家的公司登上了热搜&#xff0c;羡煞旁人&#xff0c;它就是乐视。 起因是因为#乐视开始实行每周四天半工作制#&#xff0c;所谓话少事大&#xff0c;在官宣…

学历造假、拖欠工资、核心技术归属存疑?AI 独角兽创始人遭质疑后回应!

整理 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 所谓树大招风&#xff0c;正在 Stability AI 及其创造的 Stable Diffusion 模型工具身上体现出来。 Stable Diffusion&#xff08;SD&#xff09;是如今主流的文本创建图像的生成式 AI 工具&#xff0c…

『突破极限』利用ChatGPT一分钟生成思维导图,从此告别繁琐,助你轻松学习……

大家有没有经常需要做思维导图的时候&#xff1f; ChatGPT可以快速做PPT、快速做短视频、快速做图片&#xff0c;那可不可以快速做思维导图呢&#xff1f; 答案是肯定滴&#xff0c;而且出图速度超出你的想象&#xff0c;非常离谱。 以后读文献、书籍、商业文章分析简直就是…

【问题已解决】无法定位程序输入点于XXX动态链接库***.dll上

无法定位程序输入点于XXX动态链接库***.dll上 事件前因后果温馨提示解决方法 ChatGPT中文版购买 事件前因后果 在一次安装又卸载falsh&#xff08;可能非正版&#xff09;后&#xff0c;关机再开机后&#xff0c;发现除了浏览器&#xff0c;其他软件双击打开是都提 示无法定位…