美文网首页
高级框架第一天RPC:远程过程调用

高级框架第一天RPC:远程过程调用

作者: w漫漫 | 来源:发表于2020-09-05 08:37 被阅读0次

    RPC:远程过程调用

    主要内容

    1.项目结构变化

    2.RPC简介

    3.RMI实现RPC

    4.HttpClient实现RPC

    5.Zookeeper安装

    6.Zookeeper客户端常用命令

    7.向Zookeeper中注册内容

    8.从Zookeeper中发现内容

    9.手写RPC框架

    一.今天学什么?为什么讲?

    Dubbo是RPC的一个框架.

    二.项目架构变化

    1.单体架构

    1.1架构图

    单体架构就是一个项目里面包含这个项目中全部代码.一个应用搞定全部功能.

    DNS服务器可以是单映射,也可以配置多个映射.

    1.2软阿基代码结构

    在单体架构项目中,团队都是通过包(package)进行区分每个模块

    总体包结构:com.bjsxt.*.分层包

    项目名:

        --com

            --bjsxt

                --common

                --utils

            --user

                --controller

                --service

                --mapper

            --sys

                --controller

                --service

                --mapper

    1.3优缺点

    1.3.1优点

    部署简单,维护方便,开发成本低

    1.3.2缺点

    当项目规模大,用户访问频率高,并发量大,数据量大时,会大大降低程序执行效率,甚至出现服务器宕机等情况

    1.4适用项目

    传统管理项目,小型互联网项目

    2.分布式架构

    2.1架构图(简易版)

    分布式架构会把一个项目按照特定要求(多按照模块或功能)拆分成多个项目,每个项目分别部署到不同的服务器上.

    2.2软件代码结构

    项目1:

        --com.bjsxt.xxx

            --controller

            --service

            --mapper

    项目2:

        --com.bjsxt.mmm

            --controller

            --service

            --mapper

    2.3优缺点

    2.3.1优点

    增大了系统可用性.减少单点故障,导致整个应用不可用

    增加重用性.因为模块化,所以重用性更高.高内聚,低耦合

    增加可扩展性.有新的模块增加新的项目即可

    增加每个模块的负载能力.因为每个模块都是一个项目,所以每个模块的负载能力更强

    2.3.2缺点

    成本更高.因为技术多,难,所以开大成本,时间成本,维护成本都在变高

    架构更加复杂

    整体响应之间变长,一些业务需要多项目通信后给出结果

    通吐量更大.吞吐量=请求数/秒

    2.4适用项目

    中,大型互联网项目.客户多,数据多,访问并发高,压力大,吞吐量高

    2.5待解决问题

    分布式架构中各个模块如何进行通信?

    可以使用Http协议,也可以石红RPC协议通信,也可以使用其他的通信方式.我们本阶段使用的是RPC协议,因为它比HTTP更适合项目内部通信

    三.RPC简介

    1.RFC

    RFC(Request For Comments)是由互联网工程任务组(IEIF)发布的文件集.文件集中每个文件都有自己唯一编号.例如:rfc1831.目前RFC文件由互联网协会(Internet Society,ISOC)赞助发行

        RPC就收集在RFC1831中.可以通过下面网址查看:

    https://datatracker.ietf.org/doc/rfc1831/

    2.RPC

    RPC在rgc1831中收录,RPC(Remote Procedure Call)远程过程调用协议

    RPC协议规定允许互联网中一台主机程序调动另一台主机程序,而程序员无需对这个交互过程进行编程.在RPC协议中强调当A程序调用B程序中功能或方法时,A是不知道B中方法具体实现的

    RPC是上层协议,底层可以基于TCP协议,也可以基于HTTP协议.一般我们说RPC都是基于RPC的具体实现,如:Dubbo框架.从广义上讲只要是满足网络中进行通信调用都统称为RPC,甚至HTTP协议都可以说是RPC的具体实现,但是具体分析看来RPC协议要比HTTP协议更加高效,基于RPC的框架功能更多

    RPC协议是基于分布式架构而出现的,所以RPC在分布式项目中有着得天独厚的优势

    3.RPC和HTTP对比

    3.1具体实现

    RPC:可以基于TCP协议,也可以基于HTTP协议

    HTTP:基于HTTP协议

    3.2效率

    RPC:自定义具体实现可以减少很多无用的报文内容,使得报文体积更小

    HTTP:如果是HTTP1.1报文中很多内容都是无用的.如果是HTTP2.0以后和RPC相差不大,比RPC少的可能就是一些服务治理等功能

    3.3连接方式

    RPC:长链接支持

    HTTP:每次连接都是3次握手(断开连接为4次挥手)

    3.4性能

    RPC可以基于很多序列化方式.如:thrift

    HTTP主要是通过JSON,序列化和方序列化效率更低

    3.5注册中心

    RPC:一般RPC框架都带有注册中心

    HTTP:都是直连

    3.6负载均衡

    RPC:绝大多数RPC框架都带有负载均衡测量

    HTTP:一般都需要借助第三方工具.如:nginx

    3.7综合结论

    RPC框架一般都带有丰富的服务治理等功能,更适合企业内部接口调用.而HTTP更适合多平台之间相互调用

    四.HttpClient实现RPC

    1.HttpClient简介

    在JDK中java.net包下提供了用户HTTP访问的基本功能,但是它缺少灵活性或许多应用所需要的功能

    HttpClient起初是Apache Jackarta Common的子项目.用来提供高效的,最新的,功能丰富的支持HTTP协议的客户端编程工具包,并且他支持HTTP协议最新的版本.2007年成为顶级项目

    通俗解释:HttpClient可以实现Java代码完成标准HTTP请求及响应

    2.代码实现

    2.1服务端

    新建项目HttpClientServer

    2.1.1新建控制器

    com.bjsxt.controller.DemoController

    @Controller

    public class DemoController{

        @RequestMapping("/demo")

        @ResponseBody

        public String demo(String param){

            return "demo"+param;

        }

    }

    2.1.2新建启动器

    com.bjsxt.HttpClientServerApplication

    @SpringBootApplication

    public class HttpClientServerApplication{

        public static void main(String[] args){

            SpringApplication.run(HttpClientServerApplication.class,args);

        }

    }

    2.2客户端

    新建HttpClientDemo项目

    2.2.1添加依赖

    <dependencies>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>httpclient</artifactId>

            <version>4.5.10</version>

        </dependency>

    </dependencies>

    2.2.2新建类

    新建com.bsjxt.HttpClientDemo,编写主方法

    2.2.2.1使用POST方法访问

    public class HttpClientDemo{

        public static void main(String[] args){

            try{

                CloseableHttpClient httpClient = HttpClients.createDefault();

                HttpPost post = new HttpPost("http://localhost:8080/demo");

                HttpEntity httpEntity = null;

                List<NameValuePair>params = new ArrayList<>();

                params.add(new BasicNameValuePair("param","123"));

                StringEntity entity = new UrlEncodedFormEntity(params,"utf-8");

                post.setEntity(entity);

                CloseableHttpResponse response = httpClient.execute(post);

                String result = EntityUtils.toString(response.getEntity());

                System.out.println(result);

                response.close();

                httpClient.close();

            }catcha(IOException e){

                e.printStackTrace();

            }

        }

    }

    2.2.2.2使用GET方式访问

    public static void main(String[] args){

        try{

            CloseableHttpClient httpClient = HttpClients.createDefault();

            URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/demo");

            uriBuilder.addParameter("param","get123");

            HttpGet get = new HttpGet(uriBuilder.build());

            CloseableHttpResponse response = httpClient.execute(get);

            String result = EntityUtils.toString(response.getEntity(),"utf-8");

            System.out.println(result);

            response.close();

            httpClient.close();

        }catch(URISyntaxException e){

            e.printStackTrace();

        }catch(IOException e){

            e.printlnStackTrace();

        }

    }

    3.HttpClient请求包含JSON

    3.1java代理实现

    public class HttpClientDemo{

        public static void main(Stirng[] args){

            try{

                CloseableHttpClient httpClient = HttpClients.createDefault();

                HttpPost post = new HttpPost("http://localhost:8080/demo");

                HttpEntity httpEntity = null;

                String json = "{}";

                StringEntity entity = new StringEntity(json,ContentType.APPLICATION_JSON);

                post.setEntity(entity);

                CloseableHttpResponse response = httpClient.execute(post);

                String result - EntityUtils.toString(response.getEntity());

                System.out.println(result);

                response.close();

                httpClient.close();

            }catch(IOException e){

                e.printStackTrace();

            }

        }

    }

    4.服务端控制器接口参数

    @RequestBody把请求体中六数据转换为指定的对象.多用在请求体参数是json数据且请求的Content-Type="application/json"

    @RequestMapping("/demo4")

    @ResponseBody

    public String demo4(@RequestBody List<People>list){

        System.out.println(list);

        return list.toString();

    }

    5.Jackson用法

    5.1把对象转换为json字符串

    ObjectMapper objectMapper = new ObjectMapper();

    People peo = new People();

    String jsonStr = objectMapper.writeValueAsString(peo);

    5.2把json字符串转换为对象

    ObjectMapper objectMapper= new ObjectMapper();

    People peo = objectMapper.readValue(jsonStr,People.class);

    5.3把json字符串转换为List集合

    ObjectMapper objectMapper = new ObjectMapper();

    JavaType javaType = objectMapper.getTypeFactory().constructParametericType(List.class,People.class);

    List<People> list = objectMapper.readValue(jsonStr,javaType);

    6.Ajax发送json参数写法

    var json = '[{"id":123,"name":"bjsxt"},{"id":123,"name:"bjsxt"}]';

    $.ajax({

        url:'/demo5',

        success:function(data}{

            alert(data);

            for(var i=0; i<data.length; i++){

                alert(data[i].id+"  "+data[i].name);

            }

        },

        contentType:'application/json',//请求体中参数类型

        dataType:'json',//响应内容类型

        data:json

    });

    7.跨域

    跨域:协议,端口,ip中只要有一个不同就是跨域请求

    同源策略:浏览器默认只允许ajax访问同源(协议,ip,端口都相同)内容

    解决同源策略:

    在控制器接口上天啊及@CrossOrigin.表示允许跨域.本质在响应头中添加

    Access-Control-Allow-Origin:*

    @RequestMapping("/demo5")

    @ResponseBody

    @CrossOrigin

    public List<People>demo5(@RequestBody List<People>list){

        System.put.println(list);

        return list;

    }

    五.RMI实现RPC

    1.RMI简介

    RMI(Remote Method Invocation)远程方法调用

    RMI是从JDK1.2推出的功能,它可以实现在一个java应用中可以像调用本地方法一样调用另一个服务器中java应用(JVM)中的内容

    RMI是java语言的远程调用,无法实现跨语言

    2.执行流程

    Registry(注册表)是防止所有服务器对象的命名空间.每次服务端创建一个对象时,它都会使用bind()或rebind()方法注册该对象.这些是使用称为名称的唯一名称注册的.

    要调用远程对象,客户端需要该对象的引用.即通过服务端绑定的名称从注册表中获取对象(lookup()方法)

    3.API介绍

    3.1Remote

    java.rmi.Remote定义了此接口为远程调用接口.如果接口被外部调用,需要继承次接口

    3.2RemoteException

    java.rmi.RemoteException

    继承了Remote接口,如果方法是允许被远程调用的,需要抛出此异常

    3.3UnicastRemoteObject

    java.rmi.server.UnicastRemoteObject

    此类实现了Remote接口和Serializable接口

    自定义接口实现类除了实现自定义接口还需要继承此类

    3.4LocateRegistry

    java.rmi.registry.LocateRegistry

    可以通过LocateRegistry子本机上创建Registry,通过特定的端口就可以访问这个Registry

    3.5Naming

    java.rmi.Naming

    Naming定义了发布内容可访问RMI名称.也是通过Naming获取到指定的远程方法

    4.代码实现

    4.1服务端创建

    创建RmiServer项目

    4.1.1编写接口

    com.bjsxt.service.DemoService编写

    public interface DemoService extends Remote{

        String demo(String demo)throws RemoteException;

    }

    4.1.2.编写实现类

    com.bsjxt.service.impl.DemoServiceImpl编写

    注意:

        构造方法是public的.默认生成protected

    public class DemoServiceImpl extends UnicastRemoteObject implements DemoService{

        public DemoServiceImpl() throws RemoteException{}

        @Override

        public String demo(String demo)throws RemoteException{

            return demo+"123";

        }

    }

    4.1.3编写主方法

    编写com.bjsxt.DemoService类,生成主方法

    public class DemoServer{

        public static void main(String[] args){

            try{

                DemoService demoService = new DemoServiceImpl();

                LocateRegistry.createRegistry(8888);

                Naming.bind("rmi://localhsot:8888/demoService",demoService);

            }catch(RemoteException e){

                e.printStackTrace();

            }catch(AlreadyBoundException e){

                e.printStackTrace();

            }catch(malformedURLException){

                e.printStackTrace();

            }

        }

    }

    4.1.4运行项目

    运行项目后,项目一直处于启动状态,表示可以远程访问此项目中的远程方法

    4.2创建客户端代码

    创建项目RmiClient

    4.2.1复制服务端接口

    把服务端com.bjsxt.service.DemoService粘贴到项目中

    注意:复制代码仅为快速完成案例,学习技术,在商业开发中,DemoService接口应该使用独立的工程定义,并在服务端和客户端工程中通过依赖的方式引入

    4.2.2创建主方法类

    新建com.bjsxt.DemoClient

    public class DemoClient{

        public static void main(String[] args){

            try{

                DemoService demoService = (DemoService)Naming.lookup("rmi://localhost:8888/demoService");

                String result = demoService.demo("demo34");

                System.out.println(result);

            }catch(NotBoundException e){

                e.printStackTrace();

            }catch(MalformedURLException e){

                e.printStackTrace();

            }catch(RemoteException e){

                e.printStackTrace();

            }

        }

    }

    六.Zookeeper安装

    七.常用命令

    八.向Zookeeper中注册内容

    先进项目ZookeeperClient

    1.创建/demo节点

    使用zookeeper的客户端命令工具创建/demo

    ./zkCli.sh

    create /demo

    2.添加依赖

    <dependencies>

        <dependency>

            <groupId>org.apache.zookeeper</groupId>

            <artifactId>zookeeper</artifactId>

            <version>3.5.5</version>

        </dependency>

    </dependencies>

    3.编写代码

    创建类com.bjsxt.MyApp

    ZooDefs.Ids.OPEN-ACL_UNSAFE表示权限

    CreateMode.PERSISTENT_SEQUENTIAL永久存储,文件内容编写递增

    public static void main(String[] args){

        try{

            Zookeeper zookeeper = new Zookeeper("192.168.232.132:2181",60000,new Watcher(){;

                @Override

                public void process(WatcherEvent watchedEvent){

                    System.out.println("获取连接");

                }

        });

        String content = zookeeper.create("/demo/nn","content".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL);

        System.out.println("content"+content);

        }catch(IOException e){

            e.printStackTrace();

        }catch(KeeperException e){

            e.printStackTrace();

        }catch(InterruptedException e){

            e.printStackTrace();

        }

    }

    4.查看上传数据

    ls -R /    查看列表

    get /demo/nn000000002     查看内容

    九.从zookeeper中发现内容

    在原有项目中新建一个类,类中编写主方法

    public static void main(String[] args){

        try{

            Zookeeper zookeeper = new Zookeeper("192.168.232.132:2181",60000,new Watcher(){

                @Override

                public void process(WatchedEvent watchedEvent){

                    System.out.println("获取连接");

                }

            });

            //获取列表

            List<String>list = zookeeper.getChildren("/demo",false);

            for(String child:list){

                byte[] result = zookeeper.getData("/demo/"+child,false,null);

                Ssytem.out.println(new String(result));

            }

        }catch(IOException e){

            e.printStackTrace();

        }catch(KeeperException e){

            e.printStackTrace();

        }catch(InterruptedException e){

            e.printStackTrace();

        }

    }

    十.手写RPC框架

    使用Zookeeper作为注册中心,RMI作为连接技术,手写RPC框架

    1.创建父项目ParentDemo

    包含3个集合子项目

        service:包含被serviceImpl和consumer依赖的接口

        serviceImpl:provider提供的服务内容

        consumer:消费者,调用服务内容

    2.在父项目中添加依赖

    <parent>

        <groupId>org.springframework.boot<groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.1.10.RELEASE</version>

    </parent>

    <dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.apache.zookeeper</groupId>

            <aritfactId>zookeeper</artifactId>

            <version>3.5.5</version>

        </dependency>

    </dependencies>

    3.创建service项目

    项目结构如下:此项目中重点编写需要被两个项目依赖的接口

    4.创建DemoService接口

    创建com.bjsxt.DemoService,具体内容如下

    public interface DemoService extends Remote{

        String demo(String param)throws RemoteException;

    }

    5.创建serviceImpl项目

    此项目编写接口具体实现,RMI服务发布和把信息发送到Zookeeper中

    项目结构如下:

    在pom.xml中添加对service项目的依赖

    <dependencies>

        <dependency>

            <artifactId>service</artifactId>

            <groupId>com.bjsxt</groupId>

            <version>1.0-SNAPSHOT</version>

        </dependency>

    </dependencies>

    6.创建DemoServiceImpl

    创建com.bjsxt.service.impl.DemoServiceImpl

    public class DemoServiceImpl extends UnicastRemoteObject implements DemoService{

        public DemoServiceImpl()throws RemoteException{

        }

        @Override

        public String demo(String param)throws RemoteException{

            return param+"123";

        }

    }

    7.创建RmiRun

    创建com.bsjxt.RmiRun.实现RMI服务的发布和Zookeeper消息的发布

    public class RmiRun{

        public static void main(String[] args){

            try{

                DemoService demoService = new DemoServiceImpl();

                LocateRegistry.createRegistry(8888);

                String url = "rmi://localhsot:8888/demoService";

                Naming.bind(url,demoService);

                Zookeeper zookeeper = new Zookeeper("192.168.232.132:2181",60000,new Watcher(){

                    @Override

                    public void process(WatcherEvent watchedEvent){

                        Ssytem.out.println("获取连接");

                    }

                });

                String content = zookeeper.create("/demo/demoService",url.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);

                System.out.println("服务发布成功...");

            }catch(AlreadyBoundException e){

                e.printStackTrace();

            }catch(IOException e){

                e.printStackTrace();

            }catch(KeeperException e){

                e.printStackTrace();

            }catch(InterruptedException e){

                e.printStackTrace();

            }

        }

    }

    8.创建Consumer项目

    新建consumer项目,此项目需要从zookeeper中获取rmi信息,并调用rmi服务

    在pom.xml中添加对service项目的依赖

    <dependencies>

        <dependency>

            <artifactId>service</artifactId>

            <groupId>com.bjsxt</groupId>

            <version>1.0-SNAPSHOT</version>

        </dependency>

    </dependencies>

    9.创建接口和实现类

    创建com.bjsxt.service.ConsumerService接口

    创建com.bjsxt.servioce.impl.ConsumerServiceImpl实现类

    public interface ConsumerService{

        String consumerService(String param);

    }

    @Service

    public class ConsumerServiceImpl implements ConsumerService{

        @Override

        public String consumerService(String param){

            try{

                Zookeeper zookeeper = new Zookeeper("192.168.232.132:2181",60000,new Watcher(){

                    @Override

                    public void process(WatchedEvent watchedEvent){

                        System.out.println("获取连接");

                    }

                });

                byte[] urlByte = zookeeper.getData("/demo/demoService",false,null);

                DemoService demoService = (DemoService)Naming.lookup(new String(urlByte));

                String result = demoService.demo(param);

                System.out.println(result);

                return result;

            }catch(IOException e){

                e.printStackTrace();

            }catch(InteruptedException e){

                e.printlnStackTrace();

            }catch(NotBoundException e){

                e.printStackTrace();

            }

            return null;

        }

    }

    10.创建控制器

    创建com.bjsxt.controller.DemoController控制器

    @Controller

    public class DemoController{

        @Autowired

        private ConsumerService consumerService;

        @RequestMapping("/demo")

        @ResponseBody

        public String demo(String param){

            return consumerService.consumerService(param);

        }

    }

    11.创建启动器

    创建com.bjsxt.ConsumerApplication

    @SpringBootApplication

    public class ConsumerApplication{

        public static void main(String[] args){

            SpringApplication.run(ConsumerApplication.class,args);

        }

    }

    12.测试

    在浏览器输入:http://localhost:8080/demo?param=demo

    观察结果是否是:demo123

    相关文章

      网友评论

          本文标题:高级框架第一天RPC:远程过程调用

          本文链接:https://www.haomeiwen.com/subject/fzizsktx.html