首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >golang源码分析 :gopls(3)

golang源码分析 :gopls(3)

作者头像
golangLeetcode
发布2026-03-18 18:28:51
发布2026-03-18 18:28:51
720
举报

在internal/lsprpc/lsprpc.go中有Forwarder的实现

代码语言:javascript
复制
func NewForwarder(rawAddr string, argFunc func(network, address string) []string) (jsonrpc2.StreamServer, error) {
    dialer, err := newAutoDialer(rawAddr, argFunc)
    if err != nil {
        return nil, err
    }
    fwd := &forwarder{
        dialer: dialer,
    }
    return fwd, nil
}

它返回一个实现了ServeStream方法的接口

代码语言:javascript
复制
type StreamServer interface {
    ServeStream(context.Context, Conn) error
}

具体实现如下,先建立client连接用于转发请求,然后,启动server来提供服务,最后启动一个协程,用于请求的转发工作,转发工作有两个协程,分别用于客户端侧转发和服务端侧转发

代码语言:javascript
复制
type forwarder struct {
    dialer *autoDialer
    mu sync.Mutex
    // Hold on to the server connection so that we can redo the handshake if any
    // information changes.
    serverConn jsonrpc2.Conn
    serverID   string
}
代码语言:javascript
复制
func (f *forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) error {
    client := protocol.ClientDispatcher(clientConn)
    netConn, err := f.dialer.dialNet(ctx)
    serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn))
    server := protocol.ServerDispatcher(serverConn)
    serverConn.Go(ctx,
        protocol.Handlers(
            protocol.ClientHandler(client,
                jsonrpc2.MethodNotFound)))
    
    f.handshake(ctx)
    clientConn.Go(ctx,
        protocol.Handlers(
            f.handler(
                protocol.ServerHandler(server,
                    jsonrpc2.MethodNotFound))))

在进行转发之前还初始化了一个autoDialer,实现位于internal/lsprpc/dialer.go

代码语言:javascript
复制
func newAutoDialer(rawAddr string, argFunc func(network, addr string) []string) (*autoDialer, error) {
    d := autoDialer{
        argFunc: argFunc,
    }
    bin, err := os.Executable()
    d.network, d.addr = autoNetworkAddress(bin, d.addr)

其中

代码语言:javascript
复制
autoNetworkAddress    = autoNetworkAddressDefault
代码语言:javascript
复制
func autoNetworkAddressDefault(goplsPath, id string) (network string, address string) {
    if id != "" {
        panic("identified remotes are not supported on windows")
    }
    return "tcp", "localhost:37374"
}

这里可以看下gopls默认服务端口是37374

在internal/jsonrpc2/conn.go中定义了Conn接口,转发的时候用到了其中的Go方法。

代码语言:javascript
复制
type Conn interface {
    // Call invokes the target method and waits for a response.
    // The params will be marshaled to JSON before sending over the wire, and will
    // be handed to the method invoked.
    // The response will be unmarshaled from JSON into the result.
    // The id returned will be unique from this connection, and can be used for
    // logging or tracking.
    Call(ctx context.Context, method string, params, result interface{}) (ID, error)
    // Notify invokes the target method but does not wait for a response.
    // The params will be marshaled to JSON before sending over the wire, and will
    // be handed to the method invoked.
    Notify(ctx context.Context, method string, params interface{}) error
    // Go starts a goroutine to handle the connection.
    // It must be called exactly once for each Conn.
    // It returns immediately.
    // You must block on Done() to wait for the connection to shut down.
    // This is a temporary measure, this should be started automatically in the
    // future.
    Go(ctx context.Context, handler Handler)
    // Close closes the connection and it's underlying stream.
    // It does not wait for the close to complete, use the Done() channel for
    // that.
    Close() error
    // Done returns a channel that will be closed when the processing goroutine
    // has terminated, which will happen if Close() is called or an underlying
    // stream is closed.
    Done() <-chan struct{}
    // Err returns an error if there was one from within the processing goroutine.
    // If err returns non nil, the connection will be already closed or closing.
    Err() error
}

protocol.Handlers为转发过程增加了一系列middleware,同样作用的还有f.handler,它在middleware中实现了初始化和工作区间执行命令的功能。

代码语言:javascript
复制
func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
    return CancelHandler(
        jsonrpc2.AsyncHandler(
            jsonrpc2.MustReplyHandler(handler)))
}
代码语言:javascript
复制
func (f *forwarder) handler(handler jsonrpc2.Handler) jsonrpc2.Handler {
    return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
        // Intercept certain messages to add special handling.
        switch r.Method() {
        case "initialize":
            if newr, err := addGoEnvToInitializeRequest(ctx, r); err == nil {
                r = newr
            } else {
                log.Printf("unable to add local env to initialize request: %v", err)
            }
        case "workspace/executeCommand":

ServerHandler和 ClientHandler分别提供了服务端和客户端的middleware

代码语言:javascript
复制
func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
代码语言:javascript
复制
func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {

至此转发代理模式的server初始化完毕。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档