SpringCloud----Nacos服务注册实践

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

业务的流程图:

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

在这里插入图片描述

创建服务提供方:

 第一步:创建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>1-sca</artifactId>
        <groupId>com.cy</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>

第二步:创建application.yml或者application.properties文件和ProviderApplication启动类

server:
  port: 8082 #provider的端口号
spring:
  application:
    name: sca-provider   #nacos中服务名称
  cloud:
    nacos:
      discovery:    #服务的注册和发现
        server-addr: localhost:8848    #nacos的端口号
@SpringBootApplication
@EnableDiscoveryClient   //在nacos服务注册的注解(可以写也可以不写)
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

第三步:创建处理请求的控制层对象和方法

@RestController
public class ProviderController {
    //@Value("")--默认读取项目配置文件中的配置内容
    //如果配置文件不写端口号使用默认端口,此注解会报错.8080写上不会影响其他端口的值
    @Value("${server.port:8080}")
    public String server;

    @GetMapping("/provider/echo/{msg}")
    public String restEcho(@PathVariable String msg){
        return "从消费端调用提供端的此方法"+msg+";本服务端口号"+server;
    }
}

创建服务消费方:

 第一步:创建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>1-sca</artifactId>
        <groupId>com.cy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sca-consumer</artifactId>

    <dependencies>
        <!--spring web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring cloud alibab nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

第二步:创建application.yml或者application.properties文件和ProviderApplication启动类

server:
  port: 8090 #consumer的端口号
spring:
  application:
    name: sca-consumer   #nacos中服务名称
  cloud:
    nacos:
      discovery:    #服务的注册和发现
        server-addr: localhost:8848    #nacos的端口号
@SpringBootApplication
@EnableDiscoveryClient  //服务注册注解
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
     /**RestTemplate使用过程:
     * 第一步:在项目的配置类(启动类也属于配置类)中创建RestTemplate对象(只创建一次)
     * 第二步:在框架的调用处通过 @Autowired注解注入对象--并使用对象*/
    @Bean
    /**RestTemplate--这个类是由第三方提供的类,想要调用需要在配置类中导入其构造方法添加@Bean注解
     * 这个方法的返回对象也交给spring管理,一般用于两个服务器之间第三方的方法调用*/
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

第三步:创建访问资源类

@RestController
public class ConsumerController {

    @Value("${spring.application.name}")
    private String appName;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/restEcho")
    /**
     * 调用服务提供方API
     * api:狭义--只固定的 类和接口
     *     广义--一个访问的 路径(http://ip地址:端口号/路径)*/
    public String restEcho(){
        //调用服务提供API(http://ip:port/path)
        //1.定义要调用的API
        String url ="http://localhost:8082/provider/echo/"+appName;
        //2.谁去访问这个API ?
        System.out.println("request" +url);
        return  restTemplate.getForObject(url,String.class);
    }
    

第四步:启动消费者服务,并在浏览器输入http://localhost:8090/consumer/restEcho地址进行访问,假如访问成功会出现,如图所示效果:

 小节面试分析(检测心跳)


为什么要将服务注册到nacos?(为了更好的查找这些服务)
在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
服务启动时如何找到服务启动注册配置类?(NacosNamingService)
服务消费方是如何调用服务提供方的服务的?(RestTemplate)

服务负载均衡设计及实现(重点) 

原因:一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,启动多个服务实例,在Nacos中服务的负载均衡(Nacos客户端负载均衡)的示例如下:

第一步:修改ConsumerController类,注入LoadBalancerClient对象,并添加restEcho1 方法,然后进行服务访问.

    @Autowired   //这个类看做负载均衡客户端的对象--是一个接口
    /**在两个服务之间的负载均衡使用的是 Ribbon netflix公司封装了一个LoadBalancerClient接口
     * 并且RibbonLoadBalancerClient是这个接口的实现类*/
    private LoadBalancerClient loadBalancerClient;

    /**演示有一个消费方与多个提供方如何调用
     * 两者服务之间通过 Ribbon--负载均衡的调用的关系;调用的接口--LoadBalancerClient
     * instance---实例 */
    @GetMapping("/consumer/restEcho1")
    public String restEcho1(){
        //1.从nacos注册中心获取服务实例
        ServiceInstance instance =
                loadBalancerClient.choose("sca-provider");//choose调用的是具体的服务进程的服务名
        //2.基于restTemplate
        String ip = instance.getHost();   //获取的ip地址
        int port = instance.getPort();  //获取端口号
        //%s--字符串间的占位符,format中有几个占位符就传几个参数  基于ip地址和端口号调用
        String url =String.format("http://%s:%s/provider/echo/%s",ip,port,appName);
        restTemplate.getForObject(url, String.class);
        return restTemplate.getForObject(url, String.class);
    }

第二步:打开Idea服务启动配置,如图所示:

在这里插入图片描述

 第三步:修改并发运行选项(假如没有找到这个选项我们需要通过搜索引擎基于组合查询的方法,去找到对应的解决方案,例如搜索 idea allow parallel run),如图所示

 第四步:修改sca-provider的application.yml配置文件端口,分别以8081,8082端口方式进行启动

第四步:启动sca-consumer项目模块,打开浏览器,输入如下网址进行反复服务访问,然后会发现sca-provider的两个服务都可以处理sca-consumer的请求。

 

 这里多个实例并发提供服务的方式为服务的负载均衡,这里的负载均衡实现默认是因为Nacos集成了Ribbon来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用

@LoadBalanced

当使用RestTemplate进行远程服务调用时,假如需要负载均衡,可以在RestTemplate对象构建时,使用@LoadBalanced对构建RestTemplate的方法进行修饰,例如在ConsumerApplication中构建RestTemplate对象:

@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
    return new RestTemplate();
}

在需要RestTemplate实现负载均衡调用的地方进行依赖注入.例如在ConsumerController类中添加loadBalancedRestTemplate属性

@Autowired
private RestTemplate loadBalancedRestTemplate;

接下来,可以在对应的服务端调用方的方法内,基于RestTemplate借助服务名进行服务调用, 例如:

@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03(){   //这个是基于nacos中的服务名进行调用 sca-provider
    String url=String.format("http://%s/provider/echo/%s","sca-provider",appName);
    //向服务提供方发起http请求,获取响应数据
    return loadBalancedRestTemplate.getForObject(
            url,//要请求的服务的地址
            String.class);//String.class为请求服务的响应结果类型
}

RestTemplate在发送请求的时候会被LoadBalancerInterceptor拦截,它的作用就是用于RestTemplate的负载均衡,LoadBalancerInterceptor将负载均衡的核心逻辑交给了loadBalancer,@LoadBalanced注解是属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor的拦截器。

Ribbon负载均衡策略(了解)

基于Ribbon方式的负载均衡,Netflix公司默认提供了七种负载均衡策略,对于SpringCloud Alilbaba又提供了Nacosule策略(默认的轮训策略是轮询策略),当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.

在这里插入图片描述

 Nginx和Ribbon负载均衡的区别:

Nginx服务器端负载均衡:Nginx是客户端所有请求统一交给Nginx,由Nginx进行实现负载均衡请求转发,属于服务器端负载均衡。即 所有请求由Nginx服务器端进行转发

Ribbon客户端负载均衡:Ribbon是从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,让后在本地实现轮训负载均衡策略。即 在客户端实现负载均衡。

应用场景的区别:

1.Nginx适合于服务器端实现负载均衡 比如Tomcat

2.Ribbon适合与在微服务中RPC远程调用实现本地服务负载均衡,比如Dubbo、SpringCloud中都是采用本地负载均衡。

小节面试分析:

1.  @Bean注解的作用?   一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,这个方法的名字做为bean对象的默认名,也可以通过@Bean(“bean的名字”)指定一个名字,这个注解多用于整合第三方的资源-对象
2.  @Autowired注解的作用?  此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI(注入依赖)操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字不同,就会报错.
3. Nacos中的负责均衡底层是如何实现的?  通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务
4. Ribbon 是什么? Netflix公司提供的 负载均衡-客户端,一般应用于服务的消费方法
5. Ribbon 可以解决什么问题?   基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口
6. Ribbon 内置的负载策略都有哪些?   8种,可以通过查看IRule接口的实现类进行分析
7. @LoadBalanced的作用是什么? 描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。
8. 我们可以自己定义负载均衡策略吗? 可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现

基于Feign的远程服务调用(重点)

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

Feign是什么?

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

Feign应用实践(重点)

第一步:在服务消费方 --添加依赖Open Feign依赖:

<!--SpringCloud团队基于OpenFeign研发了starter-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第二步: 在ConsumerApplication启动类上添加@EnableFeignClients注解

@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient  //服务注册注解
public class ConsumerApplication {
   //......
}

第三步:定义Http请求类型的API,基于此API借助Feign访问远程的服务提供方; 注意:@FeignClient描述的接口会为其自动创建实现类

/**
 * 定义用于实现远程provider服务调用的service接口(Http请求API,基于此API借助OpenFeign访问远端服 
    务)
 * 其中:
 * 1)@FeignClient 用于描述远程服务调用接口
 * 2)name="sca-provider" 为你要远程调用的服务名
 * 3)contextId为当前bean的名称,假如没有指定contextId,默认会采用@FeignClient注解中name属性指定的 
     名字作为 bean的名字
 * 4)fallbackFactory用于定义服务调用超时等现象发生时,一种应对措施或处理机制.
 */
@FeignClient(name = "sca-provider",contextId ="remoteProviderService",fallbackFactory =ProviderFallbackFactory.class)
public interface RemoteProviderService {
//请求的Api
    @GetMapping("/provider/echo/{msg}")
    String echoMessage(@PathVariable("msg") String msg);
}
//consumer.controller-->Feign interface-->remote call
//起步依赖,自动配置-autoconfiguration,监控监控-actuator,嵌入式WEB-tomcat)

第四部:创建FeignConsumerController用来

@RestController
@RequestMapping("/consumer")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    @GetMapping("/doEcho/{msg}")  
    public String doRestEcho(@PathVariable("msg") String msg){
        //基于feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);  
    }
}

第五步:启动程序,在浏览器中访问地址检验是否成功!!!

Feign的进阶配置

一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务
启动就会失败,例如假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败;

//启动异常所报信息
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

解决方式:为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识即可(不指定就是name的名字)

/**
 * 定义用于实现远程provider服务调用的service接口(Http请求API,基于此API借助OpenFeign访问远端服务)
 * 其中:
 * 1)@FeignClient 用于描述远程服务调用接口
 * 2)name="sca-provider" 为你要远程调用的服务名
 * 3)contextId为当前bean的名称,假如没有指定contextId,默认会采用@FeignClient注解中name属性指定的名字作为 bean的名字
 * 4)fallbackFactory用于定义服务调用超时等现象发生时,一种应对措施或处理机制.
 */
@FeignClient(name = "sca-provider",contextId ="remoteProviderService",fallbackFactory =ProviderFallbackFactory.class)
public interface RemoteProviderService {
    //业务逻辑.....
}

当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超时了,怎么办呢?  一般服务消费端会给出具体的容错方案(ProviderFallbackFactory)

第一步:定义FallbackFactory接口的实现

@Component
//创建一个提供者后备工厂--基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    @Override
    /**
     * 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行.
     *  @param throwable 用于接收异常  */
    public RemoteProviderService create(Throwable throwable) {
        return new RemoteProviderService() {
            @Override
            public String echoMessage(String msg) {
                return "服务加载中,请稍等!";
            }
        };
    }
}

第二步: 在Feign访问接口中应用FallbackFactory对象

@FeignClient(name = "sca-provider", contextId = "remoteProviderService",
             fallbackFactory = ProviderFallbackFactory.class)
//sca-provider为nacos中的服务名
public interface RemoteProviderService {
     //...
}

第三步: 在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制

feign:
  hystrix:
    enabled: true  #默认值为false--启动feign

第四步:在服务提供方对应的方法中添加 Thread.sleep(500000) 模拟耗时操作,然后启动服务进行访问测试.

Feign 调用过程分析(了解)

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

小节面试分析

1.  为什么使用feign?       (基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用)。
2.  @FeignClient注解的作用是什么?  (告诉Feign Starter,在项目启动时,为此注解描述的接口创建实现类--代理类)
3.  Feign方式的调用,底层负载均衡是如何实现的? (通过Ribbon客户端负载均衡)
4.  @EnableFeignCleints 注解的作用是什么?(描述配置类,告诉spring cloud,启动 Feign Starter 组件; 例如启动类)

总结

重难点分析

1. 什么是注册中心?  (用于记录服务信息的一个web服务,例如淘宝平台,滴滴平台,美团外卖平台,……)
2. 注册中心的核心对象?  (服务提供方,服务消费方,注册中心-Registry)
3. 市面上常用注册中心?  (Google-Consul,Alibaba-Nacos,…)
4. 微服务架构下项目的构建过程? (聚合工程)
5. Nacos安装、启动、服务的注册、发现机制以及实现过程?
6. Feign的基本应用以及底层调用原理?

FAO分析

1. Nacos是什么,提供了什么特性   (服务的注册、发现、配置)?
你为什么会选择Nacos?  (活跃度、稳定、性能、学习成本)
2. Nacos在github的源码? (github.com/alibaba/nacos)
3. Nacos在windows中的的初步配置?  (application.properties访问数据库的数据源)
4. Nacos服务注册的基本过程?(服务启动时发送web请求)
5. Nacos服务消费的基本过程?(服务启动时获取服务实例,然后调用服务)
6. 注册中心的核心数据是什么?(服务的名字和它对应的网络地址)
7. 注册中心中心核心数据的存取为什么会采用读写锁?  (底层安全和性能)
8. Nacos健康检查的方式?(基于心跳包机制进行实现)
9. Nacos是如何保证高可用的?(重试,本地缓存、集群)
10. Feign是什么,它的应用是怎样的,feign应用过程中的代理对象是如何创建的 (JDK)?

Bug分析

1. 404 ---访问路径错误

2. 400 ---客户端请求参数不一致时会发生

3. 405 ---发起端和接收端的请求方式不匹配

4. 500 ---后端代码错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值