Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • 手撸RPC

    • IO模型
    • 基于HTTP实现
      • 直接使用RestTemplate
      • 基于自定义注解
        • 自定义注解
        • 注解接口Bean
        • Consumer模块测试
    • 基于TCP实现
  • Dubbo
  • RPC
  • 手撸RPC
Nreal
2023-12-14
目录

基于HTTP实现

# 直接使用RestTemplate

  1. 引入RestTemplate的Bean:

    @Configuration
    public class RestConfig {
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  2. 在consumer中远程调用:

    @RestController
    @RequestMapping("/consumer")
    public class ConsumerController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/find/{id}")
        public Goods find(@PathVariable Long id){
            ResponseEntity<Goods> forEntity = restTemplate.getForEntity("http://localhost:9527/provider/goods/1", Goods.class);
            if (forEntity.getStatusCode().is2xxSuccessful()){
                return forEntity.getBody();
            }
            return null;
        }
        
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 基于自定义注解

# 自定义注解

  1. @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface NrealHttpClient {
        String value();
    }
    
    1
    2
    3
    4
    5
    6
  2. @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface NrealMapping {
        //api路径
        String api() default "";
        //调用的主机和端口
        String url() default "";
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 注解接口Bean

将添加了 @NrealHttpClient 的接口生成一个代理类的 Bean,交给spring管理;

  1. 动态代理

    每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现 InvocationHandler接口类的 invoke方法来调用;

    public class NrealHttpClientProxy implements InvocationHandler {
    
        public NrealHttpClientProxy(){
    
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            NrealMapping annotation = method.getAnnotation(NrealMapping.class);
            if (annotation != null){
                String url = annotation.url();
                // /provide/goods/{id}
                String api = annotation.api();
                Pattern compile = Pattern.compile("(\\{\\w+})");
                Matcher matcher = compile.matcher(api);
                if (matcher.find()){
                    //简单判断一下 代表有路径参数需要替换
                    int groupCount = matcher.groupCount();
                    for (int i=0;i<groupCount;i++){
                        String group = matcher.group(i);
                        api = api.replace(group,args[i].toString());
                    }
                }
                RestTemplate restTemplate = new RestTemplate();
                return restTemplate.getForObject(url+api,method.getReturnType());
            }
            return null;
        }
    
        public <T> T getProxy(Class<T> interfaceClass) {
            return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},this);
        }
    
        public static void main(String[] args) {
            Pattern compile = Pattern.compile("(\\{\\w+})");
            Matcher matcher = compile.matcher("test/test/{id}");
            System.out.println(matcher.find());
        }
        
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  2. 生成Bean

    将接口代理注册到 spring容器中,需要使用 FactoryBean的实现类,所有的类都是通过 bean工厂生产的;

    public class NrealHttpClientFactoryBean<T> implements FactoryBean<T> {
    
        private Class<T> interfaceClass;
    
        @Override
        public T getObject() throws Exception {
            //返回一个代理实现类
            return new NrealHttpClientProxy().getProxy(interfaceClass);
        }
    
        //类型是接口
        @Override
        public Class<?> getObjectType() {
            return interfaceClass;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        public Class<T> getInterfaceClass() {
            return interfaceClass;
        }
    
        public void setInterfaceClass(Class<T> interfaceClass) {
            this.interfaceClass = interfaceClass;
        }
        
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
  3. Bean定义注册

    ImportBeanDefinitionRegistrar接口:

    1. ImportBeanDefinitionRegistrar类只能通过其他类 @Import的方式来加载,通常是启动类或配置类;

    2. 使用 @Import,如果括号中的类是 ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成 bean;

      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Import(NrealBeanDefinitionRegistry.class)
      public @interface EnableHttpClient {
          //扫包路径
          String basePackage();
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
    3. 实现该接口的类拥有注册 bean的能力;

    public class NrealBeanDefinitionRegistry implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
        private Environment environment;
        private ResourceLoader resourceLoader;
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            registerNrealHttpClient(metadata,registry);
        }
    
        private void registerNrealHttpClient(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            //将NrealHttpClient所标识的接口,生成代理类,并且注册到spring容器当中
            Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableHttpClient.class.getCanonicalName());
            Object basePackage = annotationAttributes.get("basePackage");
            if (basePackage != null){
                String base = basePackage.toString();
                //ClassPathScanningCandidateComponentProvider是Spring提供的工具,可以按自定义的类型,查找classpath下符合要求的class文件
                ClassPathScanningCandidateComponentProvider scanner = getScanner();
                scanner.setResourceLoader(resourceLoader);
                //找到NrealHttpClient 扫描器 就会进行扫描
                AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(NrealHttpClient.class);
                scanner.addIncludeFilter(annotationTypeFilter);
                //包路径 com.Nreal.rpc 路径下进行扫描
                Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(base);
                for (BeanDefinition candidateComponent : candidateComponents) {
                    //BeanDefinition spring bean定义
                    if (candidateComponent instanceof AnnotatedBeanDefinition){
                        AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                        AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
                        Assert.isTrue(annotationMetadata.isInterface(),"MsHttpClient注解必须定义在接口上");
                        Map<String, Object> httpClientAttributes = annotationMetadata.getAnnotationAttributes(NrealHttpClient.class.getCanonicalName());
                        //获取注解当中的value值,这个value值 是我们的 bean的名称
                        String beanName = getClientName(httpClientAttributes);
                        //接口无法实例化,所以生成代理实现类
                        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NrealHttpClientFactoryBean.class);
                        beanDefinitionBuilder.addPropertyValue("interfaceClass",annotationMetadata.getClassName());
                        registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
                    }
                }
            }
        }
    
        private String getClientName(Map<String, Object> clientAnnotationAttributes) {
            if (clientAnnotationAttributes == null){
                throw new RuntimeException("value必须有值");
            }
            Object value = clientAnnotationAttributes.get("value");
            if (value != null && !value.toString().equals("")){
                return value.toString();
            }
            return null;
        }
    
        //这个方法是从Feign组件中源码找的
        protected ClassPathScanningCandidateComponentProvider getScanner() {
            return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
                @Override
                protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                    boolean isCandidate = false;
                    if (beanDefinition.getMetadata().isIndependent()) {
                        if (!beanDefinition.getMetadata().isAnnotation()) {
                            isCandidate = true;
                        }
                    }
                    return isCandidate;
                }
            };
        }
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
        
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80

# Consumer模块测试

  1. 做一个配置类,开启 NrealHttpClient的支持,设置扫包路径;

    @Configuration
    @EnableHttpClient(basePackage = "com.Nreal.rpc")
    public class RpcConfig {
    }
    
    1
    2
    3
    4
  2. 调用provider 接口;

    @NrealHttpClient(value = "goodsHttpRpc")
    public interface GoodsHttpRpc {
        @NrealMapping(url="http://localhost:9527",api="/provider/goods/{id}")
        public Goods findGoods(@PathVariable Long id);
    }
    
    1
    2
    3
    4
    5
  3. Controller接口测试;

    @Autowired
        private GoodsHttpRpc goodsHttpRpc;
    
        @GetMapping("/find/{id}")
        public Goods find(@PathVariable Long id){
            return goodsHttpRpc.findGoods(id);
        }
    
    1
    2
    3
    4
    5
    6
    7
IO模型
基于TCP实现

← IO模型 基于TCP实现→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式