package main

import (
    "net"
    "time"
)

func initProxy() {

    pLog.Infof("Proxying %s -> %s\n", pConfig.Bind, pConfig.Backend)  //输出服务地址   后端服务地址列表  

    server, err := net.Listen("tcp", pConfig.Bind)  //建立tcp连接
    if err != nil {
        pLog.Fatal(err)
    }

    waitQueue := make(chan net.Conn, pConfig.WaitQueueLen)  //建立连接队列  队列默认等待队列长度---channel  net.Conn  ==控制最大排队队列
    availPools := make(chan bool, pConfig.MaxConn)  //建立最大连接数--channel  bool=== 控制最大连接数
    for i := 0; i < pConfig.MaxConn; i++ {  //
        availPools <- true
    }

    go loop(waitQueue, availPools)

    for {
        connection, err := server.Accept()  //等待获取下一次连接
        if err != nil {
            pLog.Error(err)
        } else {
            pLog.Infof("Received connection from %s.\n", connection.RemoteAddr())
            waitQueue <- connection
        }
    }
}

func loop(waitQueue chan net.Conn, availPools chan bool) {
    for connection := range waitQueue { //循环等待队列中 排队等待需要处理的数据--连接
        <-availPools  //从通道中 连接池中取出一个
        go func(connection net.Conn) {
            handleConnection(connection)
            availPools <- true  //使用结束放回连接池中  目的控制连接数量  通道阻塞特性
            pLog.Infof("Closed connection from %s.\n", connection.RemoteAddr())
        }(connection)
    }
}

func handleConnection(connection net.Conn) {
    defer connection.Close()

    bksvr, ok := getBackendSvr(connection)
    if !ok {
        return
    }
    remote, err := net.Dial("tcp", bksvr.svrStr)

    if err != nil {
        pLog.Error(err)
        bksvr.failTimes++
        return
    }

    //等待双向连接完成
    complete := make(chan bool, 2)
    oneSide := make(chan bool, 1)
    otherSide := make(chan bool, 1)
    go pass(connection, remote, complete, oneSide, otherSide)
    go pass(remote, connection, complete, otherSide, oneSide)
    <-complete
    <-complete
    remote.Close()
}

// copy Content two-way
func pass(from net.Conn, to net.Conn, complete chan bool, oneSide chan bool, otherSide chan bool) {
    var err error
    var read int
    bytes := make([]byte, 256)

    for {
        select {

        case <-otherSide:
            complete <- true
            return

        default:

            from.SetReadDeadline(time.Now().Add(time.Duration(pConfig.Timeout) * time.Second))
            read, err = from.Read(bytes)
            if err != nil {
                complete <- true
                oneSide <- true
                return
            }

            to.SetWriteDeadline(time.Now().Add(time.Duration(pConfig.Timeout) * time.Second))
            _, err = to.Write(bytes[:read])
            if err != nil {
                complete <- true
                oneSide <- true
                return
            }
        }
    }
}

proxy.go 源码阅读的更多相关文章

  1. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  2. Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)

    我们到底能走多远系列(33) 扯淡: 各位:    命运就算颠沛流离   命运就算曲折离奇   命运就算恐吓着你做人没趣味   别流泪 心酸 更不应舍弃   ... 主题: Spring源码阅读还在继 ...

  3. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  4. 【JDK1.8】JDK1.8集合源码阅读——IdentityHashMap

    一.前言 今天我们来看一下本次集合源码阅读里的最后一个Map--IdentityHashMap.这个Map之所以放在最后是因为它用到的情况最少,也相较于其他的map来说比较特殊.就笔者来说,到目前为止 ...

  5. Spring源码阅读笔记

    前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...

  6. Rpc框架dubbo-client(v2.6.3) 源码阅读(二)

    接上一篇 dubbo-server 之后,再来看一下 dubbo-client 是如何工作的. dubbo提供者服务示例, 其结构是这样的!dubbo://192.168.11.6:20880/com ...

  7. 【Dubbo源码阅读系列】之远程服务调用(上)

    今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道 ...

  8. 【Dubbo源码阅读系列】服务暴露之远程暴露

    引言 什么叫 远程暴露 ?试着想象着这么一种场景:假设我们新增了一台服务器 A,专门用于发送短信提示给指定用户.那么问题来了,我们的 Message 服务上线之后,应该如何告知调用方服务器,服务器 A ...

  9. 【Dubbo源码阅读系列】服务暴露之本地暴露

    在上一篇文章中我们介绍 Dubbo 自定义标签解析相关内容,其中我们自定义的 XML 标签 <dubbo:service /> 会被解析为 ServiceBean 对象(传送门:Dubbo ...

随机推荐

  1. ubuntu安装最新的rails-4.2.0

    完全按照教程来,可是错误不断,还是边装边baidu吧! sudo gem install rails 安装了一大坨关联gem之后,终于好了.于是想小试一下身手,新建文件夹rails_test,cd进入 ...

  2. asp.net mvc控制器激活全分析

    控制器的激活默认情况下使用反射来实现的,这其中采用了DI,单例等设计模式.对于控制器的主要涉及到如下的类:ControllerBuilder.DefaultControllerFactory.Defa ...

  3. Shiro之身份认证、与spring集成(入门级)

    目录 1. Shro的概念 2. Shiro的简单身份认证实现 3. Shiro与spring对身份认证的实现 前言: Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境 ...

  4. Eclipse安装SVN插件(转载)

    http://www.cnblogs.com/ruiati/p/3584120.html 1.下载最新的Eclipse,我的版本是3.7.2 indigo(Eclipse IDE for Java E ...

  5. jsoup 使用总结4--高级用法之 script js 脚本

    jsoup 使用总结4--高级用法之 script js 脚本 大部分时候,我们使用jsoup解析网页的时候,都是直接找到某一类元素,或者按某种selector查询:具体使用方法可以参考jsoup官网 ...

  6. cbp2make使用

    codeblock中的cbp2make插件能自动生成makefile文件,这里介绍一下从安装到使用到的过程. 首先下载cbp2make,下载的包后缀名是.tar.gz.Linux里双击该下载文件,解压 ...

  7. Java开源生鲜电商平台-系统架构与技术选型(源码可下载)

    Java开源生鲜电商平台-系统架构与技术选型(源码可下载) 1.  硬件环境 公司服务器 2.   软件环境 2.1  操作系统 Linux CentOS 6.8系列 2.2 反向代理/web服务器 ...

  8. php数据导出excel

    /** * 导出数据为excel表格 *@param $data 一个二维数组,结构如同从数据库查出来的数组 *@param $title excel的第一行标题,一个数组,如果为空则没有标题 *@p ...

  9. PHP中的 $_SERVER 函数说明详解

    用php在开发软件的时候,我们经常用到 $_SERVER[]这个函数,今天就来讲下这个数组的值,方便以后使用: A: $_SERVER['ARGC'] #包含传递给程序的 命令行参数的个数(如果运行在 ...

  10. 基于elk 实现nginx日志收集与数据分析。

    一.背景 前端web服务器为nginx,采用filebeat + logstash + elasticsearch + granfa 进行数据采集与展示,对客户端ip进行地域统计,监控服务器响应时间等 ...