一、RPC场景和实现过程+dubbo的基础配置使用

一、RPC场景和过程+实现原理

1、rpc场景

在微服务环境下,存在大量的跨 JVM 进行方法调用的场景,如下图:
在这里插入图片描述
具体到某一个调用来说,希望 A 机器能通过网络,调用 B 机器内的某个服 务方法,并得到返回值。

2、rpc如何实现

我们知道调用一个服务的方法最基本的是获取到jvm中的bean实例,jdk还提供了一种通过反射的方式来调用,只需要提供:反射需要的目标对象、方法名称、参数即可。传递这些信息的桥梁有很多,例如:webService、Http、Rmi等等。

实现一个远程调用所需要的组件:反射所需要的信息、网络传输协议,下面用代码实现一个远程调用

2.1、反射调用类
package com.enjoy.utils;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/*
 * @author wangle25
 * @description 反射调用
 * @date 11:27 2020-07-18
 * @param 
 * @return 
 **/

public class InvokeUtils {
    public static ClassPathXmlApplicationContext context;

    /**
     * java反射
     * @param target 目标对象
     * @param methodName 目标方法
     * @param argTypes 方法参数类型
     * @param args 实参
     * @return
     */
    public static Object call(Object target,String methodName,
                                Class[] argTypes,Object[] args)
            throws NoSuchMethodException, InvocationTargetException,
            IllegalAccessException {

        Method method = target.getClass().getMethod(methodName, argTypes);
        return method.invoke(target, args);
    }

    public static Object call(Map<String,String> info, ApplicationContext ctx){

        String targetStr = info.get("target");
        String methodName = info.get("methodName");
        String arg = info.get("arg");

        try {
            return call(ctx.getBean(targetStr), methodName,new Class[]{String.class},new Object[]{arg});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}
2.2 Provider
package com.enjoy;

import com.alibaba.fastjson.JSON;
import com.enjoy.entity.OrderEntiry;
import com.enjoy.service.InfoService;
import com.enjoy.service.InfoServiceImpl;
import com.enjoy.service.OrderService;
import com.enjoy.service.OrderServiceImpl;
import com.enjoy.utils.InvokeUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import java.util.Map;

public class Provider {

    @Configuration
    static class ProviderConfiguration {
        @Bean
        public OrderService orderService() {
            return new OrderServiceImpl();
        }
    }

    public static void main(String[] args) throws Exception {
        
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        ctx.start();

        System.out.println("---------spring启动成功--------");

        // 开启rmi服务
        initProtocol2(ctx);
        System.in.read();
    }
    
    /*
     * @author wangle25
     * @description 开启rmi服务
     * @date 11:30 2020-07-18
     * @param 
     * @return 
     **/
    
    public static void initProtocol2(ApplicationContext ctx) throws RemoteException, AlreadyBoundException, MalformedURLException {
        InfoService infoService = new InfoServiceImpl(){
            public Object passInfo(Map<String, String> info) {//对象,方法,参数

                super.passInfo(info);//info内包含的信息,是反射需要的信息
                Object result = InvokeUtils.call(info,ctx);
                System.out.println("测试InvokeUtils.call调用功能,调用结果:" + JSON.toJSONString(result));
                return result;
            }
        };

        //注冊通讯端口
        LocateRegistry.createRegistry(InfoService.port);
        //注冊通讯路径
        Naming.bind(InfoService.RMI_URL, infoService);
        System.out.println("初始化rmi绑定");
    }
}

2.3、Consumer
package com.enjoy.utils;

import com.alibaba.fastjson.JSON;
import com.enjoy.entity.OrderEntiry;
import com.enjoy.service.InfoService;
import com.enjoy.service.OrderService;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class RmiClient {

    public static void main(String[] args) throws RemoteException, MalformedURLException {
        InfoService infoService = null;

        try {
            //取远程服务实现
            infoService = (InfoService) Naming.lookup(InfoService.RMI_URL);
            Object ret = infoService.sayHello("james");
            System.out.println("测试远程调用功能infoService.sayHello,调用结果:" + JSON.toJSONString(ret));

            //呼叫远程反射方法
            Map<String,String> info = new HashMap();
            info.put("target","orderService");
            info.put("methodName","getDetail");
            info.put("arg","1");
            Object result = infoService.passInfo(info);
            System.out.println("测试远程调用功能,调用结果:" + JSON.toJSONString(result));

            //静态代理方法
            OrderService service = getService(infoService);
            Object result2 = service.getDetail("1");//透明化调用,不增加开发人员的负担
            System.out.println("测试远程调用功能,调用结果:" + JSON.toJSONString(result2));
        } catch (NotBoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 静态代理,动态编译类来实现
     */
    public static OrderService getService(InfoService infoService){
        OrderService service = new OrderService(){

            @Override
            public OrderEntiry getDetail(String id) {
                Map<String,String> info = new HashMap();
                //写死了反射的目标,静态代理
                info.put("target","orderService");//对象
                info.put("methodName","getDetail");//方法
                info.put("arg",id);//参数

                OrderEntiry result = null;
                try {
                    result = (OrderEntiry)infoService.passInfo(info);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                return result;
            }
        };
        return service;
    }
}

二、dubbo简介+基础使用

简介

  • 在分布式服务架构下,各个服务间的相互 rpc 调用会越来越复杂。最终形成网状结构,此时服务的治理极为关键
  • Dubbo 是一个带有服务治理功能的 RPC 框架,提供了一套较为完整的服务治理方案,其底层直接实现了 rpc 调用的全过程,并尽力做事 rpc 远程对使用者透明

功能:

  • 远程通讯:提供对多种基于长连接的 NIO 框架抽象封装,包括多种线程 模型、序列化以及“请求-响应”模式的信息交换方式。
  • 集群容错:提供基于接口方法的透明远程过程调用,包括多协议支持以 及软负载均衡,失败容错、地址路由、动态配置等集群支持。
  • 自动发现:基于注册中心目录服务,使服务消费方能动态的查×××提 供方,使地址透明,使服务提供方可以平滑增加或减少机器。

使用方式

1、xml使用方式
  • Provider配置
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	 http://www.springframework.org/schema/beans/spring-beans.xsd
	      http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
	 http://code.alibabatech.com/schema/dubbo
	 http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!--全局配置-->
    <dubbo:provider timeout="3000" />
    <!-- 服务提供方应用名称, 方便用于依赖跟踪 -->
    <dubbo:application name="busi-server" />
    <!-- 使用本地zookeeper作为注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <!--name指示使用什么协议监听端口:dubbo/rmi/rest-->
    <dubbo:protocol id="d1"  name="dubbo" port="20880" />
    <dubbo:protocol id="d2"  name="dubbo" port="20882" />

    <!-- 通过xml方式配置为bean, 让spring托管和实例化 -->
    <bean id="orderService" class="com.enjoy.service.OrderServiceImpl"/>
    <!-- 声明服务暴露的接口,并暴露服务 -->
    <dubbo:service interface="com.enjoy.service.OrderService" ref="orderService" protocol="d1" />
</beans>
  • Provider服务启动
package com.enjoy.dubbo.xml;

import com.alibaba.fastjson.JSON;
import com.enjoy.entity.OrderEntiry;
import com.enjoy.service.OrderService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlServer {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/dubbo-server.xml");

        ctx.start();
        System.out.println("---------dubbo启动成功--------");

        // 保证服务一直开着
        synchronized (XmlServer.class) {
            try {
                XmlServer.class.wait();
            } catch (Throwable e) {
            }
        }

    }
}

  • Consumer配置
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	 http://www.springframework.org/schema/beans/spring-beans.xsd
	      http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
	 http://code.alibabatech.com/schema/dubbo
	 http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="busi-clint" />
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <dubbo:reference id="orderService" interface="com.enjoy.service.OrderService" />

</beans>
  • Consumer启动
package com.enjoy.dubbo.xml;

import com.enjoy.entity.OrderEntiry;
import com.enjoy.service.OrderService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlClient {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/dubbo-client.xml");

        ctx.start();
        System.out.println("---------dubbo启动成功--------");

        OrderService orderService = (OrderService) ctx.getBean("orderService"); // get remote service proxy

        OrderEntiry entiry = orderService.getDetail("1");
        System.out.println("echo result: " + entiry.getMoney());

    }
}

2、注解的方式

  • Provider配置
dubbo.application.name=busi-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
  • Provider服务启动

package com.enjoy.dubbo.config;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

public class Provider {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "com.enjoy.service")
    @PropertySource("classpath:/dubbo-provider.properties")
    static class ProviderConfiguration {
    }
}

  • Consumer配置
dubbo.application.name=busi-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.consumer.timeout=3000
  • Consumer服务启动
/*
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements.  See the NOTICE file distributed with
 *   this work for additional information regarding copyright ownership.
 *   The ASF licenses this file to You under the Apache License, Version 2.0
 *   (the "License"); you may not use this file except in compliance with
 *   the License.  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.enjoy.dubbo.config;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import com.enjoy.action.ServiceConsumer;
import com.enjoy.entity.OrderEntiry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

public class Consumer {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        ServiceConsumer serviceConsumer = context.getBean(ServiceConsumer.class);
        OrderEntiry entiry = serviceConsumer.getDetail("1");
        System.out.println("result: " + entiry.getMoney());

    }

    @Configuration
    @EnableDubbo(scanBasePackages = "com.enjoy.action")
    @PropertySource("classpath:/dubbo-consumer.properties")
    @ComponentScan(value = {"com.enjoy.action"})
    static class ConsumerConfiguration {
    }
}
  • 把服务拉取到consumer本地
package com.enjoy.action;

import com.alibaba.dubbo.config.annotation.Reference;
import com.enjoy.entity.OrderEntiry;
import com.enjoy.service.OrderService;
import org.springframework.stereotype.Component;

@Component("annotatedConsumer")
public class ServiceConsumer {

    @Reference
    private OrderService orderService;

    public OrderEntiry getDetail(String id) {
        return orderService.getDetail(id);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值