Ribbon是一个负载均衡客户端,可以很好的控制http和tcp的一些行为,一般都是Ribbon
搭配Feign
一起使用;Feign默认集成了ribbon
添加feign
的依赖
compile('io.github.openfeign:feign-core:9.5.0') compile('io.github.openfeign:feign-jackson:9.5.0') compile('io.github.openfeign:feign-ribbon:9.5.0')
build.gradle
文件内容:
apply plugin: 'java' apply plugin: 'eclipse' group = 'org.wjw.standardribbon' version = '1.0.0-SNAPSHOT' sourceCompatibility = 1.8 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' repositories { mavenLocal() maven{ url "http://SVN:8081/nexus/content/groups/public"} mavenCentral() jcenter() } dependencies { compile('org.slf4j:slf4j-api') compile('org.slf4j:slf4j-log4j12:1.7.25') compile('commons-logging:commons-logging:1.2') compile('io.github.openfeign:feign-core:9.5.0') compile('io.github.openfeign:feign-jackson:9.5.0') compile('io.github.openfeign:feign-ribbon:9.5.0') }
创建一个 Java Application 项目
执行: gradle init --type java-library
- Feign声明式服务消费端接口
RemoteService
接口中定义了一个名为getAdd
的方法
package org.wjw.standardribbon.service; import feign.Headers; import feign.Param; import feign.RequestLine; public interface RemoteService { /** GET /add?a=1&b=2 HTTP/1.1 Content-Type: application/json Accept: application/json User-Agent: Java/1.8.0_121 Host: 192.168.2.113:8861 Connection: keep-alive */ @Headers({ "Content-Type: application/json", "Accept: application/json" }) @RequestLine("GET /add?a={a}&b={b}") int getAdd(@Param("a") int a, @Param("b") int b); }
- 编写
ribbon.properties
,该文件位于src/main/resources
下
#所有的key都以service-one开头,表明这些配置项作用于名为service-one的服务 service-one.ribbon.MaxAutoRetries=1 service-one.ribbon.MaxAutoRetriesNextServer=1 service-one.ribbon.OkToRetryOnAllOperations=true service-one.ribbon.ServerListRefreshInterval=2000 service-one.ribbon.ConnectTimeout=3000 service-one.ribbon.ReadTimeout=3000 service-one.ribbon.listOfServers=192.168.2.113:8861,192.168.2.114:8861 service-one.ribbon.EnablePrimeConnections=false config-server.ribbon.MaxAutoRetries=1 config-server.ribbon.MaxAutoRetriesNextServer=1 config-server.ribbon.OkToRetryOnAllOperations=true config-server.ribbon.ServerListRefreshInterval=2000 config-server.ribbon.ConnectTimeout=3000 config-server.ribbon.ReadTimeout=3000 config-server.ribbon.listOfServers=192.168.2.114:7001 config-server.ribbon.EnablePrimeConnections=false
> 现在来看ribbon.properties
中的配置项 所有的key都以service-one
开头,表明这些配置项作用于名为service-one
的服务.其实就是与之前绑定RemoteService
接口的URL地址的schema相对应.
> 重点看ribbon.properties
文件里的service-one.ribbon.listOfServers
配置项,该配置项指定了服务生产端的真实地址. > 与RemoteService接口绑定的URL地址是"http://service-one/",
> 在调用时会被替换为http://192.168.2.113:8861/
或http://192.168.2.114:8861/
,再与接口中@RequestLine
指定的地址进行拼接,得到最终请求地址. > 本例中最终请求地址为http://192.168.2.113:8861/add
或http://192.168.2.114:8861/add
> 由于使用的ribbon,所以feign不再需要配置超时时长,重试策略.ribbon提供了更为完善的策略实现.
- 本例中,服务端是一个简单的spring mvc,实现如下:
package org.wjw.cloud.service.controller; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ComputeController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private DiscoveryClient client; @RequestMapping(value = "/add", method = RequestMethod.GET) public int add(@RequestParam int a, @RequestParam int b) { int r = a + b; ServiceInstance instance = client.getInstances(client.getServices().get(0)).get(0); logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r); return r; } }
> 将服务生产端部署到Eureka
中,在8861端口运行
package org.wjw.standardribbon; import org.wjw.standardribbon.model.User; import org.wjw.standardribbon.service.PropService; import org.wjw.standardribbon.service.RemoteService; import com.netflix.client.ClientFactory; import com.netflix.client.config.IClientConfig; import com.netflix.config.ConfigurationManager; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.RandomRule; import com.netflix.loadbalancer.ZoneAwareLoadBalancer; import feign.Feign; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; import feign.ribbon.LBClient; import feign.ribbon.LBClientFactory; import feign.ribbon.RibbonClient; public class StdRibbon { public static void main(String[] args) throws Exception { ConfigurationManager.loadPropertiesFromResources("ribbon.properties"); RibbonClient client = RibbonClient.builder().lbClientFactory(new LBClientFactory() { @Override public LBClient create(String clientName) { IClientConfig config = ClientFactory.getNamedConfig(clientName); ILoadBalancer lb = ClientFactory.getNamedLoadBalancer(clientName); ZoneAwareLoadBalancer zb = (ZoneAwareLoadBalancer) lb; zb.setRule(new RandomRule()); return LBClient.create(lb, config); } }).build(); /* 重点看ribbon.properties文件里的service-one.ribbon.listOfServers配置项,该配置项指定了服务生产端的真实地址. 与RemoteService接口绑定的URL地址是"http://service-one/", 在调用时会被替换为http://192.168.2.113:8861/或http://192.168.2.114:8861/,再与接口中@RequestLine指定的地址进行拼接,得到最终请求地址 本例中最终请求地址为"http://192.168.2.113:8861/add"或"http://192.168.2.114:8861/add" */ RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()).target(RemoteService.class, "http://service-one/"); /** * 调用测试 */ for (int i = 1; i <= 10; i++) { int result = service.getAdd(1, 2); System.out.println("result:" + result); } } }
> 首先利用com.netflix.config.ConfigurationManager
读取配置文件ribbon.properties
,该文件位于src/main/resources
下.
重点在于通过RibbonClient.create()使得Feign对象获得了Ribbon的特性.之后通过encoder,decoder设置编码器与解码器,并通过target方法将之前定义的接口RemoteService与一个URL地址http://service-one/
进行了绑定.
启动Spring Boot程序
执行: gradlew bootRun
> 默认端口号是:8080
在浏览器里测试
> 输入:http://127.0.0.1:8080/add?a=1&b=2
> 返回: 3