分布式 Dubbo + Zookeeper
什么是RPC
通俗理解
专业解释
RPC两个核心模块:通讯,序列化。
什么是Dubbo
调用关系
什么是zookeeper
服务或分布式的注册中心
面试问题
微服务优缺点
微服务技术栈有哪些
SpringCloud与SpringBoot关系
微服务基础架构
Spring与Dubbo的区别
SpringCloud版本号
SpringCloud
准备环境及基本调用
父项目pom.xml准备
<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层的数据进而显现给前端或交给视图层
学习源码
Eureka
]
原理及架构
步骤
开启注解
请在每个服务提供者及注册中心集群中的主启动类加入此注解
@EnableEurekaServer //EnableEurekaServer 服务端的启动类,可以接收别人注册进来 请在主启动类加入此注解
导包
配置yaml
服务提供者(service)yaml配置
同样也是将serviceurl地址复写
配置服务提供者的信息
保护机制
集群环境部署
请注意一下此时试验机已经修改本地host的文件
集群单个组件的yaml配置
要配置多个注册中心只需要在每一个后面加上逗号即可
对比Zookeeper
CAP原则
负载均衡及Ribbon
服务消费者
依赖
<!--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只能填写服务名来实现服务的负载均衡)
服务提供者
项目现在模型
- 复制db数据库,分别为db02,db03
- 复制原服务提供者模块的xml依赖
- 复制原服务提供者模块的yaml配置文件,并把port,name,数据库连接,依次改为其对应的名字,端口,及数据库表名
- 复制原服务提供者模块的controller层enty层service层
- 复制原服务提供者模块的主启动类,将原名字改为所在模块名字及类名即可
自定义负载均衡算法(客户端)
根据源码得出,只需复写IRule接口下的实现方法即可自定义算法
该接口及实现方法
新建组件文件夹com.[作者/公司].myrule(在本模块)
新建类,类名随意,并加注释Configuration
在这个类中新增该方法,以实现负载均衡的随机算法
记得在主启动类上加入该注解的形参参数,以让SpringBoot扫描到该项目包外的配置文件
// 在微服务启动的时候就能去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MengRule.class)
自定义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
- 实现接口(FeignClient的Value为要负载均衡的微服务名字)
- 实现类
个人觉得还是面向接口及注解比较好一点,当然resultTemplate也凑乎
开启Feign
- 主启动类
微服务所面临的问题
什么是Hystrix
服务熔断
步骤
- 复制一个项目并导入Hystrix依赖
-
在yaml中修改该项目名称
-
把原controller接口方法干掉,并写入以下方法
请记住在接口逻辑或者转入dto的时候一定要进行数据检查,以防出现或返回未知错误
-
新增一个熔断方法
-
在逻辑处理接口上添加@HystrixCommand
注解源码
我们可以看到有关线程的异常,回调异常,默认异常等
-
那我们在注解中加入异常回调方法,并添加之前创建的熔断方法
-
在主启动类,来启动Hystrix的熔断的机制(请记住不要导入@EnableHystrix)
添加@EnableCircuitBreaker(断路器/保险丝)
Eureka显示服务IP
服务降级
由于随着时间或地点等其他因素,我们的一些服务端后承载不同的流量与请求,这个时候容易出现一个服务端承载过大的问题(容易导致该服务端崩坏)(自我吐槽:话说这东西不是负载均衡也能做的吗?)
这个时候我们需要在客户端进行优化,来对其他承载较低的服务端进行提权或提级,这个时候就需要Hystrix的服务降级或提及的操作
步骤
-
我们在网关或者服务消费者项目中新建一个服务接口来继承一个接口FallbackFactory并重写其方法
由于我们这次要处理一个类,所以我们这次需要工厂来生产该服务的多线程关系
而这个Object类型代表着你要处理什么服务
我们需要把这个Object类型改成自己需要的类型,return服务对应接口实例
-
我们在服务对应接口的@FeignClient中加入fallbackFactory属性并将其赋值为刚自定义的FallbackFactory.class文件
-
由于我们是在Feign中进行降级操作,所以我们要在Feign服务项目中进行配置
在其主配置yaml文件中配置Feign.Hystrix并开启
服务熔断与服务降级的区别
流监控
正如其名简而言之就是负责监控服务流变化的
新建一个类似原消费者项目的监控器项目
导入原消费者项目的依赖
新增以下依赖
新增该项目主配置文件,内容如下
新建项目文件目录,并新增一个主启动类方法,内容如下类似
请记住要在每一个服务提供者引入监控依赖
然后就可以启动监控类项目了
如果我们要监控生产者服务项目,我们需要在生产者每一个主启动类中加入servletBean配置如下
我们需要将监控对象加入到Hystrix Dashboard中
监控图像解释
路由网关Zuul
新建项目
导入Hystrix doshboard依赖
引入zuul依赖
新建该项目主配置yaml,内容如下
以上域名均写入进host文件中
编写主启动类
@EnableZuulProxy为开启Zuul代理(请记住Zuul在此时不是一个服务)
自定义项目域名后缀
忽略之前的网关
当然我们在项目部署后要隐藏全部项目路径,可以用通配符
设置公共域名的后缀
分布式配置
有关git的操作我这里就不赘述了,可以参考我之前的git小技巧那篇文章
新建一个SpringCloudConfig项目,并编写applicationyaml文件并推送到远程仓库
新建一个服务端项目,导入依赖,编写配置,编写主启动类
依赖如下(请不要加入erueka依赖)
配置如下
主启动类开启配置,加入一下注解
这时我们启动该服务类,通过url可以获取到远程仓库的yaml配置文件
客户端访问服务端过程
在配置项目中新建一个yaml为config-server.yaml 内容为服务端配置文件,示例如下
推送到远程仓库
新建一个client项目:依赖,配置,主启动类
依赖
配置:bootstrap.yaml与application.yaml的区别
服务实例,请自行体会各个含义,这里没有配置端口号,请自行配置端口号
新建客户端controller接口,内容如下
我们通过调用接口,即可获取到来自server从远程仓库获取到的配置文件
示例
我们将之前的eureka环境配置进行示例
新建config-eureka.yaml配置
新建config-dept.yaml(把之前的服务提供者yaml配置文件拿来)
Eureka的配置不用更改
新增依赖
新增bootstrap.yaml
application.yaml
复制一个服务提供者项目
将其application.yaml内容改为
新建boostrap.yaml,内容如下
更改了配置之后我们需要重新部署
以上为SpringCloud Netflix及Dubbo + Zookeeper分布式入门,后续可能会更新SpringCloudAlibaba及分布式进阶(虽然我觉得工作中做微服务的职位至少需要个架构师,尽管分布式入门很简单,但是精通很难)
Q.E.D.