github.com/gogf/gf/v2@v2.7.4/os/gproc/gproc_comm_receive.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gproc
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"net"
    13  
    14  	"github.com/gogf/gf/v2/container/gqueue"
    15  	"github.com/gogf/gf/v2/container/gtype"
    16  	"github.com/gogf/gf/v2/errors/gerror"
    17  	"github.com/gogf/gf/v2/internal/json"
    18  	"github.com/gogf/gf/v2/net/gtcp"
    19  	"github.com/gogf/gf/v2/os/gfile"
    20  	"github.com/gogf/gf/v2/os/glog"
    21  	"github.com/gogf/gf/v2/util/gconv"
    22  )
    23  
    24  var (
    25  	// tcpListened marks whether the receiving listening service started.
    26  	tcpListened = gtype.NewBool()
    27  )
    28  
    29  // Receive blocks and receives message from other process using local TCP listening.
    30  // Note that, it only enables the TCP listening service when this function called.
    31  func Receive(group ...string) *MsgRequest {
    32  	// Use atomic operations to guarantee only one receiver goroutine listening.
    33  	if tcpListened.Cas(false, true) {
    34  		go receiveTcpListening()
    35  	}
    36  	var groupName string
    37  	if len(group) > 0 {
    38  		groupName = group[0]
    39  	} else {
    40  		groupName = defaultGroupNameForProcComm
    41  	}
    42  	queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} {
    43  		return gqueue.New(maxLengthForProcMsgQueue)
    44  	}).(*gqueue.Queue)
    45  
    46  	// Blocking receiving.
    47  	if v := queue.Pop(); v != nil {
    48  		return v.(*MsgRequest)
    49  	}
    50  	return nil
    51  }
    52  
    53  // receiveTcpListening scans local for available port and starts listening.
    54  func receiveTcpListening() {
    55  	var (
    56  		listen  *net.TCPListener
    57  		conn    net.Conn
    58  		port    = gtcp.MustGetFreePort()
    59  		address = fmt.Sprintf("127.0.0.1:%d", port)
    60  	)
    61  	tcpAddress, err := net.ResolveTCPAddr("tcp", address)
    62  	if err != nil {
    63  		panic(gerror.Wrap(err, `net.ResolveTCPAddr failed`))
    64  	}
    65  	listen, err = net.ListenTCP("tcp", tcpAddress)
    66  	if err != nil {
    67  		panic(gerror.Wrapf(err, `net.ListenTCP failed for address "%s"`, address))
    68  	}
    69  	// Save the port to the pid file.
    70  	if err = gfile.PutContents(getCommFilePath(Pid()), gconv.String(port)); err != nil {
    71  		panic(err)
    72  	}
    73  	// Start listening.
    74  	for {
    75  		if conn, err = listen.Accept(); err != nil {
    76  			glog.Error(context.TODO(), err)
    77  		} else if conn != nil {
    78  			go receiveTcpHandler(gtcp.NewConnByNetConn(conn))
    79  		}
    80  	}
    81  }
    82  
    83  // receiveTcpHandler is the connection handler for receiving data.
    84  func receiveTcpHandler(conn *gtcp.Conn) {
    85  	var (
    86  		ctx      = context.TODO()
    87  		result   []byte
    88  		response MsgResponse
    89  	)
    90  	for {
    91  		response.Code = 0
    92  		response.Message = ""
    93  		response.Data = nil
    94  		buffer, err := conn.RecvPkg()
    95  		if len(buffer) > 0 {
    96  			// Package decoding.
    97  			msg := new(MsgRequest)
    98  			if err = json.UnmarshalUseNumber(buffer, msg); err != nil {
    99  				continue
   100  			}
   101  			if msg.ReceiverPid != Pid() {
   102  				// Not mine package.
   103  				response.Message = fmt.Sprintf(
   104  					"receiver pid not match, target: %d, current: %d",
   105  					msg.ReceiverPid, Pid(),
   106  				)
   107  			} else if v := commReceiveQueues.Get(msg.Group); v == nil {
   108  				// Group check.
   109  				response.Message = fmt.Sprintf("group [%s] does not exist", msg.Group)
   110  			} else {
   111  				// Push to buffer queue.
   112  				response.Code = 1
   113  				v.(*gqueue.Queue).Push(msg)
   114  			}
   115  		} else {
   116  			// Empty package.
   117  			response.Message = "empty package"
   118  		}
   119  		if err == nil {
   120  			result, err = json.Marshal(response)
   121  			if err != nil {
   122  				glog.Error(ctx, err)
   123  			}
   124  			if err = conn.SendPkg(result); err != nil {
   125  				glog.Error(ctx, err)
   126  			}
   127  		} else {
   128  			// Just close the connection if any error occurs.
   129  			if err = conn.Close(); err != nil {
   130  				glog.Error(ctx, err)
   131  			}
   132  			break
   133  		}
   134  	}
   135  }