zoukankan      html  css  js  c++  java
  • 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南。

    基本介绍

    Forest是一套基于java开发的RPC框架,除了常规的点对点调用外,Motan还提供服务治理功能,包括服务节点的自动发现、摘除、高可用和负载均衡等。

    架构概述

    Forest中分为服务提供方(RPC Server),服务调用方(RPC Client)和服务注册中心(Registry)三个角色。

    Server提供服务,向Registry注册自身服务,并向注册中心定期发送心跳汇报状态; Client使用服务,需要向注册中心订阅RPC服务,Client根据Registry返回的服务列表,与具体的Sever建立连接,并进行RPC调用。 当Server发生变更时,Registry会同步变更,Client感知后会对本地的服务列表作相应调整。 三者的交互关系如下图:

    模块概述

    forest代码层面包含三个module

    forest-common:一些基础的功用的功能

    forest-rpc:和rpc相关的核心功能实现,

    forest-demo:一些forest示例的推荐示例

    forest从设计的分层包含以下基层,分层设计图如下:

    配置概述

    Forest体功能了灵活的功能,可以基于注解来配置,也可以通过spring xml来覆盖这些配置,当然你可以可以代码里面自己构造。

    配置优先级如下:注解默认配置<spring xml配置(或者代码构造)

    基于注解配置

    1.interface api的配置

    a. inteface api类上可以通过@ServiceProvider注解来提供服务,@ServiceProvider用于接口类上,可以配置参数如下:

    public @interface ServiceProvider {
        String serviceName() default "";//服务名称,隔离级别是以一个服务为粒度
        HaStrategyType haStrategyType() default HaStrategyType.FAIL_FAST;
        LoadBalanceType loadBalanceType() default LoadBalanceType.RANDOM;
        String hashKey() default "";// 仅当使用hash策略时候使用
        int connectionTimeout() default Constants.CONNECTION_TIMEOUT;
    }
    

      

    接口层提供给调用方,调用方的client可以继承interface上的@ServiceProvider配置作为默认配置。

    b. interface method可以通过@MethodProvider注解来提供具体的业务服务,参数配置如下:

    public @interface MethodProvider {
        String methodName() default "";
        SerializeType serializeType() default SerializeType.Kyro;
        CompressType compressType() default CompressType.None;
        int timeout() default Constants.DEFAULT_TIMEOUT; // 客户端超时时间
    }
    

      

    你可以针对不同的业务方法指定不同的序列化方式或者 压缩方式。

    2.server端interface impl的配置

    a. servcieImpl可以通过@ServiceExport注解来发布服务,interface impl只有加上了@ServiceExport才会发布服务。 如果不指定port,则使用默认的port,如需和其他业务隔离,建议使用不同的port来发布。

    public @interface ServiceExport {
        int port() default Constants.DEF_PORT;
    }
    

      

    当然你也可以加上@Path来暴露restful的path,Forest支持基于jersey的restful服务。

    b. servcieImpl可以通过@MethodExport注解来发布方法

    serviceImpl的方法上面同时可以支持其他的注解,类似如下:

     /**
         * 支持jersey,可以通过配置打开,同时启动http服务
         *
         * @param str
         * @return
         */
        @Path("/hello/{str}")
        @GET
        @Produces("text/plain")
    
        @MethodExport
        @Rate(2)
        @Override
        public String say(@PathParam("str") String str) {
            return "say " + str;
        }
    

      

    基于spring xml配置

    对于客户端,是没有办法控制提供方提供接口层的注解配置,如果客户端想自定义配置,可以选择基于spring xml的配置来覆盖服务提供方推荐的默认配置。

    示例如下:

      <bean id="methodConfig" class="com.zhizus.forest.common.config.MethodConfig">
            <property name="compressType">
                <util:constant static-field="com.zhizus.forest.common.CompressType.None"/>
            </property>
            <property name="serializeType">
                <util:constant static-field="com.zhizus.forest.common.SerializeType.Fastjson"/>
            </property>
            <property name="timeout" value="5000"/>
        </bean>
    
        <bean id="sampleServiceProxy" class="com.zhizus.forest.support.spring.ForestProxyFactoryBean">
            <property name="serviceInterface" value="com.zhizus.forest.demo.api.SampleService"/>
            <!--methodConfMap如果不配置,则使用接口方法注解上面的配置-->
            <property name="methodConfigMap">
                <map>
                    <entry key="echo" value-ref="methodConfig"/>
                    <entry key="say" value-ref="methodConfig"/>
                </map>
            </property>
        </bean>
    

      

    有的时候觉得xml配置很麻烦,但是又不想使用服务提供方注解上面的默认配置,那么我们可以可以使用代码自己实例化, 示例如下:

    SampleService sampleService = Forest.from(SampleService.class, ServiceProviderConfig.Builder.newBuilder()
                    .withMethodConfig("say", MethodConfig.Builder.newBuilder()
                            .withCompressType(CompressType.None)
                            .withSerializeType(SerializeType.Fastjson)
                            .build())
                    .withMethodConfig("echo", MethodConfig.Builder.newBuilder()
                            .withCompressType(CompressType.None)
                            .withSerializeType(SerializeType.Hession2)
                            .build())
                    .build());
    

      

    常用功能介绍

    压缩方式&序列化方式

    Forest在协议层面支持多种压缩方式&多种序列化方式,也就是说,你可以指定某一个请求的压缩方式和序列化的方式。压缩方式和序列化方式的粒度都是每个请求的。

    • 目前支持的压缩方式:1.gzip 2.snappy
    • 目前支持的序列化方式:1.hession2, 2.fastjson, 3.kyro

    负载均衡

    RANDOM, ROBBIN, HASH

    容错

     FAIL_FAST,
     FAIL_OVER,

    客户端的隔离

    Forest支持定制策略,(在一个时间段出现一定次数的错误,则自动将服务隔离开来,一段时间后恢复)

    内置连接池

    Forest采用common-pool2连接池来管理连接,每个客户端的proxy对应一个或者多个pool,不需要额外的创建连接池来提供吞吐。

    第三方服务的支持

    Forest基于hystrix使用示例

    hystrix是一个非常优秀的服务容错组件,这里也非常推荐能使用hystrix对forest提供的服务做一层包装来加强服务的容错降级能力。

    服务端支持连接器&限流

    Forest可以自定义拦截器,同时也可以自定义限流。只需要在发布的方法上面加上对应的注解。

    @Component
    public class SampleServiceCommand {
    
        @Resource(name = "sampleServiceProxy")
        SampleService remoteServiceRef;
    
        @HystrixCommand(groupKey = "ExampleGroup", commandKey = "HelloWorld", threadPoolKey = "HelloWorldPool", fallbackMethod = "sayFallback")
        public String say(String str) {
            String say = remoteServiceRef.say(str);
            System.out.println("say:" + say);
            str.toString();
            return say;
        }
    
        public String sayFallback(String str) {
            return "sayFallBack:" + str;
        }
    }
    

      

    xml 配置如下:

     <context:component-scan base-package="com.zhizus.forest.demo.client"/>
    
        <!--添加hystrix aop-->
        <aop:aspectj-autoproxy/>
        <bean id="hystrixAspect" class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"></bean>
    
        <bean id="methodConfig" class="com.zhizus.forest.common.config.MethodConfig">
            <property name="compressType">
                <util:constant static-field="com.zhizus.forest.common.CompressType.None"/>
            </property>
            <property name="serializeType">
                <util:constant static-field="com.zhizus.forest.common.SerializeType.Fastjson"/>
            </property>
            <property name="timeout" value="5000"/>
        </bean>
    
        <bean id="zkRegistry" class="com.zhizus.forest.common.registry.impl.ZkServiceDiscovery">
            <property name="connStr" value="localhost:2181"/>
        </bean>
    
        <bean id="sampleServiceProxy" class="com.zhizus.forest.support.spring.ForestProxyFactoryBean">
            <property name="serviceInterface" value="com.zhizus.forest.demo.api.SampleService"/>
            <!--注册本地-->
            <property name="discovery" ref="zkRegistry"/>
            <!--methodConfMap如果不配置,则使用接口方法注解上面的配置-->
            <property name="methodConfigMap">
                <map>
                    <entry key="echo" value-ref="methodConfig"/>
                    <entry key="say" value-ref="methodConfig"/>
                </map>
            </property>
        </bean>
    

      

    对jersey的支持

    有时候,我们需要同时暴露http服务供第三方服务调用或者测试。针对这些情况,Forest支持基于jersey的restful服务。

    http服务打开的开关:server.properties

    http.server.start=true
    

      

    其他的参数配置,详见:com.zhizus.forest.common.config.ServerConfig 之后,我们可以无阻碍的使用基于jersey的restful服务了。

    性能测试

    对Forest进行了简单的压测,发现在hession2的序列化方式下,win64 8g内存可以达到8w+的tps。 其他的情况也均在3w+的tps。

    当然Forest应该还有一些性能优化空间,到目前为止,还没有对其进行一些性能优化。

    client代码:

     public static void benchmarkTest() throws Exception {
            final SampleService sampleService = Forest.from(SampleService.class);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < 20; i++) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000000000; i++) {
                            String say = sampleService.echo("hello");
                            if (i % 10000 == 0) {
                                System.out.println(say);
                            }
                        }
                    }
                });
            }
        }
    

      

    服务端加上系统自带的metric拦截器:

     @Interceptor("metricInterceptor")
        @MethodExport
        @Override
        public String echo(String msg) {
            return "echo>>> " + msg;
        }
    

      

    console日志:

    23:10:10.295 [pool-1-thread-1] INFO MetricInterceptor 34 - methodName:/sampleService/say, current tps:83342, avgTime:0, maxTime:63, minTime:0
    23:10:11.298 [pool-1-thread-1] INFO MetricInterceptor 34 - methodName:/sampleService/say, current tps:86271, avgTime:0, maxTime:63, minTime:0
    23:10:12.295 [pool-1-thread-1] INFO MetricInterceptor 34 - methodName:/sampleService/say, current tps:86063, avgTime:0, maxTime:63, minTime:0
    

    源代码地址:forest

    欢迎各路大侠参与。

  • 相关阅读:
    JZOJ3404[NOIP2013模拟]卡牌游戏(2019.08.04[NOIP提高组]模拟 B 组T2)
    JZOJ3403[NOIP2013模拟]数列变换(2019.08.04[NOIP提高组]模拟 B 组T1)
    Luogu1169BZOJ1057[ZJOI2007]棋盘制作
    BZOJ1867[Noi1999]钉子和小球
    Luogu2524 Uim的情人节礼物
    error: Failed dependencies:解决
    Android 通知 相关api记录
    LitePal 之 DatabaseGenerateException
    Button重写onClick两种方式
    【洛谷3948】数据结构
  • 原文地址:https://www.cnblogs.com/dempe/p/6220488.html
Copyright © 2011-2022 走看看