应用服务框架的演变历史

在较长的一段时间里,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 有如下四点适合微服务:

  • 独立性:一个容器就是一个完整的执行环境,不依赖外部任何的东西。
  • 细粒度:一台物理机器可以同时运行成百上千个容器。其计算粒度足够的小。
  • 快速创建和销毁:容器可以在秒级进行创建和销毁,非常适合服务的快速构建和重组。
  • 完善的管理工具:数量众多的容器编排管理工具,能够快速的实现服务的组合和调度。
    原文作者:YitaiCloud
    原文地址: https://www.jianshu.com/p/62398fa9bc64
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞