1 负载均衡
1.1 ribbon是什么
- springcloud ribbon 是基于Netflix Ribbon 软件开发定制定制实现的一套客户端负载软件开发定制定制均衡的工具
- 简单的说ribbon是netfix 软件开发定制定制发布的开源项目,软件开发定制定制主要功能是提供客户端软件开发定制定制的软件负载均衡算法,将netflix软件开发定制定制的中间层服务连接在一起。
- ribbon软件开发定制定制的客户端组件提供一些软件开发定制定制列完整的配置项:连接超时,重试等等。
- 在配置文件中列出LoadBalance(简称LB:负载均衡)后面所有的机器,ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。
- 我们也很容易使用ribbon实现自定义的负载均衡算法
1.2 ribbon能干什么
- LB在微服务活分布式集群中经常用的一种应用
- 负载均衡简单来说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)
- 常见的负载均衡软件有nginx,lvs等等
- dubbo、springcloud中均给我们提供了负载均衡,springcloud的负载均衡算法可以自定义
- 负载均衡简单分类:
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方
进程式LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器
ribbon就属于进程式LB,它只是一个类库,集成于消费式进程,消费方通过它来获取到服务提供方的地址
1.3 ribbon实现
以下是针对8080消费者工程的调整
pom
在8080消费者工程中新添加两个依赖
<!--导入Ribbon的同时要导入erueka,因为它要发现服务从那里来--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.7.RELEASE</version> </dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
RestTemplateConfig
目前使用的RestTemplate调用,需要添加注解
@Configurationpublic class RestTemplateConfig { @Bean @LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡 public RestTemplate getRestTemplate() { return new RestTemplate(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
yml
添加配置
server: port: 8080# eureka配置eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka0.com:8000/eureka/,http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
启动类
@SpringBootApplication@EnableEurekaClient //服务启动后注册在eureka上public class ConsumerDept8080 { public static void main(String[] args) { SpringApplication.run(ConsumerDept8080.class, args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
DeptController
将调用地址该为服务名
@RestController@RequestMapping("/dept")public class DeptController { @Autowired private RestTemplate restTemplate; //用Ribbon做负载均衡的时候不应该写死地址,地址应该是一个变量,通过服务名来访问 private static final String REST_URL_PREFIX = "http://PROVIDER-DEPT/dept/"; //private static final String REST_URL_PREFIX = "http://localhost:8001/dept/"; @GetMapping("/findAll") public List<Dept> findAll() { return restTemplate.getForObject(REST_URL_PREFIX + "findAllDept", List.class); } @GetMapping("/findById/{id}") public Dept findById(@PathVariable("id") int id) { return restTemplate.getForObject(REST_URL_PREFIX + "findById/" + id, Dept.class); } @PostMapping("/addDept") public int addDept(@RequestBody Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "addDept", dept, Integer.class); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
自此 我们的负载均衡已经实现了,但是由于提供者只有一个,那么现实的结果也一样;那我们在分布创建8002和8003两个服务提供者
模拟数据库
整体架构图
为了便于对应这儿将8001的数据库对应为demo1
创建三个数据库demo1、demo2及demo3 数据一样只是dept_source为数据库名以便区分
创建另两个服务提供者
额外在新建两个模块 8002及8003 代码直接copy即可
只需要将配置对应调整
- 三个服务提供者代码一致(启动类根据自己的端口命名)
- pom导包一样
- yml的端口对应数据库 instance-id对应(应用名称不能修改)
8001
server: port: 8001mybatis: mapper-locations: classspath:mapper/*.xmlspring: datasource: url: jdbc:mysql://localhost:3306/demo1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource application: name: provider-depteureka: client: service-url: # 将原本注册在一台的 注册到三台上 #defaultZone: http://localhost:8000/eureka/ defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/ instance: instance-id: springcloud-provider-8001 #修改在Eureka上默认的状态名字info: app.name: Damon-springcloud company.name: www.ccct.com
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
8002
server: port: 8002mybatis: mapper-locations: classspath:mapper/*.xmlspring: datasource: url: jdbc:mysql://localhost:3306/demo2?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource application: name: provider-depteureka: client: service-url: # 将原本注册在一台的 注册到三台上 #defaultZone: http://localhost:8000/eureka/ defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/ instance: instance-id: springcloud-provider-8002 #修改在Eureka上默认的状态名字info: app.name: Damon-springcloud company.name: www.ccct.com
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
8003
server: port: 8003mybatis: mapper-locations: classspath:mapper/*.xmlspring: datasource: url: jdbc:mysql://localhost:3306/demo3?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource application: name: provider-depteureka: client: service-url: # 将原本注册在一台的 注册到三台上 #defaultZone: http://localhost:8000/eureka/ defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/ instance: instance-id: springcloud-provider-8003 #修改在Eureka上默认的状态名字info: app.name: Damon-springcloud company.name: www.ccct.com
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
项目结构
然后先启动三个注册中心集群;在启动三个提供者者;最后启动消费者
访问打印
访问地址:http://localhost:8080/dept/findAll;多访问几次你会发现是轮询,三个提供者轮流提供数据
从上述的截图信息中可以发现数据来源于不同的数据库
2 Ribbon负载均衡探究
2.1 IRule接口
Ribbon中有一个非常重要的接口IRule接口;该接口基本上实现了所有常用的负载均衡算法
里面有很多负载均衡算法,默认为轮询;如果需要别的算法,只需要要修改配置类中注册的Bean即可
RestTemplateConfig
@Configurationpublic class RestTemplateConfig { @Bean @LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡 public RestTemplate getRestTemplate() { return new RestTemplate(); } @Bean public IRule myRule(){ //return new RoundRobinRule(); //默认轮询访问 return new RandomRule(); //随机访问 }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
修改为上述配置后重启8080消费者项目,访问接口多访问几次;就会发现没有规律可循
2.2 自定义负载均衡算法
在配置包中新建负载均衡算法类MyRule
MyRule
复制一个实现算法,修改算法核心代码
public class MyRule extends AbstractLoadBalancerRule { //每个机器访问3次换下一个 总共3个 private int total = 0;//被调用的次数 private int curIndex = 0;//当前是谁在提供服务 public Server choose(ILoadBalancer lb, Object key){ if (lb == null) { return null; } else { 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; } //-------核心代码------------ if(total<=3){ total++; }else { total = 0; curIndex++; if(curIndex>=serverCount){ curIndex=0; } } server = upList.get(curIndex); //---------核心代码---------- if (server == null) { Thread.yield(); } else { if (server.isAlive()) { return server; } server = null; Thread.yield(); } } return server; } } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } @Override public Server choose(Object key) { return choose(getLoadBalancer(),key); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
RestTemplateConfig
修改注册bean
@Bean public IRule myRule(){ //return new RoundRobinRule(); //默认轮询访问 //return new RandomRule(); //随机访问 return new MyRule(); }
- 1
- 2
- 3
- 4
- 5
- 6
启动类
添加注解,让项目启动时加载我们自定义的规则即可
@SpringBootApplication@EnableEurekaClient //服务启动后注册在eureka上//项目启动时添加我们自定义的规则@RibbonClient(name = "PROVIDER-DEPT",configuration= MyRule.class)public class ConsumerDept8080 { public static void main(String[] args) { SpringApplication.run(ConsumerDept8080.class, args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
重启消费者8080项目,访问http://localhost:8080/dept/findAll;你会发现每3次就会变化数据来源。