完整源码:

应用场景描述:

  1. 服务提供者在项目启动时,创建并启动一个TCP服务器,然后将自己提供的所有服务注册到注册中心。

这里有3个角色:

  1. 1.服务提供者
  2. 2.TCP服务器
  3. 3.注册中心

引出回调:

  1. 服务提供者,要求TCP服务器启动成功之后,回调一下"注册服务"的逻辑。

开始撸码:

首先定义一个回调基类,这是一个抽象类,里面只有一个抽象的回调函数,是将来回调时要实现的方法

  1. /**
  2. * 回调基类
  3. *
  4. * @author syj
  5. */
  6. public abstract class BaseCallBack {
  7. /**
  8. * 回调执行逻辑
  9. *
  10. * @throws Exception
  11. */
  12. public abstract void run() throws Exception;
  13. }

谁来调用这个回调函数呢,前面说了,TCP服务器启动之后要调用这个回调函数,所以回调的操作要在TCP服务器中完成。

TCP服务器是一个具体的服务实现,我们可能会有多种服务器的实现,所以先定义一个抽象的服务类:

  1. /**
  2. * 服务抽象
  3. *
  4. * @author syj
  5. */
  6. public abstract class Server {
  7.  
  8. // 服务启动后回调
  9. private BaseCallBack startedCallBack;
  10.  
  11. // 服务停止后回调
  12. private BaseCallBack stopedCallBack;
  13.  
  14. /**
  15. * 设置服务启动后的回调逻辑
  16. *
  17. * @param startedCallBack
  18. */
  19. public void setStartedCallBack(BaseCallBack startedCallBack) {
  20. this.startedCallBack = startedCallBack;
  21. }
  22.  
  23. /**
  24. * 设置服务停止后的回调逻辑
  25. *
  26. * @param stopedCallBack
  27. */
  28. public void setStopedCallBack(BaseCallBack stopedCallBack) {
  29. this.stopedCallBack = stopedCallBack;
  30. }
  31.  
  32. /**
  33. * 服务启动后回调
  34. */
  35. public void onStarted() {
  36. if (startedCallBack != null) {
  37. try {
  38. startedCallBack.run();
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }
  44.  
  45. /**
  46. * 服务停止后回调
  47. */
  48. public void onStoped() {
  49. if (stopedCallBack != null) {
  50. try {
  51. stopedCallBack.run();
  52. } catch (Exception e) {
  53. e.printStackTrace();
  54. }
  55. }
  56. }
  57.  
  58. /**
  59. * 启动服务
  60. *
  61. * @param provider
  62. * @throws Exception
  63. */
  64. public abstract void start(Provider provider) throws Exception;
  65.  
  66. /**
  67. * 停止服务
  68. *
  69. * @throws Exception
  70. */
  71. public abstract void stop() throws Exception;
  72.  
  73. }

这个服务器抽象类,主要有启动服务和停止服务的方法,还持有两个回调对象,一个是服务器启动后的回调对象,一个是服务器停止后的回调对象。并有两个方法分别去调用这两个回调对象的run方法。

下面定义一个TCP服务器类,它是上面服务器抽象类的一个具体实现类:

  1. /**
  2. * 服务实现类
  3. *
  4. * @author syj
  5. */
  6. public class TcpServerImpl extends Server {
  7.  
  8. /**
  9. * 启动服务
  10. *
  11. * @param provider
  12. */
  13. @Override
  14. public void start(Provider provider) {
  15. System.out.println(">>>> start! " + provider.getTcpSrvAddr() + ":" + provider.getTcpSrvPort());
  16. // 启动后回调
  17. onStarted();
  18.  
  19. }
  20.  
  21. /**
  22. * 停止服务
  23. *
  24. */
  25. @Override
  26. public void stop() {
  27. System.out.println(">>>> stop!");
  28. // 停止后回调
  29. onStoped();
  30. }
  31. }

该类主要有两个功能,一个是启动TCP服务,一个是停止TCP服务,这两个方法的最后都需要触发回调逻辑的执行;

关于具体回调逻辑的定义写在哪里呢?自然是服务的提供者最清楚,所以写在服务提供者类中最合适:

  1. import java.util.TreeSet;
  2.  
  3. /**
  4. * 服务操作
  5. *
  6. * @author syj
  7. */
  8. public class Provider {
  9.  
  10. // 模拟要注册的服务列表
  11. public static TreeSet<String> serviceKeys = new TreeSet<String>() {{
  12. add("userService");
  13. add("productService");
  14. add("orderService");
  15. }};
  16.  
  17. // 模拟本机http服务使用的ip和端口
  18. public static String localAddress = "127.0.0.1:8081";
  19.  
  20. // TCP服务器地址
  21. private String tcpSrvAddr;
  22.  
  23. // TCP服务器端口
  24. private int tcpSrvPort;
  25.  
  26. public String getTcpSrvAddr() {
  27. return tcpSrvAddr;
  28. }
  29.  
  30. public int getTcpSrvPort() {
  31. return tcpSrvPort;
  32. }
  33.  
  34. private Server server;
  35. private Registry registry;
  36.  
  37. public Provider() {
  38. }
  39.  
  40. /**
  41. * 初始化配置
  42. *
  43. * @param tcpSrvAddr
  44. * @param tcpSrvPort
  45. */
  46. public void initConfig(String tcpSrvAddr, int tcpSrvPort) {
  47. this.tcpSrvAddr = tcpSrvAddr;
  48. this.tcpSrvPort = tcpSrvPort;
  49. }
  50.  
  51. /**
  52. * 启动服务
  53. */
  54. public void start() {
  55. try {
  56. registry = Registry.class.newInstance();
  57. server = TcpServerImpl.class.newInstance();
  58. // 设置服务启动后回调逻辑
  59. server.setStartedCallBack(new BaseCallBack() {
  60. @Override
  61. public void run() {
  62. System.out.println(">>>> setStartedCallBack:" + serviceKeys + ":" + localAddress);
  63. // 注册服务
  64. registry.start();
  65. registry.registry(serviceKeys, localAddress);
  66. }
  67. });
  68.  
  69. // 设置服务停止后回调逻辑
  70. server.setStopedCallBack(new BaseCallBack() {
  71. @Override
  72. public void run() {
  73. System.out.println(">>>> setStopedCallBack:" + tcpSrvAddr + ":" + tcpSrvPort);
  74. registry.remove(serviceKeys, localAddress);
  75. }
  76. });
  77.  
  78. // 启动服务
  79. server.start(this);
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. }
  83. }
  84.  
  85. /**
  86. * 停止服务
  87. */
  88. public void stop() {
  89. try {
  90. server.stop();
  91. } catch (Exception e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. }

由于服务提供者需要启动TCP服务器,所以它依赖一个Server对象,在他的start方法中,启动TCP服务之前,先给这个TCP服务设置两个回调回调具体逻辑,就是前面说的一个是TCP服务器启动之后要执行的逻辑,一个是TCP服务器停止之后要执行的逻辑。

TCP服务器启动成功之后,要将服务提供者的所有服务注册到注册中心,TCP服务器停止之后要从注册中心移除自己的所有服务。

下面是注册中心类,它只负责服务的注册和移除,别的事不管:

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import java.util.Set;
  4. import java.util.TreeSet;
  5.  
  6. /**
  7. * 服务注册中心
  8. *
  9. * @author syj
  10. */
  11. public class Registry {
  12.  
  13. private Map<String, TreeSet<String>> registryData;
  14.  
  15. public void start() {
  16. registryData = new HashMap<String, TreeSet<String>>();
  17. System.out.println(">>>> 注册中心创建成功");
  18. }
  19.  
  20. public void stop() {
  21. registryData.clear();
  22. }
  23.  
  24. public boolean registry(Set<String> keys, String value) {
  25. if (keys==null || keys.size()==0 || value==null || value.trim().length()==0) {
  26. return false;
  27. }
  28. for (String key : keys) {
  29. TreeSet<String> values = registryData.get(key);
  30. if (values == null) {
  31. values = new TreeSet<>();
  32. registryData.put(key, values);
  33. }
  34. values.add(value);
  35. }
  36. System.out.println(">>>> 服务注册成功");
  37. return true;
  38. }
  39.  
  40. public boolean remove(Set<String> keys, String value) {
  41. if (keys==null || keys.size()==0 || value==null || value.trim().length()==0) {
  42. return false;
  43. }
  44. for (String key : keys) {
  45. TreeSet<String> values = registryData.get(key);
  46. if (values != null) {
  47. values.remove(value);
  48. }
  49. }
  50. System.out.println(">>>> 服务移除成功");
  51. return true;
  52. }
  53. }

写个测试类测试一下:

  1. /**
  2. * 测试类
  3. *
  4. * @author syj
  5. */
  6. public class App {
  7. // TCP 服务器IP
  8. public static String tcpSrvAddr = "192.168.11.23";
  9. // TCP 服务端口
  10. public static int tcpSrvPort = 9090;
  11.  
  12. public static void main(String[] args) {
  13. Provider provider = new Provider();
  14. provider.initConfig(tcpSrvAddr, tcpSrvPort);
  15. provider.start();
  16. provider.stop();
  17. }
  18. }

输出结果:

  1. >>>> start! 192.168.11.23:
  2. >>>> setStartedCallBack:[orderService, productService, userService]:127.0.0.1:
  3. >>>> 注册中心创建成功
  4. >>>> 服务注册成功
  5. >>>> stop!
  6. >>>> setStopedCallBack:192.168.11.23:
  7. >>>> 服务移除成功

总结:

  1. A类调用B类的某个方法,B类的该方法中又调用了A类的某个方法,这就是回调。
    这仅是一个形象的描述,具体的回调机制在实际应用时会很灵活。
  2.  
  3. 比如这个例子中,Provider类的start方法调用啦Server类的start方法,而Server类的start方法中又调用了onStarted方法,
    虽然这个onStarted方法不是Provider中的方法,但其执行的回调逻辑是在Provider中通过setStartedCallBack()来设置的。

Java回调机制在RPC框架中的应用示例的更多相关文章

  1. Java实现简单的RPC框架(美团面试)

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  2. JDK动态代理在RPC框架中的应用

    RPC框架中一般都有3个角色:服务提供者.服务消费者和注册中心.服务提供者将服务注册到注册中心,服务消费者从注册中心拉取服务的地址,并根据服务地址向服务提供者发起RPC调用.动态代理在这个RPC调用的 ...

  3. java回调机制及其实现(转)

    1. 什么是回调函数 回调函数,顾名思义,用于回调的函数.回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数.回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机.回调 ...

  4. Java回调机制总结

    调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...

  5. 转:一个经典例子让你彻彻底底理解java回调机制

    一个经典例子让你彻彻底底理解java回调机制 转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273 ...

  6. Java实现简单的RPC框架

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  7. Java 实现简单的RPC框架

    0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...

  8. JAVA回调机制(CallBack)详解

    序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...

  9. JAVA回调机制解析

    一.回调机制概述     回调机制在JAVA代码中一直遇到,但之前不懂其原理,几乎都是绕着走.俗话说做不愿意做的事情叫做突破,故诞生了该文章,算是新年的新气象,新突破!     回调机制是什么?其实回 ...

随机推荐

  1. S3cmd

    一:安装方法 #wget http://nchc.dl.sourceforge.net/project/s3tools/s3cmd/1.0.0/s3cmd-1.0.0.tar.gz #tar -zxf ...

  2. linux 的常用命令(1)

    1.关于ls [选项][目录名] -a  列出包括.a开头的隐藏文件的所有文件-A  通-a,但不列出"."和".."-l  列出文件的详细信息-c  根据ct ...

  3. 剑指Offer(二十四):二叉树中和为某一值的路径

    剑指Offer(二十四):二叉树中和为某一值的路径 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...

  4. Redis中如何保证数据库和缓存双写时的数据的一致性?

    简单的场景: 直接使用 1. 使用Cache Aside pattern 读取的时候,先读取缓存中是否有数据,缓存中没有数据,再去数据库中进行查询,查询出来以后,然后再存入到缓存中 更新的时候,先删除 ...

  5. Django之路——1 Django的简介

    今天我们来学习django,在学习Django之前我们先来了解一下django和web开发中的http协议 1.mvc模型和mtv模型 既然学习Django,那么我们一定要知道web开发中的mvc模型 ...

  6. js的异常处理 try catch

    <script language="JavaScript"> try { throw new Error(10,"asdasdasd") } cat ...

  7. http健康状态检查

    来自为知笔记(Wiz)

  8. @NotNull,@NotEmpty,@NotBlank区别

    示例结果: // null String name = null; @NotNull: false @NotEmpty: false @NotBlank: false // 空字符串 String n ...

  9. laravel-nestedset:多级无限分类正确姿势

    laravel-nestedset:多级无限分类正确姿势   laravel-nestedset是一个关系型数据库遍历树的larvel4-5的插件包 目录: Nested Sets Model简介 安 ...

  10. linux 登录后有时候会出现-bash-4.1$

    转载自https://blog.csdn.net/jiedao_liyk/article/details/78470498 linux登录后有时候会出现-bash-4.1$ 造成这样的原因: 与这个用 ...