⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/SOFA-Mosn/build-debugging-environment/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1. 概述

本文,我们将基于 SOFA-Mosn 提供的 sofarpc-sample 示例,搭建一个能够断点调试的环境。

sofarpc-sample 示例的简介如下:

FROM 《配置 SOFARPC 协议 SOFAMesh》

  • 该样例工程演示了如何配置使得 SOFAMesh 作为 SofaRpc 代理。
  • SOFAMesh 之间的协议是 HTTP2 。
  • 为了演示方便,SOFAMesh 监听两个端口,一个转发 Client 的请求,一个收到请求以后转发给 Server 。

可能这样子胖友有点懵逼,下面我们来看一张图:

我是图

😈 可能有胖友又懵逼了,不是 Service Mesh 需要 Istio 么?在本文的搭建的示例里,Istio 并非必须。

2. 依赖工具

  • Go :1.9.2+

    笔者是 Mac 环境,使用 brew install go 安装的 Go 。

  • GoLand

    艿艿的惊叹:GoLand 竟然可以直接调试 Go 代码,完全不用配置环境,好棒!!!此处是不是应该让 GoLand 打一笔广告费到我账头上。

3. 获取代码

# 进入 GOPATH 下的 src 目录
cd $GOPATH/src
# 创建 github.com/alipay 目录
mkdir -p github.com/alipay
cd github.com/alipay

# clone mosn 代码
git clone git@github.com:alipay/sofa-mosn.git
cd sofa-mosn

最终 Mosn 的源代码代码路径为 $GOPATH/src/github.com/alipay/sofa-mosn

4. 导入IDE

使用萌萌的 GoLand 导入 $GOPATH/src/github.com/alipay/sofa-mosn 项目。

5. 启动 Mosn

① 右键 cmd/mosn/main 目录,Debug 运行,如下图所示:

右键 Debug 运行

② 毫无意外,我们发现并未 Debug 成功运行,日志如下:

API server listening at: 127.0.0.1:50592
NAME:
mosn - MOSN is modular observable smart netstub.

USAGE:
___go_build_github_com_alipay_sofa_mosn_cmd_mosn_main [global options] command [command options] [arguments...]

VERSION:
0.0.1

COMMANDS:
start start mosn proxy
stop stop mosn proxy
reload reconfiguration
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version

COPYRIGHT:
(c) 2018 Ant Financial

为什么呢?因为我们没有配置 Mosn 需要的配置文件。修改 go build github.com/alipay/sofa-mosn/cmd/mosn/mainRun/Debug Configurations ,如下图所示:Run/Debug Configurations

  • 红框 Program arguments 填入 start -c /Users/yunai/Go/src/github.com/alipay/sofa-mosn/examples/sofarpc-sample/config.json 。其中,/Users/yunai/Go/src/github.com/alipay/sofa-mosn/examples/sofarpc-sample/config.json 修改成自己的 /Users/yunai/Go 修改成自己的 $GOPATH

然后,重新点击小虫虫,进入调试模式。输入如下日志:

API server listening at: 127.0.0.1:50637
2018/09/06 22:52:05 load config from : /Users/yunai/Go/src/github.com/alipay/sofa-mosn/examples/sofarpc-sample/config.json
2018/09/06 22:52:05 [INFO]start by config : &{Servers:[{ServerName: DefaultLogPath:stdout DefaultLogLevel: UseNetpollMode:false GracefulTimeout:0s Processor:0 Listeners:[{Name:serverListener Address:127.0.0.1:2046 BindToPort:true Inspector:false FilterChains:[{FilterChainMatch: TLS:{Status:false Type: ServerName: CACert: CertChain: PrivateKey: VerifyClient:false InsecureSkip:false CipherSuites: EcdhCurves: MinVersion: MaxVersion: ALPN: Ticket: ExtendVerify:map[]} Filters:[{Type:proxy Config:map[downstream_protocol:Http2 upstream_protocol:SofaRpc virtual_hosts:[map[name:serverHost domains:[*] routers:[map[match:map[headers:[map[name:service value:.*]]] route:map[cluster_name:serverCluster]]]]]]}]}] StreamFilters:[] LogPath:stdout LogLevel: HandOffRestoredDestinationConnections:false AccessLogs:[]} {Name:clientListener Address:127.0.0.1:2045 BindToPort:true Inspector:false FilterChains:[{FilterChainMatch: TLS:{Status:false Type: ServerName: CACert: CertChain: PrivateKey: VerifyClient:false InsecureSkip:false CipherSuites: EcdhCurves: MinVersion: MaxVersion: ALPN: Ticket: ExtendVerify:map[]} Filters:[{Type:proxy Config:map[downstream_protocol:SofaRpc upstream_protocol:Http2 virtual_hosts:[map[name:clientHost domains:[*] routers:[map[match:map[headers:[map[name:service value:.*]]] route:map[cluster_name:clientCluster]]]]]]}]}] StreamFilters:[] LogPath:stdout LogLevel: HandOffRestoredDestinationConnections:false AccessLogs:[]}]}] ClusterManager:{AutoDiscovery:false RegistryUseHealthCheck:false Clusters:[{Name:serverCluster Type:SIMPLE SubType: LbType:LB_RANDOM MaxRequestPerConn:1024 ConnBufferLimitBytes:32768 CircuitBreakers:[] HealthCheck:{Protocol: Timeout:0s Interval:0s IntervalJitter:0s HealthyThreshold:0 UnhealthyThreshold:0 CheckPath: ServiceName:} ClusterSpecConfig:{Subscribes:[]} Hosts:[{Address:127.0.0.1:8080 Hostname: Weight:0 MetaData:map[] TLSDisable:false}] LBSubsetConfig:{FallBackPolicy:0 DefaultSubset:map[] SubsetSelectors:[]} TLS:{Status:false Type: ServerName: CACert: CertChain: PrivateKey: VerifyClient:false InsecureSkip:false CipherSuites: EcdhCurves: MinVersion: MaxVersion: ALPN: Ticket: ExtendVerify:map[]}} {Name:clientCluster Type:SIMPLE SubType: LbType:LB_RANDOM MaxRequestPerConn:1024 ConnBufferLimitBytes:32768 CircuitBreakers:[] HealthCheck:{Protocol: Timeout:0s Interval:0s IntervalJitter:0s HealthyThreshold:0 UnhealthyThreshold:0 CheckPath: ServiceName:} ClusterSpecConfig:{Subscribes:[]} Hosts:[{Address:127.0.0.1:2046 Hostname: Weight:0 MetaData:map[] TLSDisable:false}] LBSubsetConfig:{FallBackPolicy:0 DefaultSubset:map[] SubsetSelectors:[]} TLS:{Status:false Type: ServerName: CACert: CertChain: PrivateKey: VerifyClient:false InsecureSkip:false CipherSuites: EcdhCurves: MinVersion: MaxVersion: ALPN: Ticket: ExtendVerify:map[]}}]} ServiceRegistry:{ServiceAppInfo:{AntShareCloud:false DataCenter: AppName:} ServicePubInfo:[]} RawDynamicResources:[] RawStaticResources:[]}
2018/09/06 22:52:05 [WARN]healthcheck for cluster is disabled
2018/09/06 22:52:05 [WARN]healthcheck for cluster is disabled
2018/09/06 22:52:05 [WARN]Mesh Doesn't Support Dynamic Router
2018/09/06 22:52:05 [WARN]Mesh Doesn't Support Dynamic Router
2018/09/06 22:52:05 [INFO]xds client start
2018/09/06 22:52:05 [ERROR]StaticResources is null
2018/09/06 22:52:05 [WARN]fail to init xds config, skip xds: null point exception

③ 验证 Mosn 启动成功

使用 telnet 连接 2045 端口。如下是艿艿的示例:

Yunai-Macbook:sofa-mosn yunai$ telnet 127.0.0.1 2045
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

连接成功,表示 Mosn 启动成功。

6. 启动 SOFA-Server

① 启动

打开新的终端,输入命令。

# 跳到实例目录
cd $GOPATH/src/github.com/alipay/sofa-mosn/examples/sofarpc-sample
# 启动 SOFA-Server
go run server.go

② 验证

使用 telnet 连接 8080 端口。如下是艿艿的示例:

Yunai-Macbook:sofa-mosn yunai$ telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

7. 启动 SOFA-Client

① 启动

打开新的终端,输入命令。

# 跳到实例目录
cd $GOPATH/src/github.com/alipay/sofa-mosn/examples/sofarpc-sample
# 启动 SOFA-Client
go run client.go

② 日志

Client 的日志如下:

2018/09/06 23:02:08 [DEBUG]connect raw tcp, remote address = 127.0.0.1:2045 ,event = ConnectedFlag, error = <nil>
2018/09/06 23:02:08 [DEBUG]AddConnectionEventListener Success, cb = &{context:0xc000175020 Protocol:SofaRpc Connection:0xc00017e380 Host:<nil> Codec:0xc000316000 ActiveRequests:0xc000174ff0 AcrMux:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0} CodecCallbacks:<nil> CodecClientCallbacks:<nil> StreamConnectionCallbacks:<nil> ConnectedFlag:false RemoteCloseFlag:false}
2018/09/06 23:02:08 [INFO]AppendHeaders,request id = 1, direction = 0
2018/09/06 23:02:08 [DEBUG]Decoderprotocol code = 1, maybeProtocolVersion = 0
2018/09/06 23:02:08 [DEBUG]deserialize header map: map[querystring: service:testSofa accept-encoding:gzip host:127.0.0.1:2046 date:Thu, 06 Sep 2018 15:02:08 GMT path:/ user-agent:Go-http-client/2.0 content-length:0]
2018/09/06 23:02:08 [DEBUG]Response ClassName is:
2018/09/06 23:02:08 [INFO]streamID=1,protocol=bolt
[RPC Client] Receive Data:1
Response Headers are: map[path:/ protocol:1 cmdtype:0 version:1 respstatus:0 classname: service:testSofa requestid:1 content-length:0 date:Thu, 06 Sep 2018 15:02:08 GMT resptimemills:1536246128878 x-mosn-endstream:yes x-mosn-streamid:1 accept-encoding:gzip host:127.0.0.1:2046 user-agent:Go-http-client/2.0 cmdcode:2 codec:1 classlen:0 headerlen:208 contentlen:0 querystring:]

Server 的日志如下:

[RPC Server] get connection : 127.0.0.1:50802
[RPC Server] reponse connection: 127.0.0.1:50802, requestId: 1

Mosn 的日志如下:

2018/09/06 23:04:47 [INFO]accept connection from:127.0.0.1:2045
2018/09/06 23:04:47 [INFO]OnReceiveHeaders, New stream detected, Request id = 1, StreamID = 3
2018/09/06 23:04:47 [INFO]AppendHeaders,request id = 3, direction = 0
2018/09/06 23:04:47 [INFO]AppendData,request id = 3, direction = 0
2018/09/06 23:04:47 [INFO]streamID=3,protocol=bolt
2018/09/06 23:04:47 [INFO]AppendHeaders,request id = 3, direction = 1
2018/09/06 23:04:47 [INFO]AppendData,request id = 3, direction = 1
2018/09/06 23:04:50 [ERROR]Error on read. Connection = 7, Remote Address = 127.0.0.1:50801, err = EOF
2018/09/06 23:05:51 [INFO]accept connection from:127.0.0.1:2045
2018/09/06 23:05:51 [INFO]OnReceiveHeaders, New stream detected, Request id = 1, StreamID = 5
2018/09/06 23:05:51 [INFO]AppendHeaders,request id = 5, direction = 0
2018/09/06 23:05:51 [INFO]AppendData,request id = 5, direction = 0
2018/09/06 23:05:51 [INFO]streamID=5,protocol=bolt
2018/09/06 23:05:51 [INFO]AppendHeaders,request id = 5, direction = 1
2018/09/06 23:05:51 [INFO]AppendData,request id = 5, direction = 1


如果胖友希望 SOFA-Client 持续启动,可以使用 go run client.go -t ,它将像激光枪一样,每 3 秒发射一次请求。biubiubiu~

8. 瞅一瞅 config.json

因为笔者也是刚刚才看了 Istio ,所以对 config.json 不是很理解。瞎比比解释下,直接来看配置文件:

{
"servers": [ // <1>
{
"default_log_path": "stdout",
"listeners": [
{
"name": "serverListener",
"address": "127.0.0.1:2046",
"bind_port": true,
"log_path": "stdout",
"filter_chains": [
{
"tls_context": { },
"filters": [
{
"type": "proxy",
"config": {
"downstream_protocol": "Http2",
"upstream_protocol": "SofaRpc",
"virtual_hosts": [
{
"name": "serverHost",
"domains": [
"*"
],
"routers": [
{
"match": {
"headers": [
{
"name": "service",
"value": ".*"
}
]
},
"route": {
"cluster_name": "serverCluster"
}
}
]
}
]
}
}
]
}
]
},
{
"name": "clientListener",
"address": "127.0.0.1:2045",
"bind_port": true,
"log_path": "stdout",
"filter_chains": [
{
"tls_context": { },
"filters": [
{
"type": "proxy",
"config": {
"downstream_protocol": "SofaRpc",
"upstream_protocol": "Http2",
"virtual_hosts": [
{
"name": "clientHost",
"domains": [
"*"
],
"routers": [
{
"match": {
"headers": [
{
"name": "service",
"value": ".*"
}
]
},
"route": {
"cluster_name": "clientCluster" // <2>
}
}
]
}
]
}
}
]
}
]
}
]
}
],
"cluster_manager": {
"clusters": [ // <3>
{
"Name": "serverCluster",
"type": "SIMPLE",
"lb_type": "LB_RANDOM",
"max_request_per_conn": 1024,
"conn_buffer_limit_bytes": 32768,
"hosts": [
{
"address": "127.0.0.1:8080"
}
]
},
{
"Name": "clientCluster",
"type": "SIMPLE",
"lb_type": "LB_RANDOM",
"max_request_per_conn": 1024,
"conn_buffer_limit_bytes": 32768,
"hosts": [
{
"address": "127.0.0.1:2046"
}
]
}
]
}
}

  • <1> 处的 servers 中,配置了两个 listeners ,分别是:

    • clientListener :监听 2045 端口。

      • 这也就为什么在 「5. 启动 Mosn」 中,我们可以使用 telnet 连接 2045 的原因。

      • 另外,在 examples/sofarpc-sample/client.go 中,看到 SOFA-Client 请求的也是 Mosn 2045 端口。代码如下:

        func main() {
        log.InitDefaultLogger("", log.DEBUG)
        t := flag.Bool("t", false, "-t")
        flag.Parse()
        if client := NewClient("127.0.0.1:2045"); client != nil {
        for {
        client.Request()
        time.Sleep(200 * time.Millisecond)
        if !*t {
        time.Sleep(3 * time.Second)
        return
        }
        }
        }
        }

        • "127.0.0.1:2045" 处。
      • <2> 处,配置路由转发给 "clientCluster" 集群。

    • serverListener :监听 2046 端口。😈 这里先跳过,继续往下看。

  • <3> 处的 cluster_manager 中,配置了两个 clusters ,分别是:

    • clientClusterhosts 地址只有一个是 "127.0.0.1:2046" 。即,请求会转发到 serverListener 上,因为它监听了 2046 端口。

    • clustershosts 地址只有一个是 "127.0.0.1:8080" ,即对应的 SOFA-Server 的地址。为什么是 SOFA-Server 的地址呢?答案在 examples/sofarpc-sample/server.go ,代码如下:

      func main() {
      ln, err := net.Listen("tcp", "127.0.0.1:8080")
      if err != nil {
      fmt.Println(err)
      return
      }
      server := &SofaRPCServer{ln}
      server.Run()
      }

      • "127.0.0.1:8080" 处。

OK ,我们回过头来看看 serverListener 。虽然在 sofarpc-sample 示例中,我们只启动了 Mosn 一个进程,但是通过配置两个 listeners 来模拟两个 Mosn 进程:

  • clientListener 提供给 SOFA-Client 请求。
  • serverListener 转发请求给 SOFA-Server 处理。

clientListenerserverListener 之间,通过 HTTP2 协议,clientListener 将请求转发到监听 2046 端口上的 serverListener

总结来说:

SOFA-Client => [2045] clientListener => serverCluster(serverListener) => serverCluster(SOFA-Server)

666. 彩蛋

作为还没入门 Istio 和 Go 语言的小菜鸡,灰常紧张的写完了这篇文章。如果有不正确的地方,烦请大佬深刻教育,小艿艿低头学习。


知识星球

文章目录
  1. 1. 1. 概述
  2. 2. 2. 依赖工具
  3. 3. 3. 获取代码
  4. 4. 4. 导入IDE
  5. 5. 5. 启动 Mosn
  6. 6. 6. 启动 SOFA-Server
  7. 7. 7. 启动 SOFA-Client
  8. 8. 8. 瞅一瞅 config.json
  9. 9. 666. 彩蛋