SpringBoot+Zookeeper集群+Nginx反向代理+Dubbo分布式托管(提供者、消费者)
- 一、软件架构和微服务需求
- 二、搭建Zookeeper注册中心集群服务
- 三、启动Dubbo管理控制台可视化界面
- 四、创建服务提供者集群服务(三个)
- 五、创建服务消费者集群服务(四个)
-
- 5.1、新建SpringBoot_dubbo_consumer项目
- 5.2、引入pom.xml坐标依赖文件
- 5.3、新建包com.ebuy.service,在此包下新建HelloService接口,与提供者接口保持一致
- 5.4、新建包com.ebuy.controller,在此包下新建HelloController类
- 5.5、核心配置文件(使用application.yml树状结构)
- 5.6、启动第一台consumer-2011服务消费者
- 5.7、启动第一台consumer-2012服务消费者
- 5.8、启动第一台consumer-2013服务消费者
- 5.9、启动第一台consumer-2014服务消费者
- 六、使用SwitchHosts工具模拟绑定域名
- 七、使用Nginx反向代理(消费者)
- 八、通过nginx反向代理,请求消费者,通过消费者访问提供者
- 九、解决Dubbo内置负载均衡策略问题
一、软件架构和微服务需求
1.1、微服务需求
我们知道软件架构的演化过程,从单体架构,到垂直架构,再到SOA架构,最后到现在较流行的微服务架构。每个架构都有各自的优缺点,随着架构体系的演化和现如今软件整体架构的需求在不断的提高,微服务之所以比较流行就是因为它将系统服务层完全独立出来,抽取为一个一个的服务;微服务架构采用去中心化思想,服务之间采用restful等轻量协议通信,相比ESB更轻量。因为创建一个系统要考虑到全面服务,总体来说要能做到软件架构的三要素:高可用、高性能、高并发。如果一个系统做不到这三个要素,那就不能称为一个好的软件系。
本文我们着重讲解,使用SpringBoot项目整合Zookeeper注册中心,Nginx反向代理和Dubbo远程代理分布式托管,最后实现系统的高可用、高并发和高性能,其核心就是使所有服务器面对海量访问能够实现负载均衡。
1.2、框架选择
- SpringBoot是当前比较流行的框架,其底层集成了Spring、mybatis、hibernate等多种优秀框架,这个框架的好用程度就不多说了,用起来可以说是朗朗上手,带劲。
- Nginx是一 个轻量级/高性能的反向代理Web服务器,而且Nginx支持跨平台、配置简单、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,它能够实现非常高效的反向代理和负载平衡。
- Dubbo是一款微服务开发框架,它提供了RPC通信与微服务治理两大关键能力。这意味着,使用Dubbo开发的微服务,将具备相互之间的远程发现与通信能力,同时利用Dubbo提供的丰富服务治理能力,可以实现诸如服务实现、负载均衡、流量调度等服务治理诉求。同时Dubbo是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。
- Zookeeper是Apache Hadoop的子项目,是一个树型的目录服务,支持变更推送,适合作为Dubbo服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
- 我们最终要实现的效果如下图(让其Dubbo分布式中的服务器全部使用集群,实现负载均衡):
1.3、集群分布(下面为此图实战演示)
简单集成应用可以详细观看分布式与微服务系列(二)、分布式RPC框架Apache Dubbo服务。
二、搭建Zookeeper注册中心集群服务
因为本次测试是在一台电脑上做测试,所以暂不使用Linux虚拟机启动Zookeeper集群服务了,怕到时候电脑啊承受不住,挂掉。我们就先在Windows下使用Zookeeper测试即可。
2.1、配置三台zookeeper注册中心服务器(数量可选)
2.2、修改三台zookeeper配置信息
修改zoo.cfg配置文件信息:
这三行代码(不要修改)依次粘到后两台zookeeper配置文件中,别忘了更改端口号依次为2082、2083:
server.1=127.0.0.1:2801:3801
server.2=127.0.0.1:2802:3802
server.3=127.0.0.1:2803:3803
2.3、配置zookeeper集群区分id
2.4、启动zookeeper集群服务
在启动过程中,发现第一台有报错,莫要慌,不要紧,因为在第一台启动的时候会自动寻找灵台两台zokkeeper,找不到你说急不急,所以就报错了,等三台zookeeper都启动成功了,就不会报错了!
如果实在不放心,可以连接zookeeper自带的客户端,查看下当前节点是否已创建!
出现上述节点就说明zookeeper集群此时已经启动成功了,最后一个客户端窗口可以关闭,但是上述仨窗口放着,可千万别关哈!!!
三、启动Dubbo管理控制台可视化界面
我们人如果想要知道服务提供者和消费者是是否成功注册和订阅到zookeeper注册中心,Dubbo官方为我们提供了一个Dubbo管理控制台可视化界面。
启动Dubbo控制台界面有两种方式,详情可以查看我的上篇文章分布式与微服务系列(二)、分布式RPC框架Apache Dubbo服务,第四点有详细介绍如何使用,此处我们就简单介绍下jar包形式
启动Dubbo可视化管理控制台,另一个是war包形式
。
3.1、修改dubbo-admin的配置文件
3.2、打包dubbo-admin项目
在打开的DOS命令窗口中执行命令mvn clean package
,先清除一下,然后打包项目,第一次执行可能会有点慢,大概一分钟左右,稍安勿躁:
打包完成出现target目录:
进入target目录:
3.3、运行dubbo-admin-0.0.1-SNAPSHOT.jar
包
首先你要确保你的此jar
包所在的路径没有中文,你就可以直接在打开DOS命令窗口,切换到此路径下,执行命令java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
即可运行;如果有中文,将此jar包直接拷贝到另外一个没有中文的目录下,再执行命令:
如果觉得每次启动打开DOS窗口比较麻烦,那就jar包所在目录下创建一个批处理:
如果你得jar包运行出现错误,那就是你上述的配置或路径写错了,仔细检查一遍按照我上述的步骤重新打包很快的。
启动成功,然后我们就可以在浏览器地址栏输入localhost:7001
直接访问了
四、创建服务提供者集群服务(三个)
4.1、新建SpringBoot_dubbo_provider项目
4.2、引入pom.xml坐标依赖文件
<!--springboot启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--springboot web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 测试启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--dubbo与springboot集成启动器-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.6</version>
</dependency>
<!--dubbo与注册中心zookeeper整合包-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.6</version>
<exclusions>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--实现自动化创建节点路径的父节点-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
4.3、新建包com.ebuy.service,在此包下新建HelloService接口
package com.ebuy.service;
public interface HelloService {
public String sayHello(String name);
}
4.4、新建包com.ebuy.service.impl,在此包下新建接口HelloServiceImpl实现类
package com.ebuy.service.impl;
import com.ebuy.service.HelloService;
import org.apache.dubbo.config.annotation.Service;
@Service(loadbalance = "roundrobin",interfaceClass = HelloService.class)
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name)
{
return "hello---"+name;
}
}
上述
loadbalance=“roundrobin”
指的是用户通过消费者服务,访问提供者时,按照轮询的负载均衡策略访问每一台服务提供者;
random
:默认就是随机策略的;(推荐)roundrobin
:依次轮询;(推荐)leastactive
:最少活跃调用数(权重)活跃数指调用前后计数差,优先调用高的,相同活跃数的随机。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大;consistenthash
:一致性策略,同参数的请求一定分发到同一个 Provider 如果需要某一类请求都到一个节点,那么可以使用一致性 Hash 策略。
注意:
@Service注解是org.apache.dubbo.config.annotation.Service
包下的,不是spring包下的。
4.5、核心配置文件(使用application.yml树状结构)
server:
port: 8081 #服务器端口号
dubbo:
application:
name: springboot_provider #提供者应用名
registry:
address: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 #连接注册中心集群
protocol: zookeeper #dubbo采用的注册中心协议
protocol: #连接注册中心协议
port: 20881 #dubbo端口号为20880,此处我们一会为了做集群区分,先改成20881
name: dubbo
config-center: #配置中心
timeout: 25000 #最大超时时间
scan:
base-packages: com.ebuy.service.impl #扫描哪个包下提供的服务
4.6、启动第一台provider-8081提供者服务
在maven webapps项目下,可以直接使用tomcat7插件,只需修改端口号,就可以启动多台服务,在SpringBoot项目下,当然也可以启动多台服务,如下所示:
进入配置中心:
启动之前,确认服务器端口号和dubbo端口号,以及提供服务方法的返回值:
4.7、启动第二台provider-8082提供者服务
修改application.yml配置文件中的端口号,两处port:
4.8、启动第三台provider-8083提供者服务
修改application.yml配置文件中的端口号,两处port:
此时三台服务提供者已经全部启动,但是是否成功注册到zookeeper中心了,我们可以通过上述dubbo-admin管理控制台,查看:
到这,我们就可以很安心的往下接着走下去了,三个服务者已经注册到registory服务中心了。
五、创建服务消费者集群服务(四个)
5.1、新建SpringBoot_dubbo_consumer项目
5.2、引入pom.xml坐标依赖文件
<!--springboot启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--springboot web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 测试启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--dubbo与springboot集成启动器-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.6</version>
</dependency>
<!--dubbo与注册中心zookeeper整合包-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.6</version>
<exclusions>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--实现自动化创建节点路径的父节点-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- 实现对 Spring Data Redis 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<!-- 去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
5.3、新建包com.ebuy.service,在此包下新建HelloService接口,与提供者接口保持一致
package com.ebuy.service;
public interface HelloService {
public String sayHello(String name);
}
5.4、新建包com.ebuy.controller,在此包下新建HelloController类
package com.ebuy.controller;
import com.ebuy.service.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/demo")
public class HelloController {
@Reference
HelloService helloService;
int port=2011;
@RequestMapping("/hello")
@ResponseBody
public String getName(String name){
//远程调用,获取响应结果
String result = helloService.sayHello(name);
System.out.println("consumer-" + port + "\t" + result);
return result;
}
}
注意:
@Reference
HelloService helloService;
此处的HelloService访问的是远程的Provider所提供的服务,所有使用@Autowire
注解是加载不到的,要使用@Reference
注解调用远程提供者发布的@Service
注解标注的服务注册接口。
5.5、核心配置文件(使用application.yml树状结构)
server:
port: 2011 #服务器端口号
dubbo:
application:
name: springboot_consumer #消费者服务名称
registry: #注册中心
address: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 #zookeeper集群
protocol: zookeeper #dubbo采用zookeeper注册中心协议
consumer: #消费者节点
check: false #启动时不用检测提供者是否已经注册
config-center:
timeout: 25000 #超时时间 单位秒
在启动消费者前,要注意一点,因为SpringBoot内部机制会自动加载DataSource数据源和Redis相关配置,即使你没有配任何置数据库相关配置信息,它内部也会默认加载,这个时候我们启动消费者服务就会出现如下错误:
Error creating bean with name ‘enableRedisKeyspaceNotificationsInitializer‘
这个时候如果你配置了数据库和redis相关配置,暂时先全部注释掉,然后在消费者主入口类的@SpringBootApplication排除运行期间不包括的服务,如下:
package com.ebuy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, RedisAutoConfiguration.class})
public class SpringbootDubboConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDubboConsumerApplication.class, args);
}
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, RedisAutoConfiguration.class})
,exclude就代表不包括,排除的意思,这个时候再去逐个启动消费者服务就可以了。
5.6、启动第一台consumer-2011服务消费者
5.7、启动第一台consumer-2012服务消费者
5.8、启动第一台consumer-2013服务消费者
5.9、启动第一台consumer-2014服务消费者
六、使用SwitchHosts工具模拟绑定域名
6.1、修改主机配置文件hosts的只读属性
6.2、以管理员身份运行SwitchHosts工具,新建域名(自定义)
七、使用Nginx反向代理(消费者)
7.1、nginx反向代理作用
上述我们开启了四台消费者服务,那么我们怎么知道每次访问的是哪一台服务消费者,既然我们配置了四台就要实现集群的机制,那么怎么实现?
这个时候Nginx作用就来了,Nginx反向代理可以实现负载均衡,也即是我们只需在Nginx的配置文件中配置需要通过的消费者服务,nginx内部机制算法会自动帮我们实现负载均衡,配置如下:
7.2、修改nginx配置文件
7.3、启动nginx
nginx启动之后,此时我们就可以通过www.ebuy.com
域名进行测试访问了!直接访问应该会进入nginx的内置默认页面。
八、通过nginx反向代理,请求消费者,通过消费者访问提供者
在地址栏输入http://www.ebuy.com/demo/hello?name=jason
8.1、第一次请求
8.2、第二次请求
8.3、第三次请求
8.4、第四次请求
由上述测试可以看出,nginx反向代理在我们第一次请求会随机帮我们选一台消费者,然后后续请求就会轮询访问消费者,这样我们就实现了服务器的负载均衡,避免了当访问量巨大时导致某一台服务器超负载的情况。
九、解决Dubbo内置负载均衡策略问题
-
细心的伙子应该可以看出上述的四次测试中,消费者使用Nginx的反向代理实现了第一次随机访问,后续是依次轮询的负载均衡策略;
-
然而but提供者却没有真正实现我们所谓的
loadbalance="roundrobin"
轮询负载均衡策略,第一次8083、第二次和第三次都是8081,第四次才是8082,这显然不符合我们所期望的轮询机制,为什么会出现这样的情况呢?
9.1、Dubbo 的内置负载均衡策略(四种)
- Random(默认是随机负载)
- 随机访问集群中节点。访问概率和权重有关。是 Dubbo 的默认负载均衡策略。
权重(weight)
:
占有比例。集群中每个项目部署的服务器的性能可能是不同,性能好的服务器权重应该高一些。
- RoundRobin(下面着重讲解)
轮询。访问频率和
权重(weight)
有关。 - LeastActive
最少活跃调用数,相同活跃数的随机。如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给不活跃的性能差的机器分配更少的请求
- ConsistentHash
一致性 Hash 算法,相同参数的请求一定分发到同一个 Provider 如果需要某一类请求都到一个节点,那么可以使用一致性 Hash 策略。
9.2、什么是轮询策略?
-
所谓轮询是指将请求轮流分配为每台提供者服务器,举个栗子:假设我们有三台服务器A、B、C。我们将第一个请求分配给服务器A,第二个请求分配给服务器B,第三个请求分配给服务器C,第四个请求分配给服务器A,也即是当所有服务器请求一遍后,再次请求将会从第一台服务器轮流请求,这个过程就叫做
轮询
。 -
轮询负载均衡策略(并不完全是轮询的),不可忽略的一点是,每台服务器之间多少都会存在一些性能上的差异,如果我们将等量的请求分配给某一台性能较差的服务器,由于这台服务器性能较差,那么再次发出请求,请求还是会在这台性能较差的服务器上出现,很明显我们上述出现的情况就是因为如此。尽管我们上述所有的服务器是在同一台电脑上启动的(为了模拟),其实这也验证了即使是同一台电脑,自身的服务器也会对不同的tomcat进程分配不同的进程占用时长,所以就导致了上述第二次和第三次请求访问的都是8081的情况!!!说到这应该可以理解了吧!
-
要知道一般情况下,每个服务都会部署到不同的电脑上,一般很少出现连续性能问题的这种情况。
9.3、如何解决不完全轮询策略(忽略性能差异)?
-
想要解决上述问题,这个时候我们就需要对轮询过程进行加权(
weight
),以调控每台服务器的负载,经过加权后,每台服务器能够得到的请求数比例,接近或等于它们自身的权重比。 -
注解方式
@Service(loadbalance = "roundrobin",weight = "权重数")
-
配置文件方式
dubbo: provider: loadbalance: roundrobin #轮询负载均衡策略 weight: "权重数" #针对每一台服务提供者所在服务器的性能进行权重数配置(一般情况下默认都是100)
9.4、配置负载均衡策略
配置负载均衡策略有很多种方式:
9.4.1、第一种在消费者注解方式配置
@Controller
@RequestMapping("/demo")
public class HelloController {
//表示在当前业务层中所有方法都会轮询访问所有provider集群
@Reference(loadbalance = "roundrobin")
HelloService helloService;
}
9.4.2、第二种在消费者配置文件yml或者properties中配置
dubbo:
consumer:
loadbalance: roundrobiin #此处代表针对当前服务的所有controller层都会轮询的访问provider集群
9.4.3、第三种在提供者业务层注解方式配置
//此处配置,只在provider集群中所有服务中的(当前业务层)对外提供轮询服务
@Service(loadbalance = "roundrobin")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name)
{
return "hello---8081----"+name;
}
}
9.4.4、第四种在提供者配置文件方式配置
dubbo:
provider:
loadbalance: roundrobin #针对provider集群中所有服务的所有业务层对外提供轮询负载均衡策略
其实上述四种配置中,不推荐在消费者一方使用负载均衡策略配置,因为提供者配置的是集群方式,该怎么提供对外服务,很显然在提供者方配置最为合适,也比较高效。
一起学编程,让生活更随和!如果你觉得是个同道中人,欢迎关注博主公众号:【随和的皮蛋桑】。专注于Java基础、进阶、面试以及计算机基础知识分享🐳。偶尔认知思考、日常水文🐌。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/189427.html