美文网首页分布式微服务
应用服务框架的演变历史

应用服务框架的演变历史

作者: 千淘萬漉 | 来源:发表于2018-05-14 21:19 被阅读247次

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

    • 独立性:一个容器就是一个完整的执行环境,不依赖外部任何的东西。
    • 细粒度:一台物理机器可以同时运行成百上千个容器。其计算粒度足够的小。
    • 快速创建和销毁:容器可以在秒级进行创建和销毁,非常适合服务的快速构建和重组。
    • 完善的管理工具:数量众多的容器编排管理工具,能够快速的实现服务的组合和调度。

    相关文章

      网友评论

      本文标题:应用服务框架的演变历史

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