下载
安装包:https://github.com/alibaba/nacos/releases
下载nacos-server-$version.zip包,Windows下载解压后(.zip),直接点击bin/startup.cmd就可以了。
默认启动端口是8848,可以在conf下找到application.properties文件,对server.port进行修改。
可视化控制台:http://localhost:8848/nacos, 默认的用户名和密码都是nacos。
nacos.zip解压,绿色版,启动服务。注意:解压路径一定不要有中文或括号之类的字符,包括(x86),否则可能无法启动。
- 当无法启动(一闪而过)时:可以使用cmd进入nacos的bin文件夹,执行startup.cmd,出错就会打印错误信息,拿到错误信息就可以百度搜索解决办法了。
- nacos版本尽量使用Spring Cloud Alibaba里面的nacos版本。
- nacos执行startup.cmd,默认以cluster集群方式启动,此时需要mysql建库来支撑数据的持久化,建库脚本在D:\Program Files\nacos\conf\nacos-mysql.sql;不配数据库直接启动第一个会报db.num is null错误
- 以单机模式启动,进入cmd黑窗口,执行startup.cmd -m standalone,内置了一个嵌入式数据库derby,单机也可以存储数据
遇到的问题
双击startup.cmd无法启动
因为解压到了D:\Program Files (x86)下,解压路径不能包含括号,移到D:\Program Files下完美解决。
启动报db.num is null错误
nacos执行startup.cmd,默认以cluster集群方式启动,此时需要mysql建库来支撑数据的持久化,建库脚本在D:\Program Files\nacos\conf\nacos-mysql.sql;不配数据库直接启动第一个会报db.num is null错误
解决方案:
- 建库
- 单机启动
单机模式启动
执行startup.cmd -m standalone
如果不喜欢以命令的方式启动,可以通过修改D:\Program Files\nacos\bin\startup.cmd文件,
然后我们将原来的set MODE="cluster"集群改为set MODE="standalone"单机
,然后在直接鼠标点击startup.cmd启动即可。
1. 服务注册
在父工程路径下创建子工程,让子工程继承父工程的环境依赖,子工程pom.xml 中添加 nacos 发现组件(可以放在父pom,当项目庞大起来,子工程越来越多,此时继承父pom即可,避免重复代码)。
子项目(提供者)
pom.xml配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
application.yml 中配置,项目启动时是否会注册,取决于spring.application.name的配置
或者不使用application.yml ,直接在主类上使用@EnableDiscoveryClient。
server:
port: 8081
spring:
cloud:
nacos:
discovery:
# 指定nacos server地址,不用写http://,不配置默认是本机localhost:8848
server-addr: localhost:8848
application:
# 指定提供者的名字,注册服务的关键因素,注释掉name就不会再注册
name: my-provider
项目启动就会自动在注册中心注册一个实例,如图所示:
小提示:idea可以启动同一个项目多次,在run 配置中勾选Allow parallel run即可启动多个实例,记得修改端口再启动哦。
2. 服务发现
子项目(消费者)
第一步:添加依赖;
第二步:使用DiscoveryClient来获取提供者列表;
第三步:使用RestTemplate进行服务调用,但是RestTemplate没有被纳入Spring管理,无法直接使用注解,可以自己实现一个配置类,将RestTemplate纳入Spring管理,
2.1 添加依赖
pom.xml 添加 discovery,完成服务发现。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.2 通过RestTemplate 进行服务调用
DiscoveryClient是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服务
RestTemplate是专门进行服务调用的
2.2.1 RestTemplate 纳入Spring容器管理
添加一个配置类,使用@Bean将RestTemplate纳入Spring容器管理,业务层就可用@Autowired直接依赖使用。
package com.gs.consumer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConsumerConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.2.2 服务调用
通过 discoveryClient 发现注册到 nacos 中的 provider 服务,再通过RestTemplate 进行服务调用。
整合:这种方式比较麻烦,而且随机数的生成也不合理,稍后介绍更便捷用法-Ribbon。
package com.gs.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate; // 前提:RestTemplate 纳入Spring容器管理
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/instances")
public List<ServiceInstance> instances(){
List<ServiceInstance> provider = discoveryClient.getInstances("my-provider");
return provider;
}
@GetMapping("/consumer")
public String test1() {
// 获取提供者实例
List<ServiceInstance> provider = discoveryClient.getInstances("my-provider");
// 根据提供者个数生成随机数,随机调用提供者
// 整合:这种方式比较麻烦,而且随机数的生成也不合理,稍后介绍更便捷用法-Ribbon。
int index = ThreadLocalRandom.current().nextInt(provider.size());
// 调用提供者实例的index方法
String url = provider.get(index).getUri() + "/index";
return "consumer随机远程调用provier:" + this.restTemplate.getForObject(url, String.class);
}
}
3. Ribbon 负载均衡
3.1 优化RestTemplate
使用Ribbon优化上述的DiscoveryClient 和 RestTemplate 用法。
在配置类上使用@Bean + @LoadBalanced注解,将RestTemplate纳入Spring容器管理。
注意:使用Ribbon负载均衡的时候服务名中不能使用下划线,不然会找不到服务。
package com.gs.consumer.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConsumerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
在调用时不需要再使用DiscoveryClient进行服务列表的获取,不需要自行生成随机数来进行负载分发,将一切交给Ribbon。
我们只需要提供url,形如http://serviceId/api
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer")
public String index(){
return "consumer随机远程调用provier:" + this.restTemplate.getForObject("http://my-provider/index", String.class);
}
}
负载均衡,默认策略是随机,权重1:1
测试:
小提示:idea可以启动同一个项目多次,在run 配置中勾选Allow parallel run即可启动多个实例,记得修改端口再启动哦。
3.2 负载均衡策略
随机(默认)
server:
port: 8081
my-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3.2.1 自定义策略(未实现待测试)
- 自定义策略类BalanceWeightRule
- 修改nacos默认的随机策略,@Configuration使用getRule方法,返回自定义的IRule
- 配置指定要使用的策略类全限定名
3.2.1.1 自定义策略类BalanceWeightRule
自定义策略,继承AbstractLoadBalancerRule,重写choose方法
package com.gs.provider.config;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Nacos 自定义负载策略
*/
public class BalanceWeightRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public Server choose(Object o) {
// 获取负载均衡的对象
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();
// 获取当前调用的微服务的名称
String serviceName = baseLoadBalancer.getName();
// 获取Nocas服务发现的相关组件API
NamingService namingService = discoveryProperties.namingServiceInstance();
try {
// 获取一个基于 nacos client 实现权重的负载均衡算法
Instance instance = namingService.selectOneHealthyInstance(serviceName);
System.out.println("选择的实例是port={" + instance.getPort() + "},instance={" + instance + "}");
// 返回一个nacos的server
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
return null;
}
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
3.2.1.2 替换掉nacos默认的随机策略
package com.gs.provider.config;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RibbonConfig {
@Bean
public IRule getRule() {
// 实现带有权重的负载均衡策略
return new BalanceWeightRule();
}
}
3.2.1.3 配置指定策略
server:
port: 8082
spring:
application:
# 注册服务的关键因素,注释掉name就不会再注册
name: my-provider
# 针对此服务设置负载策略
my-provider:
ribbon:
NFLoadBalancerRuleClassName: com.gs.provider.config.BalanceWeightRule