What is Ribbon
I analyzed it before How to use native Feign Today, let's study Ribbon, another class library developed by Netflix team.
Ribbon and Feign have many similarities. First, they are essentially HTTP client s. Second, they both have retry, integrated circuit breaker and other functions. The biggest difference is that ribbon has a built-in load balancer, while Feign does not.
This article will introduce how to use the native Ribbon. Note that it is native, not encapsulated by Spring layers.
Why use Ribbon
Here we need to answer two questions:
- Why use HTTP client?
- Why build a load balancer in the HTTP client?
Among them, the first problem is How to use native Feign As already mentioned in, we won't be verbose here. Let's look directly at the second question.
As we know, Apache HTTP client and Feign do not have built-in load balancer, that is to say, HTTP client does not necessarily have built-in load balancer, so why does Ribbon make special efforts?
In fact, we can think that Ribbon is more used for internal calls, and this scenario has a big feature - the target service is cluster deployment. Typically, when invoking the target service, we want the request to be distributed to each instance as evenly as possible. Through the built-in load balancer, Ribbon can well meet the requirements, while Apache HTTP client and Feign cannot.
Therefore, the built-in load balancer in the HTTP client is to provide load balancing support when the target service is deployed to the cluster.
Some people may say that you can deploy a load balancer alone. Why is it so complicated. Of course, you can. However, it is important to consider that the requests of mid tier services are much larger than edge services, so you need a high-performance load balancer. From this perspective, Ribbon's solution helps you save the cost of deploying load balancers independently.
How to use Ribbon
In the project, I used RxNettty to write a simple HTTP interface (see cn.zzs.ribbon.RxUserServer) for later examples to call. This interface runs on the local 8080, 8081 and 8082 interfaces to simulate three different instances. Therefore, if you want to test the examples in the project, start the three examples first.
http://127.0.0.1:8080/user/getUserById?userId={userId} request:userId=1 response:User [id=1, name=zzs001, age=18]
Here's a reminder. Ribbon's API uses a lot of RxJava code. If you haven't touched it before, you'd better understand it first.
Project environment
os: win 10
jdk: 1.8.0_231
maven: 3.6.3
IDE: Spring Tool Suite 4.6.1.RELEASE
Ribbon: 2.7.17
Usage as HTTP client
Like Feign, Ribbon supports the use of annotations to define HTTP interfaces. In addition, Ribbon also supports the use of HttpRequestTemplate, HttpClientRequest and other methods. I also provide examples in this part. If you are interested, you can move to the project source code.
The list of service instances is set through the configuration manager. When you see the configuration manager, do you feel familiar with it? We were in Eureka detailed explanation series (III) -- explore Eureka's powerful configuration system It has been described in detail in. Yes, Ribbon still uses this configuration system. It should be emphasized that the configuration system developed by Netflix team provides dynamic configuration support (of course, you need to be able to use it). It is based on this that the dynamic adjustment of service instances can be realized by integrating eureka applications.
// Defining HTTP API s using annotations @ClientProperties(properties = { @Property(name="ReadTimeout", value="2000"), @Property(name="ConnectTimeout", value="1000"), @Property(name="MaxAutoRetries", value="1"), @Property(name="MaxAutoRetriesNextServer", value="2") }, exportToArchaius = true) interface UserService { @TemplateName("getUserById") @Http( method = HttpMethod.GET, uri = "/user/getUserById?userId={userId}", headers = { @Header(name = "X-Platform-Version", value = "xyz"), @Header(name = "X-Auth-Token", value = "abc") }) RibbonRequest<ByteBuf> getUserById(@Var("userId") String userId); } public class RxUserProxyTest { @Test public void testBase() throws InterruptedException { // Specify the address of the service instance // key: Service + ". ribbon." + configuration item name (see com.netflix.client.config.CommonClientConfigKey) ConfigurationManager.getConfigInstance().setProperty( "UserService.ribbon.listOfServers", "127.0.0.1:8080,127.0.0.1:8081,127.0.0.1:8082"); UserService userService = Ribbon.from(UserService.class); userService.getUserById("1") .toObservable() .subscribe(new Subscriber<Object>() { @Override public void onCompleted() { LOG.info("onCompleted"); } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(Object t) { LOG.info("onNext:{}", t); if(t != null && t instanceof ByteBuf) { LOG.info(ByteBuf.class.cast(t).toString(Charset.defaultCharset())); } } }); // Because the request HTTP interface is asynchronous, let the main test thread sleep for a while Thread.sleep(10000); } }
Default load balancing rules
In order to observe the allocation of multiple requests in three instances, let's change the code and try to initiate six requests.
@Test public void test01() throws InterruptedException { ConfigurationManager.getConfigInstance().setProperty( "UserService.ribbon.listOfServers", "127.0.0.1:8080,127.0.0.1:8081,127.0.0.1:8082"); UserService userService = Ribbon.from(UserService.class); // Initiate multiple requests Observable<ByteBuf>[] requestList = new Observable[]{ userService.getUserById("1").toObservable(), userService.getUserById("2").toObservable(), userService.getUserById("3").toObservable(), userService.getUserById("4").toObservable(), userService.getUserById("5").toObservable(), userService.getUserById("6").toObservable() }; Observable.concat(Observable.from(requestList)) .subscribe(subscriber); Thread.sleep(10000); }
Running the test, you can see that the six requests are evenly allocated to three instances.
In the log, you can see the default load balancing rules.
It can be seen from the source code that the default rule essentially adopts the polling policy roundrobin rule. In addition, Ribbon also defines RandomRule, RetryRule and other rules for us to choose from.
public class AvailabilityFilteringRule { RoundRobinRule roundRobinRule = new RoundRobinRule(); }
Custom load balancing rules
Custom load balancing rules need to inherit com.netflix.loadbalancer.AbstractLoadBalancerRule and implement the choose method. The rule I define here is: no matter how many instances there are, the first one is accessed by default.
public class MyLoadBalancerRule extends AbstractLoadBalancerRule { @Override public Server choose(Object key) { ILoadBalancer lb = getLoadBalancer(); List<Server> allServers = lb.getAllServers(); return allServers.stream().findFirst().orElse(null); } }
Next, you just need to configure the custom rules through the configuration manager.
@Test public void test01() throws InterruptedException { ConfigurationManager.getConfigInstance().setProperty( "UserService.ribbon.listOfServers", "127.0.0.1:8080,127.0.0.1:8081,127.0.0.1:8082"); // Configure custom rules ConfigurationManager.getConfigInstance().setProperty( "UserService.ribbon.NFLoadBalancerRuleClassName", "cn.zzs.ribbon.MyLoadBalancerRule"); UserService userService = Ribbon.from(UserService.class); Observable<ByteBuf>[] requestList = new Observable[]{ userService.getUserById("1").toObservable(), userService.getUserById("2").toObservable(), userService.getUserById("3").toObservable(), userService.getUserById("1").toObservable(), userService.getUserById("2").toObservable(), userService.getUserById("3").toObservable() }; Observable.concat(Observable.from(requestList)) .subscribe(subscriber); Thread.sleep(10000); }
Running the test, you can see that all requests are assigned to the first instance. Custom load balancing rules take effect.
epilogue
Above, I've basically finished using the Ribbon. In fact, the Ribbon has other things that can be expanded, such as circuit breaker, Retry, etc. If you are interested, you can analyze it yourself.
Finally, thanks for reading.
reference material
For relevant source code, please move to: https://github.com/ZhangZiSheng001/ribbon-demo
This article is an original article. Please attach the original source link for Reprint: https://www.cnblogs.com/ZhangZiSheng001/p/15484505.html