微服务架构day-2

注册中心简介

背景分析

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

Nacos概述

Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。

构建Nacos服务

准备工作:

  1. 确保电脑已配置JAVA_HOME环境变量
  2. 确保MySQL版本为5.7以上

初始化配置

  1. 打开操作系统命令行,登录mysql,执行sql脚本,不是nacos/conf目录下自带的,我们可以使用mysql自带的客户端,在任意命令行登录mysql,然后执行如下指令:
    source d:/nacos-mysql.sql
    执行成功以后会创建一个nacos_config数据库

  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

服务启动于访问

启动nacos服务(可以在nacos的bin目录下通过指令启动)

Linux:

./startup.sh -m standalone

Windows:

startup.cmd -m standalone

访问nacos服务
浏览器输入:http://localhost:8848/nacos地址,其中账号密码为:nacos/nacos

服务注册于调用入门

业务描述

创建两个项目Module分别为服务提供者和服务消费者(假如已有则无需创建),两者都要注册到NacosServer中,然后服务提供者可以为服务消费者提供远端调用服务:

在这里插入图片描述

生产者服务创建及注册

创建服务提供者工程,继承父工程,修改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>

<!--        基于SpringBoot工程的 web服务依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--        服务的注册和发现(我们要讲服务注册到nacos)
当我们项目中添加了这样的一个依赖,就表示此项目是nacos服务的一个客户端对象-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
    </dependencies>

</project>

创建并修改配置文件application.yml,实现服务注册:

#服务的端口
server:
  port: 8081
#服务名
spring:
  application:
    name: sca-provider
#服务注册地址(服务启动时向配置的地址发送心跳包)
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #将来nacos会在一台独立的机器上进行部署

创建其启动类(注意其启动类要在com.jt总包下):
在这里插入图片描述
启动启动类,刷新nacos服务,检查是否服务注册成功:
在这里插入图片描述

消费者服务发现及调用

在sca-provider项目中创建服务提供方对象,基于次对象对外提供服务:

通过@value注解拿到配置文件中配置的内容 8080为没有读到server.port的值时,给的默认值
在这里插入图片描述
创建服务消费者工程,继承父工程,修改其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">
    <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>

<!--        添加服务注册依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

<!--        添加Feign组件(我们要基于这里提供的API进行远程服务调用)-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

</project>

创建sca-consumer服务中的配置文件application.yml:

server:
  port: 8090
spring:
  application:
    name: sca-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

#服务的负载均衡配置
sca-provider:
  ribbon: #ribbon是一个负载均衡组件,是Ribbon组件中提供了IRule接口及相关实现
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

feign:
  hystrix:
    enabled: true #打开熔断机制

创建消费端启动类并实现服务消费(注意启动类在com.jt总包下):

在这里插入图片描述
在sca-consumer启动类中添加方法,用于创建RestTemplate对象.

@Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

我们基于RestTemplate 对象可以实现远端服务调用,例如sca-consumer调用sca-privider

定义sca-consumer服务的消费端Controller,在此对象方法内部实现远端服务调用:
在这里插入图片描述
此方法中通过RestTemplate对象来远程调用sca-provider中的服务
在这里插入图片描述
getForObject 基于RestTemplate对象中的相关方法进行服务调用

启动消费者服务,在浏览器输入http://localhost:8090/consumer/doRestEcho1地址进行访问测试。

小结

  • 为什么要将服务注册到nacos?(为了更好的查找这些服务)
  • 在Nacos中服务提供者是如何想Nacos注册中心(Registry)续约的?(5秒心跳)
  • 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
  • 服务消费方是如何调用服务提供方的服务的?(通过RestTemplate对象实现远端调用)

服务负载均衡设计及实现

业务描述

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

LoadBalancerClient应用

LoadBalancerClient对象可以从nacos中基于服务名获取服务实例,然后在工程中基于特点算法实现负载均衡方式的调用:

  1. 修改ConsumerController类,注入LoadBalancerClient对象,并添加doRestEcho2方法,然后进行服务访问.
@RestController
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;


    /**
     * 负载均衡客户端对象,基于次对象可以获取nacos中的服务实例,
     * 并且通过负载均衡算法从所有实例中获取一个实例
     */
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Value("${spring.application.name:8090}")	//从配置文件中获取值,如果没有则为8090
    private String appName;

    @GetMapping("/consumer/doRestEcho2")
    public String doRestEcho2(){
        //从nacos获取服务实例 (choose方法内置了负载均衡算法)
        ServiceInstance instance = loadBalancerClient.choose("sca-provider");

        String ip = instance.getHost();
        int port = instance.getPort();

        String url = String.format("http://%s:%s/provider/echo/%s", ip, port, appName);

        return restTemplate.getForObject(url, String.class, "嘿嘿");
    }
  1. 修改sca-provider的配置文件端口,分别以8081/8082端口启动
    在这里插入图片描述
  2. 启动成功以后,访问nacos的服务列表,检测服务是否成功注册
    在这里插入图片描述
  3. 启动sca-consumer项目模块,打开浏览器对consumer服务进行访问并不断刷新,检测页面数据变化

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

@LoadBalanced

该注解在使用RestTemplate进行远程服务调用时,如果需要负载均衡可在RestTemplate对象创建时使用@LoadBalanced注解对构建方法进行修饰,在需要使用RestTemplate实现负载均衡时进行依赖注入,可以在对应的服务端调用方法时,使用服务名进行调用。RestTemplate在发送请求时会被其对象拦截,它的作用就是作用于RestTemplate的负载均衡。该对象将负载均衡的逻辑交给了LoadBalanced。

@LoadBalanced注解是属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor的拦截器。

Ribbon负载均衡策略

基于Ribbon方式的负载均衡,Nacos提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,当系统提供的负载均衡策略不能满足我们的需求时,我们可以基于IRule接口自己定义策略

在这里插入图片描述

小结

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

基于Feign的远程服务调用

背景分析

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

Feign是什么

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

在这里插入图片描述

Feign应用实践

  1. 在服务消费方(consumer)添加项目依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在启动类上添加@EnableFeignClients注解

@EnableFeignClients 用于描述一些配置类,告诉系统底层启动时为 @FeignClient注解描述的接口创建实现类及对象,交给Spirng管理
在这里插入图片描述
3. 定义Http请求API,基于此API借助OpenFeign访问远端数据

@FeignClient 注解用于描述远程服务调用接口,其中value属性有两个层面的含义
第一个就是你要远程调用的服务名
第二个就是当前Bean的名字
当前注解描述的接口底层会为其创建实现类,创建的对象为代理对象

@FeignClient(value = "sca-provider")

public interface RemoteProviderService {

    @GetMapping("/provider/echo/{string}")
    String echoMessage(@PathVariable("string") String string);
}
  1. 创建FeignConsumerController中添加Feign访问
package com.jt.controller;

import com.jt.service.RemoteProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/consumer/")
public class FeignConsumerController {

    @Autowired
    private RemoteProviderService remoteProviderService;
	//基于Feign方式的服务调用
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable("msg") String msg){
    	//基于Feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);
    }
}
  1. 启动消费者服务,在浏览器中直接通过Feign客户端进行访问
    url:localhost:8090/consumer/echo/8090

说明,Feign方式的远程服务调用,底层会自动基于ribbon组件实现负载均衡。

Feign配置进阶实践

一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId属性用于区分,服务就会启动失败

package com.jt.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "sca-provider")
public interface RemoteOtherService {

    @GetMapping("/doSomeThing")
    String doSomeThing();
}

这时我们需要为远程调用指定一个contextId属性,作为远程调用服务的唯一标识(这个标识是Bean对象的名字)即可

@FeignClient(value = "sca-provider",contextId = "remoteProviderService",
        fallbackFactory = ProviderFallbackFactory.class)

public interface RemoteProviderService {

    @GetMapping("/provider/echo/{string}")
    String echoMessage(@PathVariable("string") String string);
}

当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用超时了怎么办?
一般服务消费端会给出具体的容错方案
在Feign应用中通过FallbackFactory接口的实现类进行默认的相关处理

  1. 定义FallbackFactory接口的实现
package com.jt.service.factory;

import com.jt.service.RemoteProviderService;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

//基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题

@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {

	//此方法会在RemoteProviderService接口服务调用时,出现异常后执行,throwable 用于接收异常

    @Override
    public RemoteProviderService create(Throwable throwable) {
        return new RemoteProviderService() {
            @Override
            public String echoMessage(String string) {
                return "服务超时...";
            }
        };
    }
}
  1. 在Feign访问接口中应用FallbackFactory对象
@FeignClient(value = "sca-provider",contextId = "remoteProviderService",
        fallbackFactory = ProviderFallbackFactory.class)

public interface RemoteProviderService {

    @GetMapping("/provider/echo/{string}")
    String echoMessage(@PathVariable("string") String string);
}
  1. 在配置文件中添加配置,启动Feign方式调用时的服务中断处理机制
feign:  
  hystrix:
    enabled: true #默认值为false
  1. 在服务提供方对应的调用方法中添加Thread.sleep(5000)模拟耗时操作,然后启动服务访问进行测试

Feign调用过程分析

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

小结

  • 为什么使用feign?(基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用)
  • @FeignClient注解的作用是什么?(告诉Feign Starter,在启动项目时,为此注解的描述的接口创建实现类-代理类)
  • Feign方式的调用,底层负载均衡是如何实现的?(Ribbon)
  • @EnableFeignClients 注解的作用是什么?(描述配置类,例如启动类)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值