目录
-
Nacos
-
nacos概述
Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力.官网地址:https://nacos.io/zh-cn/docs/quick-start.html
-
构建Nacos服务
- 检查环境变量(JAVA_HOME)
- 确保Mysql版本为5.7以上(MariaDB 10.5以上)
-
下载与安装
- Nacos下载
https://github.com/alibaba/nacos/releases
2. 解压Nacos(注意:不要出现中文目录)
-
初始化配置
-
打开/conf/application.properties里打开默认配置,并基于你当前环境配置要连接的数据库,连接数据库时使用的用户名和密码(假如前面有"#"要将其去掉):
### If use MySQL as datasource: spring.datasource.platform=mysql ### Count of DB: db.num=1 ### Connect URL of DB: db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=root
-
登录mysql,执行:source d:/nacos-mysql.sql
-
服务启动与访问
-
启动Nacos服务
- Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):
./startup.sh -m standalone
- Windows启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
说明:
1)执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
2)nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),
3)一定要确保你连接的数据库(nacos_config)是存在的.
4)假如所有的配置都正确,还连不上,检查一下你有几个数据库(mysql,…)
2.访问Nacos服务
打开浏览器,输入http://localhost:8848/nacos地址,出现如下登陆页面:
其中,默认账号密码为nacos/nacos.
-
常见报错
MySQL的版本问题
当我们在执行一些SQL脚本时(例如 nacos-mysql.sql文件),假如出现如下错误,请升级你的mysql(建议mysql5.7以上或MariaDB 10.5.11)
Nacos服务启动问题
问题1:
问题2:
-
服务注册与调用入门
创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
-
生产者服务创建及注册
-
创建服务提供者工程(module名为sca-provider),继承parent工程(01-sca),其pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>01-sca</artifactId> <groupId>com.jt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sca-provider</artifactId> <dependencies> <!--Web服务--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--服务的注册和发现(我们要讲服务注册到nacos)--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
2.创建并修改配置文件application.yml(或者application.properties),实现服务注册,关键代码如下:
server: port: 8081 spring: application: name: sca-provider cloud: nacos: discovery: server-addr: localhost:8848
注意:服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。
3.创建启动类,并定义处理请求的控制层对象和方法,关键代码如下:
package com.cy;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ScaProviderApplication.class, args);
}
/**定义Controller对象(这个对象在spring mvc中给他的定义是handler),
* 基于此对象处理客户端的请求*/
@RestController
public class ProviderController{
//@Value默认读取项目配置文件中配置的内容
//8080为没有读到server.port的值时,给定的默认值
@Value("${server.port:8080}")
private String server;
//http://localhost:8081/provider/echo/tedu
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable String msg){
return server+" say hello "+msg;
}
}
}
4.启动启动类,然后刷先nacos服务,检测是否服务注册成功,如图所示:
5.打开浏览器,输入http://localhost:8081/provider/echo/msa,然后进行访问。
-
消费者服务发现及调用
1.创建服务消费者(module名为sca-consumer),继承parent工程(01-sca),其pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>01-sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-consumer</artifactId>
<dependencies>
<!--Web服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--服务的注册和发现(我们要讲服务注册到nacos)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
2.修改配置文件application.yml,关键代码如下:
server:
port: 8090
spring:
application:
name: sca-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848 #从哪里去查找服务
3.创建启动类并实现服务消费,关键代码如下:
package com.cy;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Value("${spring.application.name}")
private String appName;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho01(){
String url = "http://localhost:8081/provider/echo/"+appName;
System.out.println("request url:"+url);
return restTemplate.getForObject(url, String.class);
}
}
}
4.动消费者服务,并在浏览器输入http://localhost:8090/consumer/doRestEcho1地址进行访问,假如访问成功会出现,如图所示效果:
-
服务负载均衡设计及实现
1.手动写负载均衡
配置启动多个启动类,修改并发运行选项:
修改sca-provider的配置文件端口,分别以8081,8082端口方式进行启动。
server:
port: 8081
spring:
application:
name: sca-provider
cloud:
nacos:
server-addr: localhost:8848
@GetMapping("/consumer/echo/{msg}")
public String doRestEcho1(@PathVariable String msg) {
//手动自己写负载均衡(随机调用服务列表中的服务对象)
//调用谁? sca-provider中的一个url
String url1 = "http://localhost:8081/provider/echo/"+msg;
String url2 = "http://localhost:8080/provider/echo/"+msg;
String url3 = "http://localhost:8082/provider/echo/"+msg;
String urls[] = new String[]{url1,url2,url3};
//随机获取一个小于urls数组长度的整数
int n = new Random().nextInt(urls.length);//手动式的负载均衡
//如何调用?
return restTemplate.getForObject(urls[n],String.class);
// return server + "Say hello" + msg;
}
启动sca-consumer项目模块,打开浏览器,输入如下网址进行反复服务访问:
http://localhost:8090/consumer/echo/tedu
然后会发现sca-provider的两个服务都可以处理sca-consumer的请求.
2.基于loadBalancerClient方式获取服务实例
@Bean
@LoadBalanced//这个注解在描述RestTemplate对象时,系统底层会对RestTemplate对象的请求进行拦截
public RestTemplate loadBalanceRestTemplate(){
return new RestTemplate();
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
@LoadBalanced//这个注解在描述RestTemplate对象时,系统底层会对RestTemplate对象的请求进行拦截
public RestTemplate loadBalanceRestTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
/**
* @Autowired注解描述属性时,会告诉spring框架,要优先按照属性类型进行对象的查找和注入,假如
* 此类型的对象存在多个,此时还会按照属性名进行查找和对比,有相同的直接注入(DI),没有相同则报错
* 也可以在属性山添加@Qualifier("bean的名字")注解,指定要注入的具体对象
* */
@Autowired
private RestTemplate loadBalanceRestTemplate;
//负载均衡客服端对象(基于此对象可以从nacos中获取服务列表,并且可以基于一定的算法从列表获取一个服务实例)
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${server.port:8080}")
private String server;
//负载均衡方式调用
@GetMapping("/consumer/echo2/{msg}")
public String doRestEcho2(@PathVariable String msg){
//基于loadBalancerClient方式获取服务实例
String serviceId = "sca-provider";//这个名字要在nacos的服务列表中
ServiceInstance choose = loadBalancerClient.choose(serviceId);
String ip = choose.getHost();
int port = choose.getPort();
// String url = "http://" + ip + ":" + port + "/provider/echo/"+msg;
String url = String.format("http://%s:%s/provider/echo/%s",ip,port,msg);
return restTemplate.getForObject(url,String.class);
}
3. 使用拦截器的方式
@Bean
@LoadBalanced//这个注解在描述RestTemplate对象时,系统底层会对RestTemplate对象的请求进行拦截
public RestTemplate loadBalanceRestTemplate(){
return new RestTemplate();
}
@GetMapping("/consumer/echo3/{msg}")
public String doRestEcho3(@PathVariable String msg) {
String url = String.format("http://sca-provider/provider/echo/%s",msg);
return loadBalanceRestTemplate.getForObject(url,String.class );
}
}
}
面试分析
- 为什么要将服务注册到nacos?(为了更好的查找这些服务)
- 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
- 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
- 服务启动时如何找到服务启动注册配置类?(NacosNamingService)
- 服务消费方是如何调用服务提供方的服务的?(RestTemplate)
- @Bean注解的作用?(一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象)
- @Autowired注解的作用?(此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.)
- Nacos中的负责均衡底层是如何实现的?(通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务)
- Ribbon 是什么?(Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法)
- Ribbon 可以解决什么问题? (基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口)
- Ribbon 内置的负载策略都有哪些?(8种,可以通过查看IRule接口的实现类进行分析)
- @LoadBalanced的作用是什么?(描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。)
- 我们可以自己定义负载均衡策略吗?(可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现)
微服务项目结构分析
服务调用案例分析
服务调用方案图解
这张图描述了远程服务调用的几中方式:
第一种:服务比较少,例如就两个服务,一个服务消费,一个服务提供,就不需要注册中心,不需要负载均衡.
第二种:并发比较大,服务服务比较多,我们需要管理服务,就需要注册中心,我们还需要服务间的负载均衡.但代码编写的复杂多相对高一些,我们需要自己获取ip,获取端口,拼接字符串等.
第三种:我们要基于第二种进行代码简化,底层提供了一种拦截器,把基于服务名获取服务实例的过程在拦截器中做了封装,简化了代码的开发.但是加了拦截器多少会在性能少有一点损耗.
第四种方式主要是从代码结构上做一个挑战,我们前面三种基于RestTemplate进行服务调用,本身属于一种远程服务调用业务,能够将这种业务写到一个业务对象中,Feign方式就诞生了,它主要对代码结构的一种优化.