手写RPC通信和服务发现——模拟Dubbo的实现机制(下篇)

接上篇。

服务端如何将某些指定的方法暴露出去,比如服务端有:

类A,方法A1()

类B,方法B1(),方法B2()

类C,方法C1()

我们想把类中的方法暴露一部分出来,供客户端调用,如:

类A :暴露,方法A1():暴露

类B:暴露,方法B1():暴露,方法B2():不暴露

类C:不暴露,方法C1()

怎么才能做到动态方便的进行设置?

这种情况下,注解方式则派上了用场。

注解定义

我们编写2个注解,1个为类注解,表示该类暴露给客户端;另一个为方法注解,表示该方法暴露给客户端。

类级别的注解:

package com.leehao.rpc.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName RpcClazz
 * @Description RpcClazz
 * @Author lihao
 * @Date 2019/9/25 14:58
 * @Version 1.0
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE )
public @interface RpcClazz {
}

方法级别的注解:

package com.leehao.rpc.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName RpcClazz
 * @Description RpcClazz
 * @Author lihao
 * @Date 2019/9/25 14:58
 * @Version 1.0
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD )
public @interface RpcMethod {
}

注解解析

具体步骤为:

1、传入包名,得到all业务类,得到文件名

2、通过反射得到类

3、解析类是否有注解

4、解析方法是否有注解 *

5、发布

package com.leehao.rpc.utils;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName AnnoUtil
 * @Description AnnoUtil
 * @Author lihao
 * @Date 2019/9/25 15:02
 * @Version 1.0
 **/
public class AnnoUtil {


    //传入包名packageName
    //返回多个类 Map表示
    //一个类、多个方法
    //一个方法,多个参数
    //所以最后返回结果为:Map<String, List<Map<String,List<String>>>> 
    public static Map<String, List<Map<String,List<String>>>>  parseAnno(String packageName) throws Exception {
        Map<String, List<Map<String,List<String>>>> map = new HashMap<>();
        //获取项目跟路径
        String basePath = AnnoUtil.class.getResource("/").getPath();
        //获取当前包所在的路径,将.替换为/
        File file = new File(basePath + packageName.replace(".", "/"));
        //获取包路径下所有的文件(严格讲,需要获取.class后缀的文件)
        String[] names = file.list();
        for (String name : names) {
            //去掉后缀,拿到类名
            name = name.replaceAll(".class", "");
            //通过反射获取该类
            Class<?> clazz = Class.forName(packageName + "." + name);
            //是否有类级别的注解
            if (clazz.isAnnotationPresent(RpcClazz.class)) {
                List<Map<String,List<String>>> methodsReturn = new ArrayList<>();
                //通过反射获取所有方法,包括私有方法
                Method[] methods = clazz.getDeclaredMethods();
                for (Method method : methods) {
                    //判断方法是否有注解
                    if (method.isAnnotationPresent(RpcMethod.class)){
                        Map<String,List<String>> methodReturn = new HashMap<>();
                        List<String> paras = new ArrayList<>();
                        //遍历方法类型
                        Class<?>[] paraTypes = method.getParameterTypes();
                        for (Class<?> paraType:paraTypes){
                            //将变量类型加入list中
                            paras.add(paraType.getSimpleName());
                        }
                        //将该方法中所有的参数类型加入map中,其中key为方法名
                        methodReturn.put(method.getName(),paras);
                        //将方法都加入到list中去
                        methodsReturn.add(methodReturn);
                    }
                        
                }
                //将该类下的所有暴露方法都加入到map中,其中key为包全名
                map.put(packageName+"."+ name,methodsReturn);
            }
        }
        //返回最终结果
        return map;
    }
}

编写几个类和方法,加入我们的注解来测试:

package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName OrderDao
 * @Description OrderDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
@RpcClazz
public class OrderDao {
    @RpcMethod
    public void query(String name){
        System.out.println("name="+name);
    }
    @RpcMethod
    public void query1(String name1,String name2){
        System.out.println("name="+name1+name2);
    }
    @RpcMethod
    public void query2(int name){
        System.out.println("name="+name);
    }

    public void query3(int name){
        System.out.println("name="+name);
    }
}
package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName UserDao
 * @Description UserDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
@RpcClazz
public class UserDao {
    @RpcMethod
    public void sayHello(String name){
        System.out.println("name="+name);
    }
    @RpcMethod
    public void sayBye(String name1,String name2){
        System.out.println("name="+name1+name2);
    }

}
package com.leehao.rpc.server.dao;

import com.leehao.rpc.anno.RpcClazz;
import com.leehao.rpc.anno.RpcMethod;

/**
 * @ClassName UserDao
 * @Description UserDao
 * @Author lihao
 * @Date 2019/9/24 18:58
 * @Version 1.0
 **/
public class OrgDao {
    @RpcMethod
    public void test(){
        System.out.println("name");
    }

}

预期:

OrderDao类有注解,query、query1、query2方法有注解:

所以query、query1、query2暴露,query3不暴露出来

UserDao类有注解,sayHello、sayBye有注解,

所以2个均暴露出来

OrgDao类没有注解,所以下面的方法无论是否加注解,均无法暴露。

测试

在这里我们采用SpringBoot作为web容器,调用上述的接口,来将结果显示到页面上,如对springboot了解不够,可自行学习,默认大家都了解Maven、springboot等相关知识。

pom中引入依赖:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

编写Controller

package com.leehao.rpc.controller;

import com.leehao.rpc.utils.AnnoUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;

/**
 * @ClassName RegistryController
 * @Description RegistryController
 * @Author lihao
 * @Date 2019/9/25 15:36
 * @Version 1.0
 **/
@Controller
@RequestMapping("/")
public class RegistryController {

    @ResponseBody
    @GetMapping("/getInstance")
    public Map<String, List<Map<String, List<String>>>> getInstance(@RequestParam("packageName") String packageName) throws Exception {
        return AnnoUtil.parseAnno(packageName);
    }
}

启动类启动:

package com.leehao.rpc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RpcServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(RpcServerApplication.class, args);
    }

}

 

在浏览器地址栏输入(端口号根据实际情况而定):http://localhost:8765/getInstance?packageName=com.leehao.rpc.server.dao 

即可发现已暴露的所有接口方法:

 

和我们的预期是一致的,至此服务注册和发现已完成。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RPC(Remote Procedure Call)是一种用于分布式系统间通信机制。它允许一个系统中的应用程序调用另一个系统中的远程服务,就像调用本地服务一样。RPC的核心思想是将不同系统间的网络通信、数据传输和序列化等复杂细节进行封装,使开发者可以像调用本地函数一样方便地调用远程函数。 Dubbo是一种基于RPC机制的分布式服务框架。它由阿里巴巴集团开发,用于解决大规模分布式系统中的服务治理问题。Dubbo提供了服务注册发现、负载均衡、容错处理、服务路由等功能,使得分布式系统的开发和管理更加简单高效。Dubbo框架支持多种远程通信协议,如HTTP、TCP等,可根据实际场景选择适合的协议。 Dubbo的架构由三层组成:服务提供者、注册中心和服务消费者。服务提供者将自己提供的服务注册注册中心,服务消费者从注册中心获取服务的地址列表,并通过远程调用的方式调用服务提供者提供的功能。Dubbo框架还支持服务的负载均衡,使得调用请求可以在多个服务提供者之间进行分发,提高系统的并发能力和稳定性。 总之,RPC机制Dubbo框架都是用于构建分布式系统的重要工具。RPC机制提供了远程调用的能力,使得分布式系统中的不同应用程序可以互相调用;而Dubbo框架在RPC机制的基础上实现了更加便捷的服务治理功能,简化了分布式系统的开发和管理过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值