注册中心:
Eureka:
nacos
依赖:
父项目依赖
我用的是2.6.11版本的springboot,所以要选择2021.0.5版本的springcloud
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring.cloud-version>2021.0.5spring.cloud-version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud-version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
子项目依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
我们需要将 eureka 注册到spring容器中,所以需要在配置文件中做相关配置。
server:
port: 8099
spring:
application:
name: eureka_server
eureka:
client:
# 配置eureka服务地址
service-url:
defaultZone: http://127.0.0.1:8099/eureka
为了让项目能启动 eureka,需要在启动类上加一个注解:@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
启动项目,访问地址:http://127.0.0.1:8099:
注册的服务名称就是配置文件中的名称的大写。
在上一步,已经将 eureka-server (eureka服务中心)搭建完毕,现在就开始注册服务实例了。
注意:
无论是 服务提供者 还是 服务消费者,他们的身份都是 eureka-client
记得添加 spring-boot-starter-web 的依赖,不然会报错:Field optionalArgs in org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration required a bean of type ‘com.netflix.discovery.AbstractDiscoveryClientOptionalArgs’ that could not be found.
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
服务发现、服务注册统一都封装在eureka-client依赖。
需要在配置文件中配置 eureka-server 的地址。
spring:
application:
name: service_provider
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8099/eureka
为了让项目能启动 eureka,需要在启动类上加一个注解:@EnableEurekaClient。
@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
我们可以通过 IDEA 自带功能模仿启动多个服务实例。
依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
配置文件:
spring:
application:
name: service_user
server:
port: 8084
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8099/eureka
服务拉取:
修改 controller 代码,将 url 路径的 ip、端口 修改为 服务名:
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{id}")
public Book getBookById(@PathVariable("id") Integer id) {
// String url = "http://127.0.0.1:8081/provider" + id;
String url = "http://service_provider/provider";
if (id != null) {
url = url + id;
}
Book book = restTemplate.getForObject(url, Book.class);
return book;
}
}
注册 RestTemplate 的时候加上注解 @LoadBalanced
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
接口调用:
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{id}")
public Book getBookById(@PathVariable("id") Integer id) {
// String url = "http://127.0.0.1:8081/provider" + id;
String url = "http://PROVIDER/pro/";
if (id != null) {
url = url + id;
}
Book book = restTemplate.getForObject(url, Book.class);
return book;
}
}
多测试几下接口,可以发现,user一会儿调用的是 provider:8081 一会儿调用的是 provider:8082。这就是负载均衡算法选择的。
eureka-server 搭建:
引入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
启动类添加 @EnableEurekaServer
配置文件配置 eureka 地址
server:
port: 8099
spring:
application:
name: server
eureka:
client:
# 配置eureka服务地址
service-url:
defaultZone: http://127.0.0.1:8099/eureka
服务注册:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
server:
port: 8081
spring:
application:
name: provider
eureka:
client:
# 配置eureka服务地址
service-url:
defaultZone: http://127.0.0.1:8099/eureka
服务发现:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
server:
port: 8089
spring:
application:
name: user
eureka:
client:
# 配置eureka服务地址
service-url:
defaultZone: http://127.0.0.1:8099/eureka
http://PROVIDER/pro/4 并非真实的地址,这个需要Ribbon负载均衡去拦截,然后选择具体的服务地址。而,Ribbon就是通过 LoadBalancerInterceptor 的 intercept 方法来实现拦截请求并解析选择地址。
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
IRule常见规则:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule (默认) | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
自定义负载均衡策略:
方式一: 在user中,@Bean 注入自定义 IRule
@Bean
public IRule randomRule(){
return new RandomRule();
}
方式二: 在user,配置文件修改 IRule
provider: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意: 方式一的作用范围是:在访问任何微服务的时候,都是使用 RandomRule 负载均衡策略;方式二的作用范围是:在访问 provider 微服务的时候才是采用 RandomRule 策略,其他的还是使用默认策略。
Ribbon默认采用懒加载,即会在第一次访问时才会去创建 LoadBalanceClient ,所以会在第一次请求的时候花上较长的等待时间。
可以通过配置文件更改加载策略为饿加载策略,即初始化时就创建 LoadBalanceClient ,降低第一次访问的耗时。
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: provider # 指定饥饿加载的服务名称
Nacos下载地址: https://github.com/alibaba/nacos/releases
下载好之后,解压就行了。
默认端口: 8848 ,可通过 conf/application.properties 的 server.port 修改端口。
单击启动: bin/
父工程添加 SpringCloudAlibaba 的管理依赖。
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
子工程中注释掉 eureka 依赖,引入 nacos 依赖。
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
配置文件配置nacos地址:
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos服务地址
注意: eureka 注册名会变成大写,nacos 不会,所以改成 nacos 之后,需要把访问地址改成小写。
一个服务可以拥有多个实例,如:provider 的 8081 和 8082,如果这些实例分布于全国不同的机房,如:provider:8081 在成都机房、provider:8082 在重庆机房,Nacos就将同一机房内的实例 划分为一个集群。
微服务相互之间访问时,访问本地的速度更快,所以应该尽可能访问相同集群的实例,只有当本集群内存不够时,才去访问其他集群。
配置服务集群:
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos服务地址
discovery:
cluster-name: CD
VM Options:
-Dserver.port=8082 -Dspring.cloud.nacos.discovery.cluster-name=CQ
默认的 ZoneAvoidanceRule 负载均衡策略并不能实现据同集群优先来实现负载均衡,因此Nacos中提供了一个NacosRule
的实现,可以优先从同集群中挑选实例。
修改负载均衡策略:
方式一:
@Bean
public IRule nacosRule(){
return new NacosRule();
}
方式二:
provider: # 访问的服务名
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
服务器的性能之间存在差异,为了让性能能好的服务器承担更多的用户请求,Nacos提供了权重配置来控制访问率:权重越大、访问率越高。
注意: 权重为0的服务永远不会被访问。
Nacos提供了namespace来实现环境隔离功能。
spring:
cloud:
nacos:
discovery:
namespace: e11eb3bc-8eed-4cdd-93f0-7a6c01b85eb4 # 命名空间,填ID
注意: 因为此时 dev 命名空间中只有 user,导致无法访问到 public 中的 provider ,发起访问请求会报错。
Nacos的服务实例分为两种类型:
临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型
非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例
临时实例是实例向注册中心发起心跳,永久实例是注册中心主动向实例询问心跳。
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为永久实例
Nacos不仅可以担任微服务的注册中心,还可以担任配置管理。
可以使用统一配置管理来处理因为部署的微服务数量过多,配置繁杂等问题。
Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
注意: 只把那些需要热更新的配置文件交给Nacos
Data ID: 配置文件id:服务名称-profile.后缀名 。
spring引入了一种新的配置文件:bootstrap.yaml文件,会在application.yml之前被读取,流程如下:
引入nacos-config依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
添加bootstrap.yml
spring:
application:
name: user # 服务名称
profiles:
active: dev #开发环境,这里是dev
cloud:
nacos:
server-addr: localhost:8848 # Nacos地址
config:
file-extension: yaml # 文件后缀名
这里会根据spring.cloud.nacos.server-addr获取nacos地址,再根据
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
作为文件id,来读取配置。
读取nacos配置
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("now")
public String now() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
启动报错: org.springframework.beans.factory.BeanCreationException: Error creating bean with name xxx
解决方案:
配置热更新: 修改nacos中的配置后,微服务中无需重启即可让配置生效。
实现方式:
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
}
@Autowired
private PatternProperties patternProperties;
@GetMapping("now")
public String now() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat));
}
其实微服务启动时,会去nacos读取多个配置文件,因为nacos管理的配置文件不包含环境信息,可以被多个环境共享。
只需要取名的时候,不加上 profile 就能被共享,如下: