實現(xiàn)一個簡單Dubbo完整過程詳解
Dubbo
Dubbo最早的定位是rpc框架,即遠程服務調用,解決的是跨服務之間的方法調用問題,本文還是在這個定位基礎上嘗試手寫一個簡單的Dubbo
需求
首先要搭建測試的項目結構,兩個服務consumer
和provider
,分別代表調用方和提供方,二者功能依賴于interface
,其中暴露接口
interface
包中定義一個接口
// interface public interface HelloService { String sayHello(String name); }
provider
實現(xiàn)
// provider public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "hello "+name; } }
consumer
調用
// consumer public class Consumer { public static void main(String[] args) { // todo 獲取不到HelloService的實現(xiàn) HelloService helloService = null; System.out.println(helloService.sayHello("pq")); } }
當前的需求即consumer服務調用provider服務里sayHello
方法的實現(xiàn),顯然當前無法實現(xiàn),這是一種遠程發(fā)放調用,我們在新建一個Module命名為dubbo
,意圖通過依賴它來實現(xiàn)遠程方法的調用
dubbo
網(wǎng)絡
由于跨服務了,所以遠程調用必然是要走網(wǎng)絡的,dubbo使用了netty,我們也用netty來實現(xiàn)通訊
首先定義網(wǎng)絡請求的數(shù)據(jù),遠程調用需要的信息:哪個類,哪個方法,什么參數(shù),我們把這些信息封裝一下
// dubbo @Data @AllArgsConstructor public class Invocation implements Serializable { private String className; private String methodName; private Class<?>[] paramTypes; private Object[] args; }
服務端
provider
作為服務的提供方,需要依靠netty搭建一個服務器,當接受到請求(Invocation對象)時,可以根據(jù)className,methodName等信息找到對應的本地方法進行調用
所以provider
首先要維護一個map存儲className和class的對應關系,這樣在收到請求時可以通過className找到對應的類,再通過反射獲取對應的方法進行調用
在我們的dubbo框架中封裝這么一個map結構供provider
使用
// dubbo public class LocalRegister { private static Map<String, Object> map = new HashMap<String, Object>(); public static void register(String className, Object impl) { map.put(className, impl); } public static Object get(String className) { return map.get(className); } }
然后再做一個處理請求netty服務供provider
使用
// dubbo public class NettyServer { public void start(Integer port) { try { final ServerBootstrap bootstrap = new ServerBootstrap(); EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("bossGroup", true)); EventLoopGroup workerGroup = new NioEventLoopGroup(10, new DefaultThreadFactory("workerGroup", true)); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers .weakCachingConcurrentResolver(this.getClass() .getClassLoader()))); channel.pipeline().addLast("encoder", new ObjectEncoder()); channel.pipeline().addLast("handler", new RequestHandler()); } }); ChannelFuture cf = bootstrap.bind(port).sync(); cf.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } }
對應的handler如下
// dubbo public class RequestHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Invocation invocation = (Invocation) msg; // 根據(jù)className獲取寄存的服務對象 Object serviceImpl = LocalRegister.get(invocation.getClassName()); // 通過methodName等信息獲取對應的方法 Method method = serviceImpl.getClass().getMethod(invocation.getMethodName(), invocation.getParamTypes()); // 調用方法 Object result = method.invoke(serviceImpl, invocation.getArgs()); // 返回服務結果 ctx.writeAndFlush(result); } }
provider
啟動類Starter
// provider public class Starter { public static void main(String[] args) { // 存儲服務于名字映射關系 HelloServiceImpl helloService = new HelloServiceImpl(); String className = HelloService.class.getName(); LocalRegister.register(className, helloService); // 開啟netty服務 NettyServer nettyServer = new NettyServer(); System.out.println("provider 端口號9001"); nettyServer.start(9001); } }
代理
consumer
只能拿到到HelloService接口,那么實例化的方法可以采用jdk動態(tài)代理生成代理實現(xiàn),而代理的實際執(zhí)行方式是通過netty網(wǎng)絡發(fā)送請求給provider
,
首先還是在dubbo框架中封裝一個netty的客戶端供consumer
發(fā)起請求
// dubbo @Setter public class NettyClient { /** * 管道上下文 */ private volatile ChannelHandlerContext channelHandlerContext; /** * 返回消息暫存 */ private Object message; public void start(String hostName, Integer port) { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers .weakCachingConcurrentResolver(this.getClass() .getClassLoader()))); channel.pipeline().addLast("encoder", new ObjectEncoder()); channel.pipeline().addLast(new ResponseHandler(NettyClient.this)); } }); bootstrap.connect(hostName, port).sync(); } catch (Exception e) { e.printStackTrace(); } } /** * 發(fā)送遠程調用 * @param hostName * @param port * @param invocation * @return */ public synchronized String send(String hostName, Integer port, Invocation invocation) { start(hostName, port); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 發(fā)送數(shù)據(jù) channelHandlerContext.writeAndFlush(invocation); // 等待 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 返回數(shù)據(jù) return message.toString(); } }
其中的ResponseHandler
入下
// dubbo public class ResponseHandler extends ChannelInboundHandlerAdapter { private final NettyClient client; public ResponseHandler(NettyClient client) { this.client = client; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { synchronized (client) { client.notify(); } client.setChannelHandlerContext(ctx); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { client.setMessage(msg); synchronized (client) { client.notify(); } } }
然后在我們的dubbo
框架中實現(xiàn)創(chuàng)建代理
// dubbo public class ProxyFactory { /** * 根據(jù)接口創(chuàng)建代理 jdk動態(tài)代理 * @param interfaceClass * @param <T> * @return */ public static <T> T getProxy(final Class<T> interfaceClass) { return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 請求封裝成對象 Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(), method.getParameterTypes(), args); NettyClient nettyClient = new NettyClient(); // 發(fā)起網(wǎng)絡請求 String response = nettyClient.send("127.0.0.1", 9001, invocation); return response; } }); } }
最后回到consumer
添加啟動類,通過代理創(chuàng)建HelloService的實現(xiàn),嘗試調用provider
的sayHello方法
// consumer public class Consumer { public static void main(String[] args) { HelloService helloService = ProxyFactory.getProxy(HelloService.class); System.out.println(helloService.sayHello("pq")); } }
測試
- 啟動
provider
,輸出如下
provider
- 啟動
consumer
,輸出如下
consumer
證明已實現(xiàn)跨遠程方法調用~
以上就是實現(xiàn)一個簡單Dubbo完整過程詳解的詳細內(nèi)容,更多關于Dubbo實現(xiàn)完整過程的資料請關注腳本之家其它相關文章!
相關文章
教你使用java將excel數(shù)據(jù)導入MySQL
今天教大家如何使用Java將excel數(shù)據(jù)導入MySQL,文中有非常詳細的代碼示例,對正在學習java的小伙伴呢很有幫助,需要的朋友可以參考下2021-05-05Java畢業(yè)設計實戰(zhàn)項目之倉庫管理系統(tǒng)的實現(xiàn)流程
這是一個使用了java+SSM+Maven+Bootstrap+mysql開發(fā)的倉庫管理系統(tǒng),是一個畢業(yè)設計的實戰(zhàn)練習,具有一個倉庫管理系統(tǒng)該有的所有功能,感興趣的朋友快來看看吧2022-01-01java實現(xiàn)十六進制字符unicode與中英文轉換示例
當需要對一個unicode十六進制字符串進行編碼時,首先做的應該是確認字符集編碼格式,在無法快速獲知的情況下,通過一下的str4all方法可以達到這一目的2014-02-02jackson在springboot中的使用方式-自定義參數(shù)轉換器
這篇文章主要介紹了jackson在springboot中的使用方式-自定義參數(shù)轉換器,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10