在服务消费端进行负载均衡
Ribbon和Eureka整合以后,客户端可以直接调用,不用关心IP地址和端口
Ribbon做两件事情
找Eureka查询可用的服务列表(Eureka是集群,随便挂几台没事)
通过负载均衡机制向服务提供者调用服务(服务提供者也是集群,随便挂几台没事;可用性高,一致性不强)
服务消费端改造
依赖
<!--ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
配置
eureka:
client:
register-with-eureka: false #laozi不是要去向eureka中注册自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
开启注解
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
配置类
@Configuration//就相当于spring的applicationContext.xml
public class ConfigBean {
@Bean
@LoadBalanced//配置ribbon负载均衡实现RestTemplate
public RestTemplate getTemplate(){
return new RestTemplate();
}
}
调用的地方需要改造
@RestController
public class DeptConsumerController {
//RestTemplate
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法,简单的restful服务模板
// private static final String REST_URL_PREFIX = "http://localhost:8001";
//ribbon去实现时 地址应该是个变量 通过服务名访问
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add/",dept,Boolean.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list/", List.class);
}
}
Eureka注册中心改造
搞成集群形式
服务提供者改造
搞成集群形式,都注册到注册中心
负载均衡算法自定义
回到这个配置类
@Configuration//就相当于spring的applicationContext.xml
public class ConfigBean {
//IRule接口的实现类:
//AvailabilityFilteringRule:先过滤掉跳闸的服务 对剩下的服务进行轮询
//RoundRobinRule:轮询 默认使用这个
//RandomRule:随机
//RetryRule:先轮询,失败的话,在指定的时间内重试
@Bean
@LoadBalanced//配置ribbon负载均衡实现RestTemplate
public RestTemplate getTemplate(){
return new RestTemplate();
}
}
该配置类使程序根据负载均衡策略请求服务
如何自定义负载均衡的策略?
https://www.springcloud.cc/spring-cloud-netflix.html
如上图指示的方式自定义配置类
在启动类指定自定义配置类
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就去加载 自定义的Ribbon类
@RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = RefrainRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
自定义配置类
@Configuration
public class RefrainRule {
@Bean
public IRule myRule(){
return new RefrainRandomRule();//自定义负载均衡策略
}
}
具体算法(照猫画虎;算法有缺陷,为了说明问题)
public class RefrainRandomRule extends AbstractLoadBalancerRule {
//每个服务,访问5次,换下一个服务
//total=0 如果=5 指向下一个服务节点
//total=5 index+1
private int total = 0;//被调用的次数
private int currentIndex = 0;//当前是谁在提供服务
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
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) {
return null;
}
// int index = chooseRandomInt(serverCount);//生成区间随机数
// server = upList.get(index);//从活着的服务中随机获取一个
//===============================================================================
if(total<5){
server = upList.get(currentIndex);
total++;
}else{
total = 0;
currentIndex++;
if(currentIndex>upList.size()){
currentIndex = 0;
}
upList.get(currentIndex);
}
//===============================================================================
if (server == null) {
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;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
Feign
社区里的大佬觉着Ribbon是以服务名的方式进行调用的,不符合Java面向接口编程的习惯,于是搞了一套“接口+注解”形式的调用方式。
原(springcloud-consumer-dept-80)
新(springcloud-consumer-dept-feign)
(springcloud-api)
示例跑成功了,但是当真没咋看懂,为啥要去(springcloud-api)这个工程里边绕一圈丶
https://github.com/ChenCurry/springcloud.git
击石乃有火,不击元无烟!!