
介绍完rpc方式实现后我们看看stdio方式的实现,首先是初始化Stream
func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*settings.Options)) jsonrpc2.StreamServer {
return &streamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc}
}type streamServer struct {
cache *cache.Cache
// daemon controls whether or not to log new connections.
daemon bool
// optionsOverrides is passed to newly created sessions.
optionsOverrides func(*settings.Options)
// serverForTest may be set to a test fake for testing.
serverForTest protocol.Server
}它的ServeStream方法定义如下
func (s *streamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error {
client := protocol.ClientDispatcher(conn)
session := cache.NewSession(ctx, s.cache)
svr := s.serverForTest
if svr == nil {
options := settings.DefaultOptions(s.optionsOverrides)
svr = server.New(session, client, options)
if instance := debug.GetInstance(ctx); instance != nil {
instance.AddService(svr, session)
}
}
// Clients may or may not send a shutdown message. Make sure the server is
// shut down.
// TODO(rFindley): this shutdown should perhaps be on a disconnected context.
defer func() {
if err := svr.Shutdown(ctx); err != nil {
event.Error(ctx, "error shutting down", err)
}
}()
executable, err := os.Executable()
if err != nil {
log.Printf("error getting gopls path: %v", err)
executable = ""
}
ctx = protocol.WithClient(ctx, client)
conn.Go(ctx,
protocol.Handlers(
handshaker(session, executable, s.daemon,
protocol.ServerHandler(svr,
jsonrpc2.MethodNotFound))))
if s.daemon {
log.Printf("Session %s: connected", session.ID())
defer log.Printf("Session %s: exited", session.ID())
}
<-conn.Done()
return conn.Err()
}func ClientDispatcher(conn jsonrpc2.Conn) ClientCloser {
return &clientDispatcher{sender: clientConn{conn}}
}type clientDispatcher struct {
sender connSender
}首先通过os.Executable()获取可以执行的命令行工具,然后通过
svr=server.New(session, client, options)获取server,最后调用conn.Go进行转发
conn.Go(ctx,
protocol.Handlers(
handshaker(session, executable, s.daemon,
protocol.ServerHandler(svr,
jsonrpc2.MethodNotFound))))其中middleware handshaker 完成了握手和会话的功能
func handshaker(session *cache.Session, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
switch r.Method() {
case handshakeMethod:
// We log.Printf in this handler, rather than event.Log when we want logs
// to go to the daemon log rather than being reflected back to the
// client.
var req handshakeRequest
if err := json.Unmarshal(r.Params(), &req); err != nil {
if logHandshakes {
log.Printf("Error processing handshake for session %s: %v", session.ID(), err)
}
sendError(ctx, reply, err)
return nil
}
if logHandshakes {
log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", session.ID(), req.Logfile, req.DebugAddr)
}
event.Log(ctx, "Handshake session update",
cache.KeyUpdateSession.Of(session),
label.DebugAddress.Of(req.DebugAddr),
label.Logfile.Of(req.Logfile),
label.ServerID.Of(req.ServerID),
label.GoplsPath.Of(req.GoplsPath),
)
resp := handshakeResponse{
SessionID: session.ID(),
GoplsPath: goplsPath,
}
if di := debug.GetInstance(ctx); di != nil {
resp.Logfile = di.Logfile
resp.DebugAddr = di.ListenedDebugAddress()
}
return reply(ctx, resp, nil)
case sessionsMethod:
resp := serverState{
GoplsPath: goplsPath,
CurrentClientID: session.ID(),
}
if di := debug.GetInstance(ctx); di != nil {
resp.Logfile = di.Logfile
resp.DebugAddr = di.ListenedDebugAddress()
for _, c := range di.State.Clients() {
resp.Clients = append(resp.Clients, clientSession{
SessionID: c.Session.ID(),
Logfile: c.Logfile,
DebugAddr: c.DebugAddress,
})
}
}
return reply(ctx, resp, nil)
}
return handler(ctx, reply, r)
}
}对应的请求路径如下
const (
handshakeMethod = "gopls/handshake"
sessionsMethod = "gopls/sessions"
)func New(session *cache.Session, client protocol.ClientCloser, options *settings.Options) protocol.Server {
const concurrentAnalyses = 1
// If this assignment fails to compile after a protocol
// upgrade, it means that one or more new methods need new
// stub declarations in unimplemented.go.
return &server{
diagnostics: make(map[protocol.DocumentURI]*fileDiagnostics),
watchedGlobPatterns: nil, // empty
changedFiles: make(map[protocol.DocumentURI]unit),
session: session,
client: client,
diagnosticsSema: make(chan unit, concurrentAnalyses),
progress: progress.NewTracker(client),
options: options,
viewsToDiagnose: make(map[*cache.View]uint64),
}
}对应的server就返回了pls 的接口具体实现。
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!