在较长的一段时间里,LAMP框架是Web网站的常见方式:Linux+ Apache+ PHP+ MySQL,或者另外一套MVC规范,java里比较常见的选择通常是Spring +Struts +MyBatis +Tomcat,有时候也会选择重量级的EJB来实现,尽管在技术细节上的处理各不相同,但是都有一个共性:垂直应用架构。
一、传统的垂直应用架构
1.MVC架构
以经典的MVC垂直应用架构为栗子,通常分为三层:
- View展示层,是用户看到并与之交互的界面。
- Control层,用于前端Web请求的分发,调度后台的业务处理。
- Model模型层,包含业务数据和执行逻辑。
标准的MVC模式并不包括数据访问层,所以通常还需要专门的ORM框架,可以屏蔽对底层数据库连接池和数据源的实现,提供对上层JDBC的访问,提升开发效率,常见的一般都是Hibernate和Mybatis。通常基于MVC框架的应用都会打成一个war包,部署在Tomcat等Web容器中。
业务组网也不复杂,通常做好双热机即可,可通过watchDog来检测应用,判断应用进程是否异常,如果一个出现问题可以立即启动到备机,如果考虑到更复杂的并发场景,可在后端做集群部署,还有前端F5等负载均衡处理。
双机逻辑组网图
2.垂直应用架构的缺陷
1.难以应付复杂的业务场景,且开发和维护的成本会增高。
2.团队协作效率差,公共功能重复开发,重复率高。
3.系统的可靠性变差,某个节点的故障会导致整个系统的“雪崩效应”。
4.维护和定制困难,复杂应用的业务拆分困难,代码修改牵一发而动全身。
当垂直应用越来越多,应用之间的交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使得前端能够更快的相应市场需求,同时将公共的API抽取出来,可以作为独立的公共服务给其他调用者消费,实现服务的共享和重用,于是有了RPC框架的需求。
二、PRC框架
RPC的全称为(Remote Procedure Call),远程过程调用,是一种进程间的通信方式,在2006年后的移动互联网时代开始兴起,出现了各种各样的开源RPC框架。
PRC框架原理
RPC的框架屏蔽了底层的传输方式(TCP/UDP),序列化方式(XML / JASON / ProtoBuf)和通信细节,使用者只需要知道who(谁)在where(哪里)提供了what(什么)服务即可。
一个最简单的RPC框架只需要考虑如下三个部分的实现:
- 服务提供者,运行在服务端,负责提供服务接口定义和实现。
- 服务发布者,运行在RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用;
- 本地服务代理,运行在RPC客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者;
在这里根据思路来简单提供一段代码实现,首先是服务的接口定义和实现:
/**
* HelloService 服务接口
*/
public interface HelloService {
String hello(String name);
}
/**
* HelloServiceImpl 服务接口的实现
*/
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
return "Hello " + name;
}
}
服务的发布:
/**
* RpcFramework
**/
public class RpcFramework {
/**
* 暴露服务
*
* @param service 服务实现
* @param port 服务端口
*/
public static void export(final Object service, int port) throws Exception {
if (service == null) {
throw new IllegalArgumentException("service instance == null");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port " + port);
}
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
ServerSocket server = new ServerSocket(port);
for(;;) {
try {
final Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
try {
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
Method method = service.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(service, arguments);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} finally {
input.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 引用服务
* @param interfaceClass 接口类型
* @param host 服务器主机名
* @param port 服务器端口
* @return 远程服务
*/
@SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
if (interfaceClass == null)
throw new IllegalArgumentException("Interface class == null");
if (! interfaceClass.isInterface())
throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
if (host == null || host.length() == 0)
throw new IllegalArgumentException("Host == null!");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("Invalid port " + port);
System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
}
}
public class RpcProvider {
public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
RpcFramework.export(service, 1234);
}
}
服务的调用:
public class RpcConsumer {
public static void main(String[] args) throws Exception {
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
for (int i = 0; i < Integer.MAX_VALUE; i ++) {
String hello = service.hello("World" + i);
System.out.println(hello);
Thread.sleep(1000);
}
}
}
2.RPC的不足
在大规模服务化以前,应用以前只能通过暴露接口和应用远程服务的方式去调用,服务越来越多的时候会有以下情况:
- 服务URL的配置管理变的困难
- 服务间的依赖关系变成错综复杂,难以分清哪个应用要在哪个应用前启动
- 服务的调用量越来越大,服务的容量问题出现问题,某个服务需要多少机器、什么时候该加机器
- 缺乏一个服务注册中心,动态的注册和发现服务。
服务化之后,随之而来的就是服务治理问题,现在的RPC框架在这方面都有所欠缺,要解决这些问题必须通过服务框架+服务治理来完成,单凭RPC框架无法解决服务治理的问题。
三、SOA服务化架构
SOA,Service-Oriented Architecture,面向服务的架构(SOA)是一个组件模型,是一种粗粒度、松耦合的以服务为中心的架构,接口之间通过定义明确的协议和接口进行通信。
面向服务的核心是对传统的垂直架构进行改造,其中的核心技术就是分布式服务框架,应用也从集中式走向了分布式,大规模系统的架构设计原则就是尽可能的拆分,以达到更好的独立扩展与伸缩,更灵活的部署、更好的隔离和容错,更高的开发效率,具体的拆分策略是:横向拆分和纵向拆分。
业务纵向拆分:
根据业务的特性把应用拆开,不同的业务模块独立部署,将复杂的业务线拆分成相对独立的、灵活的具体能力域,由大到小分而治之。
纵向拆分示意图
业务横向拆分:
将核心的、公共的业务拆分出来,通过分布式服务框架对业务进行服务化,消费者通过标准的契约来消费这些服务,服务提供者独立打包、部署,与消费者解耦。
纵向拆分示意图
服务治理
拆分了之后,随着服务数的增多,亟需一个服务治理框架,有效管理服务,提升服务的运行质量,服务治理需要满足:服务生命周期管理,服务容量规划,运行期治理和服务安全等。目前较为成熟的商用服务框架有Spring cloud,阿里巴巴提供的开源的Dubbo框架,非开源的HSF框架,
至于Dubbo和HSF这两者的差别,抄一段来展示:阿里巴巴第一代RPC框架Dubbo是国内第一款成熟的商用级RPC框架,已于2011年正式对外开源,目前已发展成为国内开源价值最高、用户使用规模最大的开源软件之一。2016年度中国开源软件Top10。最新一代RPC框架HSF,全称High Speed Framework,也叫"好舒服","很舒服"框架,是阿里内部对这一款高性能服务框架的昵称,是一款面向企业级互联网架构量身定制的分布式服务框架。HSF以高性能网络通信框架为基础,提供了诸如服务发布与注册,服务调用,服务路由,服务鉴权,服务限流,服务降级和服务调用链路跟踪等一系列久经考验的功能特性。
架构原理
分布式服务的架构可以抽象为三层:
1、RPC层:底层通信框架(例如NIO框架的封装),序列化和反序列化框架等。
2、FilterChain层:服务调用职责链,例如负载均衡,服务调用性能统计,服务调用完成通知,失败重发等等。
3、Service层:java动态代理,将服务提供者的接口封装成远程服务调用;java反射,服务提供者使用,根据消费者请求消息中的接口名、方法名、参数列表反射调用服务提供者的接口本地实现类。
分布式服务框架的两个核心功能:服务治理和服务注册中心,服务中心中dubbo默认使用的是ZooKeeper,HSF默认使用的为ConfigServer。
四、微服务
SOA解决了应用服务化的问题,随着服务化实践的深入,服务的规模也越来越大,服务治理的问题也越来越多,这时候出现了微服务的思想。微服务架构由多个微小服务构成,每个服务就是一个独立的可部署单元或组件,它们是分布式的,相互解耦的,通过轻量级远程通信协议(比如REST)来交互,每个服务可以使用不同的数据库,而且是语言无关性的。它的特征是彼此独立、微小、轻量、松耦合,又能方便的组合和重构,犹如《超能陆战队》中的微型机器人,个体简单,但组合起来威力强大。
微服务之所以这么火,另一个原因是因为 Docker 的出现,它让微服务有一个非常完美的运行环境,Docker 的独立性和细粒度非常匹配微服务的理念,Docker的优秀性能和丰富的管理工具,让大家对微服务有了一定的信息,概括来说 Docker 有如下四点适合微服务:
- 独立性:一个容器就是一个完整的执行环境,不依赖外部任何的东西。
- 细粒度:一台物理机器可以同时运行成百上千个容器。其计算粒度足够的小。
- 快速创建和销毁:容器可以在秒级进行创建和销毁,非常适合服务的快速构建和重组。
- 完善的管理工具:数量众多的容器编排管理工具,能够快速的实现服务的组合和调度。
网友评论