使用的版本
springboot:2.4.1
spingcloud:2020.0.1
Eureka服务端配置
1、pom依赖
org.springframework.cloud
spring-cloud-starter-eureka-server
1.4.6.RELEASE
2、yaml配置
server:
port: 7001
# Eureka配置
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否向eureka注册中心注册自己 (服务器不需要注册)
fetch-registry: false # 如果为false ,则表示自己为注册中心
service-url:
# 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 集群(关联) :
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
这里实现了eureka客户端的集群
3、主启动类设置
@SpringBootApplication
@EnableEurekaServer // 服务端的启动类
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
Eureka客户端的服务提供者
1、pom配置
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2.2.6.RELEASE
org.springframework.boot
spring-boot-starter-actuator
com.zhong
springcloud-api
1.0-SNAPSHOT
junit
junit
mysql
mysql-connector-java
com.alibaba
druid
ch.qos.logback
logback-core
org.mybatis.spring.boot
mybatis-spring-boot-starter
org.springframework.boot
spring-boot-test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jetty
org.springframework.boot
spring-boot-devtools
2、yml 配置
server:
port: 8001
# mybatis 配置
mybatis:
type-aliases-package: com.zhong.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
# spring 配置
spring:
application:
name: springcloud-provider-dept # 三个服务名字一致是前提
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
# Eureka 的配置
# 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8001 # 修改eureka上的默认描述信息
prefer-ip-address: true
# info配置
info:
app.name: zhong
company.name: blog.zhong.com
3、主启动类配置
// 启动类
@EnableEurekaClient // 在服务开启后,自动注册到eureka中,
@SpringBootApplication
@EnableDiscoveryClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
Eureka客户端消费者和Ribbon配置
1、pom配置
com.zhong
springcloud-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
3.0.0
com.netflix.ribbon
ribbon-loadbalancer
2.7.18
compile
com.netflix.ribbon
ribbon-core
2.7.18
compile
com.zhong
springcloud
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-actuator
2.3.1.RELEASE
这里要注意:在spring2020的版本中spring-cloud-starter-netflix-eureka-client依赖中已经去除了Ribbon依赖,官方移除了Netflix的组件。
需要额外的添加依赖,另外,实现Ribbon的自定义负载均衡也有改变。
2、yml配置
server:
port: 80
spring:
application:
name: springcloud-consumer-dept
# eureka 配置
eureka:
client:
healthcheck:
enabled: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-consumer-dept80 # 修改eureka上的默认描述信息
register-with-eureka: false # 表示是否向eureka注册中心注册自己 (消费者不需要注册)
fetch-registry: true
3、config配置RestTemplate
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
4、编写自定义负载均衡器
按照官方文档进行配置https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new MyLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
这里使用了自己定义的MyLoadBalancer
定义如下
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.zhong.myrule;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {
// ======
private int total = 0; // 被调用的次数
private int currentIndex = 0; // 当前是谁在提供
// ======
private static final Log log = LogFactory.getLog(RandomLoadBalancer.class);
private final String serviceId;
private ObjectProvider serviceInstanceListSupplierProvider;
public MyLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider, String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
public Mono> choose(Request request) {
ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
private Response processInstanceResponse(ServiceInstanceListSupplier supplier, List serviceInstances) {
Response serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response getInstanceResponse(List instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
}
return new EmptyResponse();
} else {
// int index = ThreadLocalRandom.current().nextInt(instances.size());
// ServiceInstance instance = (ServiceInstance)instances.get(index);
// ============================
ServiceInstance instance = null;
if (total < 5){
// server = upList.get(currentIndex);
instance = (ServiceInstance)instances.get(currentIndex);
total ++;
}else{
total = 1;
currentIndex++;
if (currentIndex >= instances.size()){
currentIndex = 0;
}
// server = upList.get(currentIndex);
instance = (ServiceInstance)instances.get(currentIndex);
}
// ============================
return new DefaultResponse(instance);
}
}
}
5、编写controller测试
package com.zhong.springcloud.controller;
import com.zhong.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import java.util.List;
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate; // 提供rest中五种便捷访问远程http服务的方法
@Autowired
private LoadBalancerClient loadBalancerClient;
// Ribbon ,这里的前缀地址应该是一个变量,通过服务名来访问
// 原本地址:http://localhost:8001
// ribbon: http://SPRINGCLOUD-PROVIDER-DEPT
private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";
@RequestMapping("/consumer/dept/list")
public List list(){
return restTemplate.getForObject(REST_URL_PREFIX+ "/dept/list",List.class);
}
@RequestMapping("/consumer/dept")
public String test(){
ServiceInstance choose = this.loadBalancerClient.choose("springcloud-provider-dept");
System.out.println("测试:"+choose.getServiceId()+":"+choose.getHost()+":"+choose.getPort());
return "测试";
}
}
使用/consumer/dept进行测试
得到输出结果
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8001
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
测试:SPRINGCLOUD-PROVIDER-DEPT:192.168.146.1:8002
可以得到按照自定义的规则进行负载均衡
总结
本人按照狂神说java里得springcloud进行学习,在eureka服务端和eureka客户端提供者配置时没有遇到问题,但在eureka和Ribbon实现客户端得负载均衡时遇到了两个问题。
通过服务名进行获取服务时出现找不到服务得错误
解决方法:
在服务提供者得yml配置中添加prefer-ip-address: true
在服务消费者里面pom依赖中使用spring-cloud-starter-netflix-eureka-client 3.0版本,去除Ribbon依赖。
至此可以使用服务名获取服务,采用轮询得默认方式
在使用自定义负载均衡时不生效
解决方法:
http://oschina.net/question/3430787_2320770?sort=default文章中说明了springcloud2020版本中去除了Ribbon依赖,并且自定义负载均衡使用http://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer官网得解决方案,要在主启动类上配置@LoadBalancerClient 注解,问题解决