[Enjoy Netflix] 38, Ribbon Core API Source Parsing: ribbon-core

Keywords: Apache Spring Attribute Java

There are two ways to design software: one is to make the software too simple and obviously free from defects; the other is to make the software too complex and free from obvious defects.

->Return to Column Directory <-
Code download address: https://github.com/f641385712/netflix-learning

Catalog

Preface

The last article introduced Ribbon as a whole, and my little buddy may have the same feeling as me: knowing what Ribbon is about is big, just understanding the status, not being down-to-earth.The advantage of Java libraries is that they are open source, which greatly reduces the difficulty of learning (you can grasp them as a whole from the design context without relying on memory alone).

From the beginning of this article, Ribbon will start from API source, with examples to explain, one by one way to crack, step by step, a comprehensive analysis of Ribbon.Because Ribbon won't find an alternative technology for a while, and there is little information to learn about it in China, I hope this series of articles will help you.

text

ribbon-core is Ribbon's core package on which any other package depends.The Jar package only defines a public API and does not work on its own, so you can think of it as a public abstraction.

<dependency>
    <groupId>com.netflix.ribbon</groupId>
    <artifactId>ribbon-core</artifactId>
    <!-- Version number is 2 as specified above.3.0 Version, keep and Spring Cloud Dependency Consistency -->
    <version>${ribbon.version}</version>
</dependency>


As you can see from the diagram, there is no concept of load balance in the core package, nor is there any concept of http in it, so core is a highly abstract package: independent of lb and protocol.

Note: Ribbon's source code has a specification for naming classes - > Interfaces must start with I, such as IClient, followed by IRule, IPing, etc.

IClient

Ribbon needs to be able to send requests in order to load balance, and this is its most core interface, around which all other components are designed and built, including LB.

The interface represents a client that can execute a single Request: send a Request request Request, get a Response response, and note that no protocol is bound (http, tcp, udp, file protocol, local calls are all broadband).

public interface IClient<S extends ClientRequest, T extends IResponse> {
	// Execute the request and return the response
    public T execute(S request, IClientConfig requestConfig) throws Exception; 
}

There are no implementations of this interface in the core package, as follows under Spring Cloud:


ClientRequest

Represents a generic client request object that applies to all communication protocols.The object is immutable.

public class ClientRequest implements Cloneable {

	// Requested URI
    protected URI uri;
    protected Object loadBalancerKey = null;
    // Is it retryable?true: the request can be retried false: the request cannot be retried
    protected Boolean isRetriable = null;
    // Outside incoming configurations can override the built-in IClientConfig configurations
    protected IClientConfig overrideConfig;
    
	... // Omit various constructors
	... // Live various get methods.Note: There is no set method because the instance is immutable

	// Determine if the request can be retried (important)
    public boolean isRetriable() {
        return (Boolean.TRUE.equals(isRetriable));
    }
    
    ...
	
	// Create a ** new ** ClientRequest with a new URI
	// It clones a new ClientRequest(this) new instance using the clone method first
	// Subclass replication is recommended to provide more effective implementation.
    public ClientRequest replaceUri(URI newURI) {
        ClientRequest req;
        try {
            req = (ClientRequest) this.clone();
        } catch (CloneNotSupportedException e) {
            req = new ClientRequest(this);
        }
        req.uri = newURI;
        return req;
    }
}

There are no subclasses in the core package.The inheritance map under Spring Cloud is as follows:


IResponse

Client Framework Response Interface, please note that it is an interface and Request request is a class.

public interface IResponse extends Closeable {

	// Get entities from the response.If it's the Http protocol, it's the Body body
	// Payload is the only name here because it's not related to the protocol
	public Object getPayload() throws ClientException;
	public boolean hasPayload();
	
	// True if the response is considered successful, for example, 200 response codes for the http protocol.
	public boolean isSuccess();
	public URI getRequestedURI();
	// Response Headers
	public Map<String, ?> getHeaders();  
}

Small details: This interface is not like getStatus'way of getting response status codes because it is not related to the protocol and response status codes are exclusive to Http.

There is no implementation of this interface in the core package, as in Spring Cloud:


Local Test Environment Setup

Based on the principle that the main method is the entry to all programs, that any component can be tested locally, and that Ribbon's core design is protocol independent, the instructions on Ribbon's core are done using unit tests (non-integrated tests), which I believe will help you learn load balancing even more.

Description: This environment builds tests that focus on the core part, namely ribbon-core and ribbon-loadbalancer, because they are both protocol inertia-free and network inertia-free, so they can be tested locally and everything can be tested.

  • Customize MyClient implementation
/**
 * A Client without Load Balancing
 *
 * @author yourbatman
 * @date 2020/3/14 22:09
 */
public class MyClient implements IClient<ClientRequest, MyResponse> {

    @Override
    public MyResponse execute(ClientRequest request, IClientConfig requestConfig) throws Exception {
        MyResponse response = new MyResponse();
        response.setRequestUri(request.getUri());
        return response;
    }
}
  • Customize MyResponse Implementation
public class MyResponse implements IResponse {

    private URI requestUri;
    public void setRequestUri(URI requestUri) {
        this.requestUri = requestUri;
    }

    @Override
    public Object getPayload() throws ClientException {
        return "ResponseBody";
    }

    @Override
    public boolean hasPayload() {
        return true;
    }

    // Always Success
    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public URI getRequestedURI() {
        return requestUri;
    }

    @Override
    public Map<String, ?> getHeaders() {
        return null;
    }

    @Override
    public void close() throws IOException {

    }
}
  • The most basic test case showcase:
@Test
public void fun1() throws Exception {
    // client Configuration
    IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues("YourBatman");

    // Client client, used to send requests (using ClientConfig configuration)
    // Because wood has LB function, it doesn't matter if you want IClientConfig or not
    MyClient client = new MyClient();

    // Execute the request and get a response
    MyResponse response = client.execute(createClientRequest(), null);
    System.out.println(response.isSuccess());
}

Console output: true.This is the simplest local test environment where you will expand more test case s later

Configure key management

As an open source library, you need to use configuration to improve its flexibility, so Ribbon also has its own set of configuration management, with its core API documented in the core package.It needs to manage a large number of recognized configuration keys, which is how Robbon manages keys.

IClientConfigKey

Used to define the key used by IClientConfig, note that it is only a key, not a k-v.

// Note: Generic interfaces
public interface IClientConfigKey<T> {
	// String representation for Hash
	public String key();
	// Types of key s, such as Integer.class
	// If not specified, it is automatically determined by the generic type
	public Class<T> type();
}

A simple and crude understanding of this interface: it means the same thing as a normal Object key.It has a subclass: CommonClientConfigKey, which records some common keys (too many, 40 +).

CommonClientConfigKey

It is an abstract class, so IClientConfigKey appears as its anonymous subclass.

public abstract class CommonClientConfigKey<T> implements IClientConfigKey<T> {

	public static final IClientConfigKey<String> AppName = new CommonClientConfigKey<>("AppName"){};
	public static final IClientConfigKey<String> Version = new CommonClientConfigKey<>("Version"){};
	public static final IClientConfigKey<Integer> Port = new CommonClientConfigKey<>("Port"){};
	... // Because there are so many,
	public static final IClientConfigKey<Integer> ConnectTimeout = new CommonClientConfigKey<Integer>("ConnectTimeout"){};
	public static final IClientConfigKey<Integer> ReadTimeout = new CommonClientConfigKey<Integer>("ReadTimeout"){};
	... // Because there are so many,
	// This key is used the most: specify the Server address in the form of configuration (you can specify more than one)
	public static final IClientConfigKey<String> ListOfServers = new CommonClientConfigKey<String>("listOfServers") {};
}

Description: The meaning of configuration is an integral part of any program, so what exactly does each key mean?What is the default value?What is the effect?This is explained specifically in the Configuration section topics that follow

This class maintains many constants of public static s internally so that they can be used directly outside.But no amount of it can cover all of it, so this class also provides a way to customize how key s are constructed:

CommonClientConfigKey: 

	// Create an IClientConfigKey instance from the string name
	public static IClientConfigKey valueOf(final String name) {
		// First check the keys cache to see if it already exists and return directly if it already exists
        for (IClientConfigKey key: keys()) {
            if (key.key().equals(name)) {
                return key;
            }
        }
	
		// If not, create a new return
        return new IClientConfigKey() {
            @Override
            public String key() {
                return name;
            }

            @Override
            public Class type() {
                return String.class;
            }
        };

	}

Expenditure here is a small "Bug": The newly created key is not put in the cache, is it actually better to put it in??

Example
@Test
public void fun1() {
    IClientConfigKey key1 = CommonClientConfigKey.valueOf("YourBatman");
    IClientConfigKey key2 = CommonClientConfigKey.valueOf("YourBatman");
    System.out.println(key1.key());
    System.out.println(key1 == key2);
}

Console Output:

YourBatman
false

Visibly, key s of the same String type are built twice, resulting in two different instances of IClientConfigKey, which is not memory friendly most of the time, which is why I think it's a small "bug" above.

summary

This paper introduces the core API: IClient in ribbon-core package, and establishes a local test environment, which provides basic support for the subsequent addition of load balancing logic.Additionally, we know that Ribbon manages keys using the IClientConfigKey interface abstraction and manages 40+ commonly used keys that are internally identifiable for ease of use using CommonClientConfigKey.

The first part of the core API for ribbon-core begins with this and continues below.

statement

The original is not easy, the code is not easy. Thank you for your compliment, collection and attention.Sharing this article with your circle of friends is allowed, but you refuse to copy it.You can also invite you to join my family of Java engineers and architects to learn and communicate.

338 original articles were published, 504 were praised, 440,000 visits were received+
His message board follow

Posted by blacksmoke26 on Sat, 14 Mar 2020 17:34:45 -0700