Skip to content

Latest commit

 

History

History
448 lines (319 loc) · 17.1 KB

09.微服务治理之ApacheDubbo的基本认识.md

File metadata and controls

448 lines (319 loc) · 17.1 KB

负载均衡

负载均衡的背景

到目前为止,dubbo 集成 zookeeper 解决了服务注册以及服务动态感知的问题。那么 当服务端存在多个节点的集群时,zookeeper 上会维护不同集群节点,对于客户端而 言,他需要一种负载均衡机制来实现目标服务的请求负载。通过负载均衡,可以让每 个服务器节点获得适合自己处理能力的负载。

负载均衡可以分为软件负载和硬件负载,在实际开发中,我们基础软件负载比较多, 比如 nginx,硬件负载现在用得比较少而且有专门的人来维护。

Dubbo 里面默认就集成了负载均衡的算法和实现,默认提供了 4 中负载均衡实现。

Dubbo中负载均衡的应用

配置的属性名称: roundrobin/random/leastactive/ consistenthash

<dubbo:service interface="..." loadbalance="roundrobin" />
<dubbo:reference interface="..." loadbalance="roundrobin" />

可以在服务端配置,也可以在客户端配置。 如果是基于注解,配置如下 @Service(loadbalance = "roundrobin") public class HelloServiceImpl implements IHelloService{

@Reference(loadbalance = "random") IHelloService helloService;

Dubbo 负载均衡算法

RandomLoadBalance

权重随机算法,根据权重值进行随机负载

它的算法思想很简单。假设我们有一组服务器 servers = [A, B, C],他们对应的权重为 weights = [5, 3, 2],权重总和为 10。现在把这些权重值平铺在一维坐标值上,[0, 5) 区 间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。接下来通过 随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个 区间上。比如数字 3 会落到服务器 A 对应的区间上,此时返回服务器 A 即可。权重 越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会 有更大的概率落到此区间内。只要随机数生成器产生的随机数分布性很好,在经过多 次选择后,每个服务器被选中的次数比例接近其权重比例

LeastActiveLoadBalance

最少活跃调用数算法,活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求这个是比较科学的负载均衡算法。

每个服务提供者对应一个活跃数 active。初始情况下,所有服务提供者活跃数均为 0。 每收到一个请求,活跃数加 1,完成请求后则将活跃数减 1。在服务运行一段时间后, 性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服 务提供者能够优先获取到新的服务请求

ConsistentHashLoadBalance

hash 一致性算法,相同参数的请求总是发到同一提供者 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者, 不会引起剧烈变动

RoundRobinLoadBalance

加权轮询算法 所谓轮询是指将请求轮流分配给每台服务器。举个例子,我们有三台服务器 A、B、C。 我们将第一个请求分配给服务器 A,第二个请求分配给服务器 B,第三个请求分配给 服务器 C,第四个请求再次分配给服务器 A。这个过程就叫做轮询。轮询是一种无状 态负载均衡算法,实现简单,适用于每台服务器性能相近的场景下。但现实情况下, 我们并不能保证每台服务器性能均相近。如果我们将等量的请求分配给性能较差的服 务器,这显然是不合理的。因此,这个时候我们需要对轮询过程进行加权,以调控每 台服务器的负载。经过加权后,每台服务器能够得到的请求数比例,接近或等于他们 的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在 8 次请求中,服务器 A 将 收到其中的 5 次请求,服务器 B 会收到其中的 2 次请求,服务器 C 则收到其中的 1次请求

一致性 hash 算法原理

Dubbo的基本使用

创建项目client和server

添加 jar 包依赖

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.2</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.1</version>
</dependency>

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-simple</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.4.19.v20190610</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
    <version>9.4.19.v20190610</version>
</dependency>
<!--JAX-RS  CXF / Jersey /RESTEasy-->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>3.8.1.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-client</artifactId>
    <version>4.0.0.Final</version>
</dependency>

定义接口以及实现

@Path("/pay")
public interface IPayService {


    //暴露出去的服务
    //完成支付的功能
    @GET
    @Path("/{info}")
    String pay(@PathParam("info") String info);
}
public class PayServiceImpl implements IPayService{

    //执行支付的服务
    @Override
    public String pay(String info) {
        System.out.println("execute pay:"+info);
        return "Hello Dubbo :"+info;
    }
}

创建配置文件发布服务

在 resources/META-INF/spring 下创建 application.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="pay-service"/>
    <!--配置注册中心-->
    <dubbo:registry address="N/A"/>

    <dubbo:service interface="cn.moremind.IPayService" ref="payService"/>
    <dubbo:service interface="cn.moremind.IQueryService" ref="queryService"/>

    <bean id="payService" class="cn.moremind.PayServiceImpl"/>
    <bean id="queryService" class="cn.moremind.QueryServiceImpl"/>

    <dubbo:protocol name="dubbo" port="20880"/>
</beans>

启动应用

public class App 
{
    public static void main( String[] args ) throws IOException {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/application.xml"});

        applicationContext.start();
        System.in.read();
    }
}

添加日志的支持

客户端配置

添加配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="order-service"/>
    <!--配置注册中心-->
    <dubbo:registry address="N/A"/>

    <dubbo:reference id="payService" interface="cn.moremind.IPayService"
    url="dubbo://127.0.0.1:20880/cn.moremind.IPayService"/>

</beans>

访问远程服务

public class App 
{
    public static void main( String[] args )
    {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext(new String[]{"application.xml"});

        IPayService payService = (IPayService) applicationContext.getBean("payService");
        String rs = payService.pay("Test");
        System.out.println(rs);

        //applicationContext.start();
        System.out.println( "Hello World!" );
    }
}

关于 DubboMain 启动的真相

public void start() {
    String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
    if (StringUtils.isEmpty(configPath)) {
        configPath = DEFAULT_SPRING_CONFIG;
    }
    context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false);
    context.refresh();
    context.start();
}

容器类型

Spring Container :自动加载 META-INF/spring 目录下的所有 Spring 配置。

logback Container :自动装配 logback 日志

Log4j Container:自动配置 log4j 的配置

Dubbo 提供了一个 Main.main 快速启动相应的容器,默认情况下,只会启 动 spring 容器

image-20220427233733074

原理分析

默认情况下,spring 容器,本质上,就是加在 spring ioc 容器,然后启动 一个 netty 服务实现服务的发布,所以并没有特别多的黑科技,下面是 spring 容器启动的代码

基于注册中心的 Dubbo 服务

使用 zookeeper 作为注册中心

Jar 包依赖

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
</dependency>

修改application.xml文件

<dubbo:registry address="zookeeper://127.0.0.1:2181"/>

如果是 zookeeper 集群,则配置的方式是 address="zookeeper://ip:host?backup=ip:host,ip:host"

客户端改造

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="order-service"/>
    <!--配置注册中心-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <dubbo:reference id="payService" interface="cn.moremind.IPayService"/>
</beans>

Dubbo 集成 Zookeeper 的实现原理

image-20220427233242519

dubbo 每次都要连 zookeeper?

答案很显然是通过缓存实现 在消费端的配置文件中指定如下路径.

<dubbo:registry id="zookeeper" address="zookeeper://192.168.13.102:2181" file="d:/dubbo�server" />

其他注册中心的实现,核心本质是一样的,都是为了管理服务地址,后续会 讲到 nacos;

多注册中心支持

Dubbo 中可以支持多注册中心,有的时候,客户端需要用调用的远程服务 不在同一个注册中心上,那么客户端就需要配置多个注册中心来访问。演示 一个简单的案例

配置多个注册中心

<dubbo:registry address="zookeeper://192.168.13.102:2181" id="registryCenter1"/>
<dubbo:registry address="zookeeper://192.168.13.102:2181" id="registryCenter2"/>

将服务注册到不同的注册中心

通过 registry 设置注册中心的 ID

<dubbo:service interface="com.gupaoedu.practice.LoginService" registry="registryCenter1" ref="loginService" />

消费端配置多个注册中心

实现的代码和服务端一样

注册中心的其他支持

1.当设置 时,记录失败注册和订阅请求, 后台定时重试

2.可通过 设置 zookeeper 登录信息

3.可通过 设置 zookeeper 的根节 点,默认使用 dubbo 作为 dubbo 服务注册的 namespace

Dubbo 仅仅是一个 RPC 框架?

Dubbo 从另一个方面来看也可以认为是一个服务治 理生态。从目前已经讲过的内容上可以看到。

1.Dubbo 可以支持市面上主流的注册中心

2.Dubbo 提供了 Container 的支持,默认提供了 3 种 container。我们可以 自行扩展

3.Dubbo 对于 RPC 通信协议的支持,不仅仅是原生的 Dubbo 协议,它还 围绕着 rmi、hessian、http、webservice、thrift、rest

有了多协议的支持,使得其他 rpc 框架的应用程序可以快速的切入到 dubbo 生态中。 同时,对于多协议的支持,使得不同应用场景的服务,可以选择合 适的协议来发布服务,并不一定要使用 dubbo 提供的长连接方式。

集成 Webservice 协议

jar包依赖

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-simple</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
</dependency>

修改 application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="pay-service"/>
    <!--配置注册中心-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <dubbo:service interface="cn.moremind.IPayService" ref="payService"/>
    <dubbo:service interface="cn.moremind.IQueryService" ref="queryService" protocol="dubbo,webservice"/>

    <bean id="payService" class="cn.moremind.PayServiceImpl"/>
    <bean id="queryService" class="cn.moremind.QueryServiceImpl"/>

    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="webservice" port="8080" server="jetty"/>
</beans>

添加多协议支持,一个服务可以发布多种协议的支持,也可以实现不同服务 发布不同的协议

启 动 服 务 之 后 , 可 以 使 用 :http://127.0.0.1:8099/cn.moremind.IQueryService?wsdl

客户端使用 webservice 请求服务

1.客户端的配置 jar 包依赖

2 配置多个协议支持,实现方式和服务端一致

Dubbo 对于 REST 协议的支持

我在用 REST 协议发布服务的时候,遇到 Dubbo 的一个坑, Dubbo 启动时 后写地方的服务直接被吞掉了,使得服务启动不了的同时,还看不到错误信 息。

(可以把 resteasy-client 包暂时不导入,看到效果) Dubbo 中的 REST(表述性资源转移)支持,是基于 JAX-RS2.0(Java API for RESTful Web Services)来实现的。

REST 是一种架构风格,简单来说就是对于 api 接口的约束,基于 URL 定位 资源,使用 http 动词(GET/POST/DELETE)来描述操作

JAX-RS 协议说明

REST 很早就提出来了,在早期开发人员为了实现 REST,会使用各种工具来 实现,比如 Servlets 就经常用来开发 RESTful 的程序。随着 REST 被越来越 多的开发人员采用,所以 JCP(Java community process)提出了 JAX-RS 规 范,并且提供了一种新的基于注解的方式来开发 RESTful 服务。有了这样的 一个规范,使得开发人员不需要关心通讯层的东西,只需要关注资源以以及 数据对象。

JAX-RS 规范的实现有:Apache CXF、Jersey(由 Sun 公司提供的 JAX-RS 的 参考实现)、RESTEasy( jboss 实现)等。

而 Dubbo 里面实现的 REST 就是基于 Jboss 提供的 RESTEasy 框架来实现的SpringMVC 中的 RESTful 实现我们用得比较多,它也是 JAX-RS 规范的一种 实现

jar包依赖

image-20220428000330109

添加新的协议支持

<dubbo:protocol name="rest" port="8888" server="jetty"/>

提供新的服务

@Path("/users"):指定访问 UserService 的 URL 相对路径是/users,即 http://localhost:8080/users

@Path("/register"):指定访问 registerUser()方法的 URL 相对路径是/register,

@Path("/pay")
public interface IPayService {


    //暴露出去的服务
    //完成支付的功能
    @GET
    @Path("/{info}")
    String pay(@PathParam("info") String info);
}

http://127.0.0.1:8888/pay/33