精华内容
下载资源
问答
  • 阿里组织架构的”大中+小前台

    千次阅读 2019-04-23 11:01:41
    希望你也加入到人工智能的队伍来!点击浏览教程 一、什么是“大中、小前台” 关键词:精准打击、管理高效、资源整合、灵活敏捷 阿里巴巴 “大中、小前台”机制的提出,某种程度上是从传统的事业部制向准...

    分享一个大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!点击浏览教程

    一、什么是“大中台、小前台”

    关键词:精准打击、管理高效、资源整合、灵活敏捷

    阿里巴巴 “大中台、小前台”机制的提出,某种程度上是从传统的事业部制向准事业部制的转换。就阿里巴巴而言,“前台”就是贴近最终用户/商家的业务部门,包括零售电商、广告业务、云计算、物流以及其它创新业务等;而“中台”则是强调资源整合、能力沉淀的平台体系,为“前台”的业务开展提供底层的技术、数据等资源和能力的支持,中台将集合整个集团的运营数据能力、产品技术能力,对各前台业务形成强力支撑

    前台和中台本质上是工作分工的问题。比如,某部门要开发一款App,是部门内部(大前台)自己组织一套技术、产品、设计、运营的团队,还是集团为其提供资源(大中台),由专门的技术团队来帮助其开发、设计产品等等。

    所以说, “小前台+大中台”的运营模式,就是美军的“特种部队(小前台)+航母舰群(大中台)”的组织结构方式,以促进管理更加扁平化。十几人甚至几人组成的特种部队在战场一线,可以根据实际情况迅速决策,并引导精准打击。而精准打击的导弹往往是从航母舰群上发射而出,后方会提供强大的侦查火力后勤支援。所以如果中台没有办法承接前线的需求,前线就会不认可它服务的价值。

    二、原因、解决的问题

    关键词:组织膨胀、避免重复、加强集权、管理高效、资源整合、灵活敏捷、存在风险

    当我们开展具体的业务时,每个团队都需要有技术、产品、市场等方面的基础支持,传统互联网公司的每个业务部门都会有自己专属的业务、市场、产品等人员。随着公司的发展壮大,许多业务部门内提供基础支持的工作可能会有很大程度上的重复例:两个相互独立的业务部门同时开发APP,两个团队很可能在同时开发同样的功能,重复解决同样技术问题,同时写差不多的代码),信息不能共享,导致许多资源被浪费。

    并且,每个业务团队的水平参差不齐,怎样使每个团队都能够在既保证质量、又保证效率的前提下完成任务。为此,我们急需一个有效的机制来将公司内部的技术、数据、产品等资源进行整合,统一为业务线提供支持和帮助。同时,各事业部就像一个个子公司,都是实行独立核算,导致各事业部往往从自身利益出发,影响事业部之间的协作,难以形成企业合力

    阿里巴巴近年来迅速扩张、员工众多,所以可能会存在管理不善、效率低下、各事业部各自为政等问题。为了解决以上问题,阿里提出了“大中台,小前台”机制。“小前台+大中台”的运营模式促使组织管理更加扁平化,使得管理更加高效,组织运作效率提高,业务更加敏捷灵活:

    其“中台”的设置就是为了提炼各个业务条线的共性需求,并将这些打造成组件化的资源包,然后以接口的形式提供给前台各业务部门使用,可以使产品在更新迭代、创新拓展的过程中研发更灵活、业务更敏捷最大限度地减少“重复造轮子”的KPI项目。前台要做什么业务,需要什么资源可以直接同公共服务部要。搜索、共享组件、数据技术等模块不需要每次去改动底层进行研发,而是在底层不变动的情况下,在更丰富灵活的“大中台”基础上获取支持,让“小前台”更加灵活敏捷,让每一个新的前台业务创新能够真正意义上“站在阿里巴巴这个巨人的肩膀上”,而不用每次开辟一个新业务都像新建一家创业公司那么艰难。

    三、具体做法

    关键词:组织架构调整、整合支持、网状结构、设立中台事业群、业务小前台

    (1)阿里巴巴的组织架构调整

    一直以“拥抱变化”著称的阿里巴巴,于2015年12月7日公布了新一轮组织调整。已经拥有3万员工的阿里巴巴将此前的“树状”管理模式改为“网状”,成立整合数据、搜索等技术平台的“中台事业群”,对前台各业务模块进行整合支持

    【整合前(2013年)阿里巴巴组织架构】

    2013年的阿里巴巴把具体业务划分为了9类,并对每一块的业务进行了很明确的细分,共下设25个事业部,分别由9名事业部总裁负责。这时的组织架构是较为传统的树状结构。

    【调整后阿里巴巴的组织架构】

    2015年经过调整后阿里巴巴的组织架构不再是传统的树状结构,而变成了网状结构。同时,其不再采用具体的业务模块下分设事业部的方式,而是将之前细分的25个事业部打乱,根据具体业务将其中一些能够为业务线提供基础技术、数据等支持的部门整合成为“大中台”,统一为业务线提供支持和帮助。

    四、问题与建议

    1、【问题】

    ① 中台无法为前台提供其想要的支持和帮助

    首先,是因为资源有限,中台无法为前台提供其想要的支持和帮助。在开展业务的过程中,各前台会向中台提出需求,因为中台的资源有限,所以当中台收到许多来自前线团队提出的需求后会进行评估。如果其认为某个前台项目的重要程度比较低,拒绝为前台提供支持。那么这个前台可能会为自己的业绩考虑去自行组团队完成项目,这就与传统的事业部制没有什么区别了。所以说,如果中台和前台没有找好平衡点的话,这种机制很容易被破坏。同时,也存在中台能力受限无法为前台提供有力支持的情况,最后项目做出来的效果可能与前台所想相差甚远。

    同时,此制度受限于中台的知识沉淀的能力,沟通协调能力和其对前台知识理解的能力等,这些都是非常大的挑战。

    ② 中台和前台分工不明确

    中台和前台之间存在许多灰色地带,这个时候就会出现分工不明确的问题:哪些工作是属于前台的,哪些工作是属于中台的?面对某一具体业务时,这一块任务是应该让中台来为业务线提供支持,还是让业务线自己做?如果中台和业务线都去完成这个任务,工作上会不会有重复?会不会有前台和中台之间互相推诿“踢皮球”的情况发生?

    ③ 传统的绩效考核方式不再适用

    中台用怎样的标准去衡量前台的业务值不值得提供支持?以什么为评估依据去分配资源?如果中台为许多小前台提供了差不多的支持,而最后只有一个小前台完成了业务目标,利润要怎么分配?如果所有小前台都没有达成业务目标怎么办?

    2、【建议】

    关键词:重建绩效考核体系 、划分清楚灰色地带、进行培训、评估过程标准化

    针对以上三个问题,我提出了以下解决方法:

    ① 最大程度上对资源进行整合:以保证中台能够为各个小前台提供强有力的支持。

    ② 建立评估部门,将评估过程标准化:组建专业的评估部门去对小前台开展的业务进行考察和评价,并根据评估结果向中台提出建议,使中台能够将资源合理分配。

    ③ 建立业务bp岗位(类似hrbp):深入前台了解前台的业务需求并反馈给中台,在前台和中台中起到沟通和协调作用,以免前台、中台有重复完成同一工作或“踢皮球”的情况发生。

    展开全文
  • 文章目录架构设计请列举出在JDK几个常用的设计模式?什么是设计模式?你是否在你的代码里面使用过任何设计模式?静态代理、JDK动态代理以及CGLIB动态代理静态代理动态代理cglib代理单例模式工厂模式观察者模式装饰...

    大家好,我是CSDN的博主ThinkWon,“2020博客之星年度总评选"开始啦,希望大家帮我投票,每天都可以投多票哦,点击下方链接,然后点击"最大”,再点击"投TA一票"就可以啦!
    投票链接:https://bss.csdn.net/m/topic/blog_star2020/detail?username=thinkwon
    在技术的世界里,ThinkWon将一路与你相伴!创作出更多更高质量的文章!2020为努力奋斗的你点赞👍,️新的一年,祝各位大牛牛气冲天,牛年大吉!😊😊

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号 内容 链接地址
    1 Java基础知识面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390612
    2 Java集合容器面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588551
    3 Java异常面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390689
    4 并发编程面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992
    5 JVM面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390752
    6 Spring面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397516
    7 Spring MVC面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397427
    8 Spring Boot面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397299
    9 Spring Cloud面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397367
    10 MyBatis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/101292950
    11 Redis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/103522351
    12 MySQL数据库面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104778621
    13 消息中间件MQ与RabbitMQ面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588612
    14 Dubbo面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390006
    15 Linux面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588679
    16 Tomcat面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397665
    17 ZooKeeper面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397719
    18 Netty面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104391081
    19 架构设计&分布式&数据结构与算法面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/105870730

    架构设计

    请列举出在JDK中几个常用的设计模式?

    单例模式(Singleton pattern)用于Runtime,Calendar和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像Boolean.valueOf,观察者模式(Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator design pattern)被用于多个 Java IO 类中。

    什么是设计模式?你是否在你的代码里面使用过任何设计模式?

    设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式是代码可用性的延伸

    设计模式分类:创建型模式,结构型模式,行为型模式

    静态代理、JDK动态代理以及CGLIB动态代理

    代理模式是java中最常用的设计模式之一,尤其是在spring框架中广泛应用。对于java的代理模式,一般可分为:静态代理、动态代理、以及CGLIB实现动态代理。

    对于上述三种代理模式,分别进行说明。

    静态代理

    静态代理其实就是在程序运行之前,提前写好被代理方法的代理类,编译后运行。在程序运行之前,class已经存在。
    下面我们实现一个静态代理demo:

    img

    定义一个接口Target

    package com.test.proxy;
    
    public interface Target {
    
        public String execute();
    }
    

    TargetImpl 实现接口Target

    package com.test.proxy;
    
    public class TargetImpl implements Target {
    
        @Override
        public String execute() {
            System.out.println("TargetImpl execute!");
            return "execute";
        }
    }
    

    代理类

    package com.test.proxy;
    
    public class Proxy implements Target{
    
        private Target target;
    
        public Proxy(Target target) {
            this.target = target;
        }
    
        @Override
        public String execute() {
            System.out.println("perProcess");
            String result = this.target.execute();
            System.out.println("postProcess");
            return result;
        }
    }
    

    测试类:

    package com.test.proxy;
    
    public class ProxyTest {
    
        public static void main(String[] args) {
    
            Target target = new TargetImpl();
            Proxy p = new Proxy(target);
            String result =  p.execute();
            System.out.println(result);
        }
    
    }
    

    运行结果:

    perProcess
    TargetImpl execute!
    postProcess
    execute
    

    静态代理需要针对被代理的方法提前写好代理类,如果被代理的方法非常多则需要编写很多代码,因此,对于上述缺点,通过动态代理的方式进行了弥补。

    动态代理

    动态代理主要是通过反射机制,在运行时动态生成所需代理的class.

    img

    接口

    package com.test.dynamic;
    
    public interface Target {
    
        public String execute();
    }
    

    实现类

    package com.test.dynamic;
    
    public class TargetImpl implements Target {
    
        @Override
        public String execute() {
            System.out.println("TargetImpl execute!");
            return "execute";
        }
    }
    

    代理类

    package com.test.dynamic;
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class DynamicProxyHandler implements InvocationHandler{
    
        private Target target;
    
        public DynamicProxyHandler(Target target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("========before==========");
            Object result = method.invoke(target,args);
            System.out.println("========after===========");
            return result;
        }
    }
    

    测试类

    package com.test.dynamic;
    
    import java.lang.reflect.Proxy;
    
    public class DynamicProxyTest {
    
        public static void main(String[] args) {
            Target target = new TargetImpl();
            DynamicProxyHandler handler = new DynamicProxyHandler(target);
            Target proxySubject = (Target) Proxy.newProxyInstance(TargetImpl.class.getClassLoader(),TargetImpl.class.getInterfaces(),handler);
            String result = proxySubject.execute();
            System.out.println(result);
        }
    
    }
    

    运行结果:

    ========before==========
    TargetImpl execute!
    ========after===========
    execute
    

    无论是动态代理还是静态带领,都需要定义接口,然后才能实现代理功能。这同样存在局限性,因此,为了解决这个问题,出现了第三种代理方式:cglib代理。

    cglib代理

    CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

    img

    目标类

    package com.test.cglib;
    
    public class Target {
    
        public String execute() {
            String message = "-----------test------------";
            System.out.println(message);
            return message;
        }
    }
    

    通用代理类

    package com.test.cglib;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class MyMethodInterceptor implements MethodInterceptor{
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println(">>>>MethodInterceptor start...");
            Object result = proxy.invokeSuper(obj,args);
            System.out.println(">>>>MethodInterceptor ending...");
            return "result";
        }
    }
    

    测试类

    package com.test.cglib;
    
    import net.sf.cglib.proxy.Enhancer;
    
    public class CglibTest {
    
        public static void main(String[] args) {
            System.out.println("***************");
            Target target = new Target();
            CglibTest test = new CglibTest();
            Target proxyTarget = (Target) test.createProxy(Target.class);
            String res = proxyTarget.execute();
            System.out.println(res);
        }
    
        public Object createProxy(Class targetClass) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetClass);
            enhancer.setCallback(new MyMethodInterceptor());
            return enhancer.create();
        }
    
    }
    

    执行结果:

    ***************
    >>>>MethodInterceptor start...
    -----------test------------
    >>>>MethodInterceptor ending...
    result
    

    代理对象的生成过程由Enhancer类实现,大概步骤如下:

    1. 生成代理类Class的二进制字节码;

    2. 通过Class.forName加载二进制字节码,生成Class对象;

    3. 通过反射机制获取实例构造,并初始化代理类对象。

    单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    主要解决:一个全局使用的类频繁地创建与销毁。

    懒汉式,线程安全

    代码实例

    public class Singleton2 {
    
        private static Singleton2 instance;
    
        private Singleton2() {}
    
        public static synchronized Singleton2 getInstance() {
            if (instance == null) {
                instance = new Singleton2();
            }
    
            return instance;
        }
    
    }
    

    饿汉式,线程安全

    代码实例

    public class Singleton3 {
    
        private static Singleton3 instance = new Singleton3();
    
        private Singleton3() {}
    
        public static Singleton3 getInstance() {
            return instance;
        }
    
    } 
    

    双检锁/双重校验锁 + volatile关键字

    代码实例

    public class Singleton7 {
    
        private static volatile Singleton7 instance = null;
    
        private Singleton7() {}
    
        public static Singleton7 getInstance() {
            if (instance == null) {
                synchronized (Singleton7.class) {
                    if (instance == null) {
                        instance = new Singleton7();
                    }
                }
            }
    
            return instance;
        }
    
    }
    

    工厂模式

    工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    主要解决:主要解决接口选择的问题。

    我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory

    FactoryPatternDemo,我们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。

    工厂模式

    步骤 1

    创建一个接口。

    Shape.java

    public interface Shape {
        void draw();
    }
    

    步骤 2

    创建实现接口的实体类。

    Rectangle.java

    public class Rectangle implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Rectangle::draw() method.");
        }
    }
    

    Square.java

    public class Square implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Square::draw() method.");
        }
    }
    

    Circle.java

    public class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Circle::draw() method.");
        }
    }
    

    步骤 3

    创建一个工厂,生成基于给定信息的实体类的对象。

    ShapeFactory.java

    public class ShapeFactory {
    
        //使用 getShape 方法获取形状类型的对象
        public Shape getShape(String shapeType) {
            if (shapeType == null) {
                return null;
            }
            shapeType = shapeType.toLowerCase();
    
            switch (shapeType) {
                case "circle":
                    return new Circle();
                case "rectangle":
                    return new Rectangle();
                case "square":
                    return new Square();
                default:
                    return null;
            }
    
        }
    
    }
    

    步骤 4

    使用该工厂,通过传递类型信息来获取实体类的对象。

    FactoryPatternDemo.java

    public class FactoryPatternDemo {
    
        public static void main(String[] args) {
            ShapeFactory shapeFactory = new ShapeFactory();
    
            //获取 Circle 的对象,并调用它的 draw 方法
            Shape shape1 = shapeFactory.getShape("CIRCLE");
            //调用 Circle 的 draw 方法
            shape1.draw();
    
            //获取 Rectangle 的对象,并调用它的 draw 方法
            Shape shape2 = shapeFactory.getShape("RECTANGLE");
            //调用 Rectangle 的 draw 方法
            shape2.draw();
    
            //获取 Square 的对象,并调用它的 draw 方法
            Shape shape3 = shapeFactory.getShape("SQUARE");
            //调用 Square 的 draw 方法
            shape3.draw();
        }
    
    }
    

    步骤 5

    验证输出。

    Inside Circle::draw() method.
    Inside Rectangle::draw() method.
    Inside Square::draw() method.
    

    观察者模式

    当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

    意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

    实现

    观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

    ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。

    观察者模式

    步骤 1

    创建 Subject 类。

    Subject.java

    public class Subject {
    
        private List<Observer> observers = new ArrayList<>();
    
        private int state;
    
        public int getState() {
            return state;
        }
    
        public void setState(int state) {
            this.state = state;
            notifyAllObservers();
        }
    
        public void attach(Observer observer) {
            observers.add(observer);
        }
    
        public void notifyAllObservers() {
            for (Observer observer : observers) {
                observer.update();
            }
        }
    
    }
    

    步骤 2

    创建 Observer 类。

    Observer.java

    public abstract class Observer {
    
        protected Subject subject;
    
        public abstract void update();
    
    }
    

    步骤 3

    创建实体观察者类。

    BinaryObserver.java

    public class BinaryObserver extends Observer {
    
        public BinaryObserver(Subject subject) {
            this.subject = subject;
            this.subject.attach(this);
        }
    
        @Override
        public void update() {
            System.out.println("Binary String: "
                    + Integer.toBinaryString(subject.getState()));
        }
    
    }
    

    OctalObserver.java

    public class OctalObserver extends Observer {
    
        public OctalObserver(Subject subject){
            this.subject = subject;
            this.subject.attach(this);
        }
    
        @Override
        public void update() {
            System.out.println( "Octal String: "
                    + Integer.toOctalString( subject.getState() ) );
        }
    
    }
    

    HexaObserver.java

    public class HexaObserver extends Observer {
    
        public HexaObserver(Subject subject){
            this.subject = subject;
            this.subject.attach(this);
        }
    
        @Override
        public void update() {
            System.out.println( "Hex String: "
                    + Integer.toHexString( subject.getState() ).toUpperCase() );
        }
    
    }
    

    步骤 4

    使用 Subject 和实体观察者对象。

    ObserverPatternDemo.java

    public class ObserverPatternDemo {
    
        public static void main(String[] args) {
            Subject subject = new Subject();
    
            new BinaryObserver(subject);
            new HexaObserver(subject);
            new OctalObserver(subject);
    
            System.out.println("First state change: 15");
            subject.setState(15);
            System.out.println();
    
            System.out.println("Second state change: 10");
            subject.setState(10);
        }
    
    }
    

    步骤 5

    验证输出。

    First state change: 15
    Binary String: 1111
    Hex String: F
    Octal String: 17
    
    Second state change: 10
    Binary String: 1010
    Hex String: A
    Octal String: 12
    

    装饰器模式

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

    意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

    主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

    实现

    我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。

    RedShapeDecorator 是实现了 ShapeDecorator 的实体类。

    DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。

    装饰器模式

    步骤 1

    创建一个接口。

    Shape.java

    public interface Shape {
    
        void draw();
    
    }
    

    步骤 2

    创建实现接口的实体类。

    Rectangle.java

    public class Rectangle implements Shape {
    
        @Override
        public void draw() {
            System.out.println("Shape: Rectangle");
        }
    
    }
    

    Circle.java

    public class Circle implements Shape {
    
        @Override
        public void draw() {
            System.out.println("Shape: Circle");
        }
    
    }
    

    步骤 3

    创建实现了 Shape 接口的抽象装饰类。

    ShapeDecorator.java

    public abstract class ShapeDecorator implements Shape {
    
        protected Shape decoratorShape;
    
        public ShapeDecorator(Shape decoratorShape) {
            this.decoratorShape = decoratorShape;
        }
    
        @Override
        public void draw() {
            decoratorShape.draw();
        }
    }
    

    步骤 4

    创建扩展了 ShapeDecorator 类的实体装饰类。

    RedShapeDecorator.java

    public class RedShapeDecorator extends ShapeDecorator {
    
        public RedShapeDecorator(Shape decoratorShape) {
            super(decoratorShape);
        }
    
        @Override
        public void draw() {
            decoratorShape.draw();
            setRedBorder(decoratorShape);
        }
    
        private void setRedBorder(Shape decoratorShape) {
            System.out.println("Border Color: Red");
        }
    
    }
    

    步骤 5

    使用 RedShapeDecorator 来装饰 Shape 对象。

    DecoratorPatternDemo.java

    public class DecoratorPatternDemo {
    
        public static void main(String[] args) {
            Shape circle = new Circle();
            Shape redCircle = new RedShapeDecorator(new Circle());
            Shape redRectangle = new RedShapeDecorator(new Rectangle());
    
            System.out.println("Circle with normal border");
            circle.draw();
    
            System.out.println("\nCircle of red border");
            redCircle.draw();
    
            System.out.println("\nRectangle of red border");
            redRectangle.draw();
    
        }
    
    }
    

    步骤 6

    验证输出。

    Circle with normal border
    Shape: Circle
    
    Circle of red border
    Shape: Circle
    Border Color: Red
    
    Rectangle of red border
    Shape: Rectangle
    Border Color: Red
    

    秒杀系统设计

    什么是秒杀

    通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动

    业务特点

    • 高并发:秒杀的特点就是这样时间极短瞬间用户量大

    • 库存量少:一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。

    • 业务简单:流程比较简单,一般都是下订单、扣库存、支付订单

    • 恶意请求,数据库压力大

    解决方案

    前端:页面资源静态化,按钮控制,使用答题校验码可以防止秒杀器的干扰,让更多用户有机会抢到

    nginx:校验恶意请求,转发请求,负载均衡;动静分离,不走tomcat获取静态资源;gzip压缩,减少静态文件传输的体积,节省带宽,提高渲染速度

    业务层:集群,多台机器处理,提高并发能力

    redis:集群保证高可用,持久化数据;分布式锁(悲观锁);缓存热点数据(库存)

    mq:削峰限流,MQ堆积订单,保护订单处理层的负载,Consumer根据自己的消费能力来取Task,实际上下游的压力就可控了。重点做好路由层和MQ的安全

    数据库:读写分离,拆分事务提高并发度

    秒杀系统设计小结

    • 秒杀系统就是一个“三高”系统,即高并发、高性能高可用的分布式系统
    • 秒杀设计原则:前台请求尽量少,后台数据尽量少,调用链路尽量短,尽量不要有单点
    • 秒杀高并发方法:访问拦截、分流、动静分离
    • 秒杀数据方法:减库存策略、热点、异步、限流降级
    • 访问拦截主要思路:通过CDN和缓存技术,尽量把访问拦截在离用户更近的层,尽可能地过滤掉无效请求。
    • 分流主要思路:通过分布式集群技术,多台机器处理,提高并发能力。

    分布式

    分布式概述

    分布式

    分布式(distributed)是为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段,将一个业务拆分成不同的子业务,分布在不同的机器上执行。服务之间通过远程调用协同工作,对外提供服务。

    该领域需要解决的问题极多,在不同的技术层面上,又包括:分布式缓存、分布式数据库、分布式计算、分布式文件系统等,一些技术如MQ、Redis、zookeeper等都跟分布式有关。

    从理念上讲,分布式的实现有两种形式:

    水平扩展:当一台机器扛不住流量时,就通过添加机器的方式,将流量平分到所有服务器上,所有机器都可以提供 相同的服务;

    垂直拆分:前端有多种查询需求时,一台机器扛不住,可以将不同的业务需求分发到不同的机器上,比如A机器处理余票查询的请求,B机器处理支付的请求。

    集群

    集群(cluster)是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务。

    两个特点

    可扩展性:集群中的服务节点,可以动态的添加机器,从而增加集群的处理能力。

    高可用性:如果集群某个节点发生故障,这台节点上面运行的服务,可以被其他服务节点接管,从而增强集群的高可用性。

    两大能力

    负载均衡:负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。

    集群容错:当我们的系统中用到集群环境,因为各种原因在集群调用失败时,集群容错起到关键性的作用。

    微服务

    微服务就是很小的服务,小到一个服务只对应一个单一的功能,只做一件事。这个服务可以单独部署运行,服务之间通过远程调用协同工作,每个微服务都是由独立的小团队开发,测试,部署,上线,负责它的整个生命周期。

    多线程

    多线程(multi-thread):多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。多线程是为了提高CPU的利用率。

    高并发

    高并发(High Concurrency)是一种系统运行过程中发生了一种“短时间内遇到大量请求”的情况,高并发对应的是访问请求,多线程是解决高并发的方法之一,高并发还可以通过分布式,集群,算法优化,数据库优化等方法解决。

    分布式系统设计理念

    分布式系统的目标与要素

    分布式系统的目标是提升系统的整体性能和吞吐量另外还要尽量保证分布式系统的容错性(假如增加10台服务器才达到单机运行效果2倍左右的性能,那么这个分布式系统就根本没有存在的意义)。

    即使采用了分布式系统,我们也要尽力运用并发编程、高性能网络框架等等手段提升单机上的程序性能。

    分布式系统设计两大思路:中心化和去中心化

    分布式系统设计两大思路:中心化和去中心化

    中心化设计

    • 两个角色: 中心化的设计思想很简单,分布式集群中的节点机器按照角色分工,大体上分为两种角色: “领导”“干活的”
    • 角色职责: “领导”通常负责分发任务并监督“干活的”,发现谁太闲了,就想发设法地给其安排新任务,确保没有一个“干活的”能够偷懒,如果“领导”发现某个“干活的”因为劳累过度而病倒了,则是不会考虑先尝试“医治”他的,而是一脚踢出去,然后把他的任务分给其他人。其中微服务架构 Kubernetes 就恰好采用了这一设计思路。
    • 中心化设计的问题
      1. 中心化的设计存在的最大问题是“领导”的安危问题,如果“领导”出了问题,则群龙无首,整个集群就奔溃了。但我们难以同时安排两个“领导”以避免单点问题。
      2. 中心化设计还存在另外一个潜在的问题,既“领导”的能力问题:可以领导10个人高效工作并不意味着可以领导100个人高效工作,所以如果系统设计和实现得不好,问题就会卡在“领导”身上。
    • 领导安危问题的解决办法: 大多数中心化系统都采用了主备两个“领导”的设计方案,可以是热备或者冷备,也可以是自动切换或者手动切换,而且越来越多的新系统都开始具备自动选举切换“领导”的能力,以提升系统的可用性。

    去中心化设计

    • 众生地位平等: 在去中心化的设计里,通常没有“领导”和“干活的”这两种角色的区分,大家的角色都是一样的,地位是平等的,全球互联网就是一个典型的去中心化的分布式系统,联网的任意节点设备宕机,都只会影响很小范围的功能。
    • “去中心化”不是不要中心,而是由节点来自由选择中心。 (集群的成员会自发的举行“会议”选举新的“领导”主持工作。最典型的案例就是ZooKeeper及Go语言实现的Etcd)
    • 去中心化设计的问题: 去中心化设计里最难解决的一个问题是 “脑裂”问题 ,这种情况的发生概率很低,但影响很大。脑裂指一个集群由于网络的故障,被分为至少两个彼此无法通信的单独集群,此时如果两个集群都各自工作,则可能会产生严重的数据冲突和错误。一般的设计思路是,当集群判断发生了脑裂问题时,规模较小的集群就“自杀”或者拒绝服务。

    分布式与集群的区别是什么?

    • 分布式: 一个业务分拆多个子业务,部署在不同的服务器上
    • 集群: 同一个业务,部署在多个服务器上。比如之前做电商网站搭的redis集群以及solr集群都是属于将redis服务器提供的缓存服务以及solr服务器提供的搜索服务部署在多个服务器上以提高系统性能、并发量解决海量存储问题。

    CAP定理

    CAP定理

    在理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

    选项 描述
    Consistency(一致性) 指数据在多个副本之间能够保持一致的特性(严格的一致性)
    Availability(可用性) 指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应(不保证获取的数据为最新数据)
    Partition tolerance(分区容错性) 分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障

    Spring Cloud在CAP法则上主要满足的是A和P法则,Dubbo和Zookeeper在CAP法则主要满足的是C和P法则

    CAP仅适用于原子读写的NOSQL场景中,并不适合数据库系统。现在的分布式系统具有更多特性比如扩展性、可用性等等,在进行系统设计和开发时,我们不应该仅仅局限在CAP问题上。

    注意:不是所谓的3选2(不要被网上大多数文章误导了)

    现实生活中,大部分人解释这一定律时,常常简单的表述为:“一致性、可用性、分区容忍性三者你只能同时达到其中两个,不可能同时达到”。实际上这是一个非常具有误导性质的说法,而且在CAP理论诞生12年之后,CAP之父也在2012年重写了之前的论文。

    当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能2选1。也就是说当网络分区之后P是前提,决定了P之后才有C和A的选择。也就是说分区容错性(Partition tolerance)我们是必须要实现的。

    CAP定理的证明

    关于CAP这三个特性我们就介绍完了,接下来我们试着证明一下为什么CAP不能同时满足

    img

    为了简化证明的过程,我们假设整个集群里只有两个N1和N2两个节点,如下图:

    N1和N2当中各自有一个应用程序AB和数据库,当系统满足一致性的时候,我们认为N1和N2数据库中的数据保持一致。在满足可用性的时候,我们认为无论用户访问N1还是N2,都可以获得正确的结果,在满足分区容错性的时候,我们认为无论N1还是N2宕机或者是两者的通信中断,都不影响系统的运行。

    我们假设一种极端情况,假设某个时刻N1和N2之间的网络通信突然中断了。如果系统满足分区容错性,那么显然可以支持这种异常。问题是在此前提下,一致性和可用性是否可以做到不受影响呢?

    我们做个假象实验,如下图,突然某一时刻N1和N2之间的关联断开:

    img

    有用户向N1发送了请求更改了数据,将数据库从V0更新成了V1。由于网络断开,所以N2数据库依然是V0,如果这个时候有一个请求发给了N2,但是N2并没有办法可以直接给出最新的结果V1,这个时候该怎么办呢?

    这个时候无法两种方法,一种是将错就错,将错误的V0数据返回给用户。第二种是阻塞等待,等待网络通信恢复,N2中的数据更新之后再返回给用户。显然前者牺牲了一致性,后者牺牲了可用性。

    这个例子虽然简单,但是说明的内容却很重要。在分布式系统当中,CAP三个特性我们是无法同时满足的,必然要舍弃一个。三者舍弃一个,显然排列组合一共有三种可能。

    BASE理论

    BASE理论由eBay架构师Dan Pritchett提出,在2008年上被分表为论文,并且eBay给出了他们在实践中总结的基于BASE理论的一套新的分布式事务解决方案。

    BASEBasically Available(基本可用)Soft-state(软状态)Eventually Consistent(最终一致性) 三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,它大大降低了我们对系统的要求。

    BASE理论的核心思想

    即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。也就是牺牲数据的一致性来满足系统的高可用性,系统中一部分数据不可用或者不一致时,仍需要保持系统整体“主要可用”。

    针对数据库领域,BASE思想的主要实现是对业务数据进行拆分,让不同的数据分布在不同的机器上,以提升系统的可用性,当前主要有以下两种做法:

    • 按功能划分数据库
    • 分片(如开源的Mycat、Amoeba等)。

    由于拆分后会涉及分布式事务问题,所以eBay在该BASE论文中提到了如何用最终一致性的思路来实现高性能的分布式事务。

    BASE理论三要素

    BASE理论三要素

    1. 基本可用

    基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。但是,这绝不等价于系统不可用。

    比如:

    • 响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
    • 系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
    2. 软状态

    软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时

    3. 最终一致性

    最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

    数据结构与算法

    冒泡排序

    冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,依次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

    算法描述

    • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
    • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
    • 针对所有的元素重复以上的步骤,除了最后一个;
    • 重复步骤1~3,直到排序完成。

    动图演示

    冒泡排序

    代码实现

    下面的排序算法统一使用的测试代码如下,源码GitHub链接

    public static void main(String[] args) {
        int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
    	// 只需要修改成对应的方法名就可以了
        bubbleSort(array);
    
        System.out.println(Arrays.toString(array));
    }
    
    /**
     * Description:冒泡排序
     *
     * @param array 需要排序的数组
     * @author JourWon
     * @date 2019/7/11 9:54
     */
    public static void bubbleSort(int[] array) {
    	if (array == null || array.length <= 1) {
    		return;
    	}
    
    	int length = array.length;
    
    	// 外层循环控制比较轮数i
    	for (int i = 0; i < length; i++) {
    		// 内层循环控制每一轮比较次数,每进行一轮排序都会找出一个较大值
    		// (array.length - 1)防止索引越界,(array.length - 1 - i)减少比较次数
    		for (int j = 0; j < length - 1 - i; j++) {
    			// 前面的数大于后面的数就进行交换
    			if (array[j] > array[j + 1]) {
    				int temp = array[j + 1];
    				array[j + 1] = array[j];
    				array[j] = temp;
    			}
    		}
    	}
    
    }
    

    算法分析

    最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

    选择排序

    表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。

    选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

    算法描述

    n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

    • 初始状态:无序区为R[1…n],有序区为空;
    • 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
    • n-1趟结束,数组有序化了。

    动图演示

    选择排序

    代码实现

    下面的排序算法统一使用的测试代码如下,源码GitHub链接

    public static void main(String[] args) {
        int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
    	// 只需要修改成对应的方法名就可以了
        selectionSort(array);
    
        System.out.println(Arrays.toString(array));
    }
    
    /**
     * Description: 选择排序
     *
     * @param array
     * @return void
     * @author JourWon
     * @date 2019/7/11 23:31
     */
    public static void selectionSort(int[] array) {
    	if (array == null || array.length <= 1) {
    		return;
    	}
    
    	int length = array.length;
    
    	for (int i = 0; i < length - 1; i++) {
    		// 保存最小数的索引
    		int minIndex = i;
    
    		for (int j = i + 1; j < length; j++) {
    			// 找到最小的数
    			if (array[j] < array[minIndex]) {
    				minIndex = j;
    			}
    		}
    
    		// 交换元素位置
    		if (i != minIndex) {
    			swap(array, minIndex, i);
    		}
    	}
    
    }
    
    /**
     * Description: 交换元素位置
     *
     * @param array
     * @param a
     * @param b
     * @return void
     * @author JourWon
     * @date 2019/7/11 17:57
     */
    private static void swap(int[] array, int a, int b) {
    	int temp = array[a];
    	array[a] = array[b];
    	array[b] = temp;
    }
    

    算法分析

    最佳情况:T(n) = O(n2) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

    快速排序

    快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

    算法描述

    快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

    • 从数列中挑出一个元素,称为 “基准”(pivot);
    • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
    • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

    动图演示

    快速排序

    代码实现

    下面的排序算法统一使用的测试代码如下,源码GitHub链接

    public static void main(String[] args) {
        int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
    	// 只需要修改成对应的方法名就可以了
        quickSort(array);
    
        System.out.println(Arrays.toString(array));
    }
    
    /**
     * Description: 快速排序
     *
     * @param array
     * @return void
     * @author JourWon
     * @date 2019/7/11 23:39
     */
    public static void quickSort(int[] array) {
    	quickSort(array, 0, array.length - 1);
    }
    
    
    private static void quickSort(int[] array, int left, int right) {
    	if (array == null || left >= right || array.length <= 1) {
    		return;
    	}
    	int mid = partition(array, left, right);
    	quickSort(array, left, mid);
    	quickSort(array, mid + 1, right);
    }
    
    
    private static int partition(int[] array, int left, int right) {
    	int temp = array[left];
    	while (right > left) {
    		// 先判断基准数和后面的数依次比较
    		while (temp <= array[right] && left < right) {
    			--right;
    		}
    		// 当基准数大于了 arr[left],则填坑
    		if (left < right) {
    			array[left] = array[right];
    			++left;
    		}
    		// 现在是 arr[right] 需要填坑了
    		while (temp >= array[left] && left < right) {
    			++left;
    		}
    		if (left < right) {
    			array[right] = array[left];
    			--right;
    		}
    	}
    	array[left] = temp;
    	return left;
    }
    

    算法分析

    最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)

    递归

    什么叫递归

    递归函数就是直接或间接调用自身的函数,也就是自身调用自己。

    一般什么时候使用递归?

    递归是常用的编程技术,其基本思想就是“自己调用自己”,一个使用递归技术的方法即是直接或间接的调用自身的方法。递归方法实际上体现了“以此类推”、“用同样的步骤重复”这样的思想。

    还有些数据结构如二叉树,结构本身固有递归特性;此外,有一类问题,其本身没有明显的递归结构,但用递归程序求解比其他方法更容易编写程序。

    需满足的两个条件

    1. 有反复执行的过程(调用自身)
    2. 有跳出反复执行过程的条件(递归出口)

    经典问题:阶乘

    递归阶乘n! = n * (n-1) * (n-2) * ...* 1(n>0)
    
    public static Integer recursionMulity(Integer n) {
        if (n == 1) {
            return 1;
        }
        return n * recursionMulity(n - 1);
    }
    

    经典问题:不死神兔(斐波那契数列)

    3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

    分析:首先我们要明白题目的意思指的是每个月的兔子总对数;假设将兔子分为小中大三种,兔子从出生后三个月后每个月就会生出一对兔子,

    那么我们假定第一个月的兔子为小兔子,第二个月为中兔子,第三个月之后就为大兔子,那么第一个月分别有1、0、0,第二个月分别为0、1、0,

    第三个月分别为1、0、1,第四个月分别为,1、1、1,第五个月分别为2、1、2,第六个月分别为3、2、3,第七个月分别为5、3、5……

    兔子总数分别为:1、1、2、3、5、8、13……

    于是得出了一个规律,从第三个月起,后面的兔子总数都等于前面两个月的兔子总数之和,即为斐波那契数列。

    public static int fib(int mon) {
        if (mon < 2) {
            return 1;
        } else {
            return fib(mon - 1) + fib(mon - 2);
        }
    }
    

    二分查找

    在数组[130,150,170,190,210,230,250,270,290,310]中查找数字190,红色为二分线(折半线),灰色为查找区域,黑色为排除区域。

    img二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法,前提是数据结构必须先排好序,时间复杂度可以表示O(h)=O(log2n),以2为底,n的对数。其缺点是要求待查表为有序表,且插入删除困难。

    左加右不加,找右缩左,找左缩右

    public class BinarySearch {
        public static void main(String[] args) {
            int[] arr = {5, 12, 23, 43, 66, 98, 100};
            System.out.println(binarySort(arr, 23));
        }
    
        /**
         * 循环实现二分查找
         *
         * @param arr
         * @param key
         * @return
         */
        public static int binarySearch(int[] arr, int key) {
            //第一个下标
            int low = 0;
            //最后一个下标
            int high = arr.length - 1;
            int mid = 0;
            //防越界
            if (key < arr[low] || key > arr[high] || low > high) {
                return -1;
            }
            while (low <= high) {
                mid = (low + high) >>> 1;
                if (key < arr[mid]) {
                    high = mid - 1;
                } else if (key > arr[mid]) {
                    low = mid + 1;
                } else {
                    return mid;
                }
            }
            return -1;
        }
    }
    

    二分查找中中间值的计算

    这是一个经典的话题,如何计算二分查找中的中值?大家一般给出了两种计算方法:

    • 算法一: mid = (low + high) / 2
    • 算法二: mid = low + (high – low)/2

    乍看起来,算法一简洁,算法二提取之后,跟算法一没有什么区别。但是实际上,区别是存在的。算法一的做法,在极端情况下,(low + high)存在着溢出的风险,进而得到错误的mid结果,导致程序错误。而算法二能够保证计算出来的mid,一定大于low,小于high,不存在溢出的问题。

    一致性Hash算法

    概述

    一致性Hash是一种特殊的Hash算法,由于其均衡性、持久性的映射特点,被广泛的应用于负载均衡领域和分布式存储,如nginx和memcached都采用了一致性Hash来作为集群负载均衡的方案。

    普通的Hash函数最大的作用是散列,或者说是将一系列在形式上具有相似性质的数据,打散成随机的、均匀分布的数据。不难发现,这样的Hash只要集群的数量N发生变化,之前的所有Hash映射就会全部失效。如果集群中的每个机器提供的服务没有差别,倒不会产生什么影响,但对于分布式缓存这样的系统而言,映射全部失效就意味着之前的缓存全部失效,后果将会是灾难性的。一致性Hash通过构建环状的Hash空间代替线性Hash空间的方法解决了这个问题。

    良好的分布式cahce系统中的一致性hash算法应该满足以下几个方面:

    • 平衡性(Balance)

    平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。

    • 单调性(Monotonicity)

    单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

    • 分散性(Spread)

    在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。

    • 负载(Load)

    负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

    • 平滑性(Smoothness)

    平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致的。

    一致性Hash算法原理

    简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),整个哈希空间环如下:整个空间按顺时针方向组织。0和232-1在零点中方向重合。

    下一步将各个服务器使用Hash进行一次哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上文中四台服务器使用ip地址哈希后在环空间的位置如下:

    接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。

    例如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:

    根据一致性哈希算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。

    下面分析一致性哈希算法的容错性和可扩展性。现假设Node C不幸宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。

    下面考虑另外一种情况,如果在系统中增加一台服务器Node X,如下图所示:

    此时对象Object A、B、D不受影响,只有对象C需要重定位到新的Node X 。一般的,在一致性哈希算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据也不会受到影响。

    综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

    Java代码实现

    public class ConsistentHash<T> {
    
        /**
         * 节点的复制因子,实际节点个数 * numberOfReplicas = 虚拟节点个数
         */
        private final int numberOfReplicas;
        /**
         * 存储虚拟节点的hash值到真实节点的映射
         */
        private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
    
        public ConsistentHash(int numberOfReplicas, Collection<T> nodes) {
            this.numberOfReplicas = numberOfReplicas;
            for (T node : nodes) {
                add(node);
            }
        }
    
        public void add(T node) {
            for (int i = 0; i < numberOfReplicas; i++) {
                // 对于一个实际机器节点 node, 对应 numberOfReplicas 个虚拟节点
                /*
                 * 不同的虚拟节点(i不同)有不同的hash值,但都对应同一个实际机器node
                 * 虚拟node一般是均衡分布在环上的,数据存储在顺时针方向的虚拟node上
                 */
                String nodestr = node.toString() + i;
                int hashcode = nodestr.hashCode();
                System.out.println("hashcode:" + hashcode);
                circle.put(hashcode, node);
    
            }
        }
    
        public void remove(T node) {
            for (int i = 0; i < numberOfReplicas; i++) {
                circle.remove((node.toString() + i).hashCode());
            }
        }
    
    
        /**
         * 获得一个最近的顺时针节点,根据给定的key 取Hash
         * 然后再取得顺时针方向上最近的一个虚拟节点对应的实际节点
         * 再从实际节点中取得 数据
         *
         * @param key
         * @return
         */
        public T get(Object key) {
            if (circle.isEmpty()) {
                return null;
            }
            // node 用String来表示,获得node在哈希环中的hashCode
            int hash = key.hashCode();
            System.out.println("hashcode----->:" + hash);
            //数据映射在两台虚拟机器所在环之间,就需要按顺时针方向寻找机器
            if (!circle.containsKey(hash)) {
                SortedMap<Integer, T> tailMap = circle.tailMap(hash);
                hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
            }
            return circle.get(hash);
        }
    
        public long getSize() {
            return circle.size();
        }
    
        /**
         * 查看表示整个哈希环中各个虚拟节点位置
         */
        public void testBalance() {
            //获得TreeMap中所有的Key
            Set<Integer> sets = circle.keySet();
            //将获得的Key集合排序
            SortedSet<Integer> sortedSets = new TreeSet<Integer>(sets);
            for (Integer hashCode : sortedSets) {
                System.out.println(hashCode);
            }
    
            System.out.println("----each location 's distance are follows: ----");
            /*
             * 查看相邻两个hashCode的差值
             */
            Iterator<Integer> it = sortedSets.iterator();
            Iterator<Integer> it2 = sortedSets.iterator();
            if (it2.hasNext()) {
                it2.next();
            }
            long keyPre, keyAfter;
            while (it.hasNext() && it2.hasNext()) {
                keyPre = it.next();
                keyAfter = it2.next();
                System.out.println(keyAfter - keyPre);
            }
        }
    
        public static void main(String[] args) {
            Set<String> nodes = new HashSet<String>();
            nodes.add("A");
            nodes.add("B");
            nodes.add("C");
    
            ConsistentHash<String> consistentHash = new ConsistentHash<String>(2, nodes);
            consistentHash.add("D");
    
            System.out.println("hash circle size: " + consistentHash.getSize());
            System.out.println("location of each node are follows: ");
            consistentHash.testBalance();
    
            String node = consistentHash.get("apple");
            System.out.println("node----------->:" + node);
        }
    
    }
    
    展开全文
  • 正是基于这些经历,汪源才提出以能力组、技术平台和方法论为主的标准能力组织、包含专向组的胖中台组织中台组织到平台组织的转化等方法,以应对不同的情况。 台和各前台业务团队是支撑和合作的关系,这两类...

     

    汪源,网易副总裁,网易杭州研究院执行院长。2006 年获浙江大学计算机专业博士学位,之后加入网易公司。现作为网易杭州研究院执行院长,全面负责网易集团公共技术支撑工作与云计算、大数据业务,主要包括云计算与服务端架构、前端技术、大数据挖掘分析、信息安全、多媒体、运维、质量保障等方向。

    贾喜顺,长期专注于数据仓库、数据中心和大数据平台建设,先后任职东方国信、搜狐畅游等企业,现为百分点大数据平台负责人,负责底层平台研发及企业级服务。

    王健,ThoughtWorks 首席咨询师, 十多年国内外大型企业软件设计开发,团队组织转型从业经验。一直保持着对技术的热爱,享受着编码的快乐,热衷于技术分享。目前专注在企业平台化转型、中台战略规划,微服务架构与实施,大型遗留系统服务化改造,敏捷精益转型,以及 DevOps 和持续交付等实践在大型企业中的推广与落地。

    对于建设中台的必要性,每家企业肯定都有自己的想法,诸如现有体系不能满足新产品快速迭代和试错的渴望;产品越来越多时,多分支的维护和开发凸显了人力不足的问题等。如果中台确实是解决企业现有问题的合理方案,那么建设过程中伴随的组织架构问题就是企业需要关心的,比如中台团队的人从哪来?经费从哪来?建设中台之后,业务团队的决策权力是不是被大幅缩减?本文采访了多位中台实践者,揭秘大多数人不愿意公开分享的中台组织架构调整。

    1 写在前面

    中台确实是一个热度极高的话题,InfoQ 也曾在过往做过很多类似的选题,但是每当涉及到组织架构调整的话题,大部分采访嘉宾都选择了沉默,一位地产企业的 IT 架构师曾在我的追问下表示:组织架构一定要调整,但动组织架构就意味着在动“权力、金钱和人员”。不难看出,这个话题太敏感。

    2 中台禁区:战略下的矛盾和冲突

    在中台战略制定中,组织架构调整的矛盾与冲突时刻存在,即便是阿里的“共享业务事业部(业务中台)”,早期也是非常艰难地活在淘宝和天猫的夹缝中。汪源在采访中表示:建设中台需要考虑组织、支撑技术和方法论三个方面的因素,一般来讲后两者都可以买到,但组织买不到。所以,最关键的是要对中台组织怎么构建有明确的方案,并下决心去推行。

    为什么大量中台只抄到了阿里的“形”?

    关于中台,很多人都喜欢研究阿里,但很少有人钻研阿里每年两次组织架构调整背后的原因。钟华(花名:古谦)在《企业 IT 架构转型之道(阿里巴巴中台战略思想与架构实战》一书开篇提到:

    与任何公司一样,阿里巴巴组织架构的战略调整势必对公司现有组织架构、部门间的协作等各方面都将带来深远影响...... 假若没能很好地控制战略执行过程中带来的风险,对组织架构的动荡过大,都会给现有业务带来不小的影响。

    因此,企业的每一次组织架构调整都是需要经过慎重考虑的,并且也需要让每一位员工清楚自己的定位。采访中,王健表示:“我认为阿里的中台战略之所以能够成功落地,最关键得益于其组织架构调整的能力。组织架构调整最难的就是让每个员工都能快速了解其背后的战略意图,并快速适应新的组织架构,知道接下来在新的组织架构下自己该干什么,从而让战略调整落地,这是需要一套机制和文化基础来保证的。我看到很多企业在学阿里,高层不断的在调整组织,但底下的员工基本都是懵的,不知道为什么调整,以及接下来自己要做什么。”

    虽然没有采访到阿里,但我们可以通过公开信的方式来获取阿里当年的组织架构调整情况。

    2015 年底,阿里巴巴集团对外宣布全面启动阿里巴巴集团 2018 年中台战略,构建符合 DT 时代的更具创新性、灵活性的“大中台、小前台”组织机制和业务机制。与此同时,张勇在致员工信中宣布了新的组织架构调整计划,如下图:

     

    不难看出,阿里推进中台战略的关键人物是行癫(现任阿里巴巴集团首席技术官),他直接向张勇汇报。在公开信中,张勇表示:

    行癫是淘宝第一代的技术 Leader 和产品经理,后来成功转型为业务 Leader, 在 1688、淘宝、天猫、聚划算等多项业务中为集团做出过突出的贡献。作为集团内为数不多的兼具技术和商业背景和经验的领导者,行癫是担纲落实集团中台战略的最佳人选。他也将作为阿里集团和蚂蚁金融服务集团统一中台体系的总架构师,全面负责两大集团中台体系的规划和建设。

    这次组织架构调整让阿里正式开启中台战略,但阿里建设中台最早可以追溯至 2009 年共享业务事业部的诞生。当时,淘宝的技术团队同时支持着淘宝和天猫的业务,这样的组织架构决定了技术团队对来自淘宝的业务需求满足的优先级一定要优于天猫,这让天猫的业务团队怨声载道。此外,当时的淘宝和天猫电商系统是完全独立的两套体系,都包含商品、交易、评价、支付、物流等功能。

    基于上述原因,共享业务事业部诞生,在组织架构上与淘宝、天猫具有同样级别的事业部。然而,如前文言,这个事业部起初受到了天猫和淘宝事业部的双重碾压,团队成员如何加班都很难及时、周到地满足两大业务部门的需求,连带着话语权几乎没有(注意:职级相同并不代表话语权相同,想必很多人都对此深有同感)。钟华在书中是这样描述的:员工则是有苦说不出,只能默默流泪。

    2010 年,聚划算出现,淘宝和天猫都希望接入。据不完全统计,这至少可以带来 25 倍的销售额增长,这时的阿里高层做出了一个对后来产生重大影响的决定,也是这个决定才让共享业务事业部得以真正成长起来:电商平台要想接入聚划算必须先通过共享业务事业部。钟华认为,这让整个共享业务事业群有了极强的业务抓手,而这对天猫、淘宝、1688(当时已经出现)事业部而言,也是一次权力的调整,这意味着要想获得更高的增长(接入聚划算),就必须接入共享业务事业部。

     

    此外,这种调整不仅停留在集团层面,团队内部也进行了相应调整。早期,淘宝技术团队的组织人员组成跟今天企业中信息中心和技术部门的人员组成几乎一样,整个团队基本都是拥有较强技术技能的人员组成。阿里巴巴集团在构建共享服务体系之后,对各技术团队的组织架构也做了如下调整:

     

    针对每一个建设的服务中心,从组织架构的形态上发生对应调整,不同角色的人员(架构师、开发人员、UED 工程师等)组建成了一个新的组织,每一个这样的组织都针对某一服务中心提供持续的服务能力——开发及运维。所以,在如今的阿里巴巴共享服务体系中,每一个服务中心都是由一个少则 100 多人,多则 4、5 百人的团队对负责的服务中心进行专业的运营。

    如果回顾整个阿里中台战略的推进过程,组织架构其实一直伴随着整个战略执行,只是每次调整解决的问题不尽相同(感兴趣的同学推荐阅读《企业 IT 架构转型之道(阿里巴巴中台战略思想与架构实战》,此处不再赘述)。在接受 InfoQ 采访时,贾喜顺表示,这种组织架构的调整其实是与业务复杂性相关的,类似阿里、京东等大厂需要调整组织架构来保障战略的顺利推进,但一些规模较小的企业可能不必如此复杂,这也要看企业对中台的态度,上述大厂已经将中台看做为重要战略,因此陆续进行了组织架构调整。

    如果要说组织架构调整的过程是否会出现矛盾和冲突,贾喜顺明确表示:“肯定会有”。

    他补充道,任何组织架构调整都涉及到这种矛盾,尤其是集中式的组织结构变更:一方面,这会把责任和权限更聚集,涉及组织管理的权力缩减问题;另一方面,业务边界的划分上需要进行磨合,要理清中台、前台的边界在哪里,如果边界尚不确定,就会涉及很多矛盾、冲突。

    当然,不排除有些边界很难界定,比如基于数据中台出指标体系,统计访问业务的 PV、UV 这种关键指标,不同部门统计的口径不一样,定义中台之后,类似的会有成千上万的指标需要重新进行边界划分,确定哪些指标体系由中台提供,哪些由前台搭建,很多情况下需要一事一议,不是那么容易能划分清楚的。

    既然这并非易事,为什么很多人不愿意谈论这个问题呢?

    为什么鲜少人谈组织架构?

    如果说完全没有人公开对外讨论这个话题,确实太过绝对也不准确,但大部分讨论还是比较委婉的,如开篇所言,这是一个非常敏感的话题。此外,如果不是整个中台战略的重要负责人也很难说清楚这个问题。

    “中台是一个跨团队、跨业务的问题,这是一个企业级的问题,这就是为什么我说它是一个企业级治理的问题”,王健在采访中说道:“互联网公司做中台,每一家企业都有不同的背景和原因,如果把这些推到传统企业,有各种条件需要解决,比如组织能不能调,如果不可以调,那很可能做到一定程度就不能再往前走了,会碰到一个组织的天花板,或者说是组织先行,还是技术先行,这与互联网公司的情况还不太一样,传统企业在组织架构调整上是一个大的坎,调组织就是在动利益关系,这个很难

    因此,王健表示,这就需要中台推动者(下文详述这一人选如何确定)给出一把“尚方宝剑”,不然难以切动这些利益。具体来说,中台团队最初的处境可能会如阿里的“共享业务事业部”一样,不被相关事业部接受,这很容易理解,原事业部已经存在只为此事业部服务的技术支持团队,所有需求都可以得到及时响应,如果接入中台这样一个同时服务多事业群的技术支持团队,如何确保业务需求可以被及时响应。此外,一旦接入中台,这意味着原有团队的人员需要进行调整,决策权可能也会被缩减,这种情况下,中台团队的处境十分艰难。

    如果想做,能不能照搬阿里的组织架构呢?

    这个肯定不可以,这里还需要区分中台的类型和原有企业的组织架构方式,毕竟现在有关中台的概念太多,比如数据中台、技术中台和业务中台等,类型不同、目的不同,调整的方式也有所区别。

    贾喜顺基于数据中台的搭建经验表示,如果企业在搭建数据中台后相应地进行架构调整,肯定会对中台落地提供更大保障。这是因为,中台涉及各业务系统数据的汇集、治理、标准和推广,这些都需要从组织层面来保障。就原来垂直业务线的团队而言,部门人员很大可能会缩减,中台是把以往企业各部门做的同样的事情进行集中,这样的话,其中有部分人员可能要被调整到其他部门,比如偏业务分析类工作;另一部分比较偏底层的技术人员在垂直业务领域就可能不需要了。此外,人员结构也会有变化:原有的底层技术、研发人员会缩减,在实践数据中台的垂直业务线团队更偏重面向业务的应用产品研发人员和业务分析人员。

    在王健看来,数据中台和技术中台其实是传统企业最喜欢率先引入建设的,因为相对而言,传统企业内部对于技术和数据团队的划分相对明确,尤其是业务并不是非常杂的时候,技术团队和数据团队往往就是一个独立的、全面支持的角色,比如原来做数据平台的团队,现在就可以直接继续承担数据中台的建设,不需要做太多结构上的调整,毕竟不是非常影响业务,而业务中台则是一定要动组织的,这个过程涉及多条业务线的利益关系,这之间的屏障很难打破。照搬肯定不行,其他人调整是为了业务的横向扩展(例如做全球化),你的目的是为了纵向数据打通,这显然不可复制,还是得想清楚调整的目的是什么。

    然而,在汪源的定义中,所有的中台都是业务中台,数据中台是一种特殊的业务中台,一般指以负责数据采集、数据集成、数据治理,指标体系和数据仓库统一建设等数据管理活动的组织。当前阶段,他认为不存在技术中台,现在业界所谓的技术中台其实都是云计算、AI 算法等技术平台,这些技术平台没有业务属性,做这些平台的人也不懂业务,称做中台是不合适的。

    管理上的困难主要来自于技术属性较弱的 PMO(Project Management Office)、推荐搜索、数据分析等专业能力部门,这些部门的发展存在很大的不稳定性,如果能力不能持续的提升和沉淀,就可能导致部门的分拆。这么多年分分合合都有。正是基于这些经历,汪源才提出以能力组、技术平台和方法论为主的标准能力组织、包含专向组的胖中台组织、中台组织到平台组织的转化等方法,以应对不同的情况。

    中台和各前台业务团队是支撑和合作的关系,这两类团队需要一起制定工作规划,一起确定绩效目标,因为有一些绩效目标是中台和前台业务团队一起背。除此之外,中台团队应该有部分人员是专职对接和服务每个核心的前台业务,这些人应该和前台业务团队坐在一起,团建应该一起去,汪源称之为“专人就位”,这样才能塑造信任和默契。如果前台团建都不叫你去,那你这个中台就危险了。(阿里各服务中心的核心架构师和运营人员会定期参与前端业务方的业务会议或重要项目的研讨会)

    对于前台业务团队来说,最大的变化是得放弃完全控制权,承担一定风险,换回来的是更低成本、更高效率、更高质量的服务,更强的能力。

    基于如上种种可能发生的变化,中台的组织架构调整问题鲜少人谈。

    3组织架构怎么调?

    中台往往是企业里面最费力不讨好、最容易失败的团队,做不好看起来就是在给各个团队制造麻烦。——王健

    组织架构调整是一个很大的问题,典型的组织架构就包括:直线职能型(U)、事业部型(M)、矩阵型、网络型、平台型,还不算各种组合和变体;如果结合经营模式,还需要了解大家常常提到的阿米巴经营模式以及海尔的自主经营体……所以,这些肯定不是一篇文章就可以讲清楚的,此处着重讲解中台战略的推动者和中台团队的人员构成。

    中台的推动者

    如上文言,阿里推进中台战略的关键人物是行癫,行癫在 2015 年 3 月份开始已经是阿里巴巴中国零售平台负责人;2018 年底,京东商城调整组织架构,中台组织规划(如下图)首次完整亮相,负责人徐雷是京东零售集团的轮值 CEO;2019 年 1 月 4 日,腾讯正式宣布成立技术委员会,腾讯高级执行副总裁、技术工程事业群总裁卢山和腾讯高级执行副总裁、云与智慧产业事业群总裁汤道生两名腾讯总办成员牵头,几大事业群的技术负责人悉数进入技术委员会决策圈,这被评为中台战略的良好开端......

    综上,大厂推进中台战略的决策人物(团队成员)的职级均不低,三位专家在采访中也在这一点上达成了统一:中台战略的推进过程一定是自顶向下的,至少是 CIO/CTO 层面向下推动,一定需要得到高层的支持。否则,很难切动组织架构调整中的利益网。

    “中台建设的前期可能没有给业务带来多大帮助,反而制造了很多问题,如果没有 CIO、CTO 甚至 CEO,或者技术委员会、战略规划部门等的支持,很难进行,可能原来想做一个业务中台,后来因为无法撬动业务,数据也拿不到,最终的建设效果就会大打折扣”,王健进一步补充道,“如果这时领导层没有驱动力帮助继续推进这件事情,这个中台推也不是,不推也不是,因此我认为中台建设应该是一个自上而下的驱动过程。”

    此外,大部分员工是很难站在一定的高度去做一个”看十年、做一年“的规划,特别是当一件事和眼前的 KPI 难以达成平衡时,中台的工作会受到各个方面的挑战。因此高层的坚定支持是中台战略的第一必要条件。中台的价值是有条件的,搭建完成后还得有机会来享受成果,这个判断也需要高层来完成。额外的,高层还需要推动一些规范的建设,如交互规范、视觉规范、视觉配套的前端组件规范等,在这些规范的约束下,中台服务搭建的难度会大大降低。

    对于数据中台的建设,贾喜顺同样表示:“数据中台是“一把手”工程,需要从上到下进行。中台数据是战略层面的事情而不是战术层面,自下向上推动几乎没有可能,比如涉及标准统一,从下而上只能看到一个点,难免会以偏概全。”

    中台团队的人员构成

    建设中台对原有垂直业务线的人员可能会有很大影响,那么,中台团队的人员又是从哪来的呢?

    就数据中台团队的搭建而言,贾喜顺表示,这与团队一把手的想法也有关系,通常会从企业内部的垂直业务线进行人才筛选,在组织架构调整或者成立中台团队中,会有一部分人从垂直业务线剥离出来。这是因为企业内部的人更了解业务,会比外部招聘的人更容易进入状态。不过,对于传统企业,其内部更偏管理,数据中台团队建设更多的需要借助外部力量,如搭建数据中台的企业,再在企业内部辅助配一些团队人员进行支撑。

    王健也基本赞同上述观点,一开始大多是从前台抽调,毕竟企业资源有限,不可能纯招一批人,这些人可能包含架构师等实力非常强的人,因为业务中台的建设者要比各垂直业务线更懂业务,不仅仅是懂技术就可以的,尤其是很多公司是产品型文化,可能还会配备产品经理,这可能与传统企业里面的项目经理有些类似,但要有很强的沟通和协调能力,需要跟所有业务线战斗,然后服务好所有业务方,但如果没处理好组织关系,业务方给到中台的人可能是自己不想用的。

    汪源同样认为:外部招聘对于构建中台团队往往很难,因为中台团队一要懂业务,还需要得到前台业务的信任,所以最好是从内部培养和选拔,可以从平台团队选拔,但此时要配置懂业务的人配合,也可以从业务线抽调。

    中台团队的核心管理层要求具备业务经验和敏感度,中台一要懂业务;二要有强的执行力、目标感和沟通能力,因为中台要跨部门推动工作落地;三要有强的逻辑分析能力,因为中台工作比较抽象,要提炼。

    综上,中台团队的人员组成就比较清晰了:内部抽调是重要来源;人员大都既懂业务,又得懂技术;团队内部最好有一个沟通和协调能力较强的人员,这个人是中台团队对外沟通的桥梁,不同企业叫法不同,比如阿里最为核心的角色是业务架构师,但可以简单理解为中台的产品经理。

    如上图,中台产品经理也被认为是 B 端产品经理的一种类型,有 B 端通用的能力要求,比如擅长做抽象建模、具备一定的研发技术功底、懂 UML 等,不同企业要求不同,此处就不一一展开了。

    4 造中台,钱应该谁出?

    企业在推动一些决策落地时,往往背后还有两个重要考量:这件事情能带来成本的节省吗?如果不能,这件事情可以带来更多营收吗?如果都不能,这件事情如此复杂,做的必要性是什么?

    众所周知,阿里建设中台最初源于马云带领集团高管拜访 Supercell 说起,这是一家位于赫尔辛基的移动游戏公司,这家公司号称是世界上最成功的移动游戏公司,其当时已经在使用“中台模式”接连推出爆款游戏,在不到 200 人的情况下成为了年税前利润 15 亿美元的公司。2016 年 6 月,中国腾讯公司以 86 亿美元收购了这家公司 84.3% 的股权,每一名员工人均贡献估值超过 3.54 亿人民币。

    通过建设中台,Supercell 拥有了快速推出产品、快速试错(一旦产品公测期间反馈不好,团队会快速决定放弃)的能力,进而可以在人数不多的情况下创造如此大的利润。虽然中台可能创造的利润非常可观,但是企业在建设中台的过程中,对前路尚不清晰时,钱应该谁来出呢?

    对于技术服务类企业,中台对外售卖,这个问题很好解决,起码这个团队是盈利的,但在很多传统企业,技术往往仅支持内部业务,是一个纯成本的部门,当然这肯定是为业务带来了价值,只是直观来看,这是一个不营收的部门。如果站在为业务赋能的角度,业务中台的钱显然需要业务部门出一部分,建成之后可能还需要从业务方分走部分利润。

    但这条路很难走通,每条业务线都有自己的 OKR 或者 KPI,这件事情对各业务部门的考核可能没有直接帮助,甚至于在局部较短时间内是阻碍当年 KPI 达成的。

    王健表示,试想一下,如果一家企业要推出一款产品,很少有上来就采用众筹模式,让用户预付款,这样会对产品产生很大的短期交付压力,也不利于产品的初始阶段研发。相反,现在很多企业推出的产品,前期都是免费的,先通过投资机构注资研发,快速推向市场获取用户反馈,再不断改进并适时考虑向用户增值收费。如果考虑让公司从战略投资层面出这个钱,那么公司肯定也是要考虑收支平衡问题,投资的目的是什么,需要多长时间可以带来收益,如何合理制定中台团队的 OKR,这些都是需要提前想清楚的。

    比如,阿里业务中台团队的绩效考核会考虑服务稳定性、业务创新、服务接入量以及客户满意度四项指标。其中,服务稳定是重中之重,这部分的考核比重会占整体的 40% 左右;适当允许一定数量因为创新业务上线带来的事故,鼓励团队创新,这部分可能会在 25% 左右;吸引更多前端业务方接入,一般会占 20% 左右,剩下的是客户满意度。

    但是,贾喜顺也提醒道:建设数据中台是个长期过程,不是三个月或者半年就能出成效的,决策者需要在理念上进行转变。言外之意,即便企业决定通过战略投资的方式建设中台,但短期内也未必可以看到收益。

    5 结束语

    建设中台是一个非常复杂的过程,这不是简单的决策而是战略层面的调整。汪源在采访最后表示:“建设中台是比较复杂的,所以首先要判断是否有必要,比如是否觉得核心能力确实存在较大短板;跨业务或项目的共享做得不够;需求变化快且多;数据经常出错或者不一致等。如果需求不大,时机不成熟,可以再观察观察。”

    对于确实规划建设中台的,汪源还是建议要从组织、支撑技术和方法论三个方面都要做好准备,这方面的内容很多,很难给出几条简单的建议就能起到很大的作用。建议第一步先做自主研究,对建设中台有一个总体的理解和把握;第二步找有建设经验的团队学习。

    最后,本文引用王健在采访中的一句话作为结尾:建设中台不能天天喊口号,如果既不敢调动资源,也不敢调整组织架构,还整天担心对前台业务的影响,难以承担失败可能带来的风险,这很难成功。中台承载的是企业最核心的业务能力,最核心的差异化竞争力,是战略层面的事情,没有战略层面的决心和耐心坚持下来就很难看到成果,有的时候,中台建设确实也需要“因为相信,所以看见”。

     

    展开全文
  •  设计一个可解决组织的难题和需求的 FC 交换结构拓扑。证明您的连接结构拓扑选择的合理性。如果 72 端口交换机可用于 FC SAN 实施,请确定结构中需要的交换机的最小数目。 期待高手解决问题。
  • React Native项目组织结构介绍

    千次阅读 2016-09-28 18:57:41
    代码组织:目录结构:. ├── components //组成应用的各个组件 │ ├── Routers.android.js //每个组件若实现不一样,分为android的实现和ios的实现。 │ ├── Routers.ios.js │ ├── common //公共组件...

    代码组织:

    目录结构:

    .
    ├── components    //组成应用的各个组件
    │   ├── Routers.android.js     //每个组件若实现不一样,分为android的实现和ios的实现。
    │   ├── Routers.ios.js
    │   ├── common     //公共组件
    │   ├── issues        //议题页面
    │   ├── navigation  //导航组件,android用侧边栏,ios准备用tab
    │   └── project      //项目页面
    └── network    //网络服务
        └── DataService.js
    1. 我自己的代码全部放在src目录下,这样写代码过程中搜索啊什么操作比较方便,从逻辑上也比较清晰。

    2. react的应用,是用自定义组件或原生组件层层嵌套而成的。因此我将整个应用划分为组件部分(组成各个页面)和一些其他服务(目前比较简单,只抽象出发get请求的网络服务)。

    3. components内,根据自己的业务逻辑进行抽象,把整个应用划分为层层嵌套的组件,目录结构的组织形式基本就是我页面的组织形式。如果有一些比较通用的功能,可以提取成公共组件,我放在common目录下。

    4. 每个组件如果ios和android的实现不太一样,则创建两个文件,如Routers.android.jsRouters.ios.js

    基本逻辑:

    1. 根组件:
      我定义了一个Routers组件,作为整个app的根组件。Router组件实际上包装的官方的Navigator组件,主要作用:

      • 负责整个app的所有路由,当使用navigator去跳转路由时,会最终进入renderScene函数来渲染不同的页面。

      • 提供了默认router,整个程序启动时,默认加载页面ProjectList

    2. 各个页面:不同路由对应不同的页面,如RoutersrenderScene函数中,每个if分支是一个页面。这些页面实际上就是一个个导出的组件。比如ProjectList组件是用来做项目列表的,但他自身又包含了一个用来渲染每个项目单元格的projectCell组件。如此,所有组件都是对上层呈现成一个统一的组件接口,对下层自己去组装多个不同组件,最终形成一个模块化的统一的app。

    3. 组件之间的关联:组件之间经常会发生关联。我自己用到了以下情况:

      • 父改变子:

        • 子通过state对外提供接口,父可以通过setState去改变子的状态,并让子重新渲染。state是React的一个很重要的概念。在组件上可以设一些属性,这些属性都有一个初始状态,然后用户的操作产生交互,只要是用setState去触发这个组件状态变化,则会触发这个组件重新渲染 UI 。

        • 父直接调用子导出的方法,比如官方组件DrawerLayoutAndroid提供的openDrawer方法。可以使用react的refs机制去调用。比如我在NavTab组件的openNavDrawer函数中,以this.refs['drawer'].openDrawer();这样的函数方式去调用。那么如何像这种方式导出自己的方法供父组件直接以函数方式调用?注意导出的方法必须是作为类方法就可以了,比如openNavDrawer这个函数就是导出给父用的。

      • 子调用父:
        这其实有点类似是反向依赖的设计模式。就是子提供触发回调的接口,但是究竟是触发后执行什么,子并不关心。比如我封装的NavToolbar(就是很多界面上面的工具条)组件的onClicked方法。

      • 很多地方的按钮都是返回上一级。
        <NavToolbar ... onClicked={() => {this.props.nav.pop();}} />
        但是最底层的几个界面上的按钮,换成了弹出侧面导航条,以供切换。
        <NavToolbar ... onClicked={this.onToolbarClicked} />
        对于这种情况,导航条要想抽象成公共的组件,他就不能依赖于他的父究竟是哪个界面。触发的具体动作就需要通过回调注入进来,这时就用这种方式。
      • 兄弟关系:
        在共同的父中组合上面两种情况就可以了。比如ProjectList.android.js

      • onToolbarClicked:  function (){
          this.refs['navTab'].openNavDrawer();
        },
        <NavTab ref='navTab' nav={this.props.nav}>
          <NavToolbar icon={"ic_menu_white"} title={'项目'} onClicked={this.onToolbarClicked} />
          {content}
        </NavTab>
      • 其他情况:
        参考这篇文章,不过目前我还没用到这种毫无关系的事件触发,所以尚未研究。

    调试

    1. chrome调试:
      安装react dev的chrome官方插件。在手机上设置host的ip,点击start chrome debugging。 chrome会自动跳转到调试地址,在浏览器上打开调试窗口,会发现里面多了一个react页签。

    2. inspect元素:在模拟器中打开inspect element面板,点击模拟器中的元素,chrome会跳转到对应dom。

    3. 槽点:

      • 在浏览器改动css后,模拟器的布局不跟着更新。注意每个dom都有个RN的包裹,需要更改这个以RCT开头的包裹元素。参考issue

      • 浏览器的dom和手机上的元素位置对不准确。我有时会分不清哪个dom对应我屏幕哪一块。

      • 调试经常失效,调试窗口的react页签动不动就找不到了,我大部分时候是直接改代码,在模拟器看效果的。

    遇到的坑:

    1. 模拟器中的程序经常崩溃,代码语法有低级错误,一但reload js,程序就有很大概率崩溃,需要react-native run-android重新开始。

    2. 换工程运行项目,react-native run-android 前最好关下后台,否则两个项目会互相影响。

    3. 出错提示很不完善。
      比如有时我会将<View>误写成<view>,或者忘记关闭标签。而这些低级错误,RN里面往往会非常难排除,提示往往都很奇怪,我都是靠走读代码发现。
      比如有一次,我看了ECMAScript 6 Features的语法后,将DataServicevar SERVER = 'http://www.yudianer.com/api';这句改成了const SERVER = 'http://www.yudianer.com/api';,当时没发现什么问题。但后面发现了奇怪的问题,只有在浏览器调试的时候,app才能正常运行,否则什么也不显示,而且没有任何提示。最后打包运行无数次都没反应,只能一点一点注释代码排除,才发现是我用了ECMAScript 6 Features,却没有配置。。。

    4. RN的有些组件有些限制,往往是后知后觉。例如:

      • DrawerLayoutAndroid这个组件外面不能再包一个<View></View>。如果你不幸这么做了,会整个页面不显示了,而没有任何提示。。。

      • 如果ListView包在一个View中,那么外面这个View需要设置style={flex: 1}。否则ListView将不能滚动。

      • 当遇到这种问题,最好去google一下,或去github看下有没有类似的议题。实在不行就通过注释代码的方法排除。

    5. JSX的语法经常搞错,跟一般的模板语言不太一样。比如:

    renderProject: function(project){
        return(
          <ProjectCell
            onSelect={() => this.selectProject(project)}
            project={project}/>
        );
      },

    我会经常忘记这是个函数,而直接写成:

    renderProject: function(project){
          <ProjectCell
            onSelect={() => this.selectProject(project)}
            project={project}/>
      },

    这看上去没什么,问题是这种类似错误的提示很奇怪,不好定位。

    总结:

    RN在android上确实不太完善,调试工具,错误提示,文档等都不是很友好。但去学习下还是挺酷的,而且在facebook不遗余力的推动,相信会越来越完善的。

    展开全文
  • 浅析x86架构cache的组织结构

    千次阅读 2016-03-31 15:51:59
    就拿操作系统内核而言,尽管所谓的微内核组织结构更好,但是在目前所有知名的操作系统是找不到完全符合学术意义上的微内核的例子。工程上某些时候就是一种折衷,选择更“接地气”的做法,而不是一味的契合理论模型...
  • 2. 业务中台采用领域驱动设计(DDD),在其上构建业务能力SAAS,持续不断进行迭代演进。 3. 平台化定位,进行了业务隔离设计,方便一套系统支撑不同玩法的业务类型和便于定制化扩展。 4. 前后端分离,通过服务接入层...
  • 要理解中台,得先将时间拉回到二十年前。那时的CRM不叫CRM,叫九七。大家办业务还只有去营业厅这一条途径。营业员大妈高傲地在九七系统里录入用户信息、业务办理信息。倒是够用。 后来随着业务和客户的增多,原来几...
  • | 前提1:技术组织结构垂直化 | 前提2:业务线又多又复杂 有了技术中台,是不是就能上天? 总结 就在刚过去的半年里,「中台」成了技术圈内讨论的热门词汇,就连一些名不见经传的小公司,也都纷纷喊出了「要...
  • IT痴汉的工作现状19-公司组织结构

    千次阅读 2014-10-15 17:20:32
    每个公司都有一套适合自己的组织结构,它是公司正常运作的基石,公司的每个人都在这个组织结构里有自己的位置,大家同心协力,维护公司这机器的正常运转。公司的主要目标是利润,那么我们每个人的首要任务是创造...
  • 大数据中台

    千次阅读 2020-08-28 11:17:11
    数据中台的由来 数据中台最早是阿里提出的,但真正火起来是2018 年,我们能感受到行业文章谈论数据中台的越来越多。大量的互联网、非互联网公司都开始建设数据中台。为什么很多公司开始建设数据中台?尽管数据中台...
  • 今天,随着中国加入WTO,面临全球信息技术的飞速发展和市场竞争的加剧,原有的企业组织结构和管理模式已经不能适应新的环境,企业迫切需要进行组织变革,以便在激烈的竞争处于不败之地。信息技术的应用不但需要...
  • “你觉得我对中台的「企业级能力复用平台」这个定义咋样?” 我沾沾自喜,满怀期待地问到。 “没有说到点子上,不解决实际问题。” 徐昊此时就斜靠在我旁边的桌子上,磕绊儿都没打一下,一上来就给我浇了一盆凉水,...
  • 技术部的组织结构

    千次阅读 2011-07-02 18:19:41
    那么以我所在的组织来说,核心业务应该是设计、生产并优化商业产品,使之能够更好的为公司贡献收入。能够生产出可以为公司贡献收入的产品,应当就算是我们的职责了。当然说到产品可能会分成很多阶段,包括产品的规划...
  • 希望你也加入到人工智能的队伍来!点击浏览教程 saas平台由于其本身“按需购买”的特性,在设计规划权限时,需要考虑统一配置权限如何规避企业没有购买的应用,以及如有部分应用存在数据权限不同的问题。现在,...
  • 全渠道零售中台与数字化转型(1)-中台的前世今身

    千次阅读 多人点赞 2019-06-25 15:37:59
    本系列博客的目标是计划使用近半年时间创造: ... 全渠道零售中台与数字化转型(2)-中台给企业业务带来什么实际的价值 全渠道零售中台与数字化转型(3)-中台给企业技术带来什么实际的价值? 全渠...
  • 《软件工程》第6章体系结构设计

    千次阅读 2020-06-02 09:33:24
    体系结构设计关注理解一个软件系统应当如何组织,以及设计该系统的整体结构。体系结构设计是设计和需求工程之间的关键性衔接环节,因为它会确定组成一个系统的主要结构构件以及它们之间的关系。体系结构设计过程的...
  • Web设计师Manu Cornet在自己的博客上,画了一组美国科技公司的组织结构图。在他笔下,亚马逊等级森严且有序;谷歌结构清晰,产品和部门之间却相互交错且混乱;Facebook架构分散,就像一张散开的网络;微软内部各自...
  • 业务中台采用领域驱动设计(DDD),在其上构建业务能力SAAS,持续不断进行迭代演进。 平台化定位,进行了业务隔离设计,方便一套系统支撑不同玩法的业务类型和便于定制化扩展。 前后端分离,通过服务接入层进行...
  • 树形结构的数据库表设计

    万次阅读 多人点赞 2018-04-23 15:11:30
    树形结构的数据库表Schema设计 程序设计过程,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门、栏目结构、商品分类等等,通常而言,这些树状结构需要借助于数据库完成持久化。然而目前的各种基于...
  • PDM系统的结构设计

    千次阅读 2018-11-04 11:46:51
    1 PDM系统需求分析   PDM是依托IT技术实现企业最优化...它必须是一种可以实现的技术,必须是一种可以在不同行业、不同企业实现的技术,必须是一种与企业文化相结合的技术。因此,它与企业自身密切相 关。 ...
  • Python-如何设计结构清晰的目录结构

    千次阅读 2018-05-18 14:47:43
    设计项目目录结构”就和”代码编码风格”一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度: 一类同学认为,这种个人风格问题”无关紧要”。理由是能让程序work就好,风格问题根本不是问题。 ...
  • 《计算机组成与系统结构课程设计》是计算机学院各专业集中实践性环节之一,是学习完《计算机组成与系统结构》课程后进行的一次全面的综合练习。其目的是综合运用所学计算机原理知识,设计并实现一模型计算机,以便...
  • 为了应对地府管理危机,阎王打算找“人”开发一套地府后台管理系统,于是就在地府总经办群发了项目需求。 话说还是中国电信的信号好,地府都是满格,哈哈!!! 经常会有外行朋友问:看某网站做的不错,功能...
  • 中台打破了应用系统的壁垒,从企业全局梳理和规划业务程,重构了组织架构、业务架构与IT 架构。 在梳理了企业的IT 现状并回顾了SOA 的历史之后,我们需要对中台架构进行一番详细的介绍,阿里巴巴的Aliware 团队曾经...
  • 1、解读中台 -- 什么是中台

    千次阅读 2019-10-06 18:57:15
    解读中台 中台,通过对业务、数据和技术的抽象,对服务能力进行复用,构建了企业级的服务能力,消除了企业内部各业务部门、各分子公司间的壁垒,适应了企业,特别是大型企业集团业务多元化的发展战略。基于中台,可快速...
  • 数据中台(七) 数据中台架构

    千次阅读 多人点赞 2020-09-23 12:59:15
    数据汇聚是把数据资源通过实时、批量的方式存储到数据中台。基本是按照数据的原始状态堆砌在一起的,是企业对过往所有IT信息化建设积累的成果的融合。 数据开发 数据开发是数据资产内容建设的主战场,是数据价值...
  • 6月 27日,Web设计师Manu Cornet在自己的博客上,画了一组美国科技公司的组织结构图。在他笔下,亚马逊等级森严且有序;谷歌结构清晰,产品和部门之间却相互交错且混乱;Facebook架构分散,就像一张散开的网络;微软...
  • 管理后台界面基本框架设计

    万次阅读 2018-10-18 13:56:54
    电商类项目分为前端系统与后台管理系统,前端系统面向终端用户使用,一般设计十分炫动,色彩对比比较鲜明,容易引起消费者注意力,需要由...古方红糖创客系统后台管理界面使用Easyui开发设计界面框架,在本节讲...
  • 施工组织设计

    万次阅读 热门讨论 2004-08-10 23:55:00
    在收到招标文件及施工图后,我公司立即组织有关技术力量,对招标文件和施工图进行细致探讨、研究,并根据招标文件的规定,着手编制本工程的技术标书及施工预算报价单,以报给建设单位XXXX以及代理单位上海XXXXXX公司...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 124,016
精华内容 49,606
关键字:

中台组织结构设计