精华内容
下载资源
问答
  • RPC模型原理

    2020-11-26 15:16:38
    1. 基本的RPC模型 主要介绍RPC是什么,基本的RPC代码,RPC与REST的区别,gRPC的使用 1.1 基本概念 RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务 本地过程调用:...

    1. 基本的RPC模型

    主要介绍RPC是什么,基本的RPC代码,RPC与REST的区别,gRPC的使用

    1.1 基本概念

    • RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
    • 本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
    • 远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?
    1. 首先客户端需要告诉服务器,需要调用的函数,这里函数和进程ID存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,然后执行函数的代码。
    2. 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不再同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程。
      3.数据准备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。
      总结一下上述过程:

     

    // Client端 
    //    Student student = Call(ServerAddr, addAge, student)
    1. 将这个调用映射为Call ID。
    2. 将Call ID,student(params)序列化,以二进制形式打包
    3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
    4. 等待服务器返回结果
    5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
    
    // Server端
    1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
    2. 等待服务端请求
    3. 得到一个请求后,将其数据包反序列化,得到Call ID
    4. 通过在callIdMap中查找,得到相应的函数指针
    5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
    6. 将student结果序列化后通过网络返回给Client
    

    • 在微服务的设计中,一个服务A如果访问另一个Module下的服务B,可以采用HTTP REST传输数据,并在两个服务之间进行序列化和反序列化操作,服务B把执行结果返回过来。

       

    • 由于HTTP在应用层中完成,整个通信的代价较高,远程过程调用中直接基于TCP进行远程调用,数据传输在传输层TCP层完成,更适合对效率要求比较高的场景,RPC主要依赖于客户端和服务端之间建立Socket链接进行,底层实现比REST更复杂。

    1.2 rpc demo

    系统类图

    系统调用过程

    客户端:

     

    public class RPCClient<T> {
        public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {
            // 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
            return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Socket socket = null;
                            ObjectOutputStream output = null;
                            ObjectInputStream input = null;
                            try{
                                // 2.创建Socket客户端,根据指定地址连接远程服务提供者
                                socket = new Socket();
                                socket.connect(addr);
    
                                // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
                                output = new ObjectOutputStream(socket.getOutputStream());
                                output.writeUTF(serviceInterface.getName());
                                output.writeUTF(method.getName());
                                output.writeObject(method.getParameterTypes());
                                output.writeObject(args);
    
                                // 4.同步阻塞等待服务器返回应答,获取应答后返回
                                input = new ObjectInputStream(socket.getInputStream());
                                return input.readObject();
                            }finally {
                                if (socket != null){
                                    socket.close();
                                }
                                if (output != null){
                                    output.close();
                                }
                                if (input != null){
                                    input.close();
                                }
                            }
                        }
                    });
        }
    }
    

    服务端:

     

    public class ServiceCenter implements Server {
    
        private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    
        private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();
    
        private static boolean isRunning = false;
    
        private static int port;
    
    
        public ServiceCenter(int port){
            ServiceCenter.port = port;
        }
    
    
        @Override
        public void start() throws IOException {
            ServerSocket server = new ServerSocket();
            server.bind(new InetSocketAddress(port));
            System.out.println("Server Start .....");
            try{
                while(true){
                    executor.execute(new ServiceTask(server.accept()));
                }
            }finally {
                server.close();
            }
        }
    
        @Override
        public void register(Class serviceInterface, Class impl) {
            serviceRegistry.put(serviceInterface.getName(), impl);
        }
    
        @Override
        public boolean isRunning() {
            return isRunning;
        }
    
        @Override
        public int getPort() {
            return port;
        }
    
        @Override
        public void stop() {
            isRunning = false;
            executor.shutdown();
        }
       private static class ServiceTask implements Runnable {
            Socket client = null;
    
            public ServiceTask(Socket client) {
                this.client = client;
            }
    
            @Override
            public void run() {
                ObjectInputStream input = null;
                ObjectOutputStream output = null;
                try{
                    input = new ObjectInputStream(client.getInputStream());
                    String serviceName = input.readUTF();
                    String methodName = input.readUTF();
                    Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                    Object[] arguments = (Object[]) input.readObject();
                    Class serviceClass = serviceRegistry.get(serviceName);
                    if(serviceClass == null){
                        throw new ClassNotFoundException(serviceName + "not found!");
                    }
                    Method method = serviceClass.getMethod(methodName, parameterTypes);
                    Object result = method.invoke(serviceClass.newInstance(), arguments);
    
                    output = new ObjectOutputStream(client.getOutputStream());
                    output.writeObject(result);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if(output!=null){
                        try{
                            output.close();
                        }catch (IOException e){
                            e.printStackTrace();
                        }
                    }
                    if (input != null) {
                        try {
                            input.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (client != null) {
                        try {
                            client.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    

     

    public class ServiceProducerImpl implements ServiceProducer{
        @Override
        public String sendData(String data) {
            return "I am service producer!!!, the data is "+ data;
        }
    }
    
    

     

    public class RPCTest {
        public static void main(String[] args) throws IOException {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Server serviceServer = new ServiceCenter(8088);
                        serviceServer.register(ServiceProducer.class, ServiceProducerImpl.class);
                        serviceServer.start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            ServiceProducer service = RPCClient.getRemoteProxyObj(ServiceProducer.class, new InetSocketAddress("localhost", 8088));
            System.out.println(service.sendData("test"));
        }
    }
    

    1.3 完整源码

    RPCdemo

    1.4 分析

    这里客户端只需要知道Server端的接口ServiceProducer即可,服务端在执行的时候,会根据具体实例调用实际的方法ServiceProducerImpl,符合面向对象过程中父类引用指向子类对象。

    2. gRPC的使用

    2.1. gRPC与REST

    • REST通常以业务为导向,将业务对象上执行的操作映射到HTTP动词,格式非常简单,可以使用浏览器进行扩展和传输,通过JSON数据完成客户端和服务端之间的消息通信,直接支持请求/响应方式的通信。不需要中间的代理,简化了系统的架构,不同系统之间只需要对JSON进行解析和序列化即可完成数据的传递。
    • 但是REST也存在一些弊端,比如只支持请求/响应这种单一的通信方式,对象和字符串之间的序列化操作也会影响消息传递速度,客户端需要通过服务发现的方式,知道服务实例的位置,在单个请求获取多个资源时存在着挑战,而且有时候很难将所有的动作都映射到HTTP动词。
    • 正是因为REST面临一些问题,因此可以采用gRPC作为一种替代方案,gRPC 是一种基于二进制流的消息协议,可以采用基于Protocol Buffer的IDL定义grpc API,这是Google公司用于序列化结构化数据提供的一套语言中立的序列化机制,客户端和服务端使用HTTP/2以Protocol Buffer格式交换二进制消息。
    • gRPC的优势是,设计复杂更新操作的API非常简单,具有高效紧凑的进程通信机制,在交换大量消息时效率高,远程过程调用和消息传递时可以采用双向的流式消息方式,同时客户端和服务端支持多种语言编写,互操作性强;不过gRPC的缺点是不方便与JavaScript集成,某些防火墙不支持该协议。
    • 注册中心:当项目中有很多服务时,可以把所有的服务在启动的时候注册到一个注册中心里面,用于维护服务和服务器之间的列表,当注册中心接收到客户端请求时,去找到该服务是否远程可以调用,如果可以调用需要提供服务地址返回给客户端,客户端根据返回的地址和端口,去调用远程服务端的方法,执行完成之后将结果返回给客户端。这样在服务端加新功能的时候,客户端不需要直接感知服务端的方法,服务端将更新之后的结果在注册中心注册即可,而且当修改了服务端某些方法的时候,或者服务降级服务多机部署想实现负载均衡的时候,我们只需要更新注册中心的服务群即可。

       

      RPC调用过程

    2.2. gRPC与Spring Boot

    这里使用SpringBoot+gRPC的形式实现RPC调用过程
    项目结构分为三部分:client、grpc、server

     

    项目结构

    2.2.2 grpc

     

    pom.xml中引入依赖:

     

    <dependency>
          <groupId>io.grpc</groupId>
          <artifactId>grpc-all</artifactId>
           <version>1.12.0</version>
     </dependency>
    

    引入bulid

     

    <build>
            <extensions>
                <extension>
                    <groupId>kr.motd.maven</groupId>
                    <artifactId>os-maven-plugin</artifactId>
                    <version>1.4.1.Final</version>
                </extension>
            </extensions>
            <plugins>
                <plugin>
                    <groupId>org.xolstice.maven.plugins</groupId>
                    <artifactId>protobuf-maven-plugin</artifactId>
                    <version>0.5.0</version>
                    <configuration>
                        <pluginId>grpc-java</pluginId>
                        <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact>
                        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier}</pluginArtifact>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>compile-custom</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    

    创建.proto文件

     

    syntax = "proto3";   // 语法版本
    
    // stub选项
    option java_package = "com.shgx.grpc.api";
    option java_outer_classname = "RPCDateServiceApi";
    option java_multiple_files = true;
    
    // 定义包名
    package com.shgx.grpc.api;
    
    // 服务接口定义,服务端和客户端都要遵守该接口进行通信
    service RPCDateService {
        rpc getDate (RPCDateRequest) returns (RPCDateResponse) {}
    }
    
    // 定义消息(请求)
    message RPCDateRequest {
        string userName = 1;
    }
    
    // 定义消息(响应)
    message RPCDateResponse {
        string serverDate = 1;
    }
    
    

    mvn complie

     

    生成代码:

     

    2.2.3 client

     

    根据gRPC中的项目配置在client和server两个Module的pom.xml添加依赖

     

     

            <dependency>
                <groupId>com.shgx</groupId>
                <artifactId>grpc</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <scope>compile</scope>
            </dependency>
    

    编写GRPCClient

     

    public class GRPCClient {
        private static final String host = "localhost";
        private static final int serverPort = 9999;
    
        public static void main( String[] args ) throws Exception {
            ManagedChannel managedChannel = ManagedChannelBuilder.forAddress( host, serverPort ).usePlaintext().build();
            try {
                RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub( managedChannel );
                RPCDateRequest rpcDateRequest = RPCDateRequest
                        .newBuilder()
                        .setUserName("shgx")
                        .build();
                RPCDateResponse rpcDateResponse = rpcDateService.getDate( rpcDateRequest );
                System.out.println( rpcDateResponse.getServerDate() );
            } finally {
                managedChannel.shutdown();
            }
        }
    }
    
    

    2.2.4 server

     

    按照2.2.3 client的方式添加依赖
    创建RPCDateServiceImpl

     

    public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase{
        @Override
        public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) {
            RPCDateResponse rpcDateResponse = null;
            Date now=new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyy年MM月dd日 E kk点mm分");
            String nowTime = simpleDateFormat.format( now );
            try {
                rpcDateResponse = RPCDateResponse
                        .newBuilder()
                        .setServerDate( "Welcome " + request.getUserName()  + ", " + nowTime )
                        .build();
            } catch (Exception e) {
                responseObserver.onError(e);
            } finally {
                responseObserver.onNext( rpcDateResponse );
            }
            responseObserver.onCompleted();
        }
    }
    

    创建GRPCServer

     

    public class GRPCServer {
       private static final int port = 9999;
       public static void main( String[] args ) throws Exception {
           Server server = ServerBuilder.
                   forPort(port)
                   .addService( new RPCDateServiceImpl() )
                   .build().start();
           System.out.println( "grpc服务端启动成功, 端口=" + port );
           server.awaitTermination();
       }
    }
    

     


     

    展开全文
  • 首次对ERS-SAR卫星影像进行了基于SRTM DEM无需初值的RPC模型参数求解试验,对比了9种形式RPC模型参数的求解精度,并对控制点格网大小及高程分层对参数求解精度的影响作了评价。试验表明SAR卫星遥感影像的成像几何...
  • 遥感影像几何校正模型(RPC模型

    千次阅读 2018-07-19 17:16:45
    遥感影像几何校正模型(RPC模型)    我们下载的国产影像数据中会发现有*.rpc/*.rpb文件,其实那就是所谓的RPC文件,用于几何校正的RPC模型。简单来说,RPC模型就是有理函数纠正函数,是将地面点大地坐标与其...

    遥感影像几何校正模型(RPC模型)

     

        我们下载的国产影像数据中会发现有*.rpc/*.rpb文件,其实那就是所谓的RPC文件,用于几何校正的RPC模型。简单来说,RPC模型就是有理函数纠正函数,是将地面点大地坐标与其对应的像点坐标用比值多项式关联起来,这就像数字摄影测量学上在外场用单反相机拍张照片,并求出其内外方位元素、已知对应像点坐标的大地坐标值(一般三对以上)将相片的所有像点坐标转换为大地坐标的求解过程。举个简单例子,用ENVI打开无法支持对应影像RPC文件的数据(比如GF-2)就会显示无参考坐标系统,即没有对应的坐标信息,软件需要自动链接RPC文件才得以显示影像的坐标信息;或者进行RPC校正后的影像就带有坐标信息了,之后影像需要根据问题本身、精度要求、用途等考虑是否需要进行GCPs控制点校正以及是否需要生产数字正射影像产品(DOM)。

     

        提供RPC文件的主要原因:影像供应商不提供卫星和传感器参数;当然其RPC模型的方便性和实用性也是重要的因素啦。

     

     

     

    微信关注  奔跑的GISer  获取更多资源

    展开全文
  • 从卫星遥感影像区域网平差的模型出发,利用SPOT-5影像附带的参数拟合RPC模型参数,根据RPC模型和像面的仿射变换模型建立了。Y-星遥感影像区域网平差的数学模型。按照所构建的数学模型,对两个地区SPOT-5HRS影像进行...
  • 为了提高有理函数(rational polynomial coefficient ,RPC)模型校正模型的定位...实验表明:可通过添加少量控制点提高 RPC模型的正射校正精度,在缺少控制点的情况下,使用补偿模型亦能使RPC模型达到相当的影像校正精度.
  • 基于RPC模型的线阵卫星影像核线排列及其几何关系重建
  • Spring - 几种RPC模型的使用与比较

    千次阅读 2017-05-26 08:58:45
    Spring中,用JMS搞RPC时会用到: org.springframework.jms.remoting.JmsInvokerServiceExporterorg.springframework.jms.remoting.JmsInvokerProxyFactoryBean ...在这里我打算把几种RPC模型记录下来并作比较

    Spring中,用JMS搞RPC时会用到:

    • org.springframework.jms.remoting.JmsInvokerServiceExporter
    • org.springframework.jms.remoting.JmsInvokerProxyFactoryBean

    spring在实现RPC的几种方式上都提供了风格一致的支持。
    在这里我打算把几种RPC模型记录下来并作比较。

    • RMI
    • Hessian/Burlap
    • HTTP Invoker
    • JAX-WS 

    RMI

    先从最基本的RMI开始。
    RMI相关的API早在JDK1.1时就有了,我在这里简单描述一下RMI的原生实现(代码可以从别的地方参考)。

    • 声明一个远程接口,接口必须继承java.rmi.Remote,方法需要抛java.rmi.RemoteException
    • 为远程接口提供实现,实现类需要继承UnicastRemoteObject。
    • 或者可以使用rmi相关命令创建skelton和stub。
    • 启动一个RMI注册表并注册。

    如果是spring实现RMI,方法会简单很多。
    我们只需要用到两个类:

    • org.springframework.remoting.rmi.RmiServiceExporter
    • org.springframework.remoting.rmi.RmiProxyFactoryBean


    我简单定义一下接口和实现类:

    package pac.testcase.ws;
    public interface MyService {
        public boolean inviteMeIn();
        public String welcome();
    }
    

     

    package pac.testcase.ws.impl;
    import pac.testcase.ws.MyService;
    public class MyServiceImpl implements MyService{
        public boolean inviteMeIn() {
            return true;
        }
        public String welcome() {
            return "Everybody is welcome!!";
        }
    }
    

    简简单单,不需要继承其他任何东西,非常pojo。

    下面是spring相关配置:

    <bean id="myService" class="pac.testcase.ws.impl.MyServiceImpl" />
    <bean class="org.springframework.remoting.rmi.RmiServiceExporter"
        p:service-ref="myService"
        p:serviceName="welcomeService"
        p:serviceInterface="pac.testcase.ws.MyService"
    />
    


    将我们的pojo导出为RMI服务,在这里我采用默认配置。
    地址在默认情况时如下:

    /**
     * Set the host of the registry for the exported RMI service,
     * i.e. {@code rmi://HOST:port/name}
     * <p>Default is localhost.
     */
    public void setRegistryHost(String registryHost) {
        this.registryHost = registryHost;
    }
    
    /**
     * Set the port of the registry for the exported RMI service,
     * i.e. {@code rmi://host:PORT/name}
     * <p>Default is {@code Registry.REGISTRY_PORT} (1099).
     * @see java.rmi.registry.Registry#REGISTRY_PORT
     */
    public void setRegistryPort(int registryPort) {
        this.registryPort = registryPort;
    }
    


    客户端方面使用RmiProxyFactoryBean,被代理的服务就像一个简单的bean一样:

    <bean id="clientSideService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"
        p:serviceUrl="rmi://localhost:1099/welcomeService"
        p:serviceInterface="pac.test.RemoteService"
    />
    


    配置中的pac.test.RemoteService就是那个简单的bean,根据客户端的需要,在这里重新定义一下。

    package pac.test;
    public interface RemoteService {
        public String welcome();
    }
    


    这样就可以在服务端调用了,不用做什么Naming.lookup(serviceUrl)之类的操作,远程调用变得透明。

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    RemoteService service = (RemoteService)context.getBean("clientSideService");
    System.out.println(service.welcome());
    

    RMI虽然简单高效,但使用RMI会存在一些问题,比如java序列化的版本问题或者防火墙问题(RMI不是基于HTTP的)。

    Hessian / Burlap 

    Hessian和Burlap,现在进Caucho的网站都几乎见不到这方面的内容了。
    我也不知道有没有人还会用这两个东东,虽然去年出了一个版本,但上一个版本是在2010年。
    刚才在群里问了一下有没有人用,结果还真有人用Hessian,他们是C#和Java做通信。
    Burlap性能更令人头疼,不知道还有没有人提及。
    虽然不知道使用情况如何,但也在这里简单记录一下,拓展一下思维。


    Hessian和Burlap都是由Caucho提供的,Hessian是Resin的一部分。
    这两个东西就像同一件事物的两个部件,比如像这样的枪+链锯?


    Hessian是binary transport protocol,但与RMI不同的是他不是java序列化对象,所以他可以和其他语言的程序通信,比如C++、C#、Python、Ruby什么的。
    Burlap是基于XML的,自然也可以支持很多不同的语言。
    当然,同样地传输内容下,XML的传输量会大一些。
    如果要说有什么好处的话也只有可读性了。


    实在懒得添加依赖再提供原生实现,但他并不复杂。 

    Creating a Hessian service using Java has four steps:

    • Create an Java interface as the public API
    • Create a client using HessianProxyFactory
    • Create the Service implementation class
    • Configure the service in your servlet engine.


    在这里我主要记录一下如何在spring中导出与调用Hessian service。
    正如上面所说,我需要把服务配置到servlet engine中;
    服务端和客户端都需要添加一个dependency:

    <dependency>
        <groupId>com.caucho</groupId>
        <artifactId>hessian</artifactId>
        <version>4.0.33</version>
    </dependency>
    


    正好我这边有个使用springMVC的应用,我就在这个基础上导出Hessian service。

    <servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    简单写一个接口与实现:

    package pac.king.common.rpc;
    public interface MyHessianService {
        public String justHadEnoughParties();
    }
    

     

    package pac.king.common.rpc.impl;
    import pac.king.common.rpc.MyHessianService;
    public class MyHessianServiceImpl implements MyHessianService {
        public String justHadEnoughParties() {
            return "Please save me..";
        }
    }
    


    我在spring-mvc.xml中曾经做了如下配置,并在*Controller中使用了RequestMapping注解去给URL做映射。
    但这并不妨碍我导出Hessian service再为其映射一个URL:

    <context:component-scan base-package="pac.king.controller"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>
                /service=myHessianService
            </value>
        </property>
    </bean>
    


    导出Hessian service:

    <bean id="myHessianServiceImpl" class="pac.king.common.rpc.impl.MyHessianServiceImpl" />
    <bean id="myHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"
        p:service-ref="myHessianServiceImpl"
        p:serviceInterface="pac.king.common.rpc.MyHessianService"
    />
    


    现在可以调用了,我需要在客户端声明一个接口(pac.test.HessianService),再用代理去调用:

    <bean id="myHessianClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"
        p:serviceUrl="http://localhost:8080/runtrain/service"
        p:serviceInterface="pac.test.HessianService"
    />
    


    调用:

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    HessianService service = (HessianService)context.getBean("myHessianClient");
    System.out.println(service.justHadEnoughParties());
    


    console输出:

    对于Burlap,几乎与Hessian的配置没什么区别;
    只需要把HessianServiceExporter改为BurlapServiceExporter,
    并将HessianProxyFactoryBean改为BurlapProxyFactoryBean即可。

    RMI使用Java的序列化,而Hessian/Burlap则为了不同语言之间通信而使用私有的序列化。
    如果我需要基于HTTP,但我并不需要多语言支持,我只想用Java...

     

     

    HttpInvoker


    我应该说这是基于Http的RMI吗?
    虽然看起来两全其美,但也存在让人"遗憾"的地方,
    (事实上不怎么遗憾的说,我曾经做过没有Spring的项目,连持久层框架都是自己实现,做得越久越痛苦...)
    他没有所谓"原生"的实现,他是Spring的一部分,只能在Spring应用中使用。


    Spring为这些RPC通信模型提供的相关类在命名上都有一致,都是:

    • 服务端:*ServiceExporter
    • 客户端:*ProxyFactoryBean


    自然地,HttpInvoker将用到

    • org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
    • org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean


    基于HttpInvoker的服务也像Hessian那样由DispatcherServlet进行分发。
    鉴于很多相同的地方,我打算继续使用在上一篇中用Hessian通信的接口和实现类。

    我几乎不用做任何工作,URL映射也不需要修改,我只需要将服务端的配置修改一下:

    <bean id="myHessianServiceImpl" class="pac.king.common.rpc.impl.MyHessianServiceImpl" />
    <!-- <bean id="myHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"
        p:service-ref="myHessianServiceImpl"
        p:serviceInterface="pac.king.common.rpc.MyHessianService"
    /> -->
    <bean id="myHessianService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"
        p:service-ref="myHessianServiceImpl"
        p:serviceInterface="pac.king.common.rpc.MyHessianService"
    />
    


    相应地,客户端也只需要修改一下class:

    <!-- <bean id="myHessianClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"
        p:serviceUrl="http://localhost:8080/runtrain/service"
        p:serviceInterface="pac.test.HessianService"
    /> -->
    <bean id="myHessianClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"
        p:serviceUrl="http://localhost:8080/runtrain/service"
        p:serviceInterface="pac.test.HessianService"
    />
    


    这样就保证了效率又解决了防火墙的问题,
    后来我听有人说,他们用Hessian做跨语言通信时,基于Http这个特征并不能解决防火墙的问题。
    不知道他们具体情况如何,似乎没说到一块儿...


    看了Hessian之后突然感觉Web service这种东西好笨重啊(虽然也有一些方法可以克服部分问题)。
    既然有Hessian,那为什么还要用Web service这种东西呢?
    我突然开始怀疑起他存在的意义。
    搜了一下,结果都是比较RPC通信模型的效率,没有人说他们为什么还要用(都应该有存在的意义吧)...
    如果仅仅是效率的话都用Hessian不就得了?
    带着这个问题我逛了逛stackoverflow,然后我得到了下面几种答案。

    • 多数人手中拿着锤子的时候,他们倾向于将所有问题都当作钉子,他们通常不会试着去寻找别的工具。导致Web service泛滥的原因也是这个。
    • 我觉得你应该重新看看Web service的优势(结果有人说了跨语言和SOA...果然关键还是相对什么做比较...)
    • Web service比那些non-xml的通信方式慢?这种相对的速度问题更多的取决于业务需求和你自己的代码实现(这个说法也同样适用于反射)。


    最后我还是没有得到让我满意的答案,倒是复习了Web service...
    很多类似场景下人们都将Web service视为"standard"option。 既然如此...那就看看Web service吧。

     

     

    JAX-WS


    看来使用web service是不可避免的。
    我曾对这个有些抵触,因为他给我印象总是麻烦+慢(后来虽然方便了许多,但还是很慢)。
    然后再去搜索"advantages of web service"什么的试着再让自己接受他。
    简单记录一下如何用Spring导出Endpoint。

    假设我想在有一个Spring应用,我需要把一个Pojo或者一部分方法导出为Web Service。
    但这会有一个问题——Endpoint的生命周期是由JAX-WS runtime来管理(The lifecycle of such an endpoint instance will be managed by the JAX-WS runtime),
    Spring context中的Bean无法autowire到Endpoint中,而我要导出的那些东东都用到了Spring管理的Bean。


    对此,我们有两个解决方法:

    • org.springframework.web.context.support.SpringBeanAutowiringSupport
    • JaxWsServiceExporter


    我上面括号中的那段话是引用的SpringBeanAutowiringSupport的javaDoc。
    使用该类的典型案例就是bean注入到JAX-WS endpoint类中(人家注释上写的),任何一个生命周期不是由Spring来管理的场景都可以用到他。
    而我们只需要继承这个类,也就是说创建一个实例时会调用父类的无参构造方法,我们来看看他的构造方法:

    /**
     * This constructor performs injection on this instance,
     * based on the current web application context.
     * <p>Intended for use as a base class.
     * @see #processInjectionBasedOnCurrentContext
     */
    public SpringBeanAutowiringSupport() {
        processInjectionBasedOnCurrentContext(this);
    }
    
    /**
     * Process {@code @Autowired} injection for the given target object,
     * based on the current web application context.
     * <p>Intended for use as a delegate.
     * @param target the target object to process
     * @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext()
     */
    public static void processInjectionBasedOnCurrentContext(Object target) {
        Assert.notNull(target, "Target object must not be null");
        WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();
        if (cc != null) {
            AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
            bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());
            bpp.processInjection(target);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Current WebApplicationContext is not available for processing of " +
                        ClassUtils.getShortName(target.getClass()) + ": " +
                        "Make sure this class gets constructed in a Spring web application. Proceeding without injection.");
            }
        }
    }
    


    那就试试看:

    @Service
    @WebService(serviceName="testMyService")
    public class MyServiceEndpoint extends SpringBeanAutowiringSupport{
        @Autowired
        MyService myService;
    
        @WebMethod
        public String sayHiFarAway(String name){
            return myService.sayHiTo(name);
        }
    }
    

     

    接着发布一下:

    ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext*.xml");
    Endpoint.publish("http://localhost:8080/myservices", (MyServiceEndpoint)context.getBean(MyServiceEndpoint.class));
    


    调用:

    javax.xml.ws.Service service = javax.xml.ws.Service.create(url, new QName("http://endpoint.king.pac/","testMyService"));
    QName q = new QName("http://endpoint.king.pac/","MyServiceEndpointPort");
    MyClientService client = service.getPort(q,MyClientService.class);
    System.out.println(client.sayHiFarAway("King"));
    


    写一个EndPoint还要继承和业务无关的类,让人不爽...而且发布和调用都麻烦。
    那试试SimpleJaxWsServiceExporter,只需要简单的配置就可以导出一个EndPoint。
    但是他也有需要注意的地方,引用一下该类的javaDoc:

    Note that this exporter will only work if the JAX-WS runtime actually supports publishing with an address argument, i.e. if the JAX-WS runtime ships an internal HTTP server. This is the case with the JAX-WS runtime that's inclued in Sun's JDK 1.6 but not with the standalone JAX-WS 2.1 RI.


    SimpleJaxWsServiceExporter会自动detect所有被WebService注解的类,因此只需要在配置中声明即可;此时Endpoint地址直接使用默认的localhost:8080:

    <bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter" />
    


    接着改善一下客户端的调用,使用JaxWsPortProxyFactoryBean:

    <bean id="clientSide" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"
        p:wsdlDocumentUrl="http://localhost:8080/testMyService?wsdl"
        p:serviceName="testMyService"
        p:portName="MyServiceEndpointPort"
        p:serviceInterface="pac.king.endpoint.MyClientService"
        p:namespaceUri="http://endpoint.king.pac/"
    />
    


    这样就可以像使用普通bean一样使用service了:

    MyClientService client = (MyClientService)context.getBean("clientSide");
    System.out.println(client.sayHiFarAway("King"));
    


    用起来方便了不少,但仍然无法改变一个事实:

    Web service requests are larger than requests encoded with a binary protocol.

    还有就是http/https的问题。
    如果使用不当,我可能需要多做些工作去处理HTTP做不来的事情,而这时候RMI又非常适合。

    Spring中如何配置Hibernate事务 http://www.linuxidc.com/Linux/2013-12/93681.htm

    Struts2整合Spring方法及原理 http://www.linuxidc.com/Linux/2013-12/93692.htm

    基于 Spring 设计并实现 RESTful Web Services http://www.linuxidc.com/Linux/2013-10/91974.htm

    Spring-3.2.4 + Quartz-2.2.0集成实例 http://www.linuxidc.com/Linux/2013-10/91524.htm

    使用 Spring 进行单元测试 http://www.linuxidc.com/Linux/2013-09/89913.htm

    运用Spring注解实现Netty服务器端UDP应用程序 http://www.linuxidc.com/Linux/2013-09/89780.htm

    Spring 3.x 企业应用开发实战 PDF完整高清扫描版+源代码 http://www.linuxidc.com/Linux/2013-10/91357.htm

    Spring 的详细介绍请点这里
    Spring 的下载地址请点这里 

    展开全文
  • **通信-RPC模型**

    2016-10-26 19:55:20
    通信-RPC模型通信模型分为4种: 远程过程调用(RPC)、远程方法调用(RMI)、面向消息中间件(MOM)以及流(stream)。这里主要讨论远程过程调用(RPC)通信模型。 RPC的目的在于将消息传递的大部分复杂性隐藏,...

    通信模型分为4种
    远程过程调用(RPC)、远程方法调用(RMI)、面向消息中间件(MOM)以及流(stream)。这里主要讨论远程过程调用(RPC)通信模型。
    RPC的目的在于将消息传递的大部分复杂性隐藏,比较实用与客户-服务器应用程序。
    定义:当机器A上的进程调用机器B上的进程时,A上的调用进程被挂起,B上被调用进程开始执行。调用方可以通过使用参数将信息传递给被调用方,而后通过传回的结果得到信息。
    思想:尽可能使远程过程调用具有与本地调用相同的形式。
    问题1:调用过程和被调用过程运行在不同的机器上,他们在不同的地址空间执行从而带来复杂性。
    问题2: 需要进行复杂参数和结果的传递。
    问题3: 双方任何一发发生崩溃而引发各种问题。

    介绍:这里引入4个名词:客户端、客户端存根、服务器端、服务器存根
    这里客户端和服务器端不做解释(如果这个都不知道,感觉也没必要浪费时间继续看下去的)
    客户存根:将参数打包成消息,并请求发送消息到服务器
    服务器存根:是客户存根在服务器端的等价物,用来将通过网络输入的请求转存为本地过程调用。
    ![这里写图片描述] (https://img-blog.csdn.net/20161026195141219)
    客户与服务器之间的RPC原理
    远程过程调用的一般步骤
    这里写图片描述
    (1)客户过程以正常的方式调用客户存根
    (2)客户存根生成一个消息,之后调用本地操作系统
    (3)客户端操作系统将消息发送给远程服务器操作系统
    (4)服务器操作系统将消息交给服务器存根
    (5)服务器存根将参数提取,并调用服务器
    (6)服务器执行要求的操作,操作完成将结果返回给服务器存根
    (7)服务器存根将结果打包成一个消息,之后调用服务器本地系统
    (8)服务器操作系统将消息发送给客户操作系统
    (9)客户操作系统将消息交给客户存根
    (10)客户存根将结果从消息中提取出来,返回给调用它的客户过程。

    展开全文
  • 高分辨率卫星影像的出现,使得利用卫星遥感立体影像实现地面目标的高精度定位与大比例尺测...现今已建立了各种传感器模型,来描述地面空间坐标与相应像点坐标的数学关系。在传统的摄影测量领域,应用较多的是物理模型
  • RPC模型和rest的区别

    2016-05-06 21:42:24
    RPC:Remote Procedure Call Protocol--远程过程调用 REST:Rerespentital Statuful Transaction--表述性状态转移 仔细理解他们的名称的含义,我们从中可以体会到他们之间的差异: RPC是远程过程调用,过程即我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 82,327
精华内容 32,930
关键字:

rpc模型