Netty自娱自乐之类Dubbo RPC 框架设计构想 【上篇】

  之前在前一篇的《Netty自娱自乐之协议栈设计》,菜鸟我已经自娱自乐了设计协议栈,gitHub地址为https://github.com/vOoT/ncustomer-protocal。先这一篇中,准备接着自娱去实现一个RPC框架,现在公司共的是Dubbo,那么先不看其代码,先自行实现一下吧。

  dubbo 包括 注册和服务调用,细节我们先不管,然后,我先先实现一个如下的简单模型

  哈哈哈,第一个版本就是这么简单,粗暴。说到自定义配置,首先想到的是Spring 自定义标签,利用标签进行配置服务。而我设计的标签页非常的简单,使用如下:

    <rpc:provider id="helloServiceImpl" class="com.qee.rpc.HelloServiceImpl"/>

    <rpc:cumsumer id="helloService" interface="com.qee.rpc.HelloService"/>

看到了没,非常像dubbo,那么如何实现一个自定义标签呢,从网上可以了解搜索的到,现在我就简单说明一下,如何编写和测试自己自定义的Spring 标签。

  一、 定义xsd 文件,该文件是xml文件的 schema 定义。从上面的例子中,我们知道xsd文件里面应该有2个节点,1个provider节点和1个cumsumer节点定义。然后制定provider节点有id 和classs属性,而cumsumer节点有 id和 interface属性。定义文件如下(该文件名为light-weight-rpc.xsd):

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.qee.com/schema/rpc"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://www.qee.com/schema/rpc"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">

    <xsd:import namespace="http://www.springframework.org/schema/beans"/>

    <xsd:element name="provider" type="rpc-provider-type"></xsd:element>

    <xsd:element name="cumsumer" type="rpc-cumsumer-type"></xsd:element>

    <xsd:complexType name="rpc-provider-type">
        <xsd:attribute name="id" type="xsd:string" use="required"></xsd:attribute>
        <xsd:attribute name="class" type="xsd:string" use="required"></xsd:attribute>
    </xsd:complexType>

    <xsd:complexType name="rpc-cumsumer-type">
        <xsd:attribute name="id" type="xsd:string" use="required"></xsd:attribute>
        <xsd:attribute name="interface" type="xsd:string" use="required"></xsd:attribute>
    </xsd:complexType>

</xsd:schema>

  上面,画上红线的地方需要注意和主要的关注点,首先需要说明这个文件的name space 为 xmlns="http://www.qee.com/schema/rpc 。其他的具体如何写可以到网上搜索。有了这个文件,我们需要在xml的文件引入他,比如如下test.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:rpc="http://www.qee.com/schema/rpc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.qee.com/schema/rpc http://www.qee.com/schema/rpc/light-weight-rpc.xsd">

    <rpc:provider id="helloServiceImpl" class="com.qee.rpc.HelloServiceImpl"/>

    <rpc:cumsumer id="helloService" interface="com.qee.rpc.HelloService"/>

</beans>

  上面就是一个spring xml 文件,主要关注的是花黄线的部分,这样就可以使用<rpc:provider> 和<rpc:cumsumer>。

  二、组织文件,即要把文件放到合适的地方,让Spring能够识别。第一步,需要把light-weight-rpc.xsd文件放到META-INF的文件夹下,然后在META-INF文件创建2个新的文件,名字固定。

文件1:spring.schemes ,该文件里面直有一行数据,如下

       http\://www.qee.com/schema/rpc/light-weight-rpc.xsd=META-INF/light-weight-rpc.xsd

  该行告诉Spring容器,http://www.qee.com/schema/rpc/light-weight-rpc.xsd ,之前定义命名空间的light-weight-rpc.xsd文件是META-INF下的light-weight-rpc.xsd

文件2:spring.handlers,该文件里面也只有一行数据,如下

       http\://www.qee.com/schema/rpc=com.qee.rpc.config.support.LightWeightRpcNamespaceHandlerSupport

  该行告诉Spring容器,命名空间http://www.qee.com/schema/rpc的解析处理器是 com.qee.rpc.config.support.LightWeightRpcNamespaceHandlerSupport。这个例子的目录如下

  

好了到现在我们基本把文件的位置放置正确了。之后就是需要编写com.qee.rpc.config.support.LightWeightRpcNamespaceHandlerSupport。

  三、编写com.qee.rpc.config.support.LightWeightRpcNamespaceHandlerSupport,该类需要继承NamespaceHandlerSupport,重写init()方法。主要的目的就是注册,节点解析处理器。

代码如下:

public class LightWeightRpcNamespaceHandlerSupport extends NamespaceHandlerSupport {

    @Override
    public void init() {
        //注册用于解析<rpc>的解析器
        registerBeanDefinitionParser("provider", new LightWeightRpcBeanDefinitionParser());
        registerBeanDefinitionParser("cumsumer", new LightWeightRpcBeanDefinitionParser());
    }
}

  从代码上我们只要,就是把解析xml文件provider和cumsumer节点进行BeanDefinition转化解析。

  因为这2个节点非常的类型。所以我就只想用痛一个解析处理器,LightWeightRpcBeanDefinitionParser,该转化器继承org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser。具体代码如下:

public class LightWeightRpcBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected Class getBeanClass(Element element) {
        return LightWeightRPCElement.class;
    }

    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        String interfaces = element.getAttribute("interface");
        String clazz = element.getAttribute("class");
        String id = element.getAttribute("id");
        bean.addPropertyValue("id", id + "Config");
        if (StringUtils.hasText(id)) {
            bean.addPropertyValue("beanName", id);
        }
        if (StringUtils.hasText(clazz)) {
            bean.addPropertyValue("clazz", clazz);
        }
        if (StringUtils.hasText(interfaces)) {
            bean.addPropertyValue("interfaces", interfaces);
        }
    }

}

  我们把xml的id 放到 bean 的beanName,把id+"Config"放到 id上,因为这个 BeanDefinitionBuilder 最终生成的对象是 LightWeightRPCElement,不是我们需要的代码对象。

@Data
@ToString
public class LightWeightRPCElement {
  private String id;

  private String beanName;

  private String clazz;

  private String interfaces;
}

  是不是非常的简单,到目前为止,我们已经完成了所有的自定义标签工作,下一步当然就是测试一下啦,代码如下:

public class RPCTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
        LightWeightRPCElement p1= (LightWeightRPCElement)ctx.getBean("helloServiceImplConfig");
        LightWeightRPCElement p2= (LightWeightRPCElement)ctx.getBean("helloServiceConfig");
        System.out.println(p1);
        System.out.println(p2);

    }
}

执行结果是:

  四、这一步的话,我们需要处理之前已经注册到Spring的 LightWeightRPCElement 的对象,在上面的例子中,这两个的Bean Id分别是helloServiceImplConfig、helloServiceConfig,之后我们需要通过这2个对象来产生我们需要代理对象。首先我们来看一下JDK的生成代理对象的方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler);

从上面的代码中,我们知道生产一个代理对象需要一个类加载器loader,和代理接口的字节码interfaces,和代理处理具柄invocationHandler。那么我程序定义了一个名为InterfaceProxyHandler的代理处理具柄,它继承InvocationHandler。代码如下:
@Data
public class InterfaceProxyHandler implements InvocationHandler {

    private CallBackExcuteHandler excuteHandler;

    public InterfaceProxyHandler(CallBackExcuteHandler excuteHandler) {
        this.excuteHandler = excuteHandler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        MessageCallback callback = ExcuteManager.invoke(excuteHandler);
        .......//这里代码还没写,其实就是处理返回结果,准备下章解决。
    }

}

  从上面的代码,我们知道,它具体的执行逻辑是invoke方法。具体内容就是通过一个ExcuteManager来处理逻辑,该ExcuteManager就是一个封装了ExecutorService的线程池管理类。其意思是每个代理对象去执行方法时,都是通过线程池的一个线程去执行,而这个线程池管理类的执行方法invoke需要一个Callable任务,所以程序自定义了一个CallBackExcuteHandler类。代码如下:

public class CallBackExcuteHandler implements Callable<MessageCallback> {

    private String beanName;

    private List<InetSocketAddress> remoteAddresses;

    private LoadBalancedStrategy loadBalancedStrategy;

    public CallBackExcuteHandler(String beanName) {
        this.beanName = beanName;
    }

    public CallBackExcuteHandler(String beanName, List<InetSocketAddress> remoteAddresses) {
        this.beanName = beanName;
        this.remoteAddresses = remoteAddresses;
    }

    public CallBackExcuteHandler(String beanName, List<InetSocketAddress> remoteAddresses, LoadBalancedStrategy loadBalancedStrategy) {
        this.beanName = beanName;
        this.remoteAddresses = remoteAddresses;
        this.loadBalancedStrategy = loadBalancedStrategy;
    }

    public CallBackExcuteHandler() {

    }

    /**
     * 线程执行
     *
     * @return
     * @throws Exception
     */
    @Override
    public MessageCallback call() throws Exception {
        if (CollectionUtils.isEmpty(remoteAddresses)) {
            List<ServiceAddressConfig> remoteUrls = ServiceRemoteUrlContext.getInstance().getRemoteUrls(beanName);
            if (CollectionUtils.isEmpty(remoteUrls)) {
                throw new RuntimeException("服务 [" + beanName + " ]远程地址错误");
            }
        }

        int size = remoteAddresses.size();

        int idx = loadBalancedStrategy.strategy(size);

        InetSocketAddress inetSocketAddress = remoteAddresses.get(idx);
        System.out.println("返回的地址" + inetSocketAddress + "  idx=" + idx);

        MessageCallback messageCallback = new MessageCallback();

        return messageCallback;
    }
}

  具体逻辑就是看call,这里就是处理的具体逻辑,这个逻辑其实就是处理Netty网络通信的内容,等下章开始讲解,这一章主要通过搭建具体的框架,之后补充细节。这里远程地址为空的话,去远程地址管理上下文获取,接着通过一个负载均衡策略对象,返回其中一个地址的index。通过这种方式实现负载均衡调用。

  远程地址管理上下文对象代码如下:

public class ServiceRemoteUrlContext {

    private Map<String, List<ServiceAddressConfig>> remoteUrls;

    private volatile static ServiceRemoteUrlContext context;

    private ServiceRemoteUrlContext() {

    }

    public static ServiceRemoteUrlContext getInstance() {
        if (context == null) {
            synchronized (ServiceRemoteUrlContext.class) {
                if (context == null) {
                    context = new ServiceRemoteUrlContext();
                    context.remoteUrls = new HashMap<>();
                }
            }
        }
        return context;
    }

    /**
     * 添加一个远程地址,地址从service-url.properties 获取
     *
     * @param beanName
     * @param serviceAddressConfig
     * @return
     */
    public boolean addServiceAddress(String beanName, ServiceAddressConfig serviceAddressConfig) {
        if (StringUtils.isEmpty(beanName) || serviceAddressConfig == null) {
            return false;
        }
        synchronized (remoteUrls) {
            if (remoteUrls.get(beanName) == null) {
                List<ServiceAddressConfig> remoteAddress = new ArrayList<>();
                remoteAddress.add(serviceAddressConfig);
                remoteUrls.put(beanName, remoteAddress);
            } else {
                List<ServiceAddressConfig> serviceAddressConfigs = remoteUrls.get(beanName);
                if (serviceAddressConfigs.contains(serviceAddressConfig)) {
                    return false;
                }
                serviceAddressConfigs.add(serviceAddressConfig);
                return true;
            }
        }
        return false;
    }

    /**
     * 获取一个服务的远程地址 ,beanName like "com.qee.rpc.config.test.HelloService"
     *
     * @param beanName
     * @return
     */
    public List<ServiceAddressConfig> getRemoteUrls(String beanName) {
        return remoteUrls.get(beanName);
    }

}

  负载均衡的接口,代码如下:

public interface LoadBalancedStrategy {

    /**
     * 从 0 -size-1 获取一个值
     *
     * @param size
     * @return
     */
    int strategy(int size);
}

  现在只实现了1中,轮询方法,之后可以写成可配置,代码如下:

public class RollPolingStrategy implements LoadBalancedStrategy {

    private int currentValue = 0;

    private Class<?> clazz;

    public RollPolingStrategy(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public int strategy(int size) {
        synchronized (clazz) {
            int nextValue = (currentValue + 1) % size;
            currentValue = nextValue;
            if (currentValue > size) {
                nextValue = 0;
            }
            return currentValue;
        }
    }
}

  接着,我们需要看一下简单的ExcuteManager类,代码如下:

public class ExcuteManager {

    /**
     * 默认是200个线程
     */
    private static final int DEFAULT_THRED_NUM = 200;

    /**
     * 超时时间为1秒
     */
    private static final int DEFAULT_TIME_OUT_TIME = 1000;

    private static ExecutorService executorService = Executors.newFixedThreadPool(DEFAULT_THRED_NUM);

    public static MessageCallback invoke(Callable<MessageCallback> call) {
        Future<MessageCallback> submit = executorService.submit(call);
        try {
            return submit.get(DEFAULT_TIME_OUT_TIME, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            submit.cancel(true);
            throw new RuntimeException("the method is interupted ", e);
        } catch (ExecutionException e) {
            submit.cancel(true);
            throw new RuntimeException("the method cal excute exception", e);
        } catch (TimeoutException e) {
            System.out.println(Thread.currentThread().getName());
            submit.cancel(true);
            throw new RuntimeException("the method call is time out  ", e);
        }
    }

    public static void shutdown() {
        executorService.shutdown();
    }

    public static void shutdownNow() {
        executorService.shutdownNow();
    }

}

  这些参数,在后面都做成可配置的。

  最后一步了,就是需要生产一个代理对象,并把代理对象注册到Spring容器里面。那么Spring的 BeanPostProcessor可以为我们解决问题,看代码如下:

@Component
public class RegisterRpcProxyBeanProcessor implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Object target = bean;
        if (bean instanceof LightWeightRPCElement) {
        //如果是LightWeightRPCElement,则强转,否则不处理
            LightWeightRPCElement rpcElement = (LightWeightRPCElement) bean;

           // 接着就是获取 之前XML 的属性值
            Class<?> clazz = null;
            if (!StringUtils.isEmpty(rpcElement.getInterfaces())) {
                try {
                    clazz = Class.forName(rpcElement.getInterfaces());
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("获取 [" + rpcElement.getInterfaces() + " ] class字节码失败");
                }
            }
             //通过ServiceRemoteUrlContext得到这个接口的远程端口和地址
            List<ServiceAddressConfig> remoteUrls = ServiceRemoteUrlContext.getInstance().getRemoteUrls(rpcElement.getInterfaces());
            List<InetSocketAddress> remoteAddressList = ExtractUtil.extractList(remoteUrls, "remoteAddress", ServiceAddressConfig.class);
            CallBackExcuteHandler callBackExcuteHandler = new CallBackExcuteHandler(rpcElement.getInterfaces(), remoteAddressList,new RollPolingStrategy(clazz));

            InterfaceProxyHandler interfaceProxyHandler = new InterfaceProxyHandler(callBackExcuteHandler);
            //这里之后可以优化为各种方式产生动态代理,如cglib等
            target = Proxy.newProxyInstance(bean.getClass().getClassLoader(), new Class[]{clazz}, interfaceProxyHandler);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                //这里就是动态注册对象,把动态代理对象注册到Spring上
                DefaultListableBeanFactory defaultFactory = (DefaultListableBeanFactory) beanFactory;
                defaultFactory.registerSingleton(rpcElement.getBeanName(), target);
            }
        }
        return target;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
     

  从上面的注释大家应该也非常的清楚了,现在只剩下最后一步了,如何获取该接口的远程服务地址和端口,dubbo是通过注册中心zookeeper,而这里的简单的采用配置,例子如下:

   com.qee.rpc.config.test.HelloService 127.0.0.1:8888,127.0.0.1:7777,127.0.0.1:9999

  对,就是在一个properties文件上 通过服务接口全称 和指定远程服务主机和端口。之后可以改为有注册中心的方式。现在我们来看一下读取这个配置的类,代码如下:

@Component
public class ServiceRemoteUrlsInit implements InitializingBean {

    /**
     * 远程服务配置地址路径,默认
     */
    @Value("${remote-urls-path:classpath:service-urls.properties}")
    private String remoteUrlsPropertyPath;

    @Override
    public void afterPropertiesSet() throws Exception {
        Properties pps = new Properties();
        if (!remoteUrlsPropertyPath.startsWith("classpath")) {
            throw new RuntimeException(remoteUrlsPropertyPath + "不存在");
        }
        String[] filePath = remoteUrlsPropertyPath.split(":");
        if (filePath == null || filePath.length != 2) {
            throw new RuntimeException(remoteUrlsPropertyPath + "内容配置错误");
        }
        ClassPathResource resource = new ClassPathResource(filePath[1]);
        InputStream in = new BufferedInputStream(resource.getInputStream());
        pps.load(in);
        Enumeration en = pps.propertyNames();

        while (en.hasMoreElements()) {
            String beanName = (String) en.nextElement();
            String strRemoteUrls = pps.getProperty(beanName);
            String[] remoteUrls = strRemoteUrls.split(",");
            if (remoteUrls == null || remoteUrls.length == 0) {
                break;
            }
            for (String remoteUrl : remoteUrls) {
                String[] hostPort = remoteUrl.split(":");
                if (hostPort == null || hostPort.length != 2) {
                    throw new RuntimeException(remoteUrlsPropertyPath + " 配置内容错误");
                }
                ServiceAddressConfig serviceAddressConfig = new ServiceAddressConfig();
                serviceAddressConfig.setBeanName(beanName);
                serviceAddressConfig.setHostName(hostPort[0]);
                serviceAddressConfig.setRemotePort(Integer.valueOf(hostPort[1]));
                InetSocketAddress socketAddress = new InetSocketAddress(serviceAddressConfig.getHostName(), serviceAddressConfig.getRemotePort());
                serviceAddressConfig.setRemoteAddress(socketAddress);
                ServiceRemoteUrlContext.getInstance().addServiceAddress(beanName, serviceAddressConfig);
            }

        }

    }
}

  代码比较简单,就是实现 InitializingBean这个Spring接口,Spring启动在Bean创建后,初始化 afterPropertiesSet()这个配置,在这个方法里面读取类路径的配置文件。最后我们来运行一个例子。还是HelloService.我们有一个Invoker类,需要注入HelloService 对象调用。代码如下:

@Component
public class Invoker {

    @Autowired
    private HelloService helloService;

    @Resource(name = "helloService")
    private HelloService helloService2;

    public void print() {
        helloService.hello("123");
        helloService2.hello("122344");

    }
}

然后通过SpringBoot 启动测试:

@ComponentScan(basePackages = "com.qee.rpc")
@EnableAutoConfiguration
public class App {

    private static ExecutorService executorService = Executors.newCachedThreadPool();

    private static final CountDownLatch cd = new CountDownLatch(1);

    public static void main(String[] args) {

        try {
            SpringApplication.run(App.class, args);
            System.out.println("the main Thread :" + Thread.currentThread().getName());
            final Invoker invoker = (Invoker) ApplicationContextUtils.getBean("invoker");
            for (int i = 0; i < 300; i++) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            cd.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        invoker.print();
                    }
                });
            }

            cd.countDown();

            Thread.sleep(100000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            ExcuteManager.shutdown();
            executorService.shutdown();
        }

    }

  有300个线程去调这个 invoker.print();修改一下 InterfaceProxyHandler的invoke方法,因为我们底层的通信还没完成。所以以

    System.out.println("在InterfaceProxyHandler上调用invoke方法,参数是=" + args[0]);

  以这个语句来测试一下代码,其中这个大致框架已经上传到gitHub:https://github.com/vOoT/light-weight-rpc, 有什么建议和问题,大家一起讨论吧。最后贴一下执行结果:

  哈哈哈,这样我们是不是就是可以通过Spring注解 @Autowired 和 @Resource 来注入动态对象。

  

				
时间: 2024-08-27 09:28:53

Netty自娱自乐之类Dubbo RPC 框架设计构想 【上篇】的相关文章

基于Netty的高性能JAVA的RPC框架

前言 今年7月份左右报名参加了阿里巴巴组织的高性能中间件挑战赛,这次比赛不像以往的比赛,是从一个工程的视角来比赛的. 这个比赛有两个赛题,第一题是实现一个RPC框架,第二道题是实现一个Mom消息中间件. RPC题目如下 一个简单的RPC框架 RPC(Remote Procedure Call )--远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据.在OSI网络通信模型中,RPC

从零开始手写 dubbo rpc 框架

rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. 但是对于其原理一直只是知道个大概,从来没有深入学习过. 以前一直想写,但由于各种原因被耽搁. 技术准备 Java 并发实战学习 TCP/IP 协议学习笔记 Netty 权威指南学习 这些技术的准备阶段,花费了比较长的时间. 也建议想写 rpc 框架的有相关的知识储备. 其他 rpc 框架使用的经验

稳定性 耗时 监控原因分析-- dubbo rpc 框架 的线程池,io 连接模型. 客户端,服务端

上次 提到的Nagle算法特性有可能是dubbo调用”网络耗时高“的始作俑者,后来又仔细看了下dubbo的代码,发现dubbo在consumer端已经将tcp设置成非延迟(即关闭Nagle特性)了,代码片段如下: order模块中调用量最高的接口是查询未完成订单,调用量达到每10秒18000,如下: 但该接口在order这边的平均耗时特别低(尖刺是由发布引起的,这里忽略),才1毫秒: 该接口直接由kop来调用,我们看kop这边的平均耗时: 可以看出,在低峰期,kop这边的耗时和order的耗时是

Dubbo服务化框架使用整理

一.垂直应用架构拆分 在应用架构的演进过程中,垂直应用架构因为开发便捷,学习成本低,对于实现业务功能的增删改查提供了高效的开发支持,有利于前期业务高速发展的的快速实现.但是随着系统业务功能的不断扩展和系统代码的的不断攀 升业务模块与模块之间的高耦合度.核心业务的稳定性.根据市场的需求应用前端需要针对不同的业务场景实现不同的有针对性的修改这时候传统的垂直应用架构就满足不了需求,因而需要引入分布式服务框架(RPC框架) 来实现将系统进行水平和垂直拆分,拆分成各个相对独立的服务功能,实现服务的提供者和

RPC框架实现 - 汇总

复杂系统的构建和优化,不仅需要各种理论支撑,更多地来自于工程实践.系统运行过程中,面对各种大大小小的故障,优秀的工程师都能将其转化为一个个改进点,追求更高性能的同时,向着故障自愈.柔性可用等目标不断前进. 以下几篇文章从框架设计.容灾.监控等几个方面,介绍了微信后台RPC框架设计思路和具体实现.Hope you enjoy: RPC框架实现 - 框架篇 RPC框架实现 - 容灾篇 RPC框架实现 - 路由控制篇 RPC框架实现 - 通信协议篇 RPC框架实现 - 监控篇

8.如何自己设计一个类似 Dubbo 的 RPC 框架?

作者:中华石杉 面试题 如何自己设计一个类似 Dubbo 的 RPC 框架? 面试官心理分析 说实话,就这问题,其实就跟问你如何自己设计一个 MQ 一样的道理,就考两个: 你有没有对某个 rpc 框架原理有非常深入的理解. 你能不能从整体上来思考一下,如何设计一个 rpc 框架,考考你的系统设计能力. 面试题剖析 其实问到你这问题,你起码不能认怂,因为是知识的扫盲,那我不可能给你深入讲解什么 kafka 源码剖析,dubbo 源码剖析,何况我就算讲了,你要真的消化理解和吸收,起码个把月以后了.

RPC框架Dubbo分析

1,背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进 单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本 此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键 垂直应用架构 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率 此时,用于加速前端页面开发的 Web框架(MVC) 是关键 分布式服务

分布式RPC框架性能大比拼 dubbo、motan、rpcx、gRPC、thrift的性能比较

Dubbo 是阿里巴巴公司开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成.不过,略有遗憾的是,据说在淘宝内部,dubbo由于跟淘宝另一个类似的框架HSF(非开源)有竞争关系,导致dubbo团队已经解散(参见http://www.oschina.net/news/55059/druid-1-0-9 中的评论),反到是当当网的扩展版本仍在持续发展,墙内开花墙外香.其它的一些知名电商如当当.京东.国美维护了自己的分支或

RPC框架Dubbo深入分析

1,背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进 单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本 此时,用于简化增删改查工作量的?数据访问框架(ORM)?是关键 垂直应用架构?当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率此时,用于加速前端页面开发的?Web框架(MVC)?是关键分布式服务架构