目次
测试 " >测试
ホームページ Java &#&面接の質問 アリババのインタビュアー: RPC フレームワークを手書きで書いてください。

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。

Aug 17, 2023 pm 04:24 PM
RPC フレームワーク

面接官がよく尋ねる質問:

  • 登録センターをどのように設計するか?
  • メッセージ キューを設計するにはどうすればよいですか?
  • 永続化フレームワークを設計するにはどうすればよいですか?
  • RPC フレームワークを設計するにはどうすればよいですか?
  • ......

今日は、「RPC 実装原則」について話しましょう (その他の関連トピック: XX の設計方法)シリーズ、Knowledge Planet に掲載されています)それでは、まず質問を明確にしましょう: RPC とは何ですか?

RPCとはRemote Procedure Callの略で、遠隔手続き呼び出しのことです。

#RPC はコンピュータ通信プロトコルです。このプロトコルを使用すると、開発者がこの対話を追加でプログラムすることなく、あるコンピュータ上で実行されているプログラムが別のコンピュータ上のサブルーチンを呼び出すことができます。

2 つ以上のアプリケーションが異なるサーバーに分散されており、アプリケーション間の呼び出しはローカル メソッド呼び出しに似ていることに注意してください。次に、RPC 呼び出しで何が起こるかを分析してみましょう。

RPC 呼び出しの基本プロセス

提供されているものなど、業界で最も人気のある RPC フレームワークのいくつかby Dubbo インターフェイスベースのリモート メソッド呼び出しは、クライアントがリモート サービスを呼び出すためにインターフェイスの定義を知る必要があるだけであることを意味します。 Java では、インターフェイスはインスタンス メソッドを直接呼び出すことができません。この操作は実装クラス オブジェクトを通じて実行する必要があります。つまり、クライアントはこれらのインターフェイスのプロキシ オブジェクトを生成する必要があります。この目的のために、Java は動的プロキシを生成するための Proxy および InvocationHandler のサポートを提供します。プロキシ オブジェクトがある場合、それぞれの特定のメソッドはどのように呼び出されますか? JDK ダイナミック プロキシによって生成されたプロキシ オブジェクトが指定されたメソッドを呼び出すと、InvocationHandler で定義された #invoke メソッドが実際に実行され、リモート メソッドの呼び出しが完了して結果が取得されます。

クライアントのことはさておき、振り返ってみると、RPC は 2 台のコンピュータ間の呼び出しです。本質的には 2 台のホスト間のネットワーク通信です。ネットワーク通信に関しては、シリアル化、逆シリアル化、エンコードとデコードなどが必要です。考慮する必要がある問題と同時に、実際、ほとんどのシステムはクラスター内にデプロイされています。複数のホスト/コンテナーが同じサービスを外部に提供します。クラスター内のノードの数が多い場合、サービス アドレスの管理も難しくなります。一般的には、各サービスノードがそのアドレスと提供するサービスリストを登録センターに登録し、登録センターがサービスリストを一元管理することで、いくつかの問題を解決し、クライアントに新しい機能を追加します。ジョブ、つまりサービス ディスカバリは、一般的に言えば、登録センターからリモート方式に対応するサービス リストを検索し、そこから特定の戦略に従ってサービス アドレスを選択して、ネットワーク通信を完了することです。

クライアントと登録センターについて説明した後、もう 1 つの重要な役割は当然サーバーです。サーバーの最も重要なタスクは、サービス インターフェイスの実際の実装を提供し、特定のポート上のネットワーク リクエストを監視することです。リクエストの後、対応するパラメータ (サービス インターフェイス、メソッド、リクエスト パラメータなど) がネットワーク リクエストから取得され、これらのパラメータに基づいて、リフレクションを通じてインターフェイスの実際の実装が呼び出され、結果が取得されます。対応する応答ストリームに書き込まれます。

要約すると、基本的な RPC 呼び出しプロセスは大まかに次のとおりです。

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。

# #基本的な実装

サーバー (プロデューサー)

サービス インターフェイス:

RPC では、プロダクションの作成者とコンシューマー共通のサービス インターフェイス API を持っています。以下のようにHelloServiceインターフェースを定義します。

/**
 * @Descrption  服务接口
 ***/
public interface HelloService {
    String sayHello(String somebody);
}
ログイン後にコピー

サービス実装:

プロデューサは、サービス インターフェイスの実装を提供し、HelloServiceImpl 実装クラスを作成する必要があります。

/**
 * @Descrption 服务实现
 ***/
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String somebody) {
        return "hello " + somebody + "!";
    }
}
ログイン後にコピー

サービス登録:

この例では、Spring を使用して Bean を管理し、カスタム XML とパーサーを使用してサービス実装クラスをコンテナにロードします (もちろん、カスタム アノテーションも使用できます) (ここでは説明しません) サービス インターフェイス情報を登録センターに登録します。

最初に XSD をカスタマイズします:

<xsd:element name="service">
    <xsd:complexType>
        <xsd:complexContent>
            <xsd:extension base="beans:identifiedType">
                <xsd:attribute name="interface" type="xsd:string" use="required"/>
                <xsd:attribute name="timeout" type="xsd:int" use="required"/>
                <xsd:attribute name="serverPort" type="xsd:int" use="required"/>
                <xsd:attribute name="ref" type="xsd:string" use="required"/>
                <xsd:attribute name="weight" type="xsd:int" use="optional"/>
                <xsd:attribute name="workerThreads" type="xsd:int" use="optional"/>
                <xsd:attribute name="appKey" type="xsd:string" use="required"/>
                <xsd:attribute name="groupName" type="xsd:string" use="optional"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:element>
ログイン後にコピー

スキーマと XSD、スキーマと対応するハンドラーのマッピングをそれぞれ指定します。

スキーマ:

http\://www.storm.com/schema/storm-service.xsd=META-INF/storm-service.xsd
http\://www.storm.com/schema/storm-reference.xsd=META-INF/storm-reference.xsd
ログイン後にコピー

ハンドラー:

http\://www.storm.com/schema/storm-service=com.hsunfkqm.storm.framework.spring.StormServiceNamespaceHandler
http\://www.storm.com/schema/storm-reference=com.hsunfkqm.storm.framework.spring.StormRemoteReferenceNamespaceHandler
ログイン後にコピー

書き込んだファイルをクラスパスの META-INF ディレクトリに置きます:

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。
图片

在 Spring 配置文件中配置服务类:

<!-- 发布远程服务 -->
 <bean id="helloService" class="com.hsunfkqm.storm.framework.test.HelloServiceImpl"/>
 <storm:service id="helloServiceRegister"
                     interface="com.hsunfkqm.storm.framework.test.HelloService"
                     ref="helloService"
                     groupName="default"
                     weight="2"
                     appKey="ares"
                     workerThreads="100"
                     serverPort="8081"
                     timeout="600"/>
ログイン後にコピー

编写对应的 Handler 和 Parser:

StormServiceNamespaceHandler:

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * @author 孙浩
 * @Descrption 服务发布自定义标签
 ***/
public class StormServiceNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("service", new ProviderFactoryBeanDefinitionParser());
    }
}
ログイン後にコピー

ProviderFactoryBeanDefinitionParser:

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

    protected void doParse(Element element, BeanDefinitionBuilder bean) {

        try {
            String serviceItf = element.getAttribute("interface");
            String serverPort = element.getAttribute("serverPort");
            String ref = element.getAttribute("ref");
            // ....
            bean.addPropertyValue("serverPort", Integer.parseInt(serverPort));
            bean.addPropertyValue("serviceItf", Class.forName(serviceItf));
            bean.addPropertyReference("serviceObject", ref);
            //...
            if (NumberUtils.isNumber(weight)) {
                bean.addPropertyValue("weight", Integer.parseInt(weight));
            }
            //...
       } catch (Exception e) {
            // ...        
      }
    }
ログイン後にコピー

ProviderFactoryBean:

/**
 * @Descrption 服务发布
 ***/
public class ProviderFactoryBean implements FactoryBean, InitializingBean {

    //服务接口
    private Class<?> serviceItf;
    //服务实现
    private Object serviceObject;
    //服务端口
    private String serverPort;
    //服务超时时间
    private long timeout;
    //服务代理对象,暂时没有用到
    private Object serviceProxyObject;
    //服务提供者唯一标识
    private String appKey;
    //服务分组组名
    private String groupName = "default";
    //服务提供者权重,默认为 1 , 范围为 [1-100]
    private int weight = 1;
    //服务端线程数,默认 10 个线程
    private int workerThreads = 10;

    @Override
    public Object getObject() throws Exception {
        return serviceProxyObject;
    }

    @Override
    public Class<?> getObjectType() {
        return serviceItf;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //启动 Netty 服务端
        NettyServer.singleton().start(Integer.parseInt(serverPort));
        //注册到 zk, 元数据注册中心
        List<ProviderService> providerServiceList = buildProviderServiceInfos();
        IRegisterCenter4Provider registerCenter4Provider = RegisterCenter.singleton();
        registerCenter4Provider.registerProvider(providerServiceList);
    }
}

//================RegisterCenter#registerProvider======================
@Override
public void registerProvider(final List<ProviderService> serviceMetaData) {
    if (CollectionUtils.isEmpty(serviceMetaData)) {
        return;
    }

    //连接 zk, 注册服务
    synchronized (RegisterCenter.class) {
        for (ProviderService provider : serviceMetaData) {
            String serviceItfKey = provider.getServiceItf().getName();

            List<ProviderService> providers = providerServiceMap.get(serviceItfKey);
            if (providers == null) {
                providers = Lists.newArrayList();
            }
            providers.add(provider);
            providerServiceMap.put(serviceItfKey, providers);
        }

        if (zkClient == null) {
            zkClient = new ZkClient(ZK_SERVICE, ZK_SESSION_TIME_OUT, ZK_CONNECTION_TIME_OUT, new SerializableSerializer());
        }

        //创建 ZK 命名空间/当前部署应用 APP 命名空间/
        String APP_KEY = serviceMetaData.get(0).getAppKey();
        String ZK_PATH = ROOT_PATH + "/" + APP_KEY;
        boolean exist = zkClient.exists(ZK_PATH);
        if (!exist) {
            zkClient.createPersistent(ZK_PATH, true);
        }

        for (Map.Entry<String, List<ProviderService>> entry : providerServiceMap.entrySet()) {
            //服务分组
            String groupName = entry.getValue().get(0).getGroupName();
            //创建服务提供者
            String serviceNode = entry.getKey();
            String servicePath = ZK_PATH + "/" + groupName + "/" + serviceNode + "/" + PROVIDER_TYPE;
            exist = zkClient.exists(servicePath);
            if (!exist) {
                zkClient.createPersistent(servicePath, true);
            }

            //创建当前服务器节点
            int serverPort = entry.getValue().get(0).getServerPort();//服务端口
            int weight = entry.getValue().get(0).getWeight();//服务权重
            int workerThreads = entry.getValue().get(0).getWorkerThreads();//服务工作线程
            String localIp = IPHelper.localIp();
            String currentServiceIpNode = servicePath + "/" + localIp + "|" + serverPort + "|" + weight + "|" + workerThreads + "|" + groupName;
            exist = zkClient.exists(currentServiceIpNode);
            if (!exist) {
                //注意,这里创建的是临时节点
                zkClient.createEphemeral(currentServiceIpNode);
            }
            //监听注册服务的变化,同时更新数据到本地缓存
            zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
                @Override
                public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                    if (currentChilds == null) {
                        currentChilds = Lists.newArrayList();
                    }
                    //存活的服务 IP 列表
                    List<String> activityServiceIpList = Lists.newArrayList(Lists.transform(currentChilds, new Function<String, String>() {
                        @Override
                        public String apply(String input) {
                            return StringUtils.split(input, "|")[0];
                        }
                    }));
                    refreshActivityService(activityServiceIpList);
                }
            });

        }
    }
}
ログイン後にコピー

至此服务实现类已被载入 Spring 容器中,且服务接口信息也注册到了注册中心。

网络通信:

作为生产者对外提供 RPC 服务,必须有一个网络程序来来监听请求和做出响应。在 Java 领域 Netty 是一款高性能的 NIO 通信框架,很多的框架的通信都是采用 Netty 来实现的,本例中也采用它当做通信服务器。

构建并启动 Netty 服务监听指定端口:

public void start(final int port) {
        synchronized (NettyServer.class) {
            if (bossGroup != null || workerGroup != null) {
                return;
            }

            bossGroup = new NioEventLoopGroup();
            workerGroup = new NioEventLoopGroup();
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //注册解码器 NettyDecoderHandler
                            ch.pipeline().addLast(new NettyDecoderHandler(StormRequest.class, serializeType));
                            //注册编码器 NettyEncoderHandler
                            ch.pipeline().addLast(new NettyEncoderHandler(serializeType));
                            //注册服务端业务逻辑处理器 NettyServerInvokeHandler
                            ch.pipeline().addLast(new NettyServerInvokeHandler());
                        }
                    });
            try {
                channel = serverBootstrap.bind(port).sync().channel();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
ログイン後にコピー

上面的代码中向 Netty 服务的 Pipeline 中添加了编解码和业务处理器,当接收到请求时,经过编解码后,真正处理业务的是业务处理器,即 NettyServerInvokeHandler,该处理器继承自 SimpleChannelInboundHandler,当数据读取完成将触发一个事件,并调用 NettyServerInvokeHandler#channelRead0 方法来处理请求。

@Override
protected void channelRead0(ChannelHandlerContext ctx, StormRequest request) throws Exception {
    if (ctx.channel().isWritable()) {
        //从服务调用对象里获取服务提供者信息
        ProviderService metaDataModel = request.getProviderService();
        long consumeTimeOut = request.getInvokeTimeout();
        final String methodName = request.getInvokedMethodName();

        //根据方法名称定位到具体某一个服务提供者
        String serviceKey = metaDataModel.getServiceItf().getName();
        //获取限流工具类
        int workerThread = metaDataModel.getWorkerThreads();
        Semaphore semaphore = serviceKeySemaphoreMap.get(serviceKey);
        if (semaphore == null) {
            synchronized (serviceKeySemaphoreMap) {
                semaphore = serviceKeySemaphoreMap.get(serviceKey);
                if (semaphore == null) {
                    semaphore = new Semaphore(workerThread);
                    serviceKeySemaphoreMap.put(serviceKey, semaphore);
                }
            }
        }

        //获取注册中心服务
        IRegisterCenter4Provider registerCenter4Provider = RegisterCenter.singleton();
        List<ProviderService> localProviderCaches = registerCenter4Provider.getProviderServiceMap().get(serviceKey);

        Object result = null;
        boolean acquire = false;

        try {
            ProviderService localProviderCache = Collections2.filter(localProviderCaches, new Predicate<ProviderService>() {
                @Override
                public boolean apply(ProviderService input) {
                    return StringUtils.equals(input.getServiceMethod().getName(), methodName);
                }
            }).iterator().next();
            Object serviceObject = localProviderCache.getServiceObject();

            //利用反射发起服务调用
            Method method = localProviderCache.getServiceMethod();
            //利用 semaphore 实现限流
            acquire = semaphore.tryAcquire(consumeTimeOut, TimeUnit.MILLISECONDS);
            if (acquire) {
                result = method.invoke(serviceObject, request.getArgs());
                //System.out.println("---------------"+result);
            }
        } catch (Exception e) {
            System.out.println(JSON.toJSONString(localProviderCaches) + "  " + methodName+" "+e.getMessage());
            result = e;
        } finally {
            if (acquire) {
                semaphore.release();
            }
        }
        //根据服务调用结果组装调用返回对象
        StormResponse response = new StormResponse();
        response.setInvokeTimeout(consumeTimeOut);
        response.setUniqueKey(request.getUniqueKey());
        response.setResult(result);
        //将服务调用返回对象回写到消费端
        ctx.writeAndFlush(response);
    } else {
        logger.error("------------channel closed!---------------");
    }
}
ログイン後にコピー

此处还有部分细节如自定义的编解码器等,篇幅所限不在此详述,继承 MessageToByteEncoder 和 ByteToMessageDecoder 覆写对应的 encode 和 decode 方法即可自定义编解码器,使用到的序列化工具如 Hessian/Proto 等可参考对应的官方文档。

请求和响应包装:

为便于封装请求和响应,定义两个 bean 来表示请求和响应。

请求:

/**
 * @author 孙浩
 * @Descrption
 ***/
public class StormRequest implements Serializable {

    private static final long serialVersionUID = -5196465012408804755L;
    //UUID,唯一标识一次返回值
    private String uniqueKey;
    //服务提供者信息
    private ProviderService providerService;
    //调用的方法名称
    private String invokedMethodName;
    //传递参数
    private Object[] args;
    //消费端应用名
    private String appName;
    //消费请求超时时长
    private long invokeTimeout;
    // getter/setter
}
ログイン後にコピー

响应:

/**
 * @Descrption
 ***/
public class StormResponse implements Serializable {
    private static final long serialVersionUID = 5785265307118147202L;
    //UUID, 唯一标识一次返回值
    private String uniqueKey;
    //客户端指定的服务超时时间
    private long invokeTimeout;
    //接口调用返回的结果对象
    private Object result;
    //getter/setter
}
ログイン後にコピー

客户端(消费者)

客户端(消费者)在 RPC 调用中主要是生成服务接口的代理对象,并从注册中心获取对应的服务列表发起网络请求。

客户端和服务端一样采用 Spring 来管理 bean 解析 XML 配置等不再赘述,重点看下以下几点:

1、通过 JDK 动态代理来生成引入服务接口的代理对象

public Object getProxy() {
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{targetInterface}, this);
}
ログイン後にコピー

2、从注册中心获取服务列表并依据某种策略选取其中一个服务节点

//服务接口名称
String serviceKey = targetInterface.getName();
//获取某个接口的服务提供者列表
IRegisterCenter4Invoker registerCenter4Consumer = RegisterCenter.singleton();
List<ProviderService> providerServices = registerCenter4Consumer.getServiceMetaDataMap4Consume().get(serviceKey);
//根据软负载策略,从服务提供者列表选取本次调用的服务提供者
ClusterStrategy clusterStrategyService = ClusterEngine.queryClusterStrategy(clusterStrategy);
ProviderService providerService = clusterStrategyService.select(providerServices);
ログイン後にコピー

3、通过 Netty 建立连接,发起网络请求

/**
 * @author 孙浩
 * @Descrption Netty 消费端 bean 代理工厂
 ***/
public class RevokerProxyBeanFactory implements InvocationHandler {
    private ExecutorService fixedThreadPool = null;
    //服务接口
    private Class<?> targetInterface;
    //超时时间
    private int consumeTimeout;
    //调用者线程数
    private static int threadWorkerNumber = 10;
    //负载均衡策略
    private String clusterStrategy;

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

        ...

        //复制一份服务提供者信息
        ProviderService newProvider = providerService.copy();
        //设置本次调用服务的方法以及接口
        newProvider.setServiceMethod(method);
        newProvider.setServiceItf(targetInterface);

        //声明调用 AresRequest 对象,AresRequest 表示发起一次调用所包含的信息
        final StormRequest request = new StormRequest();
        //设置本次调用的唯一标识
        request.setUniqueKey(UUID.randomUUID().toString() + "-" + Thread.currentThread().getId());
        //设置本次调用的服务提供者信息
        request.setProviderService(newProvider);
        //设置本次调用的方法名称
        request.setInvokedMethodName(method.getName());
        //设置本次调用的方法参数信息
        request.setArgs(args);

        try {
            //构建用来发起调用的线程池
            if (fixedThreadPool == null) {
                synchronized (RevokerProxyBeanFactory.class) {
                    if (null == fixedThreadPool) {
                        fixedThreadPool = Executors.newFixedThreadPool(threadWorkerNumber);
                    }
                }
            }
            //根据服务提供者的 ip,port, 构建 InetSocketAddress 对象,标识服务提供者地址
            String serverIp = request.getProviderService().getServerIp();
            int serverPort = request.getProviderService().getServerPort();
            InetSocketAddress inetSocketAddress = new InetSocketAddress(serverIp, serverPort);
            //提交本次调用信息到线程池 fixedThreadPool, 发起调用
            Future<StormResponse> responseFuture = fixedThreadPool.submit(RevokerServiceCallable.of(inetSocketAddress, request));
            //获取调用的返回结果
            StormResponse response = responseFuture.get(request.getInvokeTimeout(), TimeUnit.MILLISECONDS);
            if (response != null) {
                return response.getResult();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }
    //  ...
}
ログイン後にコピー

Netty 的响应是异步的,为了在方法调用返回前获取到响应结果,需要将异步的结果同步化。

4、Netty 异步返回的结果存入阻塞队列

@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, StormResponse response) throws Exception {
    //将 Netty 异步返回的结果存入阻塞队列,以便调用端同步获取
    RevokerResponseHolder.putResultValue(response);
}
ログイン後にコピー

5、请求发出后同步获取结果

//提交本次调用信息到线程池 fixedThreadPool, 发起调用
Future<StormResponse> responseFuture = fixedThreadPool.submit(RevokerServiceCallable.of(inetSocketAddress, request));
//获取调用的返回结果
StormResponse response = responseFuture.get(request.getInvokeTimeout(), TimeUnit.MILLISECONDS);
if (response != null) {
    return response.getResult();
}

//===================================================
//从返回结果容器中获取返回结果,同时设置等待超时时间为 invokeTimeout
long invokeTimeout = request.getInvokeTimeout();
StormResponse response = RevokerResponseHolder.getValue(request.getUniqueKey(), invokeTimeout);
ログイン後にコピー

测试

Server:

/**
 * @Descrption
 ***/
public class MainServer {
    public static void main(String[] args) throws Exception {
        //发布服务
        final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("storm-server.xml");
        System.out.println(" 服务发布完成");
    }
}
ログイン後にコピー

Client:

public class Client {

    private static final Logger logger = LoggerFactory.getLogger(Client.class);

    public static void main(String[] args) throws Exception {

        final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("storm-client.xml");
        final HelloService helloService = (HelloService) context.getBean("helloService");
        String result = helloService.sayHello("World");
        System.out.println(result);
        for (;;) {

        }
    }
}
ログイン後にコピー

结果

生产者:

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。
图片

消费者:

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。
图片

注册中心:

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。
图片

总结

本文简单介绍了 RPC 的整个流程,并实现了一个简单的 RPC 调用。希望阅读完本文之后,能加深你对 RPC 的一些认识。

生产者端流程:

  • サービス インターフェイスとキャッシュをロードします
  • サービス登録、サービス インターフェイスとサービス ホスト情報を登録センターに書き込みます (この例では ZooKeeper を使用します)
  • ネットワーク サーバーを起動し、
  • Reflection をリッスンし、ローカルで呼び出します

コンシューマ側プロセス:

  • プロキシ サービス インターフェイスはプロキシ オブジェクトを生成します
  • サービス検出 (ZooKeeper に接続し、サービス アドレス リストを取得し、適切なサービスを取得します)クライアント ロード ポリシー アドレス)
  • #リモート メソッド呼び出し (この例では、Netty 経由でメッセージを送信し、応答結果を取得します)
#

以上がアリババのインタビュアー: RPC フレームワークを手書きで書いてください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

手書きの RPC フレームワークは、実際には 13 歳のふりをするためだけのものではありません。 手書きの RPC フレームワークは、実際には 13 歳のふりをするためだけのものではありません。 Aug 16, 2023 pm 05:01 PM

あなたは質問されていないかもしれません、おそらくあなたは幸運であるかもしれません、あるいはあなたはまだこのレベルに達していないかもしれません。通常、月給は20,000以上で、基本的にデザインに関する質問をいくつか受けます。面接官の観点から: この種の質問をすることは、多くの技術的なポイントを含む 8 部構成のエッセイを書くよりも効果的です。例: デザイン パターン、通信プロトコル、動的エージェント、仮想化、スレッド プールなどに関する知識。

アリババのインタビュアー: RPC フレームワークを手書きで書いてください。 アリババのインタビュアー: RPC フレームワークを手書きで書いてください。 Aug 17, 2023 pm 04:24 PM

RPC はコンピュータ通信プロトコルです。このプロトコルを使用すると、開発者がこの対話を追加でプログラムすることなく、あるコンピュータ上で実行されているプログラムが別のコンピュータ上のサブルーチンを呼び出すことができます。

Go 言語の RPC フレームワークの原理と応用 Go 言語の RPC フレームワークの原理と応用 Jun 01, 2023 pm 03:01 PM

1. RPC フレームワークの概念 分散システムでは、異なるサーバーとクライアントの間でデータを転送する必要があることが多く、RPC (RemoteProcedureCall) フレームワークは一般的に使用される技術手段です。 RPC フレームワークを使用すると、アプリケーションはリモート メッセージングを通じて別の実行環境の関数やメソッドを呼び出すことができるため、プログラムを別のコンピュータで実行できるようになります。現在、Google の gRPC、Thrift、Hessian など、多くの RPC フレームワークが市場に出回っています。この記事では主に

PHP で RPC フレームワークを開発するにはどうすればよいですか? PHP で RPC フレームワークを開発するにはどうすればよいですか? May 13, 2023 pm 03:22 PM

RPC (RemoteProcedureCall) は、異なるプロセスが異なる物理マシン上のネットワークを介して通信および共同作業できるようにするプロセス間通信プロトコルです。 RPC フレームワークは、開発者が分散システムの開発を容易に実装できるため、ますます注目を集めています。この記事では、PHP を使用して RPC フレームワークを開発する方法を段階的に紹介します。 1. RPC フレームワークとは何ですか? RPC フレームワークは、リモート プロシージャ コールを実装するために使用されるフレームワークです。 RPCベースの場合

どのような RPC フレームワークがありますか? どのような RPC フレームワークがありますか? Aug 03, 2023 am 10:17 AM

RPC フレームワークには次のものが含まれます: 1. Google によって開発された高性能のオープンソース RPC フレームワークである gRPC; 2. Facebook によって開発されオープンソース化されているクロス言語 RPC フレームワークである Apache Thrift; 3. 高パフォーマンスのオープンソース RPC フレームワークである Apache Dubboパフォーマンス、軽量な RPC フレームワーク、大規模な分散システムに適しています; 4. Apache Axis2、Web サービス標準に基づく RPC フレームワーク; 5. Spring Cloud、分散システムを構築するためのオープンソース フレームワーク。

Go 言語 RPC フレームワークの評価: パフォーマンス、使いやすさ、コミュニティ サポートの比較 Go 言語 RPC フレームワークの評価: パフォーマンス、使いやすさ、コミュニティ サポートの比較 Feb 27, 2024 pm 09:12 PM

Go 言語は重要な現代プログラミング言語として、分散システム開発でますます使用されています。分散システムを構築する場合、多くの場合、RPC (リモート プロシージャ コール) フレームワークの選択が重要になります。この記事では、現在主流の Go 言語 RPC フレームワークの水平評価を実施し、パフォーマンス、使いやすさ、コミュニティ サポートの観点から長所と短所を比較し、具体的なコード例を添付します。 1. パフォーマンスの比較 分散システムでは、多くの場合、パフォーマンスは開発者が注目する主要な指標の 1 つです。以下は主なものです

Go 言語で同時実行性の高い RPC フレームワークを実装する方法 Go 言語で同時実行性の高い RPC フレームワークを実装する方法 Aug 05, 2023 pm 12:49 PM

Go 言語で高同時実行 RPC フレームワークを実装する方法の紹介: インターネットの急速な発展に伴い、高同時実行アプリケーションがますます注目を集めています。 RPC (RemoteProcedureCall) フレームワークを使用するのが一般的な解決策です。この記事では、Go 言語で同時実行性の高い RPC フレームワークを実装する方法をコード例とともに紹介します。 RPC フレームワークの紹介: RPC は、コンピュータ プログラムが別のアドレス空間 (通常はリモート コンピュータ上にある) にあるサブルーチンを、何もせずに呼び出すことを可能にする通信プロトコルです。

PHP7.0 の RPC フレームワークとは何ですか? PHP7.0 の RPC フレームワークとは何ですか? May 29, 2023 am 11:10 AM

コンピュータ技術の継続的な発展に伴い、分散システムが主流になり、リモート プロシージャ コール (RPC) は分散システムを実装する重要な手段です。人気の Web プログラミング言語として、PHP には独自の RPC フレームワークもあり、その中には PHP7.0 バージョンでいくつかの新しい RPC フレームワークが導入されました。この記事では、PHP7.0における一般的なRPCフレームワークとその特徴を紹介します。 PHPRemoteProcedureCall(phpRPC)phpRPC は軽量 RP です

See all articles