Nacos服务注册中心(微服务)

为什么要用服务注册中心?

  1. 在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),
  2. 其次就是如何在不同的服务之间进行通信?
  3. 如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。

市面常见的注册中心(扩展)

Zookeeper(雅虎Apache)
Eureka(Netfix)
Nacos(Alibaba)
Consul(Google)

他们分别都有什么特点,我们如何进行选型呢?我们主要从社区活跃度,稳定性,功能,性能等方面进行考虑即可.本次微服务的学习,我们选择Nacos,它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好

Nacos注册中心简介

Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。其官网地址如下:
https://nacos.io/zh-cn/docs/quick-start.html

Nacos本身就是基于Spring boot MVC开发的
Nacos提供了 服务的注册、发现、配置的特性
Nacos活跃度高、稳定、性能强、学习成本底
Nacos的官网是:nacos.io
Nacos在github的源码:github.com/alibaba/nacos
Nacos在windows环境下,解压即可使用(需要JDK和数据库的依赖)

Nacos下载

https://github.com/alibaba/nacos/releases
Nacos服务注册中心(微服务)

Nacos启动

Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):
./startup.sh -m standalone

Windows启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone

说明:
1)执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
2)nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),

访问Nacos服务

打开浏览器,输入http://localhost:8848/nacos地址
默认账号密码: nacos/nacos


服务注册与调用入门(重点)

创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
Nacos服务注册中心(微服务)

Nacos版本

使用Nacos的版本选择可参考如下网址(涉及到一个兼容性问题,不能随意指定其版本):

https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

创建java微服务项目的创建

根pom文件的配置(此pom为根节点,不做代码)

<?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">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>sca-provider</module>
        <module>sca-consumer</module>
    </modules>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.2.RELEASE</version>
    </parent>
    <groupId>com.cy.jt</groupId>
    <artifactId>05-jt-sca</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <spring.cloud>Hoxton.SR8</spring.cloud>
        <spring.cloud.alibaba>2.2.5.RELEASE</spring.cloud.alibaba>
    </properties>
    <!--依赖版本管理-->
    <dependencyManagement>
        <dependencies>
            <!--spring cloud 依赖,此依赖需要springboot的支持,此依赖中定义了spring cloud微服务规范-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud}</version>
                <!--import表示此工程下的子工程可以直接引用这个依赖中版本-->
                <scope>import</scope>
                <!--当scope为import时,这里的类型必须为pom类型-->
                <type>pom</type>
            </dependency>
            <!--spring cloud alibaba 依赖,此依赖依赖于 spring cloud,当前依赖中是基于微服务规范做了具体实现-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

说明:

  1. 由于Maven只允许继承一个<parent>,为了使子项目多继承pom所以使用<dependencyManagement>添加多个依赖的pom
  2. 父工程资源初始化完成以后,将src目录删除,因为父工程只负责依赖管理.

创建provider服务和consumer项目

子项目配置

server:
  port: 8090
spring:
  application:
    name: sca-consumer  # 代表该项目的服务名,后续会在nacos注册中心呈现这个名字 
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848   # 从哪里去查找(注册)服务,链接到服务注册中心的地址

feign:     # 启用feign方式服务调用接口时,如果出现异常则执行默认的处理机制
  hystrix:
    enabled: true  # 新版本默认值为false:即使出现异常不会执行默认的处理机制

注意: 服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。
server-addr: 如果不写默认是:localhost:8848

当配置好以上几点,启动Nacos服务,并启动项目即可将服务(项目)注册到Nacos上


Nacos小结

1.为什么要将服务注册到nacos?

为了更好的查找这些服务,得到统一的管理

2.在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?

当启动一个项目时会向nacos以web方式请求发送一次心跳包注册到Nacos
程序会默认每5s向nacos发送一次心跳

3.对于Nacos服务来讲它是如何判定服务实例的状态?

如果检查到实例超过15s,还没发送心跳过来。则会将实例健康状态改成false
30s没有收到心跳,nacos会把项目移除。
如需重写添加,则重启项目,注册服务

4.服务启动时如何找到服务启动注册配置类?

通过依赖内置的NacosNamingService类

5.服务消费方是如何调用服务提供方的服务的?

通过RestTemplate对象

6.注册中心的核心数据是什么?

服务的名字和它对应的网络地址

7.注册中心中心核心数据的存取为什么会采用读写锁?

底层安全和性能(可查看AbstractHandlerMethodMapping类)

8.Nacos是如何保证高可用的?

断开链接后重试+本地缓存+集群

Nacos服务负载均衡设计及实现

说明:
一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,以下是Nacos实现的负载均衡实例

1.基于RestTemplate对象进行远程调用提供者的服务

实例化RestTemplate对象,并将对象加入IOC(Bean)容器

    //远程调用微服务对象: 作用是基于此对象可以访问远程服务,这个对象是Spring MVC 自带的
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

通过RestTemplate对象的getForObject发送一个hppt请求

 	@Autowired
  	private RestTemplate restTemplate;
  	
	@GetMapping("/consumer/doRestEcho1")
	public String doRestEcho01() {
		String url = "http://localhost:8081/provider/echo/";
		//远程过程调用,向提供方发起一个请求(简称:RPC)
		return restTemplate.getForObject(url, String.class);
    }
	

说明

该方法虽然可以远程调用接口,但是不具备负载均衡特性
它不需要依赖nacos直接通过固定的ip和端口进行服务调用
此方式不适合多服务实例调用


2.基于RestTemplate进行远程服务调用

	@Autowired
	//负载平衡器客户端: 基于此对象可以从注册中心获取服务实例
	private LoadBalancerClient loadBalancerClient;

	@GetMapping("/consumer/doRestEcho2")
	public String doRestEcho02() {
		//基于loadBalancerClient对象传的服务名,去从nacos注册中心按照服务名查找服务实例(可能有多个)
		ServiceInstance choose = loadBalancerClient.choose("sca-provider");
		
		//查询到的服务按默认轮询的方式选择,IP+端口的服务(算法去选择服务实例)
		String host = choose.getHost();//ip地址(这里ip地址不是固定的)
		int port = choose.getPort();//获取端口号(这里端口号不是固定的)
		
		String url = "http://" + host + ":" + port + "/provider/echo/";
		return restTemplate.getForObject(url, String.class); //远程调用服务
	}  

LoadBalancerClient对象说明:

  1. 该对象在项目启动时会自动注入带IOC容器里,无需配置
  2. 它返回是ServiceInstance对象,里面包含所有按需求查询到的服务,此时会在本地按照一定算法去选择服务实例
  3. 默认以轮询的方式选择IP+端口的服务

说明

该方法代码量比较多,有没有一种通过少量代码实现负载均衡的访问呢?

3.通过@LoadBalanced整合RestTemplate对象实现代码的优化

将restTemplate远程调用对象与负载均衡客户端进行整合
RestTemplate对象将会自动加入LoadBalanced负载均衡功能

    @Bean
    @LoadBalanced
    public RestTemplate loadBalancedRestTemplate() {
        return new RestTemplate();
    }
	// 该对象此时拥有 负载均衡客户端 + 远程过程调用对象 的功能
	@Autowired
    private RestTemplate loadBalancedRestTemplate;

    @GetMapping("/consumer/doRestEcho3")
    public String doRestEcho03() {
    	// http:/nacos上注册的服务名/该服务的接口
    	// 当loadBalancedRestTemplate解析时会默认把服务名换成IP+端口了
        String url = "http://sca_provider/provider/echo";
        return loadBalancedRestTemplate.getForObject(url, String.class);
    }

@LoadBalanced的作用是什么?
描述了RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,
这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略

说明

这个方法虽然简化了代码,但是不是特别高效,原因如下:

  1. 它虽然基于具备了负载均衡特性的RestTemplate进行远程服务调用,
  2. 但从效率上会稍微逊色于方式2,因为底层会对请求进行拦截,
  3. 拦截到以后再基于loadBalancerClient获取服务实例进行调用

小结说明

1. Nacos是如何实现负载均衡的?

这里多个实例并发提供服务的方式为负载均衡,
是由Nacos集成了Ribbon来实现的,默认的负载均衡时轮询策略
Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。

2. 什么是Ribbon?

Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡
(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息)
这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用

3. Ribbon负载均衡策略是什么
Nacos服务注册中心(微服务)

基于Ribbon方式的负载均衡,Netflix默认提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,默认的负载均衡策略是轮训策略!
共8种可以通过查看IRule接口的实现类进行分析

当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.

4. Nacos中的负责均衡底层是如何实现的?

通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务

5. Ribbon 是什么

Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法,后因开源被Spring整合了

6.Ribbon 可以解决什么问题?

基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口

7.我们可以自己定义负载均衡策略吗?

可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现

基于Feign远程服务的调用与实践

背景分析:

服务消费方基于rest的请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,打码量复杂且不易维护,此时Feign诞生

Feign是什么

Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:

Nacos服务注册中心(微服务)

Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由社区进行维护,更名为OpenFeign。

为什么使用Feign

结构清晰,代码逻辑强,可重复调用

Feign的实现

1. 依赖jar包(SpringCloud团队基于OpenFeign研发了starter)

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2. Service层定义 Feign的远程服务调用接口

@FeignClient(name = "sca-provider",contextId = "remoteProviderService", fallbackFactory = RemoteProviderFallbackFactory.class)
public interface RemoteProviderService {
    //GetMapping的参数须与远程服务的地址相同(请求方式相同)
    @GetMapping("/provider/echo/{msg}")
    String echoMsg(@PathVariable("msg") String msg);
}

接口说明:
这个接口不需要我们自己写实现类,底层会在服务启动时帮我们创建(代理对象),实现类内部进行远程服务调用


参数说明:
name: 定义Feign访问nacos中的那个服务名下的服务器(值:为nacos中的服务名)


contextId:
       为一个上下文id,一般就写当前接口名, 用于区分服务调用;因为将来一个服务提供方会提供很多资源调用,假如服务消费方法基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务启动就会失败


fallbackFactory:
用于指定服务终断或报异常时,应该返回给客户端默认的结果远程调用出现异常会调用该对象,
如果不指定contestId,那么Feign在注册服务到Bean时会按照name去注册,
接下来是具体的实现:

3. 配置Feign接口调用时出现的异常,默认调用的类方法

@Service
//RemoteProviderService:表示该类只负责RemoteProviderService出现的异常后执行的处理机制
public class RemoteProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
	//此方法会在指定的Feign接口服务,调用失败时出现了异常,执行的方法
	//throwable 用于接收异常
    @Override
    public RemoteProviderService create(Throwable throwable) {
        return (msg) -> {
            System.out.println(throwable.getLocalizedMessage());
            return throwable.getMessage();
        };
    }
}

注意:此类在实现该FallbackFactory接口时,注意写入泛型
泛型的类是需要捕获,可能出现异常的Feign接口类

4. 开启Feign的异常处理机制
spring中配置文件设置

feign:    
  hystrix:
    enabled: true  # 开启Feign的异常处理机制

它默认值为false表示:
以feign的方式调用服务接口时,如果出现异常则执行默认是不做任何处理的

5. 主类或者配置类声明Spring cloud启动feign方式的服务调用

@EnableFeignClients
@SpringBootApplication
public class ScaConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ScaConsumerApplication.class, args);
    }
}

@EnableFeignClients :可描述再配置类中,又因主类默认也是配置类,所以也可以用在主类
作用:Spring cloud启动时将使用feign方式去调用nacos中的提供者的服务
实现:服务启动时自动扫描@FeignClient注解描述的接口,并基于接口创建代理对象将由Feign代理对象帮我们发起远程服务调用

Feign应用调用过程分析(底层逻辑)

  1. 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
  2. Feign Starter 在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
  3. 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request对象,基于此对象进行远程过程调用。
  4. 请求对象经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
  5. 通过 Client 携带 Request 调用远端服务返回请求响应。
  6. 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据。

Feign的总结

1.为什么使用feign?

基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用

2.@FeignClient注解的作用是什么?

告诉Feign Starter,在项目启动时,为此注解描述的接口创建实现类-代理类

3.Feign方式的调用,底层负载均衡是如何实现的?

Ribbon

4.@EnableFeignCleints 注解的作用是什么?

描述配置类,例如启动类

Nacos大总结

1.何为注册中心?

用于记录服务信息的一个web服务

2.注册中心的核心对象?

服务提供方,服务消费方,注册中心-Registry 等等

3.市面上常用注册中心?

Google-Consul,Alibaba-Nacos,…

4.微服务架构下项目的构建过程

需要使用聚合工程

上一篇:SpringCloud搭建_2.Feign


下一篇:No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalanc