集中式LB
即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方
进程式LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器
ribbon就属于进程式LB,它只是一个类库,集成于消费式进程,消费方通过它来获取到服务提供方的地址
以下是针对8080消费者工程的调整
在8080消费者工程中新添加两个依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
<version>1.4.7.RELEASEversion>
dependency>
目前使用的RestTemplate调用,需要添加注解
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
添加eureka配置
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/
@SpringBootApplication
@EnableEurekaClient //服务启动后注册在eureka上
public class ConsumerDept8080 {
public static void main(String[] args) {
SpringApplication.run(ConsumerDept8080.class, args);
}
}
将调用地址该为服务名
@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);
}
}
自此 我们的负载均衡已经实现了,但是由于提供者只有一个,那么现实的结果也一样;那我们在分布创建8002和8003两个服务提供者
整体架构图
为了便于对应这儿将8001的数据库对应为demo1
创建三个数据库demo1、demo2及demo3 数据一样只是dept_source为数据库名以便区分
额外在新建两个模块 8002及8003 代码直接copy即可
只需要将配置对应调整
8001
server:
port: 8001
mybatis:
mapper-locations: classspath:mapper/*.xml
spring:
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-dept
eureka:
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
8002
server:
port: 8002
mybatis:
mapper-locations: classspath:mapper/*.xml
spring:
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-dept
eureka:
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
8003
server:
port: 8003
mybatis:
mapper-locations: classspath:mapper/*.xml
spring:
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-dept
eureka:
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
项目结构
然后先启动三个注册中心集群;在启动三个提供者者;最后启动消费者
访问地址:http://localhost:8080/dept/findAll;多访问几次你会发现是轮询,三个提供者轮流提供数据
从上述的截图信息中可以发现数据来源于不同的数据库
Ribbon中有一个非常重要的接口IRule接口;该接口基本上实现了所有常用的负载均衡算法
里面有很多负载均衡算法,默认为轮询;如果需要别的算法,只需要要修改配置类中注册的Bean即可
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public IRule myRule(){
//return new RoundRobinRule(); //默认轮询访问
return new RandomRule(); //随机访问
}
}
修改为上述配置后重启8080消费者项目,访问接口多访问几次;就会发现没有规律可循
在配置包中新建负载均衡算法类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);
}
}
修改注册bean
@Bean
public IRule myRule(){
//return new RoundRobinRule(); //默认轮询访问
//return new RandomRule(); //随机访问
return new MyRule();
}
添加注解,让项目启动时加载我们自定义的规则即可
@SpringBootApplication
@EnableEurekaClient //服务启动后注册在eureka上
//项目启动时添加我们自定义的规则
@RibbonClient(name = "PROVIDER-DEPT",configuration= MyRule.class)
public class ConsumerDept8080 {
public static void main(String[] args) {
SpringApplication.run(ConsumerDept8080.class, args);
}
}
重启消费者8080项目,访问http://localhost:8080/dept/findAll;你会发现每3次就会变化数据来源。