在springcloud中,openfeign是取代了feign作为负载均衡组件的,feign最早是netflix提供的,他是一个轻量级的支持RESTful的http服务调用框架,内置了ribbon,而ribbon可以提供负载均衡机制,因此feign可以作为一个负载均衡的远程服务调用框架使用。feign后来不升级了,被github的openfeign取代,openfeign在feign的基础上,又支持springmvc注解的功能。
我们说openfeign其实也可以通过ribbon实现负载均衡,但是他们都可以单独使用,不一定是在springcloud大基础下,下面给出一个实例,来说明ribbon和openfeign单独使用时,如何负载均衡,并且实现远程服务调用。
首先我们需要利用springboot构建一个RESTfull的应用,提供一个接口index(),返回一个字符串。然后我们利用不同的配置文件,开启两个实例。以便我们模拟负载均衡。应用的结构如下:
springboot主要内容WebApplication.java:
package com.xxx.webapp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class WebApplication {
@Value("${webapp.message}")
private String message;
@Value("${server.port}")
private int port;
public static void main( String[] args ){
SpringApplication.run(WebApplication.class, args);
}
@GetMapping("/index")
public String index(){
return "{\"id\':101,\"name\":\"xxx\",\"message\":\""+message+"\",\"port\":\""+port+"\"}";
}
}
application.yml
server:
port: 8081
webapp:
message: springboot
application-dev.yml
server:
port: 8082
webapp:
message: springboot2
这里就是我们准备要远程调用的服务。
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
我们新建一个maven工程,单独使用openfeign和ribbon,我们需要引入如下依赖:
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
<version>2.2.5</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-httpclient</artifactId>
<version>2.2.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.5.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.5.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-ribbon</artifactId>
<version>9.5.1</version>
</dependency>
先来看看负载均衡算法:
package com.xxx.ribbon;
import java.util.ArrayList;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class BalancerApplication {
public static void main( String[] args ){
ILoadBalancer balancer = new BaseLoadBalancer();
List<Server> servers = new ArrayList<Server>();
servers.add(new Server("localhost", 8081));
servers.add(new Server("localhost", 8082));
balancer.addServers(servers);
for(int i=0;i<10;i++){
Server choosedServer = balancer.chooseServer(null);
System.out.println(choosedServer);
}
}
}
这个测试程序和前面准备的两个springboot服务没有关系,只是说明ribbon的负载均衡,默认BaseLoadBalancer类的负载均衡算法是round robin(循环),这里有两个服务地址,按照预期,我们会看到打印结果应该是两个地址交替打印。
打印结果,符合我们的预期,我们可以看看BaseLoadBalancer的源代码,看看默认的负载均衡算法:
如果我们需要定制自己的规则,可以通过实现IRule接口,这里给出一个自定义的规则:所有http请求60%到8081,剩余的到8082, 按照思路,我们的代码可以这么来实现。
package com.xxx.ribbon;
import java.util.List;
import java.util.Random;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
public class CustomerBalancerRule implements IRule {
private ILoadBalancer balancer = new BaseLoadBalancer();
@Override
public Server choose(Object key) {
List<Server> servers = balancer.getAllServers();
Random random = new Random();
final int number = random.nextInt(10);
if(number<7){
return servers.get(0);
}
return servers.get(1);
}
@Override
public void setLoadBalancer(ILoadBalancer lb) {
this.balancer = lb;
}
@Override
public ILoadBalancer getLoadBalancer() {
return this.balancer;
}
}
我们通过实际的服务调用来看看,从调用结果上来检验这个负载策略。
package com.xxx.ribbon;
import com.netflix.client.ClientFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.niws.client.http.RestClient;
@SuppressWarnings("deprecation")
public class CustomerBalancerRuleApplication {
public static void main(String[] args) throws Exception{
ConfigurationManager.getConfigInstance().setProperty("providers.ribbon.listOfServers",
"localhost:8081,localhost:8082");
ConfigurationManager.getConfigInstance().setProperty("providers.ribbon.NFLoadBalancerRuleClassName",
RoundRobinRule.class.getName());
RestClient client = (RestClient) ClientFactory.getNamedClient("providers");
HttpRequest request = HttpRequest.newBuilder().uri("/index").build();
for(int i=0;i<10;i++){
HttpResponse response = client.executeWithLoadBalancer(request);
System.out.println("loop "+i+" run result -> "+response.getEntity(String.class));
}
}
}
我们通过配置providers.ribbon.listOfServers属性来指定providers服务的两个地址,然后通过 providers.ribbon
.NFLoadBalancerRuleClassName属性来指定我们的负载均衡策略。providers是自定义的服务名,关于ribbon的属性配置,都需要冠以这个前缀,后面我们通过providers会获取client,然后拼接我们的uri,就构成了具体的请求地址。
下面运行这个程序,我们查看打印信息:
打印结果是不固定的,因为我们的规则本来就是计算的是一个概率,因此这条请求最终会落到那台服务器上,只能说60%的可能会在8081上。这里打印的10次正好有6次确实是落在了8081上,也有可能是7次甚至8次都会落在8081上。我们注释掉代码中设置负载规则的部分,再次运行程序,又回到了默认的循环规则打印的结果:
以上示例,说明了ribbon单独作为一个远程调用服务框架,是具有负载均衡功能的。下面我们使用openfeign提供的api,并且使用ribbon作为client,来实现远程服务调用。
需要先定义一个接口UserService.java:
package com.xxx.openfeign;
import feign.RequestLine;
public interface UserService {
@RequestLine("GET /index")
String index();
}
这里,我们使用从配置文件读取配置的方法,准备一个配置文件providers.properties,文件名和我们对应的服务名providers对应:
providers.ribbon.MaxAutoRetries=1
providers.ribbon.MaxAutoRetriesNextServer=1
providers.ribbon.OkToRetryOnAllOperations=true
providers.ribbon.ServerListRefreshInterval=2000
providers.ribbon.ConnectTimeout=3000
providers.ribbon.ReadTimeout=3000
providers.ribbon.listOfServers=localhost:8081,localhost:8082
providers.ribbon.EnablePrimeConnections=true
最主要的配置是需要指定providers.ribbon.listOfServers属性。
启动类:
package com.xxx.openfeign;
import java.io.IOException;
import com.netflix.config.ConfigurationManager;
import feign.Feign;
import feign.ribbon.RibbonClient;
public class FeignApplication {
public static void main(String[] args) throws IOException {
//加载配置文件
ConfigurationManager.loadPropertiesFromResources("providers.properties");
//UserService
UserService userService = Feign.builder().client(RibbonClient.create())
//.encoder(new JacksonEncoder())
//.decoder(new JacksonDecoder())
.target(UserService.class,"http://providers");
for(int i=0;i<10;i++){
String result = userService.index();
System.out.println("loop "+i+",result -> "+result);
}
}
}
运行程序,打印信息如下:
打印结果与ribbon使用默认负载均衡规则的效果是一样的。
openfeign和ribbon都可以单独使用,能够实现负载均衡的远程服务调用。