基于Netty 手写 Dubbo 框架

ゞ 浴缸里的玫瑰 2022-02-05 02:25 327阅读 0赞

基于Netty 手写 Dubbo 框架

1、Dubbo是什么,解决什么样的问题?

为了解决模块拆分后,彼此远程调用的问题。

RPC -> Remote Procedure Call 远程调用,常见的RPC框架有:

阿里的:dubbo。

当当的:dubbox。

谷歌的:grpc。

SpringCloud(一站式开发)等。

2、实现方案

查看官网dubbo结构图

在这里插入图片描述

1、首先通过register将服务提供者的url注册到Registry注册中心中。

2、客户端Consumer从注册中心获取被调用服务端注册信息,如:接口名称,URL地址等信息。

3、将获取的url地址返回到Consumer客户端,客户端通过获取的URL地址支持invoke反射机制获取服务的实现。

3、整体项目结构信息

  1. |-- netty-to-dubbo
  2. |-- netty-dubbo-api
  3. |-- cn.org.july.netty.dubbo.api
  4. |-- Iservice : 对外服务暴露接口
  5. |-- RpcRequest :服务请求对象Bean
  6. |-- netty-dubbo-common
  7. |-- cn.org.july.netty.dubbo.annotation
  8. |-- RpcAnnotation : 定义一个接口标识注解
  9. |-- netty-dubbo-server
  10. |-- cn.org.july.netty.dubbo.registry
  11. |-- IRegisterCenter :服务注册接口
  12. |-- RegisterCenterImpl:服务注册实现类
  13. |-- ZkConfigZK配置文件
  14. |-- cn.org.july.netty.dubbo.rpc
  15. |-- NettyRpcServer:基于netty实现的Rpc通讯服务
  16. |-- RpcServerHandlerRpc服务处理流程
  17. |-- cn.org.july.netty.dubbo.service
  18. |-- ServiceImpl:对外接口IService接口实现类
  19. |-- netty-dubbo-client
  20. |-- cn.org.july.netty.dubbo.loadbalance
  21. |-- LoadBalance :负载均衡实现接口
  22. |-- RandomLoadBalance:负载均衡实现类随机获取服务提供者
  23. |-- cn.org.july.netty.dubbo.proxy
  24. |-- RpcClientProxynetty客户端通讯组件
  25. |-- RpcProxyHandlernetty与服务端通讯消息处理组件
  26. |-- cn.org.july.netty.dubbo.registry
  27. |-- IServiceDiscover:从注册中心获取注册的服务接口
  28. |-- ServiceDiscoverImpl:接口IServiceDiscover的实现类
  29. |-- ZkConfigzk配置文件。

4、服务提供者Provider

4.1、实现Iservice接口

首先我们看下Iservice接口的内容:

  1. package cn.org.july.netty.dubbo.api;
  2. /** * @author july_whj */
  3. public interface IService {
  4. /** * 计算加法 */
  5. int add(int a, int b);
  6. /** * @param msg */
  7. String sayHello(String msg);
  8. }

我们编写ServiceImpl实现以上两个接口类。

  1. package cn.org.july.netty.dubbo.service;
  2. import cn.org.july.netty.dubbo.annotation.RpcAnnotation;
  3. import cn.org.july.netty.dubbo.api.IService;
  4. /** * @author july_whj */
  5. @RpcAnnotation(IService.class)
  6. public class ServiceImpl implements IService {
  7. @Override
  8. public int add(int a, int b) {
  9. return a + b;
  10. }
  11. @Override
  12. public String sayHello(String msg) {
  13. System.out.println("rpc say :" + msg);
  14. return "rpc say: " + msg;
  15. }
  16. }

该类实现比较简单,不做多处理,下面分析服务注册。

4.2、服务注册到ZK

​ 首先我们定义一个接口类IRegisterCenter,里面定义一个registry方法,该方法实现服务注册。服务注册需要将服务的名称、服务的地址注册到注册中心中,我们定义接口如下:

  1. package cn.org.july.netty.dubbo.registry;
  2. /** * @author july_whj */
  3. public interface IRegisterCenter {
  4. /** * 服务注册 * @param serverName 服务名称(实现方法路径) * @param serviceAddress 服务地址 */
  5. void registry(String serverName,String serviceAddress);
  6. }

​ 第二,我们使用zookeerper作为服务注册中心,在netty-dubbo-server模块中引入zk的客户端操作类,pom文件如下:

  1. <dependency>
  2. <groupId>org.apache.curator</groupId>
  3. <artifactId>curator-recipes</artifactId>
  4. <version>2.5.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.curator</groupId>
  8. <artifactId>curator-framework</artifactId>
  9. <version>2.5.0</version>
  10. </dependency>

注意:这里版本使用的2.5.0,我使用的zk版,

​ 第三,实现该接口编写接口实现类RegisterCenterImpl

  1. package cn.org.july.netty.dubbo.registry;
  2. import org.apache.curator.framework.CuratorFramework;
  3. import org.apache.curator.framework.CuratorFrameworkFactory;
  4. import org.apache.curator.retry.ExponentialBackoffRetry;
  5. import org.apache.zookeeper.CreateMode;
  6. /** * @author july_whj */
  7. public class RegisterCenterImpl implements IRegisterCenter {
  8. private CuratorFramework curatorFramework;
  9. {
  10. curatorFramework = CuratorFrameworkFactory.builder()
  11. .connectString(ZkConfig.addr).sessionTimeoutMs(4000)
  12. .retryPolicy(new ExponentialBackoffRetry(1000, 10)).build();
  13. curatorFramework.start();
  14. }
  15. @Override
  16. public void registry(String serverName, String serviceAddress) {
  17. String serverPath = ZkConfig.ZK_REGISTER_PATH.concat("/").concat(serverName);
  18. try {
  19. if (curatorFramework.checkExists().forPath(serverPath) == null) {
  20. curatorFramework.create().creatingParentsIfNeeded()
  21. .withMode(CreateMode.PERSISTENT).forPath(serverPath, "0".getBytes());
  22. }
  23. String addr = serverPath.concat("/").concat(serviceAddress);
  24. String rsNode = curatorFramework.create().withMode(CreateMode.EPHEMERAL)
  25. .forPath(addr, "0".getBytes());
  26. System.out.println("服务注册成功," + rsNode);
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }

我们分析下以上代码:

​ 定义一个CuratorFramework对象,通过代码块来实例化该对象,并通过curatorFramework.start();来连接ZKConfig中配置好的地址连接ZK。

​ 使用zk作为注册中心,我们了解下ZK的存储结构。zookeeper的命名空间的结构和文件系统很像。一个名字和文件一样使用/的路径表现,zookeeper的每个节点都是被路径唯一标识的。

在这里插入图片描述
​ 分析一下registry方法,首先从ZkConfig中获取要注册数据的根节点信息,并将该信息和服务名称进行拼接,判断该路径是否存在,如果不存在使用PERSISTENT方式创建该服务名称路径信息。PERSISTENT方式为持久方式,我们使用这种方式创建因为服务名称不是动态变化的,不用每次去监听它的变化。而我们服务的地址是有可能存在多个,并且有可能发生变化,我们使用EPHEMERAL方式来创建服务的实现地址。

​ 我们将ServiceImpl服务注册到zk上,我们首先获取这个服务的服务名称,和服务实现的地址,将该服务的服务名称和服务地址注册到zk上,下面看下我们的注册服务的测试类RegTest

  1. import cn.org.july.netty.dubbo.registry.IRegisterCenter;
  2. import cn.org.july.netty.dubbo.registry.RegisterCenterImpl;
  3. import java.io.IOException;
  4. public class RegTest {
  5. public static void main(String[] args) throws IOException {
  6. IRegisterCenter registerCenter = new RegisterCenterImpl();
  7. registerCenter.registry("cn.org.july.test", "127.0.0.1:9090");
  8. System.in.read();
  9. }
  10. }

​ 我们将cn.org.july.test服务,和服务实现的地址127.0.0.1:9090注册到zk中。

看下服务执行效果:

在这里插入图片描述
服务端显示注册成功,我们看以下zk服务中有没有该数据,
在这里插入图片描述

最后,我们可以看到数据注册成功。

4.3、实现NettyRpcServer

​ 我们要将ServiceImpl服务发布到zk上,并通过netty监听某个端口信息。

​ 我们先看下

  1. package cn.org.july.netty.dubbo.rpc;
  2. import cn.org.july.netty.dubbo.annotation.RpcAnnotation;
  3. import cn.org.july.netty.dubbo.registry.IRegisterCenter;
  4. import io.netty.bootstrap.ServerBootstrap;
  5. import io.netty.channel.*;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.nio.NioServerSocketChannel;
  8. import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
  9. import io.netty.handler.codec.LengthFieldPrepender;
  10. import io.netty.handler.codec.serialization.ClassResolvers;
  11. import io.netty.handler.codec.serialization.ObjectDecoder;
  12. import io.netty.handler.codec.serialization.ObjectEncoder;
  13. import java.util.HashMap;
  14. import java.util.Map;
  15. /** * @author july_whj */
  16. public class NettyRpcServer {
  17. private IRegisterCenter registerCenter;
  18. private String serviceAddress;
  19. private Map<String, Object> handlerMap = new HashMap<>(16);
  20. public NettyRpcServer(IRegisterCenter registerCenter, String serviceAddress) {
  21. this.registerCenter = registerCenter;
  22. this.serviceAddress = serviceAddress;
  23. }
  24. /** * 发布服务 */
  25. public void publisher() {
  26. for (String serviceName : handlerMap.keySet()) {
  27. registerCenter.registry(serviceName, serviceAddress);
  28. }
  29. try {
  30. EventLoopGroup bossGroup = new NioEventLoopGroup();
  31. EventLoopGroup workerGroup = new NioEventLoopGroup();
  32. //启动netty服务
  33. ServerBootstrap bootstrap = new ServerBootstrap();
  34. bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
  35. bootstrap.childHandler(new ChannelInitializer<Channel>() {
  36. @Override
  37. protected void initChannel(Channel channel) throws Exception {
  38. ChannelPipeline channelPipeline = channel.pipeline();
  39. channelPipeline.addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
  40. channelPipeline.addLast(new ObjectEncoder());
  41. channelPipeline.addLast(new RpcServerHandler(handlerMap));
  42. }
  43. }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
  44. String[] addr = serviceAddress.split(":");
  45. String ip = addr[0];
  46. int port = Integer.valueOf(addr[1]);
  47. ChannelFuture future = bootstrap.bind(ip, port).sync();
  48. System.out.println("服务启动,成功。");
  49. future.channel().closeFuture().sync();
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. /** * 子对象的实现 * * @param services 对象实现类 */
  55. public void bind(Object... services) {
  56. //将实现类通过注解获取实现类的名称、实现类的实现放入map集合中。
  57. for (Object service : services) {
  58. RpcAnnotation annotation = service.getClass().getAnnotation(RpcAnnotation.class);
  59. String serviceName = annotation.value().getName();
  60. handlerMap.put(serviceName, service);
  61. }
  62. }
  63. }

分析下以上代码:

​ 通过bind方法,将服务提供者通过RpcAnnotation注解获取服务名称,并将服务名称,服务实现类放入handlerMap 中。

​ 通过publisher方法,获取handlerMap 中的服务实现,将这些服务实现通过registerCenter.registry(serviceName, serviceAddress)将这些服务注册到zk注册中心中,完成服务的注册。下面代码是netty的基础代码,创建两个工作线程池,启动netty服务,通过channelPipeline定义序列化对象和RpcServerHandler实现。这里不做过多解析。

​ 我们看下RpcServerHandler的代码实现。

  1. package cn.org.july.netty.dubbo.rpc;
  2. import cn.org.july.netty.dubbo.api.RpcRequest;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.channel.ChannelInboundHandlerAdapter;
  5. import java.io.UnsupportedEncodingException;
  6. import java.lang.reflect.Method;
  7. import java.nio.Buffer;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. public class RpcServerHandler extends ChannelInboundHandlerAdapter {
  11. private Map<String, Object> handlerMap = new HashMap<>();
  12. public RpcServerHandler(Map<String, Object> handlerMap) {
  13. this.handlerMap = handlerMap;
  14. }
  15. @Override
  16. public void channelActive(ChannelHandlerContext ctx) throws UnsupportedEncodingException {
  17. System.out.println("channelActive:" + ctx.channel().remoteAddress());
  18. }
  19. @Override
  20. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  21. System.out.println("服务端接收到消息:" + msg);
  22. RpcRequest rpcRequest = (RpcRequest) msg;
  23. Object result = new Object();
  24. if (handlerMap.containsKey(rpcRequest.getClassName())) {
  25. Object clazz = handlerMap.get(rpcRequest.getClassName());
  26. Method method = clazz.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
  27. result = method.invoke(clazz, rpcRequest.getParams());
  28. }
  29. ctx.write(result);
  30. ctx.flush();
  31. ctx.close();
  32. }
  33. }

​ 这里复写了channelRead方法,接收客户端传递的RpcRequest对象信息。下面判断handlerMap中是否存在客户端调用的实现类,如果存在通过反射机制获取服务端实现类,通过invoke方法调用方法实现,并将执行结果result对象通过ctx.write(result);将执行结果返回客户端。

4.4、编写服务启动类ServerTest

  1. import cn.org.july.netty.dubbo.api.IService;
  2. import cn.org.july.netty.dubbo.registry.IRegisterCenter;
  3. import cn.org.july.netty.dubbo.registry.RegisterCenterImpl;
  4. import cn.org.july.netty.dubbo.rpc.NettyRpcServer;
  5. import cn.org.july.netty.dubbo.service.ServiceImpl;
  6. /** * Created with IntelliJ IDEA. * User: wanghongjie * Date: 2019/5/3 - 23:03 * <p> * Description: */
  7. public class ServerTest {
  8. public static void main(String[] args) {
  9. IService service = new ServiceImpl();
  10. IRegisterCenter registerCenter = new RegisterCenterImpl();
  11. NettyRpcServer rpcServer = new NettyRpcServer(registerCenter, "127.0.0.1:8080");
  12. rpcServer.bind(service);
  13. rpcServer.publisher();
  14. }
  15. }

启动netty服务,将服务实现类service通过bind方法绑定到handlerMap中,通过publisher方法,将service、服务实现地址发布到zk,并启动netty服务,监听8080端口。

5、实现服务消费者

​ 做为服务消费者,我们首先要连接zk注册中心,获取服务实现的地址,并实时监听获取最新的地址信息。通过远程调用实现该服务。如果服务实现是多个我们需实现客户端负载,选取我们的服务地址。

5.1、负载均衡实现

​ 定义loadbalance接口.

  1. package cn.org.july.netty.dubbo.loadbalance;
  2. import java.util.List;
  3. public interface LoadBalance {
  4. String select(List<String> repos);
  5. }

​ 定义select选择方法。

通过RandomLoadBalance 实现loadbalance接口,从实现名称可以看到Random随机获取。

  1. package cn.org.july.netty.dubbo.loadbalance;
  2. import java.util.List;
  3. import java.util.Random;
  4. public class RandomLoadBalance implements LoadBalance {
  5. @Override
  6. public String select(List<String> repos) {
  7. int len = repos.size();
  8. if (len == 0)
  9. throw new RuntimeException("未发现注册的服务。");
  10. Random random = new Random();
  11. return repos.get(random.nextInt(len));
  12. }
  13. }

5.2、获取注册中心服务注册信息

​ 定义IServiceDiscover接口,定义discover方法,进行服务发现。

  1. package cn.org.july.netty.dubbo.registry;
  2. public interface IServiceDiscover {
  3. String discover(String serviceName);
  4. }

通过ServiceDiscoverImpl实现IServiceDiscover接口。

  1. package cn.org.july.netty.dubbo.registry;
  2. import cn.org.july.netty.dubbo.loadbalance.LoadBalance;
  3. import cn.org.july.netty.dubbo.loadbalance.RandomLoadBalance;
  4. import org.apache.curator.framework.CuratorFramework;
  5. import org.apache.curator.framework.CuratorFrameworkFactory;
  6. import org.apache.curator.framework.recipes.cache.PathChildrenCache;
  7. import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
  8. import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
  9. import org.apache.curator.retry.ExponentialBackoffRetry;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /** * @author july_whj */
  13. public class ServiceDiscoverImpl implements IServiceDiscover {
  14. List<String> repos = new ArrayList<String>();
  15. private CuratorFramework curatorFramework;
  16. public ServiceDiscoverImpl() {
  17. curatorFramework = CuratorFrameworkFactory.builder()
  18. .connectString(ZkConfig.addr).sessionTimeoutMs(4000)
  19. .retryPolicy(new ExponentialBackoffRetry(1000, 10))
  20. .build();
  21. curatorFramework.start();
  22. }
  23. @Override
  24. public String discover(String serviceName) {
  25. String path = ZkConfig.ZK_REGISTER_PATH.concat("/").concat(serviceName);
  26. try {
  27. repos = curatorFramework.getChildren().forPath(path);
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. registerWatch(path);
  32. LoadBalance loadBalance = new RandomLoadBalance();
  33. return loadBalance.select(repos);
  34. }
  35. /** * 监听ZK节点内容刷新 * * @param path 路径 */
  36. private void registerWatch(final String path) {
  37. PathChildrenCache childrenCache = new PathChildrenCache(curatorFramework, path, true);
  38. PathChildrenCacheListener childrenCacheListener = new PathChildrenCacheListener() {
  39. @Override
  40. public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
  41. repos = curatorFramework.getChildren().forPath(path);
  42. }
  43. };
  44. childrenCache.getListenable().addListener(childrenCacheListener);
  45. try {
  46. childrenCache.start();
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }

​ 和服务注册同样定义CuratorFramework对象,并通过curatorFramework.start();连接ZK。

连接成功后通过zk注册的根节点加服务名称,获取该服务的服务地址。

​ 获取的服务地址有可能不是最新的服务地址,我们需要监听zk节点的内容刷新,通过调用registerWatch方法,监听该节点的数据变化。

​ 最后,将获取到的地址集合,通过LoadBalance随机选出一个地址,实现该服务。

5.3、客户端netty实现RPC远程调用

定义客户端实现类RpcClientProxy.

  1. package cn.org.july.netty.dubbo.proxy;
  2. import cn.org.july.netty.dubbo.api.RpcRequest;
  3. import cn.org.july.netty.dubbo.registry.IServiceDiscover;
  4. import io.netty.bootstrap.Bootstrap;
  5. import io.netty.channel.*;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.SocketChannel;
  8. import io.netty.channel.socket.nio.NioSocketChannel;
  9. import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
  10. import io.netty.handler.codec.LengthFieldPrepender;
  11. import io.netty.handler.codec.serialization.ClassResolvers;
  12. import io.netty.handler.codec.serialization.ObjectDecoder;
  13. import io.netty.handler.codec.serialization.ObjectEncoder;
  14. import java.lang.reflect.InvocationHandler;
  15. import java.lang.reflect.Method;
  16. import java.lang.reflect.Proxy;
  17. /** * Created with IntelliJ IDEA. * User: wanghongjie * Date: 2019/5/3 - 23:08 * <p> * Description: */
  18. public class RpcClientProxy {
  19. private IServiceDiscover serviceDiscover;
  20. public RpcClientProxy(IServiceDiscover serviceDiscover) {
  21. this.serviceDiscover = serviceDiscover;
  22. }
  23. public <T> T create(final Class<T> interfaceClass) {
  24. return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
  25. new Class<?>[]{ interfaceClass}, new InvocationHandler() {
  26. //封装RpcRequest请求对象,然后通过netty发送给服务等
  27. @Override
  28. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  29. RpcRequest rpcRequest = new RpcRequest();
  30. rpcRequest.setClassName(method.getDeclaringClass().getName());
  31. rpcRequest.setMethodName(method.getName());
  32. rpcRequest.setTypes(method.getParameterTypes());
  33. rpcRequest.setParams(args);
  34. //服务发现,zk进行通讯
  35. String serviceName = interfaceClass.getName();
  36. //获取服务实现url地址
  37. String serviceAddress = serviceDiscover.discover(serviceName);
  38. //解析ip和port
  39. System.out.println("服务端实现地址:" + serviceAddress);
  40. String[] arrs = serviceAddress.split(":");
  41. String host = arrs[0];
  42. int port = Integer.parseInt(arrs[1]);
  43. System.out.println("服务实现ip:" + host);
  44. System.out.println("服务实现port:" + port);
  45. final RpcProxyHandler rpcProxyHandler = new RpcProxyHandler();
  46. //通过netty方式进行连接发送数据
  47. EventLoopGroup group = new NioEventLoopGroup();
  48. try {
  49. Bootstrap bootstrap = new Bootstrap();
  50. bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
  51. .handler(new ChannelInitializer<SocketChannel>() {
  52. @Override
  53. protected void initChannel(SocketChannel socketChannel) throws Exception {
  54. ChannelPipeline channelPipeline = socketChannel.pipeline();
  55. channelPipeline.addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
  56. channelPipeline.addLast(new ObjectEncoder());
  57. //netty实现代码
  58. channelPipeline.addLast(rpcProxyHandler);
  59. }
  60. });
  61. ChannelFuture future = bootstrap.connect(host, port).sync();
  62. //将封装好的对象写入
  63. future.channel().writeAndFlush(rpcRequest);
  64. future.channel().closeFuture().sync();
  65. } catch (Exception e) {
  66. } finally {
  67. group.shutdownGracefully();
  68. }
  69. return rpcProxyHandler.getResponse();
  70. }
  71. });
  72. }
  73. }

​ 我们看下create方法,通过动态代理newProxyInstance方法,传入待调用的接口对象,获取getClassLoader后,实现invoke方法。定义RpcRequest对象,封装请求参数。通过interfaceClass对象获取服务实现名称,调用discover方法获取服务提供者的地址信息,netty通过该信息连接服务,并将RpcRequest对象发送到服务端,服务端解析对象,获取接口请求参数等信息,执行方法,并将结果返回到客户端RpcProxyHandler对象接收返回结果。RpcProxyHandler代码实现:

  1. package cn.org.july.netty.dubbo.proxy;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. /** * Created with IntelliJ IDEA. * User: wanghongjie * Date: 2019/5/3 - 23:21 * <p> * Description: */
  5. public class RpcProxyHandler extends ChannelInboundHandlerAdapter {
  6. private Object response;
  7. public Object getResponse() {
  8. return response;
  9. }
  10. @Override
  11. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  12. //将服务端返回的内容返回
  13. response = msg;
  14. }
  15. }

​ 我们复写channelRead方法,获取服务端返回的结果信息msg,并将msg赋值给response,通过getResponse获取返回信息。

5.4、客户单调用测试

  1. import cn.org.july.netty.dubbo.api.IService;
  2. import cn.org.july.netty.dubbo.proxy.RpcClientProxy;
  3. import cn.org.july.netty.dubbo.registry.IServiceDiscover;
  4. import cn.org.july.netty.dubbo.registry.ServiceDiscoverImpl;
  5. /** * Created with IntelliJ IDEA. * User: wanghongjie * Date: 2019/5/3 - 23:06 * <p> * Description: */
  6. public class ClientTest {
  7. public static void main(String[] args) {
  8. IServiceDiscover serviceDiscover = new ServiceDiscoverImpl();
  9. RpcClientProxy rpcClientProxy = new RpcClientProxy(serviceDiscover);
  10. IService iService = rpcClientProxy.create(IService.class);
  11. System.out.println(iService.sayHello("netty-to-dubbo"));
  12. System.out.println(iService.sayHello("你好"));
  13. System.out.println(iService.sayHello("成功咯,很高兴"));
  14. System.out.println(iService.add(10, 4));
  15. }
  16. }

我们看下执行效果。​

服务端启动:

在这里插入图片描述

客户单调用:
在这里插入图片描述
远程调用完成。
源码地址:传送门

发表评论

表情:
评论列表 (有 0 条评论,327人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Javadubbo框架

    前言 在对dubbo有了较为深入的使用和理解后,来尝试从dubbo框架的角度重新认识下它,对照着dubbo官方的这张图进行反复的理解后,我们可以从已有掌握的技术出发,来尝

    相关 RPC框架(netty+zookeeper)

      RPC是什么?远程过程调用,过程就是业务处理、计算任务,像调用本地方法一样调用远程的过程。   RMI和RPC的区别是什么?RMI是远程方法调用,是oop领域中RPC的一