话不多说,直接进入正题。其实Feign出现的目的,应该就是简化Ribbon的配置(我自己的理解,不太确定),用一个FeignClient替代RestTemplate。由于我们有可能不止Dept这一个微服务,还可能有Emp、Company等等,所以最好把FeignClient都定义到microservicecloud-api里,这样就不会导致Dept里配一个FeignClient,Emp里配一个,Company里再配一个。
首先修改microservicecloud-api里的pom.xml文件,添加Feign依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
之后和上一讲里一样,定义负载均衡策略,一个OwnRelu.java类,一个ReluConfig.java配置类,分别如下:
package com.sunsy.springcloud.config;
import java.util.List;
import java.util.Random;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class OwnRelu extends AbstractLoadBalancerRule {
Random rand;
public OwnRelu() {
rand = new Random();
}
private int repeatValue = 0;
private int currentIndex = 0;
/**
* Randomly choose from all living servers
*/
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
if(repeatValue<5 && currentIndex==0) {
server = upList.get(currentIndex);
repeatValue++;
}else {
repeatValue=0;
currentIndex++;
if(currentIndex>=upList.size()) {
currentIndex=0;
}
server = upList.get(currentIndex);
}
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
package com.sunsy.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
@Configuration
public class ReluConfig {
@Bean
public IRule ownRule() {
return new OwnRelu();
}
}
然后创建DeptClientSerivce接口,表明DeptClientSerivce调用get方法用的是MICROSERVICECLOUD-DEPT微服务的/dept/get/id接口,用的负载均衡策略是ReluConfig里定义的策略:
package com.sunsy.springcloud.service;
import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.sunsy.springcloud.config.ReluConfig;
import com.sunsy.springcloud.entity.Dept;
@FeignClient(value="MICROSERVICECLOUD-DEPT", configuration=ReluConfig.class)
public interface DeptClientService {
@RequestMapping(value="/dept/get/{id}", method=RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
@RequestMapping(value="/dept/list", method=RequestMethod.GET)
public List<Dept> list();
@RequestMapping(value="/dept/add", method=RequestMethod.POST)
public boolean add(Dept dept);
}
之后创建一个新的子工程microservicecloud-consumer-dept-feign,和api一样在pom.xml里添加Feign依赖。
application.yml复制microservicecloud-consumer-dept的application.yml即可。
主启动类如下,添加了FeignClients支持注解:
package com.sunsy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages= {"com.sunsy.springcloud"})
//@ComponentScan("com.sunsy.springcloud")
public class SpringCloudConsumerFeignApp {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsumerFeignApp.class, args);
}
}
最后加个controller:
package com.sunsy.springcloud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.sunsy.springcloud.entity.Dept;
import com.sunsy.springcloud.service.DeptClientService;
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService service;
@RequestMapping(value="/consumer/dept/list")
public List<Dept> list(){
return service.list();
}
}
启动三个EurekaServer模块,两个Provider模块以及刚刚创建的consumer-feign模块,访问http://127.0.0.1/consumer/dept/list可以获得和上一讲一样的负载均衡效果。
github地址如下:https://github.com/ssystc/springcloud-demo