The author | Pan Tianying, making ID @ pantianying, open source enthusiasts, graffiti in intelligence

Dubbo is a lightweight RPC framework with high performance based on Java. Dubbo provides rich service governance functions and excellent extension capabilities. Dubbo-go provides unified servitization capabilities and standards between Java and Golang, which is the main problem that Doodle Intelligence needs to solve at present. This article is divided into two parts: practice and fast Access. The purpose of this article is to share the practical experience of Dubbo-Go in Doodle Intelligence. It aims to help users quickly access the DUbbo-Go RPC framework, hoping to save some detdettions for everyone. In addition, the test code in this paper is based on dubo-Go version V1.4.0.

Dubo-go Gateway practice

Dubbo-go’s application to Doodle Intelligence is shown in the picture above. The implementation details will be introduced in the following section. I hope these experiences in production environment can help you.

1. The background

At Doodle Intelligence, Dubo-Go has become the RPC framework of choice for connecting Golang services with existing Dubbo clusters. The representative open-gateway gateway system (hereinafter referred to as gateway, for the open source version, see github.com/dubbogo/dub…) . The Gateway dynamically loads the internal Dubbo interface information and exposes it in the form of an HTTP API. The gateway is intended to address the following pain points of the previous generation gateway.

  • Configuring dubbo interface opening rules through the page is cumbersome and difficult to control permissions.
  • The interface is not RESTful and unfriendly to external developers;
  • Heavy dependency and high upgrade risk;
  • Concurrency performance issues.

2. Architecture design

In response to these pain points, preparations were made to design a new Gateway architecture. The first is language selection. Golang’s coroutine invocation model makes golang ideal for building IO intensive applications and simpler to deploy than Java.

After investigation, we decided to use Golang as the proxy coding language and use Dubbo – Go to connect to dubbo Provider cluster. The service application on the provider side uses a Java plug-in to configure API configuration information in the form of annotations. The plug-in updates the configuration information and Dubbo interface metadata to the metadata registry (Redis in the following figure). In this way, configuration is moved from the administrative background page to the program code. Developers can easily see the API description of the Dubbo interface when coding, without having to configure how the API is used from another admin background.

Practice 3.

As can be seen from the figure above, the gateway can dynamically load the dubbo interface information and call the Dubbo interface based on the Dubbo generalization call. Generalization calls to make the client do not need to build the provider interface code, in dubbo – go for without invoking config. SetConsumerService and hessian RegisterPOJO method, Instead, the request model is purely parametric, which makes it possible for clients to dynamically add and modify interfaces. In apache/Dubo-sample/golang/generic/Go-client there is code to demonstrate the generalization call.

func test() {
    var appName = "UserProviderGer"
    var referenceConfig = config.ReferenceConfig{
        InterfaceName: "com.ikurento.user.UserProvider",
        Cluster:       "failover",
        Registry:      "hangzhouzk",
        Protocol:      dubbo.DUBBO,
        Generic:       true. } referenceConfig.GenericLoad(appName) // appName is the unique identification of RPCService time.Sleep(3 * time.Second)  resp, err := referenceConfig.GetRPCService().(*config.GenericService). Invoke([]interface{}{"GetUser", []string{"java.lang.String"}, []interface{}{"A003"}})
    iferr ! = nil { panic(err) } }Copy the code

The implementation of the generalization call is actually quite simple. Its function is in dubbo’s Filter layer. The Generic Filter has been added to the Dubbo Filter chain as the default Filter. Its core logic is as follows:

func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
    if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
        oldArguments := invocation.Arguments()
        if oldParams, ok := oldArguments[2].([]interface{}); ok {
            newParams := make([]hessian.Object, 0, len(oldParams))
            for i := range oldParams {
                newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
            }
            newArguments := []interface{}{
                oldArguments[0],
                oldArguments[1],
                newParams,
            }
            newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
            newInvocation.SetReply(invocation.Reply())
            return invoker.Invoke(ctx, newInvocation)
        }
    }
    return invoker.Invoke(ctx, invocation)
}
Copy the code

Generic Filter transforms the struct parameters requested by the user into a unified map (struct2MapAll in the code), and serializes the forward and reverse operations of the class (struct in Golang) into the forward and reverse serialization operations of the Map. This eliminates the need for POJO descriptions to be hardcoded into the Hessain library.

As can be seen from the above code, there are four contents that need to be dynamically constructed in the generalization call: InterfaceName required in the ReferenceConfig, Method and ParameterTypes required in the parameter, and requestParams required in the actual input parameter.

So how do these parameters get from the HTTP API match?

This is where the plug-in for collecting metadata with the provider mentioned above is used. After the plug-in is introduced, the application scans the dubbo interface to be exposed at startup, associating the Dubbo metadata with the HTTP API. The usage of the plug-in is roughly as follows, with a few simple configurations as examples, and more annotations in actual production.

The final dubbo metadata is as follows:

{
    "key": "POST:/hello/{uid}/add"."interfaceName": "com.tuya.hello.service.template.IUserServer"."methodName": "addUser"."parameterTypes": ["com.tuya.gateway.Context"."java.lang.String"."com.tuya.hello.User"]."parameterNames": ["context"."uid"."userInfo"]."updateTimestamp": "1234567890"."permissionDO": {},"voMap": {
        "userInfo": {
            "name": "java.lang.String"."sex": "java.lang.String"."age": "java.lang.Integer"}},"parameterNameHumpToLine": true."resultFiledHumpToLine": false."protocolName": "dubbo". }Copy the code

The Gateway subscribes to the above information from the metadata configuration center to match an API request to a Dubbo interface. And grab parameters from the API request as input parameters. This function completes the flow loop.

This should give you a clear idea of the Gateway project topology. I went on to share the problems and tuning experience of the project using Dubo-Go. At the beginning of nineteen nineteen, dubo-Go was still in its early stages of construction and had no experience of landing users. I was also involved in community development and coding the company’s internal gateway project. After solving a bunch of hessain serialization and ZooKeeper registry issues, the project finally ran through the closed loop. However, as a core application, Runthrough closed Loop is still a long way from the upper production environment, especially with a new framework whose stability was being tested at the time. The whole test and functional completion took a quarter until the project was stable and the results of pressure measurement were good. Single gateway machine (2C 8G) full link simulation of the real environment pressure test reached 2000 QPS. Due to the heavy business logic introduced (an average of three Dubbo interfaces are invoked per request), the results of this pressure test were in line with or even exceeded expectations.

Some experience of dubbo-Go parameter configuration tuning is summarized, mainly some network related configurations.

When you run the demo, you should see a bunch of configurations at the end of the configuration file, but if you’re not familiar with the underlying dubbo-Go network model, it’s hard to understand what these configurations mean. At present, Getty is used as the underlying framework in dubo-Go network layer to achieve read/write separation and coroutine pool management. Getty exposes the concept of a session, which provides a set of implementations for network-layer method injection, as this is not a source code parsing document and will not be covered here. The reader can simply assume that Dubo-Go maintains a Getty Session pool, which in turn maintains a TCP connection pool. For each connection, Getty has read and write coroutines accompanying it, separating read and write. Here I try to use common comments to help you comb through several configuration meanings that have a large impact on performance:

protocol_conf:
  Under the Dubbo protocol, most of the configuration is associated with the Getty Session.
  dubbo:
      # a session will always ensure that the number of TCP connections is connection_number.
    It is recommended that you set the number of connections to a relatively small value.
    Reconnect_interval checks the number of connections. If the number of connections is less than connection_number,
    # create a connection. The default value is 300ms
    reconnect_interval: 0
    connection_number: 2
    The interval at which the client sends the heartbeat
    heartbeat_period: "30s"
    # OnCron Specifies the session timeout period. If session_timeout is not returned, the session is closed
    session_timeout: "30s"
    # Each dubbo interface client maintains a pool_size session pool.
    Select one session from the session pool per request. So the actual number of TCP connections is the number of sessions *connection_number,
    Pool_size is the maximum number of sessions. Test summary down the general program 4 TCP connections is enough.
    pool_size: 4
    If a session is not used after the session_timeout period, the session will be closed
    pool_ttl: 600
    Handle the coroutine pool size of the returned value
    gr_pool_size: 1200
    The buffer queue length in the read data and coroutine pool is deprecated. No buffer queues are used
    queue_len: 64
    queue_number: 60
    getty_session_param:
      compress_encoding: false
      tcp_no_delay: true
      tcp_keep_alive: true
      keep_alive_period: "120s"
      tcp_r_buf_size: 262144
      tcp_w_buf_size: 65536
      pkg_wq_size: 512
      tcp_read_timeout: "1s"  Timeout for each packet read
      tcp_write_timeout: "5s" Timeout for each packet write
      wait_timeout: "1s" 
      max_msg_len: 102400     # Maximum data transmission length
      session_name: "client"
Copy the code

Dubo-go fast access

Dubbo-go’s practical achievements in Doodle Intelligence have been shown in the previous article. Here is a quick way to access Dubbo-Go.

Step 1: Hello World

The Dubbo-Go usage sample is currently consistent with Dubbo and is placed in the Apache/Dubbo-samples project. Under the dubbo-sample/ Golang directory, users can select the feature directory they are interested in to quickly test the code effect.

Tree dubbo - samples/golang -l 1 dubbo - samples/golang ├ ─ ─ the README. Md ├ ─ ─ async ├ ─ ─ ci. Sh ├ ─ ─ configcenter ├ ─ ─ direct ├ ─ ─ ├─ General ├─ Generic ├─ go. Mod ├─ goCopy the code

Let’s take Hello World as an example and start the Server and client separately, following the steps in Dubo-samples /golang/ readme.md. Try golang calling Java, Java calling Golang, Golang calling golang, Java calling Java. Dubbo – Go supports protocol communication with Dubbo.

Take starting the Go-Server as an example. The registry uses ZooKeeper by default. Check whether the local ZooKeeper is running properly. Then run the following command and you will see the logs of your service starting normally.

export ARCH=mac
export ENV=dev
cd dubbo-samples/golang/helloworld/dubbo/go-server
sh ./assembly/$ARCH/$ENV.sh
cd. / target/Darwin/user_info_server - server - 20200608-1056 - dev/sh/bin/load. Sh startCopy the code

Step 2: Use Dubo-Go in your project

Above, we ran the use case through community-maintained test code and startup scripts. Next, we need to embed the Dubo-Go framework in our own code. Many friends often encounter problems in this step, here I sorted out some common questions, I hope to help you.

1) Environment variables

Dubo-go currently has three environment variables to configure:

  • CONF_CONSUMER_FILE_PATH: specifies the path of the Consumer configuration file.
  • CONF_PROVIDER_FILE_PATH: specifies the configuration file path on the Provider end, which is required when the Provider is used.
  • APP_LOG_CONF_FILE: Log Log file path, required;
  • CONF_ROUTER_FILE_PATH: File Router Rule configuration File path, required when File Router is used.

2) Code attention points

  • Inject service: Check if the following code is executed
# client
func init() {
  config.SetConsumerService(userProvider)
}
# the service side
func init() {
  config.SetProviderService(new(UserProvider))
}
Copy the code
  • Inject serialization description: Check if the following code is executed
hessian.RegisterJavaEnum(Gender(MAN))
  hessian.RegisterJavaEnum(Gender(WOMAN))
  hessian.RegisterPOJO(&User{})
Copy the code

3) Correctly understand the configuration file

  • The key under References/services, such as “UserProvider” in the following example, needs to be the same as the return value of service Reference(). This is the key that identifies the interface.
references:
"UserProvider":
  registry: "hangzhouzk"
  protocol : "dubbo"
  interface : "com.ikurento.user.UserProvider"
  cluster: "failover"
  methods :
  - name: "GetUser"
    retries: 3
Copy the code
  • Registries If there is only one registry cluster, configure only one. Multiple IP addresses are separated by commas as follows:
registries :
"hangzhouzk":
  protocol: "zookeeper"
  timeout    : "3s"
  address: "172.16.120.181:2181,172.16.120.182:2181"
  username: ""
  password: ""
Copy the code

4) Java and GO issues

Case for GO and Java interactions: Golang automatically lowercase method and property letters when calling Java services to match The Java camel’s shape. Many students intentionally wrote Their Java code as parameter definitions for Golang, capitalized the first letter, and ended up failing to serialize a match.

Step 3: Expand functionality

Both Dubo-Go and Dubbo offer a very rich expansion mechanic. You can implement custom modules to replace the default dubo-Go modules, or add some functionality. For example, Cluster, Filter, and Router are implemented to meet service requirements. These injection methods are exposed in Dubo-Go /common/ Extension and can be invoked and configured by the user.

Alibaba programming summer of the second phase is hot registration!

On May 25, 2020, the second phase of Alibaba Summer of Code (HEREINAFTER referred to as ASoC) was officially launched, and the project scale was upgraded again. There are as many as 20 star open source projects from the open source community, such as Apache Dubbo, Apache RocketMQ, Dragonfly and Nacos. There are 32 technical experts, core members of open source community and Apache incubator mentors from Alibaba Group. The field involves micro-services, containers, AI and other hot directions, aiming to unite with the open source community to create a sincere, fair and just open source internship platform, with Alibaba’s open source technology force as the “driving force”, so that The Chinese open source community and elite developers are recognized worldwide.

Click for details: developer.aliyun.com/topic/summe…

“Alibaba Cloud originator focuses on micro-service, Serverless, container, Service Mesh and other technical fields, focuses on the trend of cloud native popular technology, large-scale implementation of cloud native practice, and becomes the public account that most understands cloud native developers.”