Tars | Part 5 Java JDK implementation based on tarsgo subset routing rules (Part I)

Time:2022-1-12

catalogue

preface

Tutor LI Kaiyuan (hereinafter referred to as “Tutor LI”) implemented the subset routing rules in go language and introduced them in the interim report sharing meeting; this article will understand and supplement the details of the subset routing rules based on Tutor LI’s implementation method.

This article is the first half, which aims to record the changes of tarsgo code by Tutor LI and analyze its subset routing rules. The second half will improve the code of tarsjava related subset routing rules by comparing and referring to the implementation of go language JDK.

The upper and lower parts of the articles correspond one by one in the directory. The upper part focuses on tarsgo analysis and the lower part focuses on tarsgo Java implementation. The first point of the above articleModification Tars protocol fileRecord the code modification of Tutor LI in tarsgo, and the first point of the next article is the sameModification Tars protocol file, focusing on how to implement it in Java language. The upper and lower articles complement each other. It is suggested to study by comparison.

Some resources are linked as follows:

Link to the second half of the article
https://www.cnblogs.com/dlhjw/p/15245116.html

Tarsjava implements subset routing rule JDK link address
https://github.com/dlhjw/TarsJava/commit/cc2fe884ecbe8455a8e1f141e21341f4f3dd98a3

Tarsgo implements subset routing rule JDK link address
https://github.com/defool/TarsGo/commit/136878e9551d68c4b54c402df564729f51f3dd9c#


1. Modification Tars protocol file

In the tars protocol file;

1.1 go language modification

There are two places in the protocol file that need to be changed: one is to add a subset configuration to the endpointf node, and the other is to add an interface configuration to get the subset configuration information according to the ID in the search assistant;

Add subset configuration to endpointf node
第一处修改

Interface for obtaining subset configuration information according to ID
第二处修改

1.2 modify local logic

Original logic Present logic
nothing Add a subset configuration to the node, adding a structure of the tars protocol
nothing The interface for obtaining subset information is added, which is the same as the structure of tars protocol

be careful

  • First: add subset configuration to endpointf node
    • The final result may not be like this. It depends on the registry interface;
    • After modifying the protocol file, you need to run a command to automatically generate the corresponding code;
    • Tarsgo’s automatic generation command is intars/protocol/res/MakefileLi;
  • Second: the interface for obtaining subset configuration information according to ID
    • String ID is “application name. Service name. Port name”;
    • The ID is set in such a way that it is different from other interface commands;
    • The interface should be aligned with the interface name added by tars registry;

1.3 automatically generate code through protocol file

Tars has a powerful function, which can be based on The configuration file in tar automatically generates the corresponding bean code;

In tarsgo, the corresponding code is as follows:

TarsGo自动生成代码
After executing the above command, the corresponding code will change as follows:

根据协议文件自动生成代码

This tells us that in the tarsgo code released by the teacher, some codes do not need to be changed manually, but are automatically generated by commands. In Java, the command is executed in the project root pathmvn tars:tars2java。 The specific process will be explained in detail in the second half of the article. Here we only record where the tarsgo code changes.

2. [core] add subset core function

Go language in tar / subset Go inside

2.1 go language modification

package tars

import (
	"encoding/json"
	"math/rand"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/TarsCloud/TarsGo/tars/protocol/res/endpointf"
	"github.com/TarsCloud/TarsGo/tars/protocol/res/queryf"
	"github.com/TarsCloud/TarsGo/tars/util/consistenthash"
	"github.com/TarsCloud/TarsGo/tars/util/endpoint"
	"github.com/serialx/hashring"
)

var (
	enableSubset = true
	subsetMg     = &subsetManager{}
)

type hashString string

func (h hashString) String() string {
	return string(h)
}

type subsetConf struct {
	enable    bool
	ruleType  string // ratio/key
	ratioConf *ratioConfig
	keyConf   *keyConfig

	lastUpdate time.Time
}

type ratioConfig struct {
	ring *hashring.HashRing
}

type keyRoute struct {
	action string
	value  string
	route  string
}

type keyConfig struct {
	rules        []keyRoute
	defaultRoute string
}

type subsetManager struct {
	lock  *sync.RWMutex
	cache map[string]*subsetConf

	registry *queryf.QueryF
}

//Get its subset method according to the service name and return the subsetconf configuration item
func (s *subsetManager) getSubsetConfig(servantName string) *subsetConf {
	s.lock.RLock()
	defer s.lock.RUnlock()
	var ret *subsetConf
	//If the cache is within the latest time, it returns directly
	if v, ok := s.cache[servantName]; ok {
		ret = v
		if v.lastUpdate.Add(time.Second * 10).After(time.Now()) {
			return ret
		}
	}
	//If the last acquisition time expires, call the registry interface to obtain the corresponding configuration
	// get config from registry
	conf := &endpointf.SubsetConf{}
	retVal, err := s.registry.FindSubsetConfigById(servantName, conf)
	if err != nil || retVal != 0 {
		// log error
		return ret
	}

	ret = &subsetConf{
		ruleType:   conf.RuleType,
		lastUpdate: time.Now(),
	}
	s.cache[servantName] = ret
	
	//Parse the configuration information obtained from the registry
	// parse subset conf
	if !conf.Enable {
		ret.enable = false
		return ret
	}
	//Proportional routing
	if conf.RuleType == "ratio" {
		kv := make(map[string]int)
		json.Unmarshal([]byte(conf.RuteData), &kv)
		ret.ratioConf = &ratioConfig{ring: hashring.NewWithWeights(kv)}
	} else {
		keyConf := &keyConfig{}
		kvlist := make([]map[string]string, 0)
		json.Unmarshal([]byte(conf.RuteData), &kvlist)
		for _, kv := range kvlist {
		    //Default route
			if vv, ok := kv["default"]; ok {
				keyConf.defaultRoute = vv
			}
			if vv, ok := kv["match"]; ok {
			    //Exact match
				keyConf.rules = append(keyConf.rules, keyRoute{
					action: "match",
					value:  vv,
					route:  kv["route"],
				})
			} else if vv, ok := kv["equal"]; ok {
			    //Regular matching
				keyConf.rules = append(keyConf.rules, keyRoute{
					action: "equal",
					value:  vv,
					route:  kv["route"],
				})
			}
		}
		ret.keyConf = keyConf
	}
	return ret
}

func (s *subsetManager) getSubset(servantName, routeKey string) string {
	// check subset config exists
	subsetConf := subsetMg.getSubsetConfig(servantName)
	if subsetConf == nil {
		return ""
	}
	// route key to subset
	if subsetConf.ruleType == "ratio" {
		return subsetConf.ratioConf.findSubet(routeKey)
	}
	return subsetConf.keyConf.findSubet(routeKey)
}

//Filter nodes according to subset rules
func subsetEndpointFilter(servantName, routeKey string, eps []endpoint.Endpoint) []endpoint.Endpoint {
	if !enableSubset {
		return eps
	}
	subset := subsetMg.getSubset(servantName, routeKey)
	if subset == "" {
		return eps
	}

	ret := make([]endpoint.Endpoint, 0)
	for i := range eps {
		if eps[i].Subset == subset {
			ret = append(ret, eps[i])
		}
	}
	return ret
}

func subsetHashEpFilter(servantName, routeKey string, m *consistenthash.ChMap) *consistenthash.ChMap {
	if !enableSubset {
		return m
	}
	subset := subsetMg.getSubset(servantName, routeKey)
	if subset == "" {
		return m
	}

	ret := consistenthash.NewChMap(32)
	for _, v := range m.GetNodes() {
		vv, ok := v.(endpoint.Endpoint)
		if ok && vv.Subset == subset {
			ret.Add(vv)
		}
	}
	return ret
}

func (k *ratioConfig) findSubet(key string) string {
	//Use random mode when null
	if key == "" {
		key = strconv.Itoa(rand.Int())
	}
	v, _ := k.ring.GetNode(key)
	return v
}

func (k *keyConfig) findSubet(key string) string {
	for _, v := range k.rules {
		if v.action == "equal" && key == v.value {
			return v.route
		} else if v.action == "match" {
			if matched, _ := regexp.Match(v.value, []byte(key)); matched {
				return v.route
			}
		}
	}
	return k.defaultRoute
}

2.2 logic of new places

New type New content
structural morphology Structure of new subset configuration itemsubsetConf
structural morphology Structure of new routing rule configuration itemratioConfig
structural morphology Structure of new dyeing pathkeyRoute
structural morphology Structure of new configuration itemkeyConfig
structural morphology Structure of new subset ManagersubsetManager
method Add a method to get subset configuration itemsgetSubsetConfig
method New method for obtaining proportion / coloring route configuration itemgetSubset
method Add a method to filter nodes according to subset rulessubsetEndpointFilter
method Add a method to filter nodes according to the subset rule of consistent hashsubsetHashEpFilter
method Method of adding proportional routing pathfindSubet
method Add by default routing pathfindSubet

3. Methods of adding constants and obtaining coloring keys

In the tars / util / current / tarscurrent related package, handle context related information;

3.1 go language modification

第三处修改

3.2 modify local logic

Original logic Present logic
nothing Add a constant fieldSTATUS_ROUTE_KEY
nothing Two new methods are added: setting and obtaining dyeing keys

4. [core] modify the rules for obtaining service IP

In the relevant documents of node management; The method is implemented in point 8;

4.1 go language modification

第四处修改

4.2 modify local logic

Original logic Present logic
Get all service IP lists Based on the original IP list, according to the request packetcurrent.STATUS_ROUTE_KEYValue filter partial nodes

5. Implement transparent key transmission function (client)

In tars / tarsprotocol related files;

5.1 go language modification

第五处修改

5.2 modify local logic

Original logic Present logic
No transmission staining Add transparent transmission function in the method finally executed by the client

be careful:

  • The coloring key here is new, which is different from the coloring key in the source code;
  • You can refer to the implementation method of consistent coloring. The difference is that the names of keys are different and the implementation ideas are similar;
  • In the tars java client, the final executed method here refers toTarsInvokerThree execution methods in the class;
    • That is: synchronous call methodinvokeWithSync(). asynchronous calling methodinvokeWithAsync()And coroutine call methodinvokeWithPromiseFuture()

6. Realize transparent transmission function (server)

In tars / tarsprotocol In relevant documents;

6.1 go language modification

第六处修改

6.2 modify local logic

Original logic Present logic
No transmission staining The transparent transmission function is added to the method finally executed by the server
  • In the tarsjava server, the final execution method here refers toTarsServantProcessor.process()

7. Add a subset field to the node information

In the node information related documents;

7.1 go language modification

第七处修改

7.2 modify local logic

Original logic Present logic
nothing Add a subset field to the node information
nothing Modify the parsing function to identify the sunset field

be careful

  • It is not necessarily a string type. Just add a subset related attribute in the endpoint object structure, which can be used wherever possible;
  • In Java, this part is only endpoint Java classes, so they are put together;

* 8. New tool class

In the tool bag;

8.1 go language modification

第八处修改


last

Newcomer production, if there are mistakes, welcome to point out, thank you very much!Welcome to the official account and share some more everyday things.If you need to reprint, please mark the source!
Tars | Part 5 Java JDK implementation based on tarsgo subset routing rules (Part I)