image-20220620054810703

image-20220620192832328

分布式 Dubbo + Zookeeper

什么是RPC

通俗理解

image-20220620073124541

专业解释

image-20220620073225017

RPC两个核心模块:通讯,序列化。

什么是Dubbo

image-20220620074158062

调用关系

image-20220620074946142

什么是zookeeper

服务或分布式的注册中心

面试问题

微服务优缺点

image-20220620194904702

image-20220620195151972

微服务技术栈有哪些

image-20220620195341549

SpringCloud与SpringBoot关系

image-20220620203606325

微服务基础架构

image-20220620204627797

Spring与Dubbo的区别

image-20220620204853089

image-20220621074035751

image-20220621074400983

SpringCloud版本号

image-20220621075428601

SpringCloud中文文档

SpringCloud

准备环境及基本调用

父项目pom.xml准备

image-20220621125918117

image-20220621130121981

image-20220621130205066

image-20220621130345887

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <junit.version>4.13</junit.version>
        <lombok.version>1.18.16</lombok.version>
        <log4j.version>1.2.17</log4j.version>
        <logback.version>1.2.4</logback.version>
</properties>


<dependencyManagement>
        <dependencies>
		<!--springcloud的依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
		<!--springboot的依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        <!--数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
		<!--数据源-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.24</version>
            </dependency>
		<!--springboot 启动器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!--logback-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

新建一个APIMaven子模块

在子模块中,引入该模块所需的依赖,如果父model已经配置了版本那么就不用继续指定版本了

我们也可以将服务端与消费者进行剥离操作,可以通过以下方式来通过http请求来获取service层的数据进而显现给前端或交给视图层

2022-06-21_161130

学习源码

Eureka

image-20220621161408467]

原理及架构

image-20220621162449141

image-20220621163158734

image-20220621163233926

步骤

image-20220621164223974

开启注解

请在每个服务提供者及注册中心集群中的主启动类加入此注解

@EnableEurekaServer //EnableEurekaServer 服务端的启动类,可以接收别人注册进来	请在主启动类加入此注解

导包

image-20220621163520535

配置yaml

2022-06-21_165154

服务提供者(service)yaml配置

image-20220621171222138

同样也是将serviceurl地址复写

配置服务提供者的信息

image-20220621181406105

保护机制

image-20220622081120129

集群环境部署

请注意一下此时试验机已经修改本地host的文件

集群单个组件的yaml配置

image-20220622090649370

要配置多个注册中心只需要在每一个后面加上逗号即可

对比Zookeeper

CAP原则

image-20220623081746307

image-20220623082120219

image-20220623082302485

image-20220623082330214

image-20220623082622130

负载均衡及Ribbon

image-20220623083220958

image-20220623085002620

服务消费者

依赖

image-20220623085117470

      <!--ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
	  <!--Eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

yaml配置 (服务消费者)模块

# Eureka配置
eureka:
  client:
    register-with-eureka: false #不向eureka中注册自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

主启动器

@EnableEurekaClient
// 在微服务启动的时候就能去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MengRule.class)

服务类方法上配置负载均衡

@LoadBalanced 	//ribbon负载均衡

修改controller接口中的URL变量(这里的URL只能填写服务名来实现服务的负载均衡)

image-20220623105007635

服务提供者

项目现在模型

image-20220623121125536

  • 复制db数据库,分别为db02,db03
  • 复制原服务提供者模块的xml依赖
  • 复制原服务提供者模块的yaml配置文件,并把port,name,数据库连接,依次改为其对应的名字,端口,及数据库表名
  • 复制原服务提供者模块的controller层enty层service层
  • 复制原服务提供者模块的主启动类,将原名字改为所在模块名字及类名即可

自定义负载均衡算法(客户端)

根据源码得出,只需复写IRule接口下的实现方法即可自定义算法

该接口及实现方法

image-20220623163114197

新建组件文件夹com.[作者/公司].myrule(在本模块)

新建类,类名随意,并加注释Configuration

在这个类中新增该方法,以实现负载均衡的随机算法

image-20220623205253372

记得在主启动类上加入该注解的形参参数,以让SpringBoot扫描到该项目包外的配置文件

// 在微服务启动的时候就能去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MengRule.class)

image-20220623211114566

自定义RandomRule算法

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class MengRandomRule extends AbstractLoadBalancerRule {
    public MengRandomRule() {
    }
    // 每个服务访问5次,然后换下一个服务(3个)
    // total= 0,默认为0,如果为5,我们换下一个服务
    // index=0,默认0,如果total=5,index+1
    private int total = 0;// 被调用的次数
    private int currentIndex = 0; // 当前是谁在提供服务

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;

            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers();// 获得活着的服务
                List<Server> allList = lb.getAllServers();// 获得全部的服务
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
				//int index = this.chooseRandomInt(serverCount);// 生成区间随机数
				//server = (Server)upList.get(index);// 从活着的服务随机获取一个

                //========================================================
                if(total<5){
                    server = upList.get(currentIndex);
                    total++;
                }else{
                    total=0;
                    currentIndex++;
                    if(currentIndex>upList.size()){
                        currentIndex = 0;
                    }
                    server = upList.get(currentIndex);//从活着的服务中获取指定的服务来进行操作
                }

                //========================================================
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

不要忘了在自己配置的生成负载均衡模板的方法中引入以上方法

以上源码来源于mengxiang的代码仓库请注意类名

服务端负载均衡Feign

image-20220623215859314

image-20220623231533108

  • 实现接口(FeignClient的Value为要负载均衡的微服务名字)

image-20220722045158522

  • 实现类

image-20220722045233288

个人觉得还是面向接口及注解比较好一点,当然resultTemplate也凑乎

开启Feign

  • 主启动类

image-20220722045615447

微服务所面临的问题

image-20220722182447632

什么是Hystrix

image-20220722182606454

image-20220722182631230

服务熔断

image-20220722183915327

步骤
  • 复制一个项目并导入Hystrix依赖

image-20220725015640969

  • 在yaml中修改该项目名称2022-07-25_020033

  • 把原controller接口方法干掉,并写入以下方法

    请记住在接口逻辑或者转入dto的时候一定要进行数据检查,以防出现或返回未知错误

    image-20220725020551466

  • 新增一个熔断方法

    image-20220725020925599

  • 在逻辑处理接口上添加@HystrixCommand

    注解源码

    image-20220725021131372

    我们可以看到有关线程的异常,回调异常,默认异常等

  • 那我们在注解中加入异常回调方法,并添加之前创建的熔断方法

    image-20220725021507374

  • 在主启动类,来启动Hystrix的熔断的机制(请记住不要导入@EnableHystrix)

    添加@EnableCircuitBreaker(断路器/保险丝)

    image-20220725021818997

Eureka显示服务IP

image-20220725041154405

服务降级

由于随着时间或地点等其他因素,我们的一些服务端后承载不同的流量与请求,这个时候容易出现一个服务端承载过大的问题(容易导致该服务端崩坏)(自我吐槽:话说这东西不是负载均衡也能做的吗?)

image-20220725041921738

这个时候我们需要在客户端进行优化,来对其他承载较低的服务端进行提权或提级,这个时候就需要Hystrix的服务降级或提及的操作

步骤
  • 我们在网关或者服务消费者项目中新建一个服务接口来继承一个接口FallbackFactory并重写其方法

    image-20220725042616536

    由于我们这次要处理一个类,所以我们这次需要工厂来生产该服务的多线程关系

    而这个Object类型代表着你要处理什么服务

    我们需要把这个Object类型改成自己需要的类型,return服务对应接口实例

    image-20220725043159726

  • 我们在服务对应接口的@FeignClient中加入fallbackFactory属性并将其赋值为刚自定义的FallbackFactory.class文件

    image-20220725044206085

  • 由于我们是在Feign中进行降级操作,所以我们要在Feign服务项目中进行配置

    在其主配置yaml文件中配置Feign.Hystrix并开启

    image-20220725044513525

服务熔断与服务降级的区别

image-20220725051500202

流监控

正如其名简而言之就是负责监控服务流变化的

新建一个类似原消费者项目的监控器项目

导入原消费者项目的依赖

新增以下依赖

image-20220725060736111

新增该项目主配置文件,内容如下

image-20220725060934087

新建项目文件目录,并新增一个主启动类方法,内容如下类似

image-20220725061119923

请记住要在每一个服务提供者引入监控依赖

image-20220725061353172

然后就可以启动监控类项目了

如果我们要监控生产者服务项目,我们需要在生产者每一个主启动类中加入servletBean配置如下

image-20220725062229936

我们需要将监控对象加入到Hystrix Dashboard中

image-20220725064612607

image-20220725064709672

监控图像解释

image-20220725064758684

image-20220725064854243

image-20220725064937520

路由网关Zuul

image-20220725065525368

新建项目

导入Hystrix doshboard依赖

引入zuul依赖

image-20220725083355172

新建该项目主配置yaml,内容如下

image-20220725083913758

以上域名均写入进host文件中

编写主启动类

image-20220725084251369

@EnableZuulProxy为开启Zuul代理(请记住Zuul在此时不是一个服务)

自定义项目域名后缀

image-20220725085525042

忽略之前的网关

image-20220725085755929

当然我们在项目部署后要隐藏全部项目路径,可以用通配符

image-20220725090053982

设置公共域名的后缀

分布式配置

image-20220725090418185

image-20220725091611752

有关git的操作我这里就不赘述了,可以参考我之前的git小技巧那篇文章

新建一个SpringCloudConfig项目,并编写applicationyaml文件并推送到远程仓库

新建一个服务端项目,导入依赖,编写配置,编写主启动类

依赖如下(请不要加入erueka依赖)

image-20220725092908355

配置如下

image-20220725093033277

主启动类开启配置,加入一下注解

image-20220725093149297

这时我们启动该服务类,通过url可以获取到远程仓库的yaml配置文件

客户端访问服务端过程

在配置项目中新建一个yaml为config-server.yaml 内容为服务端配置文件,示例如下

image-20220725094137125

推送到远程仓库

新建一个client项目:依赖,配置,主启动类

依赖

image-20220725094406707

配置:bootstrap.yaml与application.yaml的区别

image-20220725094614985

服务实例,请自行体会各个含义,这里没有配置端口号,请自行配置端口号

image-20220725094952619

image-20220725095015630

新建客户端controller接口,内容如下

image-20220725095217332

我们通过调用接口,即可获取到来自server从远程仓库获取到的配置文件

示例

我们将之前的eureka环境配置进行示例

新建config-eureka.yaml配置

image-20220725100234625


image-20220725100102167


新建config-dept.yaml(把之前的服务提供者yaml配置文件拿来)

image-20220725100654471

image-20220725100713499

Eureka的配置不用更改


新增依赖

image-20220725101221965


新增bootstrap.yaml

image-20220725101322362

application.yaml

image-20220725101347067

复制一个服务提供者项目

将其application.yaml内容改为

image-20220725102421953

新建boostrap.yaml,内容如下

image-20220725102452160

更改了配置之后我们需要重新部署

以上为SpringCloud Netflix及Dubbo + Zookeeper分布式入门,后续可能会更新SpringCloudAlibaba及分布式进阶(虽然我觉得工作中做微服务的职位至少需要个架构师,尽管分布式入门很简单,但是精通很难)

Q.E.D.


一个平凡人的追梦之旅