目录
1.架构演进
1.1 单体架构
1.2 垂直架构
1.3 分布式架构
1.4 SOA架构
1.5 微服务架构
2.RPC框架
2.1 RPC基本概念介绍
2.1.1 RPC协议
2.1.2 RPC框架
2.1.3 RPC与HTTP、TCP/ UDP、Socket的区别
2.1.4 RPC的运行流程
2.1.5 为什么需要RPC
2.2 Dubbo
2.2.1 Dubbo 概述
2.2.2 Dubbo实战
架构演进如下图:
这里假设A,B,C,D为四个模块
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。 此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。
优点:
简单:开发部署都很方便,小型项目首选
缺点:
项目启动慢。可靠性差·可伸缩性差
扩展性和可维护性差·性能低
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的 Web框架(MVC) 是关键。
垂直架构是指将单体架构中的多个模块拆分为多个独立的项目。形成多个独立的单体架构。垂直架构存在的问题:·重复功能太多。
分布式架构是指在垂直架构的基础上,将公共业务模块抽取出来,作为独立的服务,供其他调用者消费,以实现服务的共亨和重用。
RPC: Remote Procedure Call远程过程调用。有非常多的协议和技术来都实现了RPC的过程。比如:HTTP REST风格,JavaRMl规范、WebService SOAP协议. Hession等等。
分布式架构存在的问题:服务提供方一旦产生变更,所有消费方都需要变更。
SOA: (Service-OrientedArchitecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。
ESB: (Enterparise Servce Bus)企业服务总线,服务中介。主要是提供了一个服务于服务之间的交互。ESB包含的功能如:负载均衡,流量控制,加密处理,服务的监控,异常处理,监控告急等。
微服务架构是在SOA上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。微服务架构=80%的SOA服务架构思想+100%的组件化架构思想+80%的领域建模思想。
特点:
1.服务实现组件化:开发者可以自由选择开发技术。也不需要协调其他团队。
2.服务之间交互一般使用REST API。
3.去中心化,每个微服务有自己私有的数据库持久化业务数据。
4.自动化部署,把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维。
远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
OSI参考模型如下图:
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
2.1.2 RPC框架
在单机时代一台电脑运行多个进程,进程之间无法通讯,显然这会浪费很多资源,因此后来出现IPC(Inter-process communication:单机中运行的进程之间的相互通信),这样就能允许进程之间进行通讯,比如在一台计算机中的A进程写了一个吃饭的方法,那在以前如果在B进程中也要有一个吃饭的方法,必须要在B进程中进行创建,但有了RPC后B只需要调用A进程的程序即可完成,再到后来网络时代的出现,大家电脑都连起来,这时可不可以调用其他电脑上的进程呢,当然可以,这样RPC框架出现了。严格意义上来讲: Unix的生态系统中RPC可以在同一台电脑上不同进程进行,也可以在不同电脑上进行;而在windows里面同一台电脑上不同进程间的通讯还可以采用LPC(本地访问)。综上:RPC或LPC是上层建筑,IPC是底层基础。
RPC框架有很多:比如JAVA RMT、Thrift、Dubbo、grpc等。
TCP/UDP:都是传输协议,主要区别是tcp协议连接需要3次握手,断开需要四次挥手,是通过流来传输的,就是确定连接后,一直发送信息,传完后断开。udp不需要进行连接,直接把信息封装成多个报文,直接发送。所以udp的速度更快写,但是不保证数据的完整性。
Http:超文本传输协议是一种应用层协议,建立在TCP协议之上
Socket:是在应用程序层面上对TCPIP协议的封装和应用。其实是一个调用接口,方便程序员使用TCPIP协议栈而已。程序员通过socket来使用tcp/ip协议。但是socket并不是一定要使用tcp/ip协议,Socket编程接口在设计的时候,就希望也能适应其他的网络协议。
RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。所以RPC的实现可以通过不同的协议去实现比如可以使http、RMl等。
2.1.4 RPC的运行流程
首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
第二,要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDD(一种目录服务,通过该目录服务进行服务注册与搜索)服务上查找。如果是RMI调用的话,还需要一个RMl Registry来注册服务的地址。
第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用
如图所示:
论复杂度,RPC框架肯定是高于简单的HTTP接口的。但毋庸置疑,HTTP接口由于受限于HTTP协议,需要带HTTP请求头,导致传输起来效率或者说安全性不如RPC。
现在问题是,遇到怎样的瓶颈了才需要或者说更适合用RPC(比如像阿里这么大的请求并发量,简单的HTTP肯定达不到预期),但问题是像阿里这么大的量是比较少的,甚至说1/1000的量可能都没有,那我们还需要使用RPC吗?技术应该不是为了使用新技术而去使用,而应该是旧技术存在某些瓶颈,存在难以支撑或者扩展性越老越差等问题暴露出来之后,用新技术来进行解决。
那RPC最大的优点,或者说它相比简单的HTTP接口,它的优势、更适合它的业务场景是怎样呢?简单的HTTP又哪里不足,哪些场景明显不太适合呢?
http接口是在接口不多、系统与系统交互较少的情况下,解决信息初期常使用的一种通信手段;优点就是简单、直接、开发方便。利用现成的http协议进行传输。但是如果是一个大型的网站,内部子系统较多、接口非常多的情况下,RPC框架的好处就显示出来了,首先就是长链接,不必每次通信都要像http一样去3次握手什么的,减少了网络开销(这个问题在http2.0已经被解决不再算是问题了);其次就是RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。第三个来说就是安全性。最后就是流行的服务化架构、服务化治理,RPC框架是一个强力的支撑。
RPC是一种概念,http也是RPC实现的一种方式,用http交互其实就已经属于RPC了。RPC框架可以做到灵活部署和解耦。系统做大了,肯定是需要做微服务的。现在我们做电商就是这样,单独有一个订单系统,支付系统,商品系统,用户系统。都是分开部署,单独上线的。
RPC:远程过程调用。RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务,这才是使用RPC的真正目的。RPC的原理主要用到了动态代理模式,至于http协议,只是传输协议而已。
RPC是一个软件结构概念,是构建分布式应用的理论基础。就好比为啥你家可以用到发电厂发出来的电?是因为电是可以传输的。至于用铜线还是用铁丝还是其他种类的导线,也就是用http还是用其他协议的问题了。这个要看什么场景,对性能要求怎么样。比如在java中的最基本的就是RMI技术,它是java原生的应用层分布式技术。我们可以肯定的是在传输性能方面,RMI的性能是优于HTTP的。那为啥很少用到这个技术?那是因为用这个有很多局限性,首先它要保证传输的两端都要要用java实现,且两边需要有相同的对象类型和代理接口,不需要容器,但是加大了编程的难度,在应用内部的各个子系统之间还是会看到他的身影,比如EJB就是基于rmi技术的。这就与目前的bs架构的软件大相径庭。用http必须要服务端位于http容器里面,这样减少了网络传输方面的开发,只需要关注业务开发即可。
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架。
dubbo提供了三大核心能力:
dubbo 架构图如下所示:
节点角色说明:
调用关系说明
1、服务容器负责启动,加载,运行服务提供者。
2、服务提供者在启动时,向注册中心注册自己提供的服务。
3、服务消费者在启动时,向注册中心订阅自己所需的服务。
4、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
5、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
2.2.2 Dubbo实战
这里设置一个订单微服务和一个用户微服务,通过查询订单返回订单信息,订单中有用户信息,所有需要根据订单中的用户id调用用户微服务进行查询然后进行赋值返回订单信息,这里采用nacos和Dubbo相结合的方式。
2.2.2.1 生产者(用户模块微服务)和消费者(订单模块微服务)导入依赖
com.alibaba.cloud spring-cloud-starter-dubbo
2.2.2.2 公共模块
设置接口用于查询用户信息
public interface UserService { public User queryUserId(Long id); }
2.2.2.3 生产者(用户模块微服务)
application.yml文件配置
spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: SH dubbo: #服务名称 application: name: dubbo-user #注册中心 registry: address: nacos://127.0.0.1:8848 protocol: #协议名称 name: dubbo #协议端口号 port: 20880 scan: base-packages: com.example.user.Imp #扫描包的位置
在com.example.user.Imp中建立DubboUserServiceImpl类并继承UserService类,代码如下;
package com.example.user.Imp; import com.example.entity.JieKou.UserService; import com.example.entity.entities.User; import org.apache.dubbo.config.annotation.Service; @Service(version = "1.0") public class DubboUserServiceImpl implements UserService { public User queryUserId(Long id) { User user=new User(); user.setId((long) 3); user.setUsername("admin"); user.setAddress("ShanDon"); return user; } }
2.2.2.4 消费者(订单模块微服务)
application.yml文件配置如下:
dubbo: protocol: port: 20881 name: dubbo registry: address: nacos://localhost:8848
Controller类业务逻辑代码如下:
@RestController @RefreshScope public class UserController { @Autowired OrdersMapper ordersMapper; @Reference(version = "1.0",parameters = {"unicast","false"}) //服务提供的可以被多个消费者消费 UserService userService; @GetMapping("/order1/{id}") public Orders orders1(@PathVariable("id") Long id){ Orders orders = ordersMapper.selectById(id); User user = userService.queryUserId(orders.getUserId()); orders.setUser(user); return orders; } }
至此代码基本完成,运行结果如下图: