欧美vvv,亚洲第一成人在线,亚洲成人欧美日韩在线观看,日本猛少妇猛色XXXXX猛叫

新聞資訊

    是本人的架構(gòu)師學(xué)習(xí)路線,分享出來希望能幫到大家,謝謝。

    https://blog.csdn.net/hguisu/article/details/78258430 架構(gòu)設(shè)計

    https://blog.csdn.net/u012410733/article/list/3?t=1 dubbo很好的博客

    源碼:springmvc、mybatis、spring、dubbbo、rocketmq

    一、常用設(shè)計模式

    1、proxy模式

    package com.sun.proxy;
    import com.sun.cglibproxy.Cglibproxy;
    import com.sun.jdkproxy.JdkProxy;
    public class Main {
     public static void main(String[] args) {
     //靜態(tài)代理
     Proxy proxy = new Proxy(new RealSubject());
     proxy.request();
     //jdk動態(tài)代理
     JdkProxy jdkProxy = new JdkProxy(new RealSubject());
     Subject subject1 = (Subject)jdkProxy.getProxyObject();
     subject1.request();
     //cglib動態(tài)代理
     Cglibproxy cglibproxy = new Cglibproxy(new RealSubject());
     Subject subject2 = (Subject)cglibproxy.getProxyObject();
     subject2.request();
     }
    }
    

    2、工廠模式

    https://www.cnblogs.com/carryjack/p/7709861.html

    https://www.cnblogs.com/zailushang1996/p/8601808.html

    https://www.jianshu.com/p/74f4c52e1bd9

    1)簡單工廠

    2)工廠方法 //一個產(chǎn)品

    2)抽象工廠 //多個產(chǎn)品,一個產(chǎn)品族

    結(jié)合spring中的FactoryBean

    閱讀原碼,F(xiàn)actoryBean是通過泛型傳遞實際對象的類型,它有三個方法:

    a)getObject(),返回生成的對象;

    b)getObjectType(),返回對象類型;

    c)isSingleton() ,是否是單例,true:是,false:不是。

    3、單例(Singleton)模式

    https://www.cnblogs.com/cielosun/p/6582333.html

    單例模式有3個要點:

    某個類只能有一個實例

    它必須自行創(chuàng)建這個實例

    它必須自行向整個系統(tǒng)提供這個實例

    1)懶漢模式

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

    如上,通過提供一個靜態(tài)的對象instance,利用private權(quán)限的構(gòu)造方法和getInstance()方法來給予訪問者一個單例。

    缺點是,沒有考慮到線程安全,可能存在多個訪問者同時訪問,并同時構(gòu)造了多個對象的問題。之所以叫做懶漢模式,主要是因為此種方法可以非常明顯的lazy loading。

    2)線程安全的懶漢模式

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

    然而并發(fā)其實是一種特殊情況,大多時候這個鎖占用的額外資源都浪費了,這種打補丁方式寫出來的結(jié)構(gòu)效率很低。

    3)餓漢模式

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

    單例加載時就加載,這種方法沒有起到lazy loading的效果

    4)靜態(tài)內(nèi)部類加載

    public class StaticClasssingleton {
     private static class StaticClasssingletonHelper{
     private static StaticClasssingleton instance = new StaticClasssingleton();
     }
     private StaticClasssingleton(){
     System.out.println("Singleton has loaded");
     }
     public static StaticClasssingleton getInstance(){
     return StaticClasssingletonHelper.instance;
     }
    }
    

    使用內(nèi)部類的好處是,靜態(tài)內(nèi)部類不會在單例加載時就加載,而是在調(diào)用getInstance()方法時才進行加載,達到了類似懶漢模式的效果,而這種方法又是線程安全的。

    5)枚舉方法

    enum SingletonDemo{
     INSTANCE;
     public void otherMethods(){
     System.out.println("Something");
     }
    }
    

    6)雙重校驗鎖法

    //雙重校驗鎖法

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

    接下來我解釋一下在并發(fā)時,雙重校驗鎖法會有怎樣的情景:

    STEP 1. 線程A訪問getInstance()方法,因為單例還沒有實例化,所以進入了鎖定塊。

    STEP 2. 線程B訪問getInstance()方法,因為單例還沒有實例化,得以訪問接下來代碼塊,而接下來代碼塊已經(jīng)被線程1鎖定。

    STEP 3. 線程A進入下一判斷,因為單例還沒有實例化,所以進行單例實例化,成功實例化后退出代碼塊,解除鎖定。

    STEP 4. 線程B進入接下來代碼塊,鎖定線程,進入下一判斷,因為已經(jīng)實例化,退出代碼塊,解除鎖定。

    STEP 5. 線程A初始化并獲取到了單例實例并返回,線程B獲取了在線程A中初始化的單例。

    理論上雙重校驗鎖法是線程安全的,并且,這種方法實現(xiàn)了lazyloading。

    //Spring單例模式與線程安全

    https://www.cnblogs.com/wxd0108/p/5524756.html

    同步機制采用了 “ 以時間換空間 ” 的方式,而 ThreadLocal 采用了 “ 以空間換時間 ” 的方式,前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響

    ThreadLocal 是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況下, ThreadLocal 比直接使用 synchronized 同步機制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性。

    4、delegate 委派模式

    class A{
     void method1(){...}
     void method2(){...}
    }
    class B{
     //delegation
     A a = new A();
     //method with the same name in A
     void method1(){ a.method1();}
     void method2(){ a.method2();}
     //other methods and attributes
     ...
    }
    public class Test{
     public static void main(String args[]){
     B b = new B();
     b.method1();//invoke method2 of class A in fact
     b.method2();//invoke method1 of class A in fact
     }
    }
    

    委托的缺點:代碼量大,類更多。

    ----- delegate委派模式和Proxy代理模式 -----

    Proxy :譯為代理, 被代理方(B)與代理方(A)的接口完全一致。

    主要使用場景:為簡化編程(或無法操作B)而把請求交給代理方(A),由代理方與被代理方進行通信,以完成請求。

    Delegete : 譯為委托

    主要使用場景:一件事情(或一個請求)對象本身不知道怎樣處理,對象把請求交給其它對象來做。

    簡單來講,可以這么理解,代理是若干個對象實現(xiàn)了一個共同的接口,而委派只是說明一個對象引用了另一個對象,并不牽扯接口。

    應(yīng)用場景

    Spring MVC框架中的DispatcherServlet其實就用了委派模式,也有人稱為是代理模式和策略模式的組合。

    DispatcherServlet的委托流程

    用戶發(fā)送請求——>DispatcherServlet,前端控制器收到請求后自己不進行處理,而是委托給其他的解析器進行處理,作為統(tǒng)一訪問點,進行全局的流程控制。

    DispatcherServlet——>HandlerMapping,映射處理器將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象。

    DispatcherServlet——>HandlerAdapter,處理器適配器將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計模式的應(yīng)用,從而很容易支持很多類型的處理器。

    DispatcherServlet——> ViewResolver, 視圖解析器將把ModelAndView對象(包含模型數(shù)據(jù)、邏輯視圖名)解析為具體的View。

    DispatcherServlet——>View,View會根據(jù)傳進來的Model模型數(shù)據(jù)進行渲染,此處的Model實際是一個Map數(shù)據(jù)結(jié)構(gòu)。

    返回控制權(quán)給DispatcherServlet,由DispatcherServlet返回響應(yīng)給用戶,到此一個流程結(jié)束。

    5、Strategy策略模式

    https://www.jianshu.com/p/7b7de81cdfbe

    準(zhǔn)備一組算法,并將每一個算法封裝起來,使得它們可以互換

    這個模式涉及到三個角色:

    ● 環(huán)境(Context)角色:持有一個Strategy的引用。

    ● 抽象策略(Strategy)角色:這是一個抽象角色,通常由一個接口或抽象類實現(xiàn)。此角色給出所有的具體策略類所需的接口。

    ● 具體策略(ConcreteStrategy)角色:包裝了相關(guān)的算法或行為。

    策略模式的優(yōu)點:

    ? 算法可以自由切換;

    ? 避免使用多重條件判斷;

    ? 擴展性良好。

    策略模式的缺點:

    ? 策略類會增多

    ? 所有策略類都需要對外暴露

    策略模式的適用場景:

    ? 當(dāng)一個系統(tǒng)中有許多類,它們之間的區(qū)別僅在于它們的行為,希望動態(tài)地讓一個對象在許多行為中選擇一種行為時;

    ? 當(dāng)一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種時;

    ? 當(dāng)一個對象有很多的行為,不想使用多重的條件選擇語句來選擇使用哪個行為時。

    6、prototype 原型模式

    https://www.cnblogs.com/cxxjohnson/p/6403949.html

    原型模式:原型模式就是從一個對象再創(chuàng)建另外一個可定制的對象,而且不需要知道任何創(chuàng)建的細(xì)節(jié)。

    所謂原型模式,就是java中的克隆技術(shù),以某個對象為原型。復(fù)制出新的對象。顯然新的對象具備原型對象的特點。效率高(避免了重新執(zhí)行構(gòu)造過程步驟)

    克隆類似于new,但和new不同。new創(chuàng)建新的對象屬性采用的是默認(rèn)值。克隆出來的對象的屬性值完全和原型對象相同。并且克隆出的新對象不會影響原型對象,克隆后。還可以再修改克隆對象的值。

    要實現(xiàn)原型模式,必須實現(xiàn)Cloneable接口,而這個接口里面是空的。

    淺克隆:copy該對象,然后保留該對象原有的引用。也就是說不克隆該對象的屬性。

    深克隆:copy該對象,并且把該對象的所有屬性也克隆出一份新的。

    通過序列化和反序列化來實現(xiàn)深克隆對象:序列化需要原型對象實現(xiàn)Serializable接口

    所以,通過原型模式創(chuàng)建對象,可以大大提高創(chuàng)建的效率,直接克隆,避免了重新執(zhí)行構(gòu)造過程。原型模式和工廠模式搭配起來,是常用的使用方式。

    新的問題:Java的clone方法實現(xiàn)的是淺復(fù)制,對基本類型的復(fù)制,這樣的操作是沒有問題的。但對非基本類型的變量,復(fù)制的是對象的引用,導(dǎo)致最后兩個變量指向同一個對象。

    解決方法:深復(fù)制

    PS:深拷貝與淺拷貝問題中,會發(fā)生深拷貝的有java中的8中基本類型以及他們的封裝類型,另外還有String類型。其余的都是淺拷貝。

    淺拷貝: 對值類型的成員變量進行值的復(fù)制,對引用類型的成員變量只復(fù)制引用,不復(fù)制引用的對象.

    深拷貝: 對值類型的成員變量進行值的復(fù)制,對引用類型的成員變量也進行引用對象的復(fù)制.

    7、template 模板模式

    https://www.cnblogs.com/qq-361807535/p/6854191.html

    模板模式是使用一個抽象類,里面定義了一系列模板方法,也就是一個算法骨架,再定義一個總的調(diào)用統(tǒng)籌方法。統(tǒng)籌方法就是定義模板方法的執(zhí)行順序,一般設(shè)定該方法為final ,是不讓子類破壞的。

    現(xiàn)在有一個問題:我們能不能用接口去代替AbstractDisplay?答案是:不可以,因為我們這種模式的側(cè)重點在于由父類決定處理流程,這處理流程display方法必須要在父類中去實現(xiàn),但是接口是不能去實現(xiàn)方法的。

    場景:

    jdbcTemplate callback結(jié)合使用

    public abstract class JdbcTemplate { 
     //template method 
     public final Object execute(String sql) throws SQLException{ 
     Connection con = HsqldbUtil.getConnection(); 
     Statement stmt = null; 
     try { 
     stmt = con.createStatement(); 
     ResultSet rs = stmt.executeQuery(sql); 
     Object result = doInStatement(rs);//abstract method 
     return result; 
     } 
     catch (SQLException ex) { 
     ex.printStackTrace(); 
     throw ex; 
     } 
     finally { 
     try { 
     stmt.close(); 
     } catch (SQLException e) { 
     e.printStackTrace(); 
     } 
     try { 
     if(!con.isClosed()){ 
     try { 
     con.close(); 
     } catch (SQLException e) { 
     e.printStackTrace(); 
     } 
     } 
     } catch (SQLException e) { 
     e.printStackTrace(); 
     } 
     } 
     } 
     //implements in subclass 
     protected abstract Object doInStatement(ResultSet rs); 
    } 
    

    在上面這個抽象類中,封裝了SUN JDBC API的主要流程,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,由子類負(fù)責(zé)實現(xiàn)。

    好,我們來定義一個子類,并繼承上面的父類:

    public class JdbcTemplateUserImpl extends JdbcTemplate { 
     @Override 
     protected Object doInStatement(ResultSet rs) { 
     List<User> userList = new ArrayList<User>(); 
     try { 
     User user = null; 
     while (rs.next()) { 
     user = new User(); 
     user.setId(rs.getInt("id")); 
     user.setUserName(rs.getString("user_name")); 
     user.setBirth(rs.getDate("birth")); 
     user.setCreateDate(rs.getDate("create_date")); 
     userList.add(user); 
     } 
     return userList; 
     } catch (SQLException e) { 
     e.printStackTrace(); 
     return null; 
     } 
     } 
    } 
    

    由代碼可見,我們在doInStatement()方法中,對ResultSet進行了遍歷,最后并返回。

    有人可能要問:我如何獲取ResultSet 并傳給doInStatement()方法啊??呵呵,問這個問題的大多是新手。因為此方法不是由子類調(diào)用的,而是由父類調(diào)用,并把ResultSet傳遞給子類的。我們來看一下測試代碼:

    String sql = "select * from User";

    JdbcTemplate jt = new JdbcTemplateUserImpl();

    List<User> userList = (List<User>) jt.execute(sql);

    為什么spring不用傳統(tǒng)的模板方法,而加之以Callback進行配合呢?

    試想,如果父類中有10個抽象方法,而繼承它的所有子類則要將這10個抽象方法全部實現(xiàn),子類顯得非常臃腫。而有時候某個子類只需要定制父類中的某一個方法該怎么辦呢?這個時候就要用到Callback回調(diào)了

    二、spring5

    1、IOC容器設(shè)計原理及高級特性

    https://www.cnblogs.com/linjiqin/archive/2013/11/04/3407126.html 設(shè)計原理

    https://blog.csdn.net/sugar_rainbow/article/details/76757383 高級特性

    https://www.cnblogs.com/ITtangtang/p/3978349.html 源碼解析

    在Spring IOC容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IOC容器最基本功能;而org.springframework.context包下的ApplicationContext接口擴展了BeanFactory,還提供了與Spring AOP集成、國際化處理、事件傳播及提供不同層次的context實現(xiàn) (如針對web應(yīng)用的WebApplicationContext)。簡單說, BeanFactory提供了IOC容器最基本功能,而 ApplicationContext 則增加了更多支持企業(yè)級功能支持。ApplicationContext完全繼承BeanFactory,因而BeanFactory所具有的語義也適用于ApplicationContext。

    讓我們來看下IOC容器到底是如何工作。在此我們以xml配置方式來分析一下:

    一、準(zhǔn)備配置文件:就像前邊Hello World配置文件一樣,在配置文件中聲明Bean定義也就是為Bean配置元數(shù)據(jù)。

    二、由IOC容器進行解析元數(shù)據(jù): IOC容器的Bean Reader讀取并解析配置文件,根據(jù)定義生成BeanDefinition配置元數(shù)據(jù)對象,IOC容器根據(jù)BeanDefinition進行實例化、配置及組裝Bean。

    三、實例化IOC容器:由客戶端實例化容器,獲取需要的Bean。

    public void refresh() throws BeansException, IllegalStateException { 
     synchronized (this.startupShutdownMonitor) { 
     //調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時時間,同時給容器設(shè)置同步標(biāo)識 
     prepareRefresh(); 
     //告訴子類啟動refreshBeanFactory()方法,Bean定義資源文件的載入從 
     //子類的refreshBeanFactory()方法啟動 
     ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 
     //為BeanFactory配置容器特性,例如類加載器、事件處理器等 
     prepareBeanFactory(beanFactory); 
     try { 
     //為容器的某些子類指定特殊的BeanPost事件處理器 
     postProcessBeanFactory(beanFactory); 
     //調(diào)用所有注冊的BeanFactoryPostProcessor的Bean 
     invokeBeanFactoryPostProcessors(beanFactory); 
     //為BeanFactory注冊BeanPost事件處理器. 
     //BeanPostProcessor是Bean后置處理器,用于監(jiān)聽容器觸發(fā)的事件 
     registerBeanPostProcessors(beanFactory); 
     //初始化信息源,和國際化相關(guān). 
     initMessageSource(); 
     //初始化容器事件傳播器. 
     initApplicationEventMulticaster(); 
     //調(diào)用子類的某些特殊Bean初始化方法 
     onRefresh(); 
     //為事件傳播器注冊事件監(jiān)聽器. 
     registerListeners(); 
     //初始化所有剩余的單態(tài)Bean. 
     finishBeanFactoryInitialization(beanFactory); 
     //初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件 
     finishRefresh(); 
     } 
     catch (BeansException ex) { 
     //銷毀以創(chuàng)建的單態(tài)Bean 
     destroyBeans(); 
     //取消refresh操作,重置容器的同步標(biāo)識. 
     cancelRefresh(ex); 
     throw ex; 
     } 
     } 
     }
    

    Spring IoC容器對Bean定義資源的載入是從refresh()函數(shù)開始的,refresh()是一個模板方法,refresh()方法的作用是:在創(chuàng)建IoC容器前,如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關(guān)閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入

    FileSystemXmlApplicationContext通過調(diào)用其父類AbstractApplicationContext的refresh()函數(shù)啟動整個IoC容器對Bean定義的載入過程

    2、spring aop

    public class MyAspect {
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
     @Pointcut("execution(public void *.method1)")
     public void pointcutName(){}
     @Around("pointcutName()")
     public Object performanceTrace(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
     try {
     logger.info("log.....");
     return proceedingJoinPoint.proceed();
     } finally {
     logger.info("log end");
     }
     }
    }
    

    定義Aspect

    @Aspect
    @Component
    public class AdviceTest {
     @Pointcut("@annotation(com.listenzhangbin.aop.Timer)")
     public void pointcut() {
     }
     @Before("pointcut()")
     public void before() {
     System.out.println("before");
     }
    }
    

    3、spring 事務(wù)

    https://baijiahao.baidu.com/s?id=1607466404459549668&wfr=spider&for=pc

    1)、配置文件開啟注解驅(qū)動,在相關(guān)的類和方法上通過注解@Transactional標(biāo)識。

    2)、spring 在啟動的時候會去解析生成相關(guān)的bean,這時候會查看擁有相關(guān)注解的類和方法,并且為這些類和方法生成代理,并根據(jù)@Transaction的相關(guān)參數(shù)進行相關(guān)配置注入,這樣就在代理中為我們把相關(guān)的事務(wù)處理掉了(開啟正常提交事務(wù),異常回滾事務(wù))。

    3)、真正的數(shù)據(jù)庫層的事務(wù)提交和回滾是通過binlog或者redo log實現(xiàn)的。

    4、springmvc九大組件以及手寫springmvc框架

    https://www.cnblogs.com/java1024/p/8556519.html

    https://blog.csdn.net/it_freshman/article/details/81365000 SpringMVC3-處理-源碼跟蹤

    https://www.cnblogs.com/kylyww/p/6405888.html

    初始化流程

    使用Spring MVC 時,需要在web.xml中配置DispatchServlet,這個DispatchServlet可以看成一個控制器的具體實現(xiàn)。作為一個控制器所有的請求都要通過它來處理,進行轉(zhuǎn)發(fā)、匹配、數(shù)據(jù)處理后并轉(zhuǎn)由頁面進行展示。因此DispatchServlet是Spring MVC的核心部分。

    在完成ContextLoaderListener的初始化后,Web容器開始初始化DispatcherServlet,這個初始化的啟動與在web.xml中對載入次序的定義有關(guān)。DispathcerServlet會建立自己的上下文來持有Spring MVC的Bean,在建立這個自己的IOC容器時,會從ServletContext中得到根上下文作為自己持有上下文的雙親上下文。有了這個根上下文再對自己持有的上下文進行初始化,最后將自己持有的上下文保存到ServletContext中。

    首先看看DispatcherSerlvet的繼承關(guān)系:DispatcherServlet繼承自FrameworkServlet,而FrameworkServet繼承自HttpServletBean.HttpServletBean有繼承了HttpServlet.

    DispatcherServlet動作大致可以分為兩個部分:初始化部分由initServletBean()啟動,通過initWebApplicationContext()方法調(diào)用DispatcherServlet的initStrategies方法。在這個方法里,DispatcherServlet對MVC的其他部分進行了初始化,比如handlerMapping,ViewResolver;另一個部分是對Http的請求進行響應(yīng),作為一個Servlet,web容器會調(diào)用Servlet的doGet()和doPost()方法,在經(jīng)過FrameServlet的processRequest()簡單處理后,會調(diào)用DispatcherServlet的doService()方法,在這個方法中封裝了doDispatch().

    在這里主要介紹初始化部分。

    作為Servlet, DispatcherServlet的啟動過程和Servlet啟動過程是相聯(lián)系的。在Servlet的初始化過程中,Servlet的init方法被調(diào)用,已進行初始化。

    HttppServletBean.init()->FrameworkServlet.initWebApplicationContext()->DispatcherServlet.onRefresh().->initStrategies(初始化九大組件)

    springmvc請求流程

    httpServlet#service->protected service->doGet/doPost->frameworkServlet#doGet/doPost->final processRequest(無論成功與否 發(fā)布publishRequestHandledEvent)->abstract doService->DispatcherServlet#doService(設(shè)置九大組件的屬性)->doDispatcher

    往下流程:

    用戶發(fā)送請求至前端控制器DispatcherServlet

    DispatcherServlet收到請求調(diào)用HandlerMapping處理器映射器。

    處理器映射器根據(jù)請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet。

    DispatcherServlet通過HandlerAdapter處理器適配器調(diào)用處理器

    執(zhí)行處理器(Controller,也叫后端控制器)。

    Controller執(zhí)行完成返回ModelAndView

    HandlerAdapter將controller執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet

    DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器

    ViewReslover解析后返回具體View

    DispatcherServlet對View進行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)。

    DispatcherServlet響應(yīng)用戶。

    從上面可以看出,DispatcherServlet有接收請求,響應(yīng)結(jié)果,轉(zhuǎn)發(fā)等作用。有了DispatcherServlet之后,可以減少組件之間的耦合度。

    doDispatch()結(jié)構(gòu)

    doDispatch的方法也非常簡潔,從頂層設(shè)計了整個請求的處理過程。doDispatch的最核心代碼只有4句,它們的任務(wù)分別是:

    根據(jù)request找到Handler以及對應(yīng)的interceptors

    根據(jù)Handler找到對應(yīng)的HandlerAdapter

    用HandlerAdapter處理Handler

    處理上面處理之后的結(jié)果(包含找到View并渲染給用戶),最終調(diào)用afterCompletion

    protected void doDispatcher(HttpServletRequest req, HttpServletResponse res) throws Exception{
     HttpServletRequest processRequet = req;
     HandlerExecutionChain mappedHandler = null;
     ModelAndView mv = null;
     processRequet = checkMultipart(req);
     //獲取handler
     mappedHandler = getHandler(processRequet);
     //獲取handlerAdapter
     HandlerAdapter h = getHandlerAdapter(mappedHandler.getHandler());
     //執(zhí)行handler注冊的interceptors中的preHandle方法
     HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
     for(int i = 0 ;i<interceptors.length;i++){
     interceptors[i].preHandle(processRequet, res, mappedHandler);
     }
     //處理handler
     mv = h.handle(processRequet, res, mappedHandler.getHandler());
     //執(zhí)行handler注冊的interceptors中的postHandle方法
     for(int i = 0 ;i<interceptors.length;i++){
     interceptors[i].postHandle(processRequet, res, mappedHandler,mv);
     }
     }
    

    三、mybatis

    1、mybatis代碼自動生成器

    generatorConfig.xml

    2、mybatis動態(tài)代理的實現(xiàn)

    https://blog.csdn.net/xiaokang123456kao/article/details/76228684

    使用jdk動態(tài)代理實現(xiàn)

    3、mybatis源碼解析

    https://blog.csdn.net/ma15732625261/article/details/81123349

    https://www.jianshu.com/p/0d7db721c4b5

    總結(jié):

    1)獲取sqlsessionFactory:把配置文件解析關(guān)保存在configuration對象中,返回包含了configuration的defaultSqlSession對象

    2)獲取sqlSession:返回sqlsession實現(xiàn)類defaultsqlsession對象,它里面包含了executor和configuration,executor在這步創(chuàng)建

    3)獲取接口代理對象MapperProxy:getmapper返回接口的代理對象,包含sqlsession對象

    4)代理對象-》defaultsqlsession->executor->statementhandler->parameterhandler,resultsethandler->typehandler->jdbc

    加載全局配置文件->配置類->MapperFactoryBean(MapperFactoryBean 實現(xiàn)了Spring的FactoryBean接口) 創(chuàng)建UserMapper實例,該實例是通過jdk的動態(tài)代理實現(xiàn)的

    ->MapperProxy(MapperProxyFactory創(chuàng)建)->sqlsessiontemplate->mapperProxy#invoke->mapperMethod#execute->sqlsessiontemplate#select->SqlSessionInterceptor#invoke

    ->sqlsession#invoke->sqlSession#commit->sqlSession#closeSqlsession

    主要構(gòu)件及其相互關(guān)系

    從MyBatis代碼實現(xiàn)的角度來看,MyBatis的主要的核心部件有以下幾個:

    SqlSession:作為MyBatis工作的主要頂層API,表示和數(shù)據(jù)庫交互的會話,完成必要數(shù)據(jù)庫增刪改查功能;

    Executor:MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩存的維護;

    StatementHandler:封裝了JDBC Statement操作,負(fù)責(zé)對JDBC statement 的操作,如設(shè)置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換成List集合。

    ParameterHandler:負(fù)責(zé)對用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所需要的參數(shù);

    ResultSetHandler:負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對象轉(zhuǎn)換成List類型的集合;

    TypeHandler:負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換;

    MappedStatement:MappedStatement維護了一條<select|update|delete|insert>節(jié)點的封裝;

    SqlSource:負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動態(tài)地生成SQL語句,將信息封裝到BoundSql對象中,并返回;

    BoundSql:表示動態(tài)生成的SQL語句以及相應(yīng)的參數(shù)信息;

    Configuration:MyBatis所有的配置信息都維持在Configuration對象之中;

    ---------------------

    https://blog.csdn.net/worn_xiao/article/details/78888640 mybatis的工作原理

    如上圖所示是mybatis的工作原理

    1 首先程序加載全局的配置文件,形成配置文件類

    2 通過Mapper的接口形成一個mapper的代理

    3 通過調(diào)用mapper的代理執(zhí)行對應(yīng)的方法,此時代理中會發(fā)現(xiàn),method.getdeclareClass并不是一個類,而是一個接口

    4 此時通過執(zhí)行mapperMethod,也就是接口的方法,因為mepperproxy是組合了sqlsession的,當(dāng)調(diào)用mapper接口的方法時,它通過方法的映射,最終還是調(diào)用的sqlsession的接口方法。

    5sqlsession本身不帶有接口的實現(xiàn)類,所以此時sqlsession類就組合進來Excutor執(zhí)行器對接口中從配置文件中獲取到的statement對應(yīng)的sql進行執(zhí)行。最終返回結(jié)果映射。

    ---------------------

    各個Executor簡單分析

    SimpleExecutor是最簡單的執(zhí)行器,根據(jù)對應(yīng)的sql直接執(zhí)行即可,不會做一些額外的操作;

    BatchExecutor執(zhí)行器,顧名思義,通過批量操作來優(yōu)化性能。通常需要注意的是批量更新操作,由于內(nèi)部有緩存的實現(xiàn),使用完成后記得調(diào)用flushStatements來清除緩存。

    ReuseExecutor 可重用的執(zhí)行器,重用的對象是Statement,也就是說該執(zhí)行器會緩存同一個sql的Statement,省去Statement的重新創(chuàng)建,優(yōu)化性能。內(nèi)部的實現(xiàn)是通過一個HashMap來維護Statement對象的。由于當(dāng)前Map只在該session中有效,所以使用完成后記得調(diào)用flushStatements來清除Map

    這些就是mybatis的三種執(zhí)行器。你可以通過配置文件的settings里面的元素defaultExecutorType,配置它,默認(rèn)是采用SimpleExecutor如果你在Spring運用它,那么你可以這么配置它:

    如果有二級緩存配置開啟,則創(chuàng)建cachingExecutor

    <bean id="sqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">

    <constructor-arg index="0" ref="sqlSessionFactory" />

    <!--更新采用批量的executor -->

    <constructor-arg index="1" value="BATCH"/>

    </bean>

    ---------------------

    這樣,它便是一個批量的執(zhí)行器。mybatis的三個executor都有一個共同的父類——BaseExecutor。

    https://blog.csdn.net/qq_38409944/article/details/82494187

    總結(jié):

    1、獲取sqlSessionFactory對象:

    根據(jù)配置文件(全局,sql映射)初始化出Configuration對象

    解析文件的每一個信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;

    注意:MappedStatement:代表一個增刪改查的詳細(xì)信息

    2、獲取sqlSession對象

    返回一個DefaultSQlSession對象,包含Executor和Configuration;

    這一步會創(chuàng)建Executor對象;

    Executor(根據(jù)全局配置文件中的defaultExecutorType創(chuàng)建出對應(yīng)的Executor)

    3、獲取接口的代理對象(MapperProxy)

    DefaultSqlSession.getMapper():拿到Mapper接口對應(yīng)的MapperProxy;

    使用MapperProxyFactory創(chuàng)建一個MapperProxy的代理對象

    代理對象里面包含了,DefaultSqlSession(Executor)

    4、執(zhí)行增刪改查方法

    1)調(diào)用DefaultSqlSession的增刪改查(Executor);

    2)會創(chuàng)建一個StatementHandler對象。

    (同時也會創(chuàng)建出ParameterHandler和ResultSetHandler)

    3)調(diào)用StatementHandler預(yù)編譯參數(shù)以及設(shè)置參數(shù)值;

    使用ParameterHandler來給sql設(shè)置參數(shù)

    4)調(diào)用StatementHandler的增刪改查方法;

    5)ResultSetHandler封裝結(jié)果

    SqlSession是過程級,一個方法中建立,方法結(jié)束應(yīng)該關(guān)閉,不是線程安全的

    四、maven

    1、 Maven中的dependency的scope作用域詳解

    1)、test范圍指的是測試范圍有效,在編譯和打包時都不會使用這個依賴

    2)、compile范圍指的是編譯范圍有效,在編譯和打包時都會將依賴存儲進去

    3)、provided依賴:在編譯和測試的過程有效,最后生成war包時不會加入,諸如:servlet-api,因為servlet-api,tomcat等web服務(wù)器已經(jīng)存在了,如果再打包會沖突

    4)、runtime在運行的時候依賴,在編譯的時候不依賴

    2、maven jar包

    https://blog.csdn.net/T2080305/article/details/82144543

    1)maven中jar包的依賴是傳遞依賴

    2)Maven采用了兩種避免沖突的策略: 1、短路優(yōu)先,2、聲明優(yōu)先

    解決方案:1、移除多余的jar包

    五、分布式架構(gòu)原理

    1、漫談分布式架構(gòu)

    https://blog.csdn.net/lj1314ailj/article/details/80765012

    2、分布式架構(gòu)及意義

    https://blog.csdn.net/qq_41555178/article/details/81484473

    https://blog.csdn.net/yp1125/article/details/79125477

    https://www.cnblogs.com/dump/p/8125539.html 大型分布式架構(gòu)詳解

    3、分布式架構(gòu)網(wǎng)絡(luò)通信原理剖析

    RMI(遠(yuǎn)程方法調(diào)用):

    1)客戶端發(fā)起請求,請求轉(zhuǎn)交至RMI客戶端的stub類;

    2)stub類將請求的接口、方法、參數(shù)等信息進行序列化;

    3)基于socket將序列化后的流傳輸至服務(wù)器端;

    4)服務(wù)器端接收到流后轉(zhuǎn)發(fā)至相應(yīng)的skelton類;

    5)skelton類將請求的信息反序列化后調(diào)用實際的處理類;

    6)處理類處理完畢后將結(jié)果返回給skelton類;

    7)Skelton類將結(jié)果序列化,通過socket將流傳送給客戶端的stub;

    8)stub在接收到流后反序列化,將反序列化后的Java Object返回給調(diào)用者。

    XML-RPC:

    1)客戶端發(fā)起請求,按照XML-RPC協(xié)議將請求信息進行填充;

    2)填充完畢后將xml轉(zhuǎn)化為流,通過傳輸協(xié)議進行傳輸;

    3)接收到在接收到流后轉(zhuǎn)換為xml,按照XML-RPC協(xié)議獲取請求的信息并進行處理;

    4)處理完畢后將結(jié)果按照XML-RPC協(xié)議寫入xml中并返回。

    4、通信協(xié)議中的序列化與反序列化

    https://blog.csdn.net/sanyaoxu_2/article/details/79722431

    1)序列化的“鼻祖”

    我知道的第一種序列化協(xié)議就是Java默認(rèn)提供的序列化機制,需要序列化的Java對象只需要實現(xiàn) Serializable / Externalizable 接口并生成序列化ID,這個類就能夠通過 ObjectInput 和 ObjectOutput 序列化和反序列化,若對Java默認(rèn)的序列化協(xié)議不了解,或是遺忘了,請參考:序列化詳解

    但是Java默認(rèn)提供的序列化有很多問題,主要有以下幾個缺點:

    無法跨語言:我認(rèn)為這對于Java序列化的發(fā)展是致命的“失誤”,因為Java序列化后的字節(jié)數(shù)組,其它語言無法進行反序列化。;

    序列化后的碼流太大::相對于目前主流的序列化協(xié)議,Java序列化后的碼流太大;

    序列化的性能差:由于Java序列化采用同步阻塞IO,相對于目前主流的序列化協(xié)議,它的效率非常差。

    2).影響序列化性能的關(guān)鍵因素

    序列化后的碼流大小(網(wǎng)絡(luò)帶寬的占用);

    序列化的性能(CPU資源占用);

    是否支持跨語言(異構(gòu)系統(tǒng)的對接和開發(fā)語言切換)。

    5、dubbo 管理平臺與監(jiān)控平臺的安裝與部署

    https://blog.csdn.net/lindonglian/article/details/78562678

    6、Dubbo集群容錯機制及負(fù)載均衡策略

    https://www.jianshu.com/p/48acb5707da5

    1)這里的Invoker是Provider的一個可調(diào)用Service的抽象,Invoker封裝了Provider地址及Service接口信息。

    2)Directory代表多個Invoker,可以把它看成List<Invoker>,但與List不同的是,它的值可能是動態(tài)變化的,比如注冊中心推送變更。

    3)Cluster將Directory中的多個Invoker偽裝成一個Invoker,對上層透明,偽裝過程包含了容錯邏輯,調(diào)用失敗后,重試另一個。

    4)Router負(fù)責(zé)從多個Invoker中按路由規(guī)則選出子集,比如讀寫分離,應(yīng)用隔離等。

    5)LoadBalance負(fù)責(zé)從多個Invoker中選出具體的一個用于本次調(diào)用,選的過程包含了負(fù)載均衡算法,調(diào)用失敗后,需要重選。

    7、dubbo 源碼解析

    https://blog.csdn.net/u012410733/article/details/77417497 dubbo說得比較到位

    cluster#join->Directory#list->router#route->LoadBalance#select

    proxyfactory#getInvoker#getProxy

    protocol#export#refer

    referenceconfig#init

    serviceconfig#ref實例

    集群容錯主要包括以下幾種模式:

    Failover Cluster:失敗自動切換,當(dāng)出現(xiàn)失敗,重試其它服務(wù)器 。通常用于讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設(shè)置重試次數(shù)(不含第一次)。

    Failfast Cluster:快速失敗,只發(fā)起一次調(diào)用,失敗立即報錯。通常用于非冪等性的寫操作,比如新增記錄。

    Failsafe Cluster:失敗安全,出現(xiàn)異常時,直接忽略。通常用于寫入審計日志等操作。

    Failback Cluster:失敗自動恢復(fù),后臺記錄失敗請求,定時重發(fā)。通常用于消息通知操作。

    Forking Cluster:并行調(diào)用多個服務(wù)器,只要一個成功即返回。通常用于實時性要求較高的讀操作,但需要浪費更多服務(wù)資源。可通過 forks=”2” 來設(shè)置最大并行數(shù)。

    Broadcast Cluster:廣播調(diào)用所有提供者,逐個調(diào)用,任意一臺報錯則報錯 2。通常用于通知所有提供者更新緩存或日志等本地資源信息。

    1)服務(wù)提供者暴露一個服務(wù)的詳細(xì)過程

    具體服務(wù)到Invoker的轉(zhuǎn)化:ServiceConfig類#對外提供服務(wù)的實際類引用ref->ProxyFactory#getInvoker->AbstractProxyInvoker

    Invoker轉(zhuǎn)換到Exporter: DubboProtocol#export->它主要是打開socket偵聽服務(wù),并接收客戶端發(fā)來的各種請求,通訊細(xì)節(jié)由Dubbo自己實現(xiàn)

    serviceconfig#ref實例-》proxyfactory#getInvoker(AbstractProxyInvoker實例,即invoker)->protocol#export

    invoker:interface的class實例、實例類對象、url、當(dāng)前接口的代理對象wrapper實例

    2)服務(wù)消費者消費一個服務(wù)的詳細(xì)過程

    ReferenceConfig#init->DubboProtocol#refer[獲取invoker]->ProxyFactory#getProxy->ref

    3)dubbo源碼分析 之 架構(gòu)原理探索

    啟動Zookeeper服務(wù):用于dubbo的注冊中心。

    啟動Zookeeper Inspector:它是Zookeeper服務(wù)信息查看工具。

    4)dubbo源碼分析 之 內(nèi)核SPI實現(xiàn)

    為什么不使用JDK SPI

    在dubbo中它實現(xiàn)了一套自己的SPI機制。JDK標(biāo)準(zhǔn)的SPI會一次性實例化擴展點所有實現(xiàn),如果有擴展實現(xiàn)初始化很耗時,但如果沒用上也加載,會很浪費資源.

    增加了對擴展點IoC和AOP的支持,一個擴展點可以直接setter注入其它擴展點。

    dubbo的領(lǐng)域?qū)ο?/p>

    在dubbo當(dāng)中它的核心領(lǐng)域?qū)ο缶褪荌nvoker,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉(zhuǎn)換成它。

    在dubbo中通過Protocol這個來管理Invoker的生命周期,包括服務(wù)的暴露與引用都是通過它來完成的。而在進行服務(wù)調(diào)用的時候通過Invocation來保存調(diào)用過程中的變量:包括方法名,參數(shù)等。所以在整個dubbo調(diào)用過程當(dāng)中:

    Invoker 是實體域,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉(zhuǎn)換成它,它代表一個可執(zhí)行體,可向它發(fā)起 invoke 調(diào)用,它有可能是一個本地的實現(xiàn),也可能是一個遠(yuǎn)程的實現(xiàn),也可能一個集群實現(xiàn)。

    Protocol 是服務(wù)域,它是 Invoker 暴露和引用的主功能入口,它負(fù)責(zé) Invoker 的生命周期管理。

    Invocation 是會話域,它持有調(diào)用過程中的變量,比如方法名,參數(shù)等。

    dubbo 遠(yuǎn)程服務(wù)(Provider)暴露最終其實就是創(chuàng)建一個 Netty Serve 服務(wù),然后在 dubbo 在服務(wù)引用的時候創(chuàng)建一個 Netty Client 服務(wù)。其實 dubbo 遠(yuǎn)程通信的原理其實就是基于 Socket 的遠(yuǎn)程通信。下面我們來看一下 dubbo 是如何創(chuàng)建一個 Netty 服務(wù)的,下面就是它創(chuàng)建的序列圖:

    它通過傳入 URL 與 requestHandler來創(chuàng)建一個 ExchangeServer,通過Netty 基于 NIO的形式通過自定義Channel來接收服務(wù)引用方傳遞過來的信息,以及發(fā)送調(diào)用遠(yuǎn)程服務(wù)的本地方法后的數(shù)據(jù)給服務(wù)調(diào)用者。URL 里面主要包含 IP 地址 與 端口信息用于創(chuàng)建 Socket 連接,而 requestHandler是一個 ExchangeHandler 通過自定義協(xié)議來處理 dubbo 的遠(yuǎn)程通信。

    https://www.cnblogs.com/lengfo/p/4293399.html dubbo各協(xié)議性能比較

    性能分析

    測試過程中盡管考慮了非常多的影響因素,但仍然有很多局限性,包括連接數(shù)限制、并發(fā)量、線程池策略、Cache、IO、硬件性能瓶頸等等因素,而且各自的適用場景不同,測試結(jié)果僅供參考。

    從單線程測試結(jié)果可以看出,dubbo協(xié)議采用NIO復(fù)用單一長連接更適合滿足高并發(fā)小數(shù)據(jù)量的rpc調(diào)用,而在大數(shù)據(jù)量下的傳輸性能并不好,建議使用rmi協(xié)議,多線程測試中dubbo協(xié)議對小數(shù)據(jù)量的rpc調(diào)用同樣保持優(yōu)勢,在大數(shù)據(jù)量的傳輸中由于長連接的原因?qū)Ρ萺mi協(xié)議傳輸耗時差距并不明顯,這點同樣驗證了上述觀點。關(guān)于數(shù)據(jù)的序列化方式選擇需要考慮序列化和反序列化的效率問題,傳輸內(nèi)容的大小,以及格式的兼容性約束,其中hessian2作為duobb協(xié)議下的默認(rèn)序列化方式,推薦使用。

    8、分布式消息通信activemq/kafka/rabbitmq/rocketmq

    能夠保證嚴(yán)格的消息順序

    提供豐富的消息拉取模式

    高效的訂閱者水平擴展能力

    實時的消息訂閱機制

    億級消息堆積能力

    Metaq3.0 版本改名,產(chǎn)品名稱改為RocketMQ

    RocketMQ是阿里review kafka的java版,如果 消息性能要求高 用rocketmq與kafka可以更優(yōu)

    總結(jié):

    1、性能小 量小 用什么都沒有關(guān)系,性質(zhì)是一樣的,如果 消息性能要求高 用rocketmq與kafka可以更優(yōu),rocketmq與kafka 比較就看技術(shù)選型了,各有利弊,看業(yè)務(wù)需要。

    2、activemq rabbitmq 與 kafka、rocketmq有很大的區(qū)別就是前2個只支持主從模式,后2個是分布式消息系統(tǒng),支持分布式。

    3、持久化消息比較: zeroMq不支持,activeMq和rabbitMq都支持。

    持久化消息主要是指:MQ down或者MQ所在的服務(wù)器down了,消息不會丟失的機制。

    4、其中包括持久化消息和瞬時消息的測試。注意這篇文章里面提到的MQ,都是采用默認(rèn)配置的,并無調(diào)優(yōu)。

    ZeroMq 最好,RabbitMq 次之, ActiveMq 最差。這個結(jié)論來自于以下這兩篇文章。

    http://blog.x-aeon.com/2013/04/10/a-quick-message-queue-benchmark-activemq-rabbitmq-hornetq-qpid-apollo/

    其中包括持久化消息和瞬時消息的測試。注意這篇文章里面提到的MQ,都是采用默認(rèn)配置的,并無調(diào)優(yōu)。

    http://www.cnblogs.com/amityat/archive/2011/08/31/2160293.html

    顯示的是發(fā)送和接受的每秒鐘的消息數(shù)。整個過程共產(chǎn)生1百萬條1K的消息。測試的執(zhí)行是在一個Windows Vista上進行的。

    5、技術(shù)點:可靠性、靈活的路由、集群、事務(wù)、高可用的隊列、消息排序、問題追蹤、可視化管理工具、插件系統(tǒng)、社區(qū)

    RabbitMq最好,ActiveMq次之,ZeroMq最差。當(dāng)然ZeroMq也可以做到,不過自己必須手動寫代碼實現(xiàn),代碼量不小。尤其是可靠性中的:持久性、投遞確認(rèn)、發(fā)布者證實和高可用性。

    所以在可靠性和可用性上,RabbitMQ是首選,雖然ActiveMQ也具備,但是它性能不及RabbitMQ。

    6、高并發(fā)

    從實現(xiàn)語言來看,RabbitMQ最高,原因是它的實現(xiàn)語言是天生具備高并發(fā)高可用的erlang語言。

    小結(jié):

    按照目前網(wǎng)絡(luò)上的資料,RabbitMQ、activeM、zeroMQ三者中,綜合來看,RabbitMQ是首選。下面提供一篇文章,是淘寶使用RabbitMQ的心得,可以參看一些業(yè)務(wù)場景。

    http://www.docin.com/p-462677246.html

    7、kafka和RabbitMQ的比較

    關(guān)于這兩種MQ的比較,網(wǎng)上的資料并不多,最權(quán)威的的是kafka的提交者寫一篇文章。http://www.quora.com/What-are-the-differences-between-Apache-Kafka-and-RabbitMQ

    里面提到的要點:

    1)、 RabbitMq比kafka成熟,在可用性上,穩(wěn)定性上,可靠性上,RabbitMq超過kafka

    2)、 Kafka設(shè)計的初衷就是處理日志的,可以看做是一個日志系統(tǒng),針對性很強,所以它并沒有具備一個成熟MQ應(yīng)該具備的特性

    3)、 Kafka的性能(吞吐量、tps)比RabbitMq要強,這篇文章的作者認(rèn)為,兩者在這方面沒有可比性。

    這里在附上兩篇文章,也是關(guān)于kafka和RabbitMq之間的比較的:

    1)、http://www.mrhaoting.com/?p=139

    2)、http://www.liaoqiqi.com/post/227

    總結(jié):

    兩者對比后,我仍然是選擇RabbitMq,性能其實是很強勁的,同時具備了一個成熟的MQ應(yīng)該具有的特性,我們無需重新發(fā)明輪子。

    8、redis api

    https://blog.csdn.net/zhangguanghui002/article/details/78770071

    public static void test(){
     ShardedJedis jedis = RedisManager.getJedis();
     String key = "11";
     String value = "11";
     //----------value----------------------
     boolean ex = jedis.exists(key);//該key是否存在
     long count = jedis.del(key);//刪除key,返回刪除key的數(shù)量
     //返回值:
    // none (key不存在)
    // string (字符串)
    // list (列表)
    // set (集合)
    // zset (有序集)
    // hash (哈希表)
     String type = jedis.type(key);//返回key的類型
     jedis.hkeys(key);
     jedis.srandmember(key);
    // 當(dāng) key 不存在時,返回 -2 。
    // 當(dāng) key 存在但沒有設(shè)置剩余生存時間時,返回 -1 。
    // 否則,以秒為單位,返回 key 的剩余生存時間。
     jedis.ttl(key);//獲取剩余生存時間 返回值時間
    // 設(shè)置成功返回 1 。
    // 當(dāng) key 不存在或者不能為 key 設(shè)置生存時間時(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的生存時間),返回 0 
     jedis.expire(key, 1);//設(shè)置過期時間為1秒
    // 當(dāng)生存時間移除成功時,返回 1 .
    // 如果 key 不存在或 key 沒有設(shè)置生存時間,返回 0
     jedis.persist(key);
     //----------String----------------------
    // 在 Redis 2.6.12 版本以前, SET 命令總是返回 OK 。
    // 從 Redis 2.6.12 版本開始, SET 在設(shè)置操作成功完成時,才返回 OK 。
    // 如果設(shè)置了 NX 或者 XX ,但因為條件沒達到而造成設(shè)置操作未執(zhí)行,那么命令返回空批量回復(fù)(NULL Bulk Reply)。
     jedis.set(key, value);
     jedis.setnx(key, value);//重復(fù)不插入
     jedis.setex(key, 1, value);//增加數(shù)據(jù)項并設(shè)置有效時間
     jedis.del(key);
     jedis.get(key);
     jedis.append(key, value);//在key對應(yīng)的value后面擴展字符串value
    // 將給定 key 的值設(shè)為 value ,并返回 key 的舊值(old value)。
     jedis.getSet(key, value);//獲取key對應(yīng)的value并更新value
     jedis.getrange(key, 3, 6);//截取得出的子字符串。
     jedis.incr(key);//將key對應(yīng)的值+1
     jedis.incrBy(key, 2);//將key對應(yīng)的值+2
     jedis.decr(key);//將key對應(yīng)的值-1
     jedis.decrBy(key, 2);//將key對應(yīng)的值-2
     //----------list----------------------
     jedis.lpush(key, "1","a","c");//返回值 執(zhí)行 LPUSH 命令后,列表的長度
     jedis.lpush(key, value);//左插入
     jedis.lrange(key, 0, 2);//獲取list對應(yīng)區(qū)間0到2的元素
    // 根據(jù)參數(shù) count 的值,移除列表中與參數(shù) value 相等的元素。
    //
    // count 的值可以是以下幾種:
    //
    // count > 0 : 從表頭開始向表尾搜索,移除與 value 相等的元素,數(shù)量為 count 。
    // count < 0 : 從表尾開始向表頭搜索,移除與 value 相等的元素,數(shù)量為 count 的絕對值。
    // count = 0 : 移除表中所有與 value 相等的值
     jedis.lrem(key, count, value);
     //刪除list對應(yīng)區(qū)間之外的元素
     jedis.ltrim(key, 1, 2);
     jedis.lpop(key);//key對應(yīng)list左出棧一個元素
     jedis.rpush(key, value);//右插入
     jedis.rpop(key);//key對應(yīng)list右出棧一個元素
     jedis.lset(key, 1, value);//修改key對應(yīng)list指定下標(biāo)index的值為value
     jedis.llen(key);//獲取list的長度
     jedis.lindex(key, 1);//獲取list下標(biāo)index的值
     jedis.sort(key);//list里面的值從小到大排序
     //----------set----------------------
     jedis.sadd(key, "1","2");//將一個或多個 member 元素加入到集合 key 當(dāng)中,已經(jīng)存在于集合的 member 元素將被忽略。
     jedis.smembers(key);//獲取該set對應(yīng)的所有元素
     jedis.srem(key, value);//刪除一個值為value的元素
     jedis.srem(key, "1","2","3");//刪除一個值為value1,value2的元素
     jedis.spop(key);//隨機移除一個元素
     jedis.scard(key);//獲取set中元素個數(shù)
     //----------hash----------------------
     jedis.hmset(key, new HashMap<String, String>());//添加一個hash
     jedis.hset(key, "12", value);//往hash插入一個新域
     jedis.hgetAll(key);//獲取hash所有元素
     jedis.hkeys(key);//獲取hash所有元素的key
     jedis.hvals(key);//獲取hash所有元素的value
     jedis.hincrBy(key, "12", 1);//把hash中key對應(yīng)元素value+1
     jedis.hdel(key, "12",value);//從hash中刪除一個域多個元素
     jedis.hlen(key);//獲取hash中元素個數(shù)
     jedis.hexists(key, "12");//判斷hash中是否有12對應(yīng)的元素
     jedis.hmget(key, "12","13");//獲取hash中一個或多個元素value
     jedis.hget(key, "12");//獲取hash中一個元素value
     //-----------zsort------------------
     jedis.zadd(key, new HashMap<String, Double>());//添加一個zset
     jedis.zrange(key, 1, 3);//獲取zset中index,1-3中的元素value
     jedis.zrangeWithScores(key, 1, 3);//獲取zset中index,1-3中的元素
     jedis.zscore(key, value);//獲取value為value的score值
     jedis.zrank(key, value);//獲取value為value的score排名
     jedis.zrem(key, value);//刪除value值為value的元素
     jedis.zcard(key);//獲取元素個數(shù)
     jedis.zincrby(key, 1, value);//將zset中val為value的score+=1
     //-----------排序操作------------------
     SortingParams s = new SortingParams();
     jedis.sort(key, s.alpha());//按首字母a-z排序
     jedis.sort(key, s.asc());//按數(shù)字升序排
     jedis.sort(key, s.desc());//按數(shù)字降序排
     }
    

    9、redis主從復(fù)制

    全量同步

    Redis全量復(fù)制一般發(fā)生在Slave初始化階段,這時Slave需要將Master上的所有數(shù)據(jù)都復(fù)制一份

    增量同步

    Redis增量復(fù)制是指Slave初始化后開始正常工作時主服務(wù)器發(fā)生的寫操作同步到從服務(wù)器的過程。

    增量復(fù)制的過程主要是主服務(wù)器每執(zhí)行一個寫命令就會向從服務(wù)器發(fā)送相同的寫命令,從服務(wù)器接收并執(zhí)行收到的寫命令。

    Redis主從同步策略

    主從剛剛連接的時候,進行全量同步;全同步結(jié)束后,進行增量同步。當(dāng)然,如果有需要,slave 在任何時候都可以發(fā)起全量同步。redis 策略是,無論如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步。

    無磁盤復(fù)制

    通常來講,一個完全重新同步需要在磁盤上創(chuàng)建一個RDB文件,然后加載這個文件以便為從服務(wù)器發(fā)送數(shù)據(jù)。

    如果使用比較低速的磁盤,這種操作會給主服務(wù)器帶來較大的壓力。Redis從2.8.18版本開始嘗試支持無磁盤的復(fù)制。

    使用這種設(shè)置時,子進程直接將RDB通過網(wǎng)絡(luò)發(fā)送給從服務(wù)器,不使用磁盤作為中間存儲。

    1)RDB方式(默認(rèn))

    RDB方式的持久化是通過快照(snapshotting)完成的,當(dāng)符合一定條件時Redis會自動將內(nèi)存中的所有數(shù)據(jù)進行快照并存儲在硬盤上。進行快照的條件可以由用戶在配置文件中自定義,由兩個參數(shù)構(gòu)成:時間和改動的鍵的個數(shù)。當(dāng)在指定的時間內(nèi)被更改的鍵的個數(shù)大于指定的數(shù)值時就會進行快照。RDB是redis默認(rèn)采用的持久化方式,在配置文件中已經(jīng)預(yù)置了3個條件:

    save 900 1 #900秒內(nèi)有至少1個鍵被更改則進行快照

    save 300 10 #300秒內(nèi)有至少10個鍵被更改則進行快照

    save 60 10000 #60秒內(nèi)有至少10000個鍵被更改則進行快照

    可以存在多個條件,條件之間是"或"的關(guān)系,只要滿足其中一個條件,就會進行快照。 如果想要禁用自動快照,只需要將所有的save參數(shù)刪除即可。

    Redis默認(rèn)會將快照文件存儲在當(dāng)前目錄(可CONFIG GET dir來查看)的dump.rdb文件中,可以通過配置dir和dbfilename兩個參數(shù)分別指定快照文件的存儲路徑和文件名。

    Redis實現(xiàn)快照的過程

    - Redis使用fork函數(shù)復(fù)制一份當(dāng)前進程(父進程)的副本(子進程);

    - 父進程繼續(xù)接收并處理客戶端發(fā)來的命令,而子進程開始將內(nèi)存中的數(shù)據(jù)寫入硬盤中的臨時文件;

    - 當(dāng)子進程寫入完所有數(shù)據(jù)后會用該臨時文件替換舊的RDB文件,至此一次快照操作完成。

    - 在執(zhí)行fork的時候操作系統(tǒng)(類Unix操作系統(tǒng))會使用寫時復(fù)制(copy-on-write)策略,即fork函數(shù)發(fā)生的一刻父子進程共享同一內(nèi)存數(shù)據(jù),當(dāng)父進程要更改其中某片數(shù)據(jù)時(如執(zhí)行一個寫命令 ),操作系統(tǒng)會將該片數(shù)據(jù)復(fù)制一份以保證子進程的數(shù)據(jù)不受影響,所以新的RDB文件存儲的是執(zhí)行fork一刻的內(nèi)存數(shù)據(jù)。

    Redis在進行快照的過程中不會修改RDB文件,只有快照結(jié)束后才會將舊的文件替換成新的,也就是說任何時候RDB文件都是完整的。這使得我們可以通過定時備份RDB文件來實 現(xiàn)Redis數(shù)據(jù)庫備份。RDB文件是經(jīng)過壓縮(可以配置rdbcompression參數(shù)以禁用壓縮節(jié)省CPU占用)的二進制格式,所以占用的空間會小于內(nèi)存中的數(shù)據(jù)大小,更加利于傳輸。

    除了自動快照,還可以手動發(fā)送SAVE或BGSAVE命令讓Redis執(zhí)行快照,兩個命令的區(qū)別在于,前者是由主進程進行快照操作,會阻塞住其他請求,后者會通過fork子進程進行快照操作。 Redis啟動后會讀取RDB快照文件,將數(shù)據(jù)從硬盤載入到內(nèi)存。根據(jù)數(shù)據(jù)量大小與結(jié)構(gòu)和服務(wù)器性能不同,這個時間也不同。通常將一個記錄一千萬個字符串類型鍵、大小為1GB的快照文件載入到內(nèi) 存中需要花費20~30秒鐘。 通過RDB方式實現(xiàn)持久化,一旦Redis異常退出,就會丟失最后一次快照以后更改的所有數(shù)據(jù)。這就需要開發(fā)者根據(jù)具體的應(yīng)用場合,通過組合設(shè)置自動快照條件的方式來將可能發(fā)生的數(shù)據(jù)損失控制在能夠接受的范圍。如果數(shù)據(jù)很重要以至于無法承受任何損失,則可以考慮使用AOF方式進行持久化。

    2)AOF方式

    默認(rèn)情況下Redis沒有開啟AOF(append only file)方式的持久化,可以在redis.conf中通過appendonly參數(shù)開啟:

    appendonly yes

    在啟動時Redis會逐個執(zhí)行AOF文件中的命令來將硬盤中的數(shù)據(jù)載入到內(nèi)存中,載入的速度相較RDB會慢一些

    開啟AOF持久化后每執(zhí)行一條會更改Redis中的數(shù)據(jù)的命令,Redis就會將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通過dir參數(shù)設(shè)置的,默認(rèn)的文件名是appendonly.aof,可以通過appendfilename參數(shù)修改:

    appendfilename appendonly.aof

    配置redis自動重寫AOF文件的條件

    auto-aof-rewrite-percentage 100 # 當(dāng)目前的AOF文件大小超過上一次重寫時的AOF文件大小的百分之多少時會再次進行重寫,如果之前沒有重寫過,則以啟動時的AOF文件大小為依據(jù)

    auto-aof-rewrite-min-size 64mb # 允許重寫的最小AOF文件大小

    配置寫入AOF文件后,要求系統(tǒng)刷新硬盤緩存的機制

    # appendfsync always # 每次執(zhí)行寫入都會執(zhí)行同步,最安全也最慢

    appendfsync everysec # 每秒執(zhí)行一次同步操作

    # appendfsync no # 不主動進行同步操作,而是完全交由操作系統(tǒng)來做(即每30秒一次),最快也最不安全

    Redis允許同時開啟AOF和RDB,既保證了數(shù)據(jù)安全又使得進行備份等操作十分容易。此時重新啟動Redis后Redis會使用AOF文件來恢復(fù)數(shù)據(jù),因為AOF方式的持久化可能丟失的數(shù)據(jù)更少

    10、nginx反向代理及負(fù)載均衡服務(wù)配置

    https://www.cnblogs.com/Miss-mickey/p/6734831.html

    11、netty 高性能

    https://www.cnblogs.com/Irving/p/5709130.html

    Netty是一個高性能、異步事件驅(qū)動的NIO框架,它提供了對TCP、UDP和文件傳輸?shù)闹С郑鳛橐粋€異步NIO框架,Netty的所有IO操作都是異步非阻塞的,通過Future-Listener機制,用戶可以方便的主動獲取或者通過通知機制獲得IO操作結(jié)果。

    Netty架構(gòu)分析

    Netty 采用了比較典型的三層網(wǎng)絡(luò)架構(gòu)進行設(shè)計,邏輯架構(gòu)圖如下所示:

    第一層:Reactor 通信調(diào)度層,它由一系列輔助類完成,包括 Reactor 線程 NioEventLoop 以及其父類、NioSocketChannel/NioServerSocketChannel 以及其父 類、ByteBuffer 以及由其衍生出來的各種 Buffer、Unsafe 以及其衍生出的各種內(nèi) 部類等。該層的主要職責(zé)就是監(jiān)聽網(wǎng)絡(luò)的讀寫和連接操作,負(fù)責(zé)將網(wǎng)絡(luò)層的數(shù)據(jù) 讀取到內(nèi)存緩沖區(qū)中,然后觸發(fā)各種網(wǎng)絡(luò)事件,例如連接創(chuàng)建、連接激活、讀事 件、寫事件等等,將這些事件觸發(fā)到 PipeLine 中,由 PipeLine 充當(dāng)?shù)穆氊?zé)鏈來 進行后續(xù)的處理。

    第二層:職責(zé)鏈 PipeLine,它負(fù)責(zé)事件在職責(zé)鏈中的有序傳播,同時負(fù)責(zé)動態(tài)的 編排職責(zé)鏈,職責(zé)鏈可以選擇監(jiān)聽和處理自己關(guān)心的事件,它可以攔截處理和向 后/向前傳播事件,不同的應(yīng)用的 Handler 節(jié)點的功能也不同,通常情況下,往往 會開發(fā)編解碼 Hanlder 用于消息的編解碼,它可以將外部的協(xié)議消息轉(zhuǎn)換成內(nèi)部 的 POJO 對象,這樣上層業(yè)務(wù)側(cè)只需要關(guān)心處理業(yè)務(wù)邏輯即可,不需要感知底層 的協(xié)議差異和線程模型差異,實現(xiàn)了架構(gòu)層面的分層隔離。

    第三層:業(yè)務(wù)邏輯處理層,可以分為兩類:

    純粹的業(yè)務(wù)邏輯 處理,例如訂單處理。

    應(yīng)用層協(xié)議管理,例如HTTP協(xié)議、FTP協(xié)議等。

    接下來,我從影響通信性能的三個方面(I/O模型、線程調(diào)度模型、序列化方式)來談?wù)凬etty的架構(gòu)。

    12、分布式全局id

    1)Redis生成ID

    當(dāng)使用數(shù)據(jù)庫來生成ID性能不夠要求的時候,我們可以嘗試使用Redis來生成ID。這主要依賴于Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現(xiàn)

    2)SnowFlake算法生成id的結(jié)果是一個64bit大小的整數(shù),它的結(jié)構(gòu)如下圖:

    1位,不用。二進制中最高位為1的都是負(fù)數(shù),但是我們生成的id一般都使用整數(shù),所以這個最高位固定是0

    41位,用來記錄時間戳(毫秒)。

    41位可以表示個數(shù)字,

    如果只用來表示正整數(shù)(計算機中正數(shù)包含0),可以表示的數(shù)值范圍是:0 至 ,減1是因為可表示的數(shù)值范圍是從0開始算的,而不是1。

    也就是說41位可以表示個毫秒的值,轉(zhuǎn)化成單位年則是年

    10位,用來記錄工作機器id。

    可以部署在個節(jié)點,包括5位datacenterId和5位workerId

    5位(bit)可以表示的最大正整數(shù)是,即可以用0、1、2、3、....31這32個數(shù)字,來表示不同的datecenterId或workerId

    12位,序列號,用來記錄同毫秒內(nèi)產(chǎn)生的不同id。

    12位(bit)可以表示的最大正整數(shù)是,即可以用0、1、2、3、....4094這4095個數(shù)字,來表示同一機器同一時間截(毫秒)內(nèi)產(chǎn)生的4095個ID序號

    由于在Java中64bit的整數(shù)是long類型,所以在Java中SnowFlake算法生成的id就是long來存儲的。

    SnowFlake可以保證:

    所有生成的id按時間趨勢遞增

    整個分布式系統(tǒng)內(nèi)不會產(chǎn)生重復(fù)id(因為有datacenterId和workerId來做區(qū)分)

    13、session跨域共享 單點登錄

    session cookie 存儲的是JSESSIONID

    session存儲在服務(wù)器端 cookie存儲在瀏覽器端

    服務(wù)器端(Tomcat) 會生成一個唯一的sessionId號存儲在cookie中 叫 jessionid

    在服務(wù)器端(tomcat)中存儲serssion 使用concurrentMap (ConcurrentMap key JSESSIONID values session)

    瀏覽器端下次請求服務(wù)器端是將jsessionId帶過來 找到對應(yīng)的session 獲取session中存儲的信息(用戶信息)

    14、分布式事務(wù)

    https://www.cnblogs.com/lfs2640666960/p/9476241.html

    對于TCC的解釋:

    Try階段:嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性),預(yù)留必須業(yè)務(wù)資源(準(zhǔn)隔離性)

    Confirm階段:確認(rèn)執(zhí)行真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用Try階段預(yù)留的業(yè)務(wù)資源,Confirm操作滿足冪等性。要求具備冪等設(shè)計,Confirm失敗后需要進行重試。

    Cancel階段:取消執(zhí)行,釋放Try階段預(yù)留的業(yè)務(wù)資源

    Cancel操作滿足冪等性Cancel階段的異常和Confirm階段異常處理方案基本上一致。

    舉個簡單的例子如果你用100元買了一瓶水,

    Try階段:你需要向你的錢包檢查是否夠100元并鎖住這100元,水也是一樣的。

    如果有一個失敗,則進行cancel(釋放這100元和這一瓶水),如果cancel失敗不論什么失敗都進行重試cancel,所以需要保持冪等。

    如果都成功,則進行confirm,確認(rèn)這100元扣,和這一瓶水被賣,如果confirm失敗無論什么失敗則重試(會依靠活動日志進行重試)

    對于TCC來說適合一些:

    強隔離性,嚴(yán)格一致性要求的活動業(yè)務(wù)。

    執(zhí)行時間較短的業(yè)務(wù)

    方案2 – RocketMQ 事務(wù)消息

    為了能解決該問題,同時又不和業(yè)務(wù)耦合,RocketMQ提出了“事務(wù)消息”的概念。

    具體來說,就是把消息的發(fā)送分成了2個階段:Prepare階段和確認(rèn)階段。

    具體來說,上面的2個步驟,被分解成3個步驟:

    (1) 發(fā)送Prepared消息

    (2) update DB

    (3) 根據(jù)update DB結(jié)果成功或失敗,Confirm或者取消Prepared消息。

    可能有人會問了,前2步執(zhí)行成功了,最后1步失敗了怎么辦?這里就涉及到了RocketMQ的關(guān)鍵點:RocketMQ會定期(默認(rèn)是1分鐘)掃描所有的Prepared消息,詢問發(fā)送方,到底是要確認(rèn)這條消息發(fā)出去?還是取消此條消息?

    可以考慮加定時任務(wù)自動掃描,自動努力保持一致性。

    15、服務(wù)降級、限流

    https://blog.csdn.net/vtopqx/article/details/79494599 1、mock 2、管理界面手動配置

    https://blog.csdn.net/luckykapok918/article/details/72381992

    查看dubbo的官方文檔,可以發(fā)現(xiàn)有個mock的配置,mock只在出現(xiàn)非業(yè)務(wù)異常(比如超時,網(wǎng)絡(luò)異常等)時執(zhí)行。mock的配置支持兩種,一種為boolean值,默認(rèn)的為false。如果配置為true,則缺省使用mock類名,即類名+Mock后綴;另外一種則是配置"return null",可以很簡單的忽略掉異常。

    dubbo開發(fā)中,通常是微服務(wù)架構(gòu),那么在使用過程中可能會遇到多種問題:

    1)多個服務(wù)之間可能由于服務(wù)沒有啟動或者網(wǎng)絡(luò)不通,調(diào)用中會出現(xiàn)遠(yuǎn)程調(diào)用失敗;

    2) 服務(wù)請求過大,需要停止部分服務(wù)以保證核心業(yè)務(wù)的正常運行;

    以上兩個問題可以使用Dubbo的服務(wù)降級來實現(xiàn);

    即:在服務(wù)宕掉或者并發(fā)數(shù)太高導(dǎo)致的RpcException異常時,進行友好的處理或者提示,而不是內(nèi)部報錯導(dǎo)致系統(tǒng)不可用。

    1)第一種方式:在消費者端屏蔽

    2) <dubbo:reference id="userService" check="false" interface="com.cwh.service.UserService" timeout="3000" mock="return null"/>

    3) <dubbo:reference id="userService" check="false" interface="com.cwh.service.UserService" timeout="3000" mock="true"/>

    打開soa-user-api項目,在com.cwh.service下也就是同UserService統(tǒng)計目錄下新建一個UserServiceMock,注意這里名字一點要是該接口名+Mock:

    package com.cwh.service;
    import java.util.ArrayList;
    import java.util.List;
    import com.cwh.model.Article;
    import com.cwh.model.User;
    public class UserServiceMock implements UserService{
     public List<Article>getUserArticles(int uid){
     return null;
     }
     public List<User>getUser(String name){
     //throw new RuntimeException("服務(wù)降級-----");
     User user = new User();
     user.setUserName("服務(wù)降級啦");
     user.setUserAge("500");
     List<User> list = new ArrayList<User>();
     list.add(user);
     return list;
     }
    }
    

    16、分布式任務(wù)調(diào)度框架

    https://blog.csdn.net/u012379844/article/details/82716146 總結(jié)各分布式任務(wù)調(diào)度框架

    下載Elastic-job-lite源碼,使用maven進行打包。在elastic-job-lite/elastic-job-lite-console/target/elastic-job-lite-console-3.0.0.M1-SNAPSHOT/中,然后解壓,會有start.bat和start.sh兩個腳本,啟動。

    六、微服務(wù)

    1、微服務(wù)框架集成mybatis實現(xiàn)多數(shù)據(jù)源路由實戰(zhàn)

    https://www.2cto.com/kf/201805/746354.html

    https://blog.csdn.net/xgx120413/article/details/80743959

    2、如何設(shè)計微服務(wù)及其設(shè)計原則

    https://blog.csdn.net/tiandiwuya/article/details/78543336

    我們總結(jié)了四個方面的優(yōu)點,分別如下:

    是每個微服務(wù)組件都是簡單靈活的,能夠獨立部署。不再像以前一樣,應(yīng)用需要一個龐大的應(yīng)用服務(wù)器來支撐。

    可以由一個小團隊負(fù)責(zé)更專注專業(yè),相應(yīng)的也就更高效可靠。

    微服務(wù)之間是松耦合的,微服務(wù)內(nèi)部是高內(nèi)聚的,每個微服務(wù)很容易按需擴展。

    微服務(wù)架構(gòu)與語言工具無關(guān),自由選擇合適的語言和工具,高效的完成業(yè)務(wù)目標(biāo)即可。

    我們總結(jié)了四個原則推薦給大家:

    AKF拆分原則

    前后端分離

    無狀態(tài)服務(wù)

    Restful通信風(fēng)格

    對于無狀態(tài)服務(wù),首先說一下什么是狀態(tài):如果一個數(shù)據(jù)需要被多個服務(wù)共享,才能完成一筆交易,那么這個數(shù)據(jù)被稱為狀態(tài)。進而依賴這個“狀態(tài)”數(shù)據(jù)的服務(wù)被稱為有狀態(tài)服務(wù),反之稱為無狀態(tài)服務(wù)。

    那么這個無狀態(tài)服務(wù)原則并不是說在微服務(wù)架構(gòu)里就不允許存在狀態(tài),表達的真實意思是要把有狀態(tài)的業(yè)務(wù)服務(wù)改變?yōu)闊o狀態(tài)的計算類服務(wù),那么狀態(tài)數(shù)據(jù)也就相應(yīng)的遷移到對應(yīng)的“有狀態(tài)數(shù)據(jù)服務(wù)”中。

    場景說明:例如我們以前在本地內(nèi)存中建立的數(shù)據(jù)緩存、Session緩存,到現(xiàn)在的微服務(wù)架構(gòu)中就應(yīng)該把這些數(shù)據(jù)遷移到分布式緩存中存儲,讓業(yè)務(wù)服務(wù)變成一個無狀態(tài)的計算節(jié)點。遷移后,就可以做到按需動態(tài)伸縮,微服務(wù)應(yīng)用在運行時動態(tài)增刪節(jié)點,就不再需要考慮緩存數(shù)據(jù)如何同步的問題。

    作為一個原則來講本來應(yīng)該是個“無狀態(tài)通信原則”,在這里我們直接推薦一個實踐優(yōu)選的Restful 通信風(fēng)格 ,因為他有很多好處:

    無狀態(tài)協(xié)議HTTP,具備先天優(yōu)勢,擴展能力很強。例如需要安全加密是,有現(xiàn)成的成熟方案HTTPS可用。

    JSON 報文序列化,輕量簡單,人與機器均可讀,學(xué)習(xí)成本低,搜索引擎友好。

    語言無關(guān),各大熱門語言都提供成熟的Restful API框架,相對其他的一些RPC框架生態(tài)更完善。

    當(dāng)然在有些特殊業(yè)務(wù)場景下,也需要采用其他的RPC框架,如thrift、avro-rpc、grpc。但絕大多數(shù)情況下Restful就足夠用了。

    七、性能優(yōu)化

    1、性能指標(biāo)

    https://www.cnblogs.com/wangmo/p/8074879.html

    一.系統(tǒng)吞度量要素:

    一個系統(tǒng)的吞度量(承壓能力)與request對CPU的消耗、外部接口、IO等等緊密關(guān)聯(lián)。單個reqeust 對CPU消耗越高,外部系統(tǒng)接口、IO影響速度越慢,系統(tǒng)吞吐能力越低,反之越高。

    系統(tǒng)吞吐量幾個重要參數(shù):QPS(TPS)、并發(fā)數(shù)、響應(yīng)時間

    QPS(TPS):每秒鐘request/事務(wù) 數(shù)量

    并發(fā)數(shù): 系統(tǒng)同時處理的request/事務(wù)數(shù)

    響應(yīng)時間: 一般取平均響應(yīng)時間

    (很多人經(jīng)常會把并發(fā)數(shù)和TPS理解混淆)

    2、jvm調(diào)優(yōu)

    https://www.cnblogs.com/xingzc/p/5756119.html

    概括:

    1 棧是運行時的單位 , 而堆是存儲的單元。

    2 棧解決程序的運行問題,即程序如何執(zhí)行,或者說如何處理數(shù)據(jù),

    堆解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放,放在哪兒。

    在java中一個線程就會相應(yīng)有一個線程棧與之對應(yīng),這點很容易理解,因為不同的線程執(zhí)行邏輯有所不同,因此需要一個獨立的線程棧。

    而堆則是所有線程共享的

    java中,棧的大小通過-Xss來設(shè)置,當(dāng)棧中存儲的數(shù)據(jù)比較多時,需要適當(dāng)調(diào)大這個值,否則會出現(xiàn) java.lang.StackOverflowError異常。

    常見的出現(xiàn)這個異常的是無法返回的遞歸,因為此時棧中保存的信息都是方法返回的記錄點。

    基本數(shù)據(jù)類型的大小是固定的,這里就不多說了,對于非基本類型的java對象,其大小就值得商討。

    在java中,一個空Object對象的大小是8byte,這個大小只是保存堆中一個沒有任何屬性的對象的大小。看看下面語句:

    Object ob = new Object();

    這樣在程序中完成了一個java對象的聲明,但是它所占的空間為:4byte+8byte。

    (4byte是上面部分所說的java棧中保存引用的所需要空間,而那8byte則是java堆中對象的信息)。

    持久代:

    用于存放靜態(tài)文件,如java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時候需要設(shè)置一個比較大的持久空間來存放這些運行過程中新增的類。持久代大小通過 -XX:MaxPermSize = <N> 進行設(shè)置

    有如下原因可能導(dǎo)致Full GC:

    年老代(Tenured)被寫滿

    持久代(Perm)被寫滿

    System.gc()被顯式調(diào)用

    上一次GC之后Heap的各域分配策略動態(tài)變化

    選擇合適的垃圾收集算法

    1)串行收集器

    用單線程處理所有垃圾回收工作,因為無需多線程交互,所有效率比較高。但是,也無法使用多處理器的優(yōu)勢,所以此收集器適合單處理器機器。當(dāng)然,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多處理器機器上。可以使用 -XX:+UseSerialGC打開

    2)并行收集器

    對年輕代進行并行垃圾回收,因此可以減少垃圾回收時間。一般在多線程多處理器機器上使用。使用 -XX:+UseParallelGC 打開。并行收集器在 J2SE5.0第六6更新上引入,在java SE6.0中進行了增強 --- 可以對年老代進行并行收集。如果年老代不使用并行收集的話,默認(rèn)是使用單線程進行垃圾回收,因此會制約擴展能力。使用 -XX:+UseParallelOldGC打開。

    使用 -XX:ParallelGCThreads = <N> 設(shè)置并行垃圾回收的線程數(shù)。此值可以設(shè)置與機器處理器數(shù)量相等

    此收集器可以進行如下配置:

    最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,通過-XX:MaxGCPauseMillis = <N>指定。<N> 為毫秒,如果指定了此值的話,堆大小和垃圾回收相關(guān)參數(shù)會進行調(diào)整以達到指定值。設(shè)定此值可能會減少應(yīng)用的吞吐量。

    吞吐量:吞吐量為垃圾回收時間與非垃圾回收時間的比值,通過-XX:GCTimeRatio = <N> 來設(shè)定,公式為 1/(1 + N)。例如,-XX:GCTimeRatio = 19時,表示5%的時間用于垃圾回收。默認(rèn)情況為99,即1%的時間用于垃圾回收。

    3)并發(fā)收集器

    可以保證大部分工作都并發(fā)進行(應(yīng)用不停止),垃圾回收只暫停很少的時間,此收集器適合對響應(yīng)時間要求比較高的中、大規(guī)模應(yīng)用。使用 -XX:+UseConcMarkSweepGC打開。

    小結(jié)

    串行處理器:

    -- 適用情況:數(shù)據(jù)量比較小(100M左右),單處理器下并且對相應(yīng)時間無要求的應(yīng)用。

    -- 缺點:只能用于小型應(yīng)用。

    并行處理器:

    -- 適用情況:“對吞吐量有高要求”,多CPU,對應(yīng)用過響應(yīng)時間無要求的中、大型應(yīng)用。舉例:后臺處理、科學(xué)計算。

    -- 缺點:垃圾收集過程中應(yīng)用響應(yīng)時間可能加長。

    并發(fā)處理器:

    -- 適用情況:“對響應(yīng)時間有高要求”,多CPU,對應(yīng)用響應(yīng)時間有較高要求的中、大型應(yīng)用。舉例:Web服務(wù)器/應(yīng)用服務(wù)器、電信交換、集成開發(fā)環(huán)境。

    以下配置主要針對分代垃圾回收算法而言。

    堆大小設(shè)置

    年輕代的設(shè)置很關(guān)鍵

    JVM中最大堆大小有三方面限制:相關(guān)操作系統(tǒng)的數(shù)據(jù)模型(32-bit 還是64-bit)限制;系統(tǒng)的可用虛擬內(nèi)存限制;系統(tǒng)的可用物理內(nèi)存限制。32位系統(tǒng)下,一般限制在1.5G~2G;64位操作系統(tǒng)對內(nèi)存無限制。在Windows Server 2003系統(tǒng),3.5G物理內(nèi)存,JDK5.0下測試,最大可設(shè)置為1478m。

    典型設(shè)置:

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

    -Xmx3550m:設(shè)置JVM最大可用內(nèi)存為3550m。

    -Xms3550m:設(shè)置JVM初始內(nèi)存為3550m。此值可以設(shè)置與 -Xmx 相同,以避免每次垃圾回收完成后JVM重新分配內(nèi)存。

    -Xmn2g:設(shè)置年輕代大小為2G。整個堆大小=年輕代大小+年老代大小+持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個堆的3/8。

    -Xss128k:設(shè)置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256k。根據(jù)應(yīng)用的線程所需內(nèi)存大小進行調(diào)整。在相同物理內(nèi)存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗值在3000~5000左右。

    java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

    -XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個Survivor區(qū))與年老代的比值(除去持久代)。設(shè)置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5。

    -XX:SurvivorRatio=4:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設(shè)置為4,則兩個Survivor區(qū)與一個Eden區(qū)的比值為2:4,一個Survivor區(qū)占整個年輕代的1/6。

    -XX:MaxPermSize=16m:設(shè)置持久代大小為16m。

    -XX:MaxTenuringThreshold=0:設(shè)置垃圾最大年齡。如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進入年老代。對于年老代比較多的應(yīng)用,可以提高效率。如果此值設(shè)置為一個較大值,則年輕代對象會在Survivor區(qū)進行多次復(fù)制,這樣可以增加對象在年輕代的存活時間,增加在年輕代被回收的概率。

    回收器選擇

    JVM給了三種選擇:串行收集器、并行收集器、并發(fā)收集器,但是串行收集器只適用于小數(shù)據(jù)量的情況,所以這里的選擇主要針對并行收集器和并發(fā)收集器。默認(rèn)情況下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動的時候加入相應(yīng)參數(shù)。JDK5.0以后,JVM會根據(jù)當(dāng)前系統(tǒng)配置進行判斷。

    吞吐量優(yōu)先的并行收集器

    如上文所述,并行收集器主要以到達一定的吞吐量為目標(biāo),適用于科學(xué)計算和后臺處理等。

    典型配置:

    java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

    -XX:+UseParallelGC:選擇垃圾收集器為并行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用并發(fā)收集,而年老代仍舊使用串行收集。

    -XX:+ParallelGCThreads=20:配置并行收集器的線程數(shù),即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數(shù)目相等。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

    -XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0支持對年老代并行收集。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

    -XX:MaxGCPauseMillis=100:設(shè)置每次年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調(diào)整年輕代大小,以滿足此值。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

    -XX:+UseAdaptiveSizePolicy:設(shè)置此選項后,并行收集器會自動選擇年輕代區(qū)大小和相應(yīng)的Survivor區(qū)比例,以達到目標(biāo)系統(tǒng)規(guī)定的最低響應(yīng)時間或者收集頻率等,此值建議使用并行收集器時,一直打開。

    響應(yīng)時間優(yōu)先的并發(fā)收集器

    如上文所述,并發(fā)收集器主要是保證系統(tǒng)的響應(yīng)時間,減少垃圾收集時的停頓時間。適用于應(yīng)用服務(wù)器、電信領(lǐng)域等。

    典型配置:

    java -Xmx3550m -Xms3550 -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

    -XX:+UseConcMarkSweepGC:設(shè)置年老代為并發(fā)收集。測試中配置這個以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此時年輕代大小最好用-Xmn設(shè)置。

    -XX:+UseParNewGC:設(shè)置年輕代為并行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據(jù)系統(tǒng)配置自行設(shè)置,所以無需再設(shè)置此值。

    java -Xmx3550m -Xms3550 -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

    -XX:CMSFullGCsBeforeCompaction:由于并發(fā)收集器不對內(nèi)存空間進行壓縮、整理,所以運行一段時間后會產(chǎn)生“碎片”,使得運行效率降低。此值設(shè)置運行多少次GC以后對內(nèi)存空間進行壓縮、整理。

    -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片。

    常見配置匯總

    堆設(shè)置

    -Xms:初始堆大小

    -Xmx:最大堆大小

    -XX:NewSize=n:設(shè)置年輕代大小

    -XX:NewRatio=n:設(shè)置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,表示Eden:Survivor=3:2,一個Survivor區(qū)占整個年輕代的1/5。

    -XX:MaxPermSize=n:設(shè)置持久代大小

    收集器設(shè)置

    -XX:+UseSerialGC:設(shè)置串行收集器

    -XX:+UseParallelGC:設(shè)置并行收集器

    -XX:+UseParalledlOldGC:設(shè)置并行年老代收集器

    -XX:+UseConcMarkSweepGC:設(shè)置并發(fā)收集器

    垃圾回收統(tǒng)計信息

    -XX:+PrintGC

    -XX:+PrintGCDetails

    -XX:+PrintGCTimeStamps

    -Xloggc:filename

    并行收集器設(shè)置

    -XX:ParallelGCThreads=n:設(shè)置并行收集器收集時使用的CPU數(shù)。并行收集線程數(shù)。

    -XX:MaxGCPauseMillis=n:設(shè)置并行收集最大暫停時間

    -XX:GCTimeRatio=n:設(shè)置垃圾回收時間占程序運行時間的百分比。公式為1/(1+N)

    并發(fā)收集器設(shè)置

    -XX:+CMSIncrementalMode:設(shè)置為增量模式。適用于單CPU情況。

    -XX:+ParallelGCThreads=n:設(shè)置并發(fā)收集器年輕代收集方式為并行收集時,使用的CPU數(shù)。并行收集線程數(shù)。

    八、底層知識

    1、JMM

    JMM簡介:

    JMM:Java Memory Model(Java內(nèi)存模型),圍繞著在并發(fā)過程中如何處理可見性、原子性、有序性這三個特性而建立的模型。OK,我們看一下三種特征。

    原子性(atomicity): 原子性是指一個操作不能被打斷,要么全部執(zhí)行完畢,要么不執(zhí)行。在這點上有點類似于事務(wù)操作,要么全部執(zhí)行成功,要么回退到執(zhí)行該操作之前的狀態(tài) 。由Java內(nèi)存模型來直接保證原子性變量操作包括read, load, assign, use, store和write。大致可以認(rèn)為基本數(shù)據(jù)類型的訪問讀寫是具有原子性的。如果應(yīng)用場景需要一個更大范圍的原子性保證,Java內(nèi)存模型還提供了lock和unlock操作來滿足需求,盡管虛擬機沒有把lock和unlock操作直接開放給用戶使用,但是卻提供了更高層次的字節(jié)碼指令monitorenter和monitorexit來隱式地使用這兩個操作,這兩個字節(jié)碼指令反映到Java代碼中就是同步塊——synchronized關(guān)鍵字,因此在synchronized塊之間的操作也具備原子性。

    可見性(visibility): 可見性是指當(dāng)一個線程修改了共享變量的值,其他線程能夠立即得知這個修改。Java內(nèi)存模型是通過在變量修改后將新值同步到主內(nèi)存,無論是普通變量還是volatile變量都是如此,普通變量與volatile變量的區(qū)別是,volatile的特殊規(guī)則保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。因此,可以說volatile保證了多線程操作時變量的可見性,而普通變量不能保證這一點。

    除了volatile之外,Java還有兩個關(guān)鍵字能實現(xiàn)可見性,即synchronized和final。同步塊的可見性是由“對一個變量執(zhí)行unlock操作之前,必須先把此變量同步回主內(nèi)存中”這條規(guī)則獲得的,而final關(guān)鍵字的可見性是指:被final修飾的字段在構(gòu)造器中一旦初始化完成,并且構(gòu)造器沒有把"this"的引用傳遞出去(this引用逃逸是一件很危險的事情,其他線程有可能通過這個引用訪問到“初始化了一半”的對象),那在其他線程中就能看見final字段的值。

    有序性: Java程序天然的有序性可以總結(jié)為一句話:如果本線程內(nèi)觀察,所有的操作都是有序的;如果在一個線程中觀察另一個線程,所有的操作都是無序的。前半句是指“線程內(nèi)表現(xiàn)為串行的語義”,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存與主內(nèi)存同步延遲”現(xiàn)象。JMM模型提供了volatile和synchronized來保證線程之間操作的有序性。

    總之,JMM模型就是提供一套機制來保證操作的原子性、可見性和有序性。根據(jù)數(shù)據(jù)是否被線程可見將JVM將內(nèi)存組織為主內(nèi)存和工作內(nèi)存兩個部分。主內(nèi)存中主要包括本地方法區(qū)和堆。每個線程都有一個工作內(nèi)存,工作內(nèi)存中主要包括兩個部分,一個是屬于該線程的棧和對主存部分變量拷貝的寄存器:

    總結(jié):

    ① 線程間通信是通過共享主內(nèi)存的方式去實現(xiàn)的。

    ② 線程間對共享數(shù)據(jù)操作時必須具備原子性、可見性和有序性。

    ③ 為了保證對主內(nèi)存操作數(shù)據(jù)時不同步也為了操作的方便性,Java提供了Synchronized保證操作的原子性、可見性和有序性。提供了volatile可以保證操作的可見性。

    volatile寫的內(nèi)存語義如下:當(dāng)寫一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。

    volatile讀的內(nèi)存語義如下:當(dāng)讀一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量。

    1.1線程通信

    https://blog.csdn.net/hqq2023623/article/details/51000854

    https://blog.csdn.net/u012810020/article/details/60765988

    https://www.jianshu.com/p/8a58d8335270

    同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機制

    1)共享內(nèi)存

    在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),線程之間通過寫-讀內(nèi)存中的公共狀態(tài)來隱式進行通信

    2)消息傳遞

    在消息傳遞的并發(fā)模型里,線程之間沒有公共狀態(tài),線程之間必須通過明確的發(fā)送消息來進行通信

    java中典型的消息傳遞方式就是wait()和notify()

    1.2消息傳遞

    java中典型的消息傳遞方式就是wait()和notify()

    對于一個對象的成員變量,不管它是原始類型還是包裝類型,都會被存儲到堆區(qū)

    2、徹底搞懂synchronized(從偏向鎖到重量級鎖)

    偏向鎖01->輕量級鎖00(自旋鎖,自適應(yīng)自旋鎖--輕量級鎖也被稱為非阻塞同步、樂觀鎖,因為這個過程并沒有把線程阻塞掛起,而是讓線程空循環(huán)等待,串行執(zhí)行。)->重量級鎖10(互斥鎖(重量級鎖)也稱為阻塞同步、悲觀鎖)

    https://blog.csdn.net/qq_38462278/article/details/81976428

    synchronized可重入鎖的實現(xiàn)

    之前談到過,每個鎖關(guān)聯(lián)一個線程持有者和一個計數(shù)器。當(dāng)計數(shù)器為0時表示該鎖沒有被任何線程持有,那么任何線程都都可能獲得該鎖而調(diào)用相應(yīng)方法。當(dāng)一個線程請求成功后,JVM會記下持有鎖的線程,并將計數(shù)器計為1。此時其他線程請求該鎖,則必須等待。而該持有鎖的線程如果再次請求這個鎖,就可以再次拿到這個鎖,同時計數(shù)器會遞增。當(dāng)線程退出一個synchronized方法/塊時,計數(shù)器會遞減,如果計數(shù)器為0則釋放該鎖。

    剛才我們說,鎖實際上是加在對象上的,那么被加了鎖的對象我們稱之為鎖對象,在java中,任何一個對象都能成為鎖對象。

    為了讓大家更好著理解虛擬機是如何知道這個對象就是一個鎖對象的,我們下面簡單介紹一下java中一個對象的結(jié)構(gòu)。

    java對象在內(nèi)存中的存儲結(jié)構(gòu)主要有一下三個部分:

    對象頭

    實例數(shù)據(jù)

    填充數(shù)據(jù)

    這里強調(diào)一下,對象頭里的數(shù)據(jù)主要是一些運行時的數(shù)據(jù)。

    其簡單的結(jié)構(gòu)如下

    長度 內(nèi)容 說明

    32/64bit Mark Work hashCode,GC分代年齡,鎖信息

    32/64bit Class Metadata Address 指向?qū)ο箢愋蛿?shù)據(jù)的指針

    32/64bit Array Length 數(shù)組的長度(當(dāng)對象為數(shù)組時)

    當(dāng)我們創(chuàng)建一個對象LockObject時,該對象的部分Markword關(guān)鍵數(shù)據(jù)如下。

    bit fields 是否偏向鎖 鎖標(biāo)志位

    hash 0 01

    從圖中可以看出,偏向鎖的標(biāo)志位是“01”,狀態(tài)是“0”,表示該對象還沒有被加上偏向鎖。(“1”是表示被加上偏向鎖)。該對象被創(chuàng)建出來的那一刻,就有了偏向鎖的標(biāo)志位,這也說明了所有對象都是可偏向的,但所有對象的狀態(tài)都為“0”,也同時說明所有被創(chuàng)建的對象的偏向鎖并沒有生效。

    1)偏向鎖 (在Jdk1.6中,偏向鎖的開關(guān)是默認(rèn)開啟的,適用于只有一個線程訪問同步塊的場景)

    不過,當(dāng)線程執(zhí)行到臨界區(qū)(critical section)時,此時會利用CAS(Compare and Swap)操作,將線程ID插入到Markword中,同時修改偏向鎖的標(biāo)志位。

    所謂臨界區(qū),就是只允許一個線程進去執(zhí)行操作的區(qū)域,即同步代碼塊。CAS是一個原子性操作

    此時的Mark word的結(jié)構(gòu)信息如下:

    bit fields 是否偏向鎖 鎖標(biāo)志位

    threadId epoch 1 01

    此時偏向鎖的狀態(tài)為“1”,說明對象的偏向鎖生效了,同時也可以看到,哪個線程獲得了該對象的鎖。

    bit fields 鎖標(biāo)志位

    指向LockRecord的指針 00

    注:鎖標(biāo)志位”00”表示輕量級鎖

    輕量級鎖主要有兩種

    自旋鎖

    自適應(yīng)自旋鎖

    自旋鎖的一些問題

    如果同步代碼塊執(zhí)行的很慢,需要消耗大量的時間,那么這個時侯,其他線程在原地等待空消耗cpu,這會讓人很難受。

    本來一個線程把鎖釋放之后,當(dāng)前線程是能夠獲得鎖的,但是假如這個時候有好幾個線程都在競爭這個鎖的話,那么有可能當(dāng)前線程會獲取不到鎖,還得原地等待繼續(xù)空循環(huán)消耗cup,甚至有可能一直獲取不到鎖。

    默認(rèn)情況下,自旋的次數(shù)為10次,用戶可以通過-XX:PreBlockSpin來進行更改。

    3、并發(fā)基礎(chǔ) aqs、cas

    https://blog.csdn.net/zs064811/article/details/76996727 分獨占鎖與共享鎖兩類

    4、countdownlatch cyclicbarrier semaphore

    https://www.cnblogs.com/dolphin0520/p/3920397.html

    1)countdownlatch -- await(),countdown()

    2) cyclicbarrier -- await()

    3) semaphore

    Semaphore可以控同時訪問的線程個數(shù),通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。

    下面對上面說的三個輔助類進行一個總結(jié):

    1)CountDownLatch和CyclicBarrier都能夠?qū)崿F(xiàn)線程之間的等待,只不過它們側(cè)重點不同:

    CountDownLatch一般用于某個線程A等待若干個其他線程執(zhí)行完任務(wù)之后,它才執(zhí)行;

    而CyclicBarrier一般用于一組線程互相等待至某個狀態(tài),然后這一組線程再同時執(zhí)行;

    另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。

    2)Semaphore其實和鎖有點類似,它一般用于控制對某組資源的訪問權(quán)限。

    5、并發(fā)集合concurrentHashMap

    http://www.importnew.com/28263.html

    6、threadlocal (使用場景,數(shù)據(jù)庫連接,session管理(sessionfactory管理))

    https://www.cnblogs.com/dolphin0520/p/3920407.html

    雖然ThreadLocal的get,set方法可以清除ThreadLocalMap中key為null的value,但是get,set方法在內(nèi)存泄露后并不會必然調(diào)用,所以為了防止此類情況的出現(xiàn),我們有兩種手段。

    1、使用完線程共享變量后,顯示調(diào)用ThreadLocalMap.remove方法清除線程共享變量;

    2、JDK建議ThreadLocal定義為private static,這樣ThreadLocal的弱引用問題則不存在了。

    7、fork/join框架

    https://www.cnblogs.com/senlinyang/p/7885964.html

    任務(wù)分割出的子任務(wù)會添加到當(dāng)前工作線程所維護的雙端隊列中,進入隊列的頭部。當(dāng)一個工作線程的隊列里暫時沒有任務(wù)時,它會隨機從其他工作線程的隊列的尾部獲取一個任務(wù)(工作竊取算法)

    https://www.cnblogs.com/wzqjy/p/7921063.html Java--8--新特性--串并行流與ForkJoin框架

    Fork/Join與傳統(tǒng)線程池的區(qū)別!

    Fork/Join采用“工作竊取模式”,當(dāng)執(zhí)行新的任務(wù)時他可以將其拆分成更小的任務(wù)執(zhí)行,并將小任務(wù)加到線程隊列中,然后再從一個隨即線程中偷一個并把它加入自己的隊列中。

    就比如兩個CPU上有不同的任務(wù),這時候A已經(jīng)執(zhí)行完,B還有任務(wù)等待執(zhí)行,這時候A就會將B隊尾的任務(wù)偷過來,加入自己的隊列中,對于傳統(tǒng)的線程,F(xiàn)orkJoin更有效的利用的CPU資源!

    8、callable,future

    https://www.cnblogs.com/dolphin0520/p/3949310.html

    Future類位于java.util.concurrent包下,它是一個接口:

    public interface Future<V> {
     boolean cancel(boolean mayInterruptIfRunning);
     boolean isCancelled();
     boolean isDone();
     V get() throws InterruptedException, ExecutionException;
     V get(long timeout, TimeUnit unit)
     throws InterruptedException, ExecutionException, TimeoutException;
    }
    

    9、線程池

    executors創(chuàng)建的fixed,single的線程池,隊列最大是integer.max會出現(xiàn)oom,cached,schedul創(chuàng)建的線程池會無限大integer.max,導(dǎo)致oom



    中國大陸買手發(fā)展趨勢及經(jīng)營策略研究

    文 | 徐鏑雅 李喆

    北京服裝學(xué)院中國生活方式設(shè)計研究院 100029 北京


    通過對比在網(wǎng)絡(luò)經(jīng)濟環(huán)境下歐美、日本臺灣以及中國大陸買手店的現(xiàn)狀,分析中國買手店作為新興市場,在全球買手店總體處于衰落的階段為何能夠快速發(fā)展的原因。同時對于中國買手店未來的發(fā)展趨勢以及市場經(jīng)營策略進行研究。最后就中國買手市場 快速發(fā)展所帶來的一些問題進行了分析并給出了可行性建議。關(guān)鍵詞:網(wǎng)絡(luò)經(jīng)濟;買手店;經(jīng)營策略


    1

    引 言

    隨著互聯(lián)網(wǎng)技術(shù)的飛速發(fā)展與廣泛應(yīng)用,網(wǎng)絡(luò)對于經(jīng)濟的影響力在不斷擴大,人類社會進入網(wǎng)絡(luò)經(jīng)濟時期,網(wǎng)絡(luò)成為一個互相連接的整體。互聯(lián)網(wǎng)的出現(xiàn)為人們生活帶來了便利,催生出了很多新興行業(yè)。這些新興行業(yè)為投資創(chuàng)業(yè)者帶來新的機遇的同時也為傳統(tǒng)行業(yè)帶來了沖擊挑戰(zhàn)。在網(wǎng)絡(luò)經(jīng)濟的大環(huán)境下,作為與人類生活息息相關(guān)的零售行業(yè)表現(xiàn)得尤為明顯,服裝行業(yè)競爭日趨激烈,傳統(tǒng)百貨和品牌專柜受到了極大的沖擊。消費者在互聯(lián)網(wǎng)環(huán)境的影響下,需要更多具有新意和個性的買手店,以滿足日益增長的時尚消費體驗。因此存在于轉(zhuǎn)型中的傳統(tǒng)買手店與快速發(fā)展的新興買手店的問題都亟待解決。中國作為時尚行業(yè)的最大消費市場之一,本土市場買手店的發(fā)展尤為值得關(guān)注。本文基于現(xiàn)階段網(wǎng)絡(luò)經(jīng)濟不斷發(fā)展的背景下,通過對全球買手店的現(xiàn)狀的分析,研究中國買手店發(fā)展的趨勢與問題,探索未來提升買手店經(jīng)濟效益的經(jīng)營策略。


    2

    全球買手店經(jīng)營現(xiàn)狀分析與研究綜述


    買手店,顧名思義,即以買手為核心,以目標(biāo)顧客獨特的時尚觀念和趣味為基準(zhǔn), 從全球搜羅符合其審美的產(chǎn)品,并將這些產(chǎn)品以自己的品味進行陳列和售賣[1]。買手店利用商品開發(fā)和選款,以滿足消費者的多樣化需求。在買手店的起源地法國,買手店被稱為“boutique store”,英文里買手店可被稱為“specialty store”,“select shop”“multi-brands store”,因而買手店又被稱為“多品牌店集合店”或者“精品店”。根據(jù)不同的標(biāo)準(zhǔn),買手店有多種劃分方式,本文根據(jù)研究需要以及業(yè)態(tài)形式,將全球買手店分為多品牌集合店(IT、棟梁)、買手制精品集合店(Joyce、Barneys new york)、 生活方式買手店(10 Corso Como、Colette)和百貨式買手店(Lane Crawford、Neiman Marcus)。據(jù)不完全統(tǒng)計,目前全球規(guī)模不等的買手店超過 3 萬 2 千家,涉及的營業(yè)額近 2000 億萬美元。


    2.1 歐美買手店現(xiàn)狀分析
    買手店的歷史可以追溯到上世紀(jì) 20 年代的巴黎。1929 年,法國設(shè)計師 Lucien Long創(chuàng)立了第一家買手店[2]。到了50 年代,買手店已經(jīng)開始在歐美蓬勃發(fā)展。經(jīng)過百年發(fā)展, 歐美買手店的商業(yè)模式較為成熟,傳統(tǒng)的買手店主要依靠商品的稀缺性和獨特性來吸引消費者。然而隨著國際經(jīng)濟環(huán)境的疲軟,時尚電商在網(wǎng)絡(luò)經(jīng)濟環(huán)境下瘋狂增長,自 2010 年起,傳統(tǒng)的買手店行業(yè)已經(jīng)逐漸衰退。被譽為世界上最好的買手店,并開創(chuàng)了生活方式集合店新模式的巴黎傳奇買手 Colette 在 2017 年年底結(jié)業(yè)關(guān)店。在此之前,倫敦標(biāo)志性獨立買手店 Browns 也在 2015 年被英國電商 Farfetch 收購。同年9月米蘭標(biāo)志性買手店10Corso Como實體運營公司Dieci Srl 也由于米蘭總店持續(xù)虧損負(fù)債超 1 億, 宣布申請破產(chǎn)保護。時尚零售環(huán)境不斷惡化,使得傳統(tǒng)買手店已是寸步難行。與之相反 的是,新興的歐美時尚電商卻在互聯(lián)網(wǎng)環(huán)境下大放異彩。英國電商 Farfetch于今年在紐交所上市,歐洲奢侈品電商巨頭 Yoox Net-A-Porter 2017年銷售額突破 20 億歐元[3], 包括 Mytheresa,Ssense,Shopbop,Asos 在內(nèi)的時尚電商平臺的快速增長對傳統(tǒng)買手 店行業(yè)造成了巨大沖擊。


    圖 1 近年歐洲知名買手店關(guān)店情況

    2.2 日本臺灣買手店現(xiàn)狀分析

    日本是亞洲潮流圣地,尤其東京作為街頭文化的代表,聚集了很多潮流文化和先鋒時尚的買手店。當(dāng) 70 年代日本設(shè)計師開始在巴黎嶄露頭角時,本土買手店雛形也開始

    在日本出現(xiàn)。日本買手店鼻祖 Beams 創(chuàng)建于 1976 年,從最開始引進美國西海岸的時尚風(fēng)格到現(xiàn)在打造生活方式品牌,Beams一直影響著日本的時裝潮流界。之后買手店開始 興起,1895 年日本二手古著買手店 Ragtag 在東京原宿成立;1990 年Barneys New Yorks 正式進入日本。21 世紀(jì)初開始日本買手店市場進入鼎盛時期,很多世界知名買手店都爭 相進入日本市場,如Opening Ceremony 2009 年登陸東京以及川久保玲主理的 Dover Street Market 買手店 2012 年也回歸東京。同時間各種不同風(fēng)格的本土買手店也不斷壯 大,如久保光博(Kubo Mitsuhiro)2005 年在東京成立的潮流買手店 GR8 以及谷正人 2007 年在原宿創(chuàng)立了只售賣日本設(shè)計師品牌的Studious 等。但由于市場的激烈競爭,一些 逐漸失去競爭力的買手店都退出了市場,比如曾經(jīng)在 2008 年紅極一時的以西海岸生活 風(fēng)格作為主軸的買手店鋪 American Rag Cie,在 2018 年結(jié)束營業(yè)。

    臺灣買手店大概從 2000 年開始起步,包含亞洲知名的買手店先后進入臺北市場。從 2010 年開始,越來越多的買手店進駐臺北的大街小巷之中。由于歷史原因,臺灣受 到日本文化影響較大,很多買手店都經(jīng)營以日本設(shè)計師品牌和街頭文化為主的潮牌。但在互聯(lián)網(wǎng)環(huán)境的影響下,各種文化不斷沖擊著市場,不同風(fēng)格的買手店也在日漸增長。

    2013 年來自日本的買手店 Beams 和 United Arrows 相繼在臺北正式開店。同時一些 臺北本土買手店也日漸興起,如 2017 年開業(yè),以買手店思維結(jié)合百貨公司經(jīng)營模式的DPT 買手店;由臺灣地區(qū)兩大潮流品牌 De Marco Lab 與 REMIX合并后新開設(shè)的潮流買 手店 LAB Taipei;以及本土老牌買手店 Plain-me。不過在全球經(jīng)濟下滑的影響下,日 本臺灣的買手店也面臨著倒閉的風(fēng)險,因此很多買手店都陸續(xù)往中國內(nèi)地發(fā)展。

    2.3 中國大陸買手店現(xiàn)狀

    1997年巴黎老佛爺百貨(Galeries Lafaye)第一次進入中國大陸,但僅一年后由于虧損而退出中國大陸市場。2000年開始,以 Lane Crawford、IT、Joyce 為主的香港 買手店也開始嘗試進入大陸,但由于消費市場不成熟,各家依舊慘淡收場。從2009年開始,隨著中國經(jīng)濟的穩(wěn)步發(fā)展,中產(chǎn)階級日漸壯大,消費者觀念的成熟,中國本土買手店開始出現(xiàn)并在此后的時間中迅速崛起。與此同時,歐洲與香港的各大買手店卷土重來,品牌買手店在中國數(shù)量激增。

    據(jù)2017秋冬上海時裝周的統(tǒng)計數(shù)據(jù)顯示,2015至2017年間,中國買手店數(shù)量從1636家激增到3781家。浙江、湖北、福建省的增速位列全國前三位。以上海為例,2014年上海買手店約有75 家[4],到 2016年,上海買手店總數(shù)已近300 家。全國買手店據(jù)估 算已經(jīng)超過1000 家,并且三分之二都有電商服務(wù)[5]。在地域分布上,買手店也從北京、上海等一線城市逐步向二三線城市擴張[6]。當(dāng)前我國買手店定位還是以時尚服裝設(shè)計師品牌買手店為主,店內(nèi)產(chǎn)品多以國外設(shè)計師品牌為主。隨著中國原創(chuàng)設(shè)計力量的崛起, 像棟梁、BNC、肆合、RoundRound 等以“扶持本土設(shè)計師”為標(biāo)簽的買手店也逐漸增多。目前中國本土獨立買手店占據(jù)了中國買手市場的絕大部分,其次是以連卡佛、IT 等港資背景的連鎖買手店,亞洲其他地區(qū)買手店以及多以合作方式進入中國市場的歐洲買手店位居其后。但是美國幾大知名買手店,如 Neiman Marcus 、Barneys New Yorks 和 Saks Fifth Avenue 等均由于各種原因擱置或退出中國市場。


    圖表2 2015年不同背景買手店在中國開店數(shù)量占比

    相比歐洲,中國內(nèi)地的買手店模式雖然起步晚但是發(fā)展快速,買手市場欣欣向榮。但也因為小規(guī)模買手店門檻較低,也使得這個市場處于混亂不清、良莠不齊的狀態(tài)。一些能力不足的買手所開設(shè)的買手店也很容易被市場所淘汰。由于定位失焦、庫存高企和資金壓力等原因,很多買手店悄然退出市場。例如廈門設(shè)計師上官喆開設(shè)的買手店“AKP 島群”在 2016 年閉店重修,新開后的店鋪主要以駐場酒吧為主。但總體來說,雖然存在一些問題,但國內(nèi)買手市場仍處于快速成長階段。


    3

    影響中國大陸買手店經(jīng)營狀況變化的成因分析


    目前,總體來說歐美傳統(tǒng)買手店處于衰落階段,而歐美時尚電商平臺憑借互聯(lián)網(wǎng)的優(yōu)勢風(fēng)頭正勁。日本臺灣買手市場已經(jīng)較為成熟,競爭激烈,部分品牌也開始將中心轉(zhuǎn)移到內(nèi)地市場,而國內(nèi)買手市場則正處于快速成長階段,主要原因有以下幾點:


    3.1 消費環(huán)境的改變

    近幾年全球經(jīng)濟一直處在一個不穩(wěn)定、不樂觀的狀態(tài)。國際經(jīng)濟疲弱造成全球消費者,特別是歐美市場消費者奢侈品消費能力減弱。中國市場雖然從 2017 年開始迎來反彈,但隨著消費全面升級,消費者行為習(xí)慣與之產(chǎn)生巨大變化,逐步向著理性化和精明化方向發(fā)展。根據(jù)麥肯錫與英國《時尚商業(yè)評論》聯(lián)合發(fā)布的 2019 全球時尚業(yè)態(tài)報告中稱,中國在2019 年將超過美國成為全球最大的時尚市場。不僅是奢侈品,由于中國中產(chǎn)階級日益壯大,非昂貴的服裝市場在迅猛增長。據(jù)預(yù)計,在 2015 到 2025 年期間, 中國將占到世界新中上層以及上層家庭的 28%,美國僅占 3%。

    隨著信息全球化和社交化,中國時尚消費者更加成熟,從盲目的趨同消費逐漸過度到個性化與理性化階段,知道自身所認(rèn)同的價值觀與品牌內(nèi)涵,更加注重個人消費體驗,追求精神上的愉悅。同時中國設(shè)計力量的不斷崛起,中國消費者對于本土設(shè)計品牌有了更多的信任與喜愛,使得很多買手店都引進了更多中國設(shè)計師品牌,2016 年連卡佛店內(nèi)出售的中國設(shè)計師品牌數(shù)量就達到了 50 個。

    3.2 消費者消費行為的改變

    網(wǎng)絡(luò)的便利和物流的發(fā)達改變了消費者的購物習(xí)慣。與實體店相比,消費者更加傾向于網(wǎng)上購物。根據(jù)Bain & Company 發(fā)布報告顯示奢侈品線上消費在 2018 年繼續(xù)加速, 增長22%,達到近 270 億歐元,占所有奢侈品銷售額的10%,美洲市場的線上銷售額占44%,但亞洲新興市場正在成為線上奢侈品消費增長的新引擎,略高于歐洲。據(jù)要客研 究院近日在上海發(fā)布的《2018 中國奢侈品報告》稱 2018 年中國奢侈品線上銷售營業(yè)額達到創(chuàng)紀(jì)錄的 53 億美元,約 360 億人民幣,比 2017 年的 39 億美元增長約 37%,發(fā)展迅猛。另據(jù)德勤聯(lián)合其他機構(gòu)共同發(fā)布的《2017 中國奢侈品網(wǎng)絡(luò)消費報告》指出,年輕一代將在未來10 年成為奢侈品消費的主導(dǎo)力量。中國時尚消費市場,尤其是高檔和奢侈品領(lǐng)域作為新興市場,并沒有主導(dǎo)或者傳統(tǒng)品牌[7]。消費者年輕化導(dǎo)致他們更傾向個性化消費。在數(shù)字化傳播的驅(qū)動下,消費者們愿意不斷嘗試新的品牌。雖然這種現(xiàn)象造成了中國消費者的對品牌忠誠度不高,但是卻對經(jīng)營多品牌,商品快更新的買手店提供了有利機會[8]。

    互聯(lián)網(wǎng)經(jīng)濟環(huán)境下,中國成全球社交媒體用戶最為活躍的地區(qū)之一。社交媒體催生的 KOL(Key Opinion Leader)對中國時尚行業(yè)的推動作用表現(xiàn)得也越來越明顯。中國的 KOL 已經(jīng)領(lǐng)先全球其他國家,率先成為一種真正媒介載體。在中國,KOL 營銷正在逐 漸取代電視和紙媒廣告等傳統(tǒng)營銷方式[9]。

    3.3 缺乏有效的定價策略

    買手店在中國市場的迅速發(fā)展擴張,給時尚市場注入動力的同時也帶來了一系列的問題。過去傳統(tǒng)買手店一直以售賣稀缺商品以及小眾產(chǎn)品為主,但是隨著時尚電商的大量出現(xiàn)擠壓了傳統(tǒng)買手店的生存空間。電商的瘋狂增長,使買手店商品的稀缺性和豐富性無法滿足到日漸挑剔的顧客。很多買手店的商品與時尚電商重合度極高,且由于高額店面租金的壓力,買手店定價遠(yuǎn)高于電商平臺,喪失了市場競爭力。特別是一些位于一線城市中心商貿(mào)圈的買手店租金壓力尤其大,例如位于廣州廣粵新天地的高端買手店 NY Fashion Studio 主要以售賣國際暗黑先鋒類型設(shè)計師品牌,該店于 2015 年中關(guān)閉店面, 曾經(jīng)的售賣單品包括 2.9 萬元的皮夾克以及 9000 多元牛仔褲。極高的產(chǎn)品定價導(dǎo)致店鋪銷量低迷,以至虧損關(guān)閉,NY方面負(fù)責(zé)人表示今后會根據(jù)廣州市場調(diào)整產(chǎn)品價格。

    3.4 缺乏精準(zhǔn)的客戶定位

    買手店在中國作為一個小眾新興產(chǎn)業(yè),市場不成熟,很多顧客對于買手店并不了解和信任,甚至與很多買手店店主對于買手店的實質(zhì)也不甚清楚。買手店本身并不是一個快速盈利的行業(yè),至少需要 3~5 年的時間來發(fā)[10]。但是一些買手店急功近利,并不重視買手店獨特的風(fēng)格和內(nèi)涵,而是依據(jù)大眾消費者的偏好來引進品牌,以致于不顧品牌組合和商品組合的科學(xué)性,造成買手店風(fēng)格混亂、定位不清,失去了買手店賴以生存 的根基。國內(nèi)很多買手店的售罄率只有20%~30%,庫存壓力大,運轉(zhuǎn)效率低下。

    3.5 專業(yè)買手人員欠缺

    買手店通常是由買手來選擇商品,雖然買手會根據(jù)過往銷售數(shù)據(jù)做一個理性判斷, 但很大程度上仍是仰賴買手本身的品味和審美,因而買手店往往有著很深的買手的烙印。雖然目前國內(nèi)買手行業(yè)處于上升階段,但與國外成熟的買手相比,國內(nèi)買手的專業(yè)度仍相去甚遠(yuǎn)。專業(yè)的買手能夠預(yù)判下一季時裝的風(fēng)格潮流,需要研究消費者的需求、品牌的定位以及與供貨商采買談判等,而這些可能需要超過 10 年的經(jīng)驗積累。然而國內(nèi)的時尚買手大多是由設(shè)計師、設(shè)計總監(jiān)等轉(zhuǎn)型而來,缺乏專業(yè)能力的訓(xùn)練。根據(jù)美國一所時裝設(shè)計學(xué)院統(tǒng)計,沒有經(jīng)過系統(tǒng)訓(xùn)練的買手要初步達到買手的基本要求,至少要花費5 年的實踐時間[11]。


    4

    互聯(lián)網(wǎng)背景下的中國買手店發(fā)展趨勢


    目前國內(nèi)的買手市場,除了像老佛爺百貨、連卡佛、IT 集團等個別大型集團外,更 多的是像棟梁和 BNC 一樣的本土獨立買手店和小眾買手店。這些買手店大多是出于自身 喜好而創(chuàng)立,數(shù)量眾多但質(zhì)量參差不齊,總體來說具有良好的發(fā)展?jié)摿Α?/p>

    4.1 Online To Offline(O2O)商業(yè)模式

    在全渠道零售趨勢的浪潮下,線上線下無縫鏈接消費對中國買手店至關(guān)重要。與傳統(tǒng)電商模式不同,O2O作為一種線上線下互動的商業(yè)模式,真正融合了線上線下。消費者可以在線上選定商品支付,然后在實體店中體驗[12]。早在2014年,中國買手店的線上銷售比例就達到了10%,O2O的雛形就已經(jīng)初步形成[5]。作為中國領(lǐng)先的時尚電商寺庫, 在線上已經(jīng)擁有25.3%及亞洲地區(qū)15.4%的高端市場份額,線下在北京、上海、成都等城 市中心地段開設(shè)實體體驗店,將線上消費與線下體驗相融合。經(jīng)濟全球化和科技的飛速發(fā)展使得網(wǎng)上購物的顧客比例日益加重。縱觀全球幾大奢侈品牌,如號稱拒絕電商的 CHANEL在巨大的商業(yè)利益面前也妥協(xié)。O2O模式在當(dāng)今社會已經(jīng)成一個不可逆的趨勢, 且在營業(yè)額中占的比例會不斷上升。

    4.2 數(shù)字化與人工智能趨勢

    中國數(shù)字化消費在人數(shù)和市場規(guī)模上以絕對量級領(lǐng)跑全球,特別是數(shù)字媒體對年輕一代的消費者時尚消費觀念影響巨大。據(jù)數(shù)據(jù)顯示,中國的網(wǎng)購規(guī)模是美國的 2.2 倍, 網(wǎng)購用戶是美國用戶的 2.6 倍,移動支付滲透率達到 62%。中國消費市場帶著鮮明的數(shù)字化烙印。而人工智能在數(shù)字化的基礎(chǔ)上,更進一步提升了消費者的購物體驗與個性化體驗。因此相比傳統(tǒng)買手依靠經(jīng)驗、直覺及較少的數(shù)據(jù),現(xiàn)在的買手通過互聯(lián)網(wǎng)、資源整合和大數(shù)據(jù)的輔助,則更能增強買手各方面決策準(zhǔn)確性。買手能夠更加省時省力的確定每一季商品的風(fēng)格,準(zhǔn)確的把握目標(biāo)客戶群的喜好,也能夠挖掘出更多好的設(shè)計師。同時隨著數(shù)字化與人工智能技術(shù)的提高,一點點的程序優(yōu)化,就能使顧客的網(wǎng)上購物體 驗得到極大提升,這對于買手店客流進店停留時間有很大幫助。集貨(JIHOOD)作為銀泰旗下買手店品牌,首家門店于 2015 年 11 月開業(yè),主打智能化門店特色,依托銀泰 SPP 及 RFID 技術(shù),提供魔鏡試衣、智能試衣間、魔幻收銀等科技體驗,貨品主要來源于天貓 TOP品牌、線上 TOP單品、線上原創(chuàng)設(shè)計師作品及生產(chǎn)商直采。

    4.3 時尚市場細(xì)分化趨勢

    在中國的時尚領(lǐng)域,奢侈品與快時尚一直是占據(jù)市場的兩個重要部分。但是隨著中國消費者的年齡層次年輕化以及消費能力和消費心理的日趨成熟,時尚的消費需求正朝著多樣化和個性化的方向發(fā)展。買手店的出現(xiàn)正好填補了消費者的這一需要。盡管統(tǒng)稱買手店,但是買手店所面對的目標(biāo)客戶群卻千差萬別。從風(fēng)格的不同到價格的區(qū)分,買手店的經(jīng)營方向更加明確化細(xì)分化,比如有專營眼鏡的 Coterie 和專營手袋的 Kate Zhou。而在價格方面,根據(jù)地產(chǎn)研究中心 RET 睿意徳《中國買手店研究報告》稱,中國買手店自2010年進入爆發(fā)期后,2009~2014 年間,5000-8000 區(qū)間價位的商品減少了19%,而 1000~2000元區(qū)間增加了 11.3%,甚至從 2014 年起買手店開始出現(xiàn) 500 元以下及 500~1000 元這兩個區(qū)間的產(chǎn)品。買手店作為時尚市場的新興力量需要深度挖掘細(xì)分市場,精準(zhǔn)定位,采取差異化的市場戰(zhàn)略,找準(zhǔn)自身的風(fēng)格和特色,以迎合目標(biāo)市場。這是買手店從橫向發(fā)展到縱向深入的重要體現(xiàn),也是中國買手店市場成長的必經(jīng)階段。

    4.4 購物消費到體驗消費轉(zhuǎn)變的趨勢

    中國的主流消費群體已經(jīng)發(fā)生改變,80 和 90 年代的消費人群占據(jù)了主要地位,消費者的消費習(xí)慣和心理也發(fā)生了巨大改變。年輕的消費群體愿意為優(yōu)質(zhì)的消費體驗、環(huán)境、情感和服務(wù)買單。基于此,很多買手店傾向于將時尚、藝術(shù)、生活方式和社交媒體融為一體,打造“網(wǎng)紅店”,吸引更多年輕的顧客群體。買手店在以提供個性化產(chǎn)品為核心的基礎(chǔ)上,注重對于藝術(shù)氛圍的營造,對生活方式的策劃,借此來提升新消費群體對店鋪的忠誠度和黏度。


    5

    中國買手店經(jīng)營策略建議


    目前中國買手店正處于快速成長階段,距離進入市場穩(wěn)定期還有一段距離,因此現(xiàn)在正是各個新興買手店憑借自己的特色與實力,搶占市場最好的時機。作者根據(jù)上述分析,得出以下幾條經(jīng)營策略建議:

    5.1 線上消費線下體驗的模式

    全渠道營銷導(dǎo)致數(shù)字化經(jīng)營已經(jīng)是時尚產(chǎn)業(yè)大勢所趨。通過互聯(lián)網(wǎng)、資源整合和精確的數(shù)據(jù)搜集能夠增強買手各方面決策準(zhǔn)確性。中國買手店積極發(fā)展 O2O 模式,真正融合線上線下,包括網(wǎng)上銷售渠道以及以微信微博為主的各社交媒體平臺,擴大消費群體, 降低實體買手店的經(jīng)營成本,打破地域限制,輻射更多消費人群。

    (1) 買手店應(yīng)該加大對對線上商店運行程序,頁面設(shè)計,用戶體驗感等方面的投入和優(yōu)化,為顧客呈現(xiàn)出一個與買手店風(fēng)格一致,運行流暢的官方網(wǎng)站。

    (2) 由于顧客購買習(xí)慣的改變,對于手機平板端的移動 APP 也應(yīng)當(dāng)進行優(yōu)化設(shè)計, 并且針對中國市場特有的一些購買渠道,如微信小程序、天貓旗艦店等進行設(shè)計,以最便利的方式為顧客提供服務(wù)。

    (3) 線下實體店除了售賣產(chǎn)品以外,更多的是充當(dāng)一個產(chǎn)品展示的空間以及與顧客進行情感交流的場所。通過實體店的運營加深顧客對買手店的品牌印象,維護顧客的品 牌忠誠度。

    5.2 精準(zhǔn)的品牌和市場定位

    風(fēng)格鮮明和與眾不同一直是買手店經(jīng)營的核心,找準(zhǔn)自身的特色以吸引目標(biāo)客戶群對買手店的成功至關(guān)重要。特別是在市場細(xì)分度極高的時尚領(lǐng)域,買手店的特色除了對于產(chǎn)品風(fēng)格和差異性的把控外,還包括對產(chǎn)品價格的,店鋪選址以及店面設(shè)計等。

    與歐美買手店高端小眾人群定位不同,中國買手店的目標(biāo)人群價位相對較低。很多大型買手店,如連卡佛和 IT 都調(diào)整在中國的銷售戰(zhàn)略,引入了一系列低價商品來吸引消費者。而意大利買手店 10 Corso Como 退出北京 SKP 的很大原因就是因為過高的定價以致于商品無人問津。由于國內(nèi)海關(guān)、稅收等原因,本土買手店對采購的國外設(shè)計師品 牌商品很難形成價格優(yōu)勢,因此在保證買手店風(fēng)格統(tǒng)一的前提下,利用本土優(yōu)勢,挖掘優(yōu)秀的國內(nèi)設(shè)計師品牌,加大本土品牌的商品占比,有利于控制價格,吸引更多顧客。

    買手店店鋪的位置也極為重要,它直接關(guān)系著買手店的成本高低以及營業(yè)額數(shù)量。上文提到2015年關(guān)店的暗黑先鋒風(fēng)格的高端買手店NY Fashion Studio,在店面選址上是一個反面案例。暗黑先鋒風(fēng)格設(shè)計師品牌本身就屬于十分的小眾市場,店鋪選在廣州市中心商業(yè)地帶即增加了成本又無法覆蓋顧客群,是極其失敗的。德國知名的暗黑風(fēng)格買手店Darklands Berlin的選址就與 NY Fashion Studio 不同,它遠(yuǎn)離市中心,在一 個極度偏遠(yuǎn)的工廠地帶。這樣偏僻的地址如果不是店鋪的受眾群是絕對不會去的,而同時這個地址也更符合店鋪小眾獨特的風(fēng)格,最重要的是降低了成本支出。因此,當(dāng)選擇買手店店鋪地址時更應(yīng)該多考慮是否與目標(biāo)顧客群相符,而不是一味選擇金融繁華地帶。

    為了能在眾多的店鋪中脫穎而出,買手店一般從室內(nèi)設(shè)計開始就會另辟蹊徑。大部分買手店通常會以當(dāng)?shù)爻鞘刑厣⒌赇侊L(fēng)格以及時下流行話題等作為靈感,為消費者提供 不一樣的視覺體驗,呈現(xiàn)自身獨特氣質(zhì)。買手店作為時尚先鋒聚集地,對一成不變的排斥也是它必須具備的特質(zhì)。因此定期更換店面修飾,調(diào)整商品陳列方式,不停地給予顧客新鮮感與視覺沖擊。川久保玲的 Dover Market Street 除了永遠(yuǎn)充滿真實驗前衛(wèi)的氣息外,最知名的就是它沒有櫥窗展示但是卻永遠(yuǎn)充滿新鮮感的店內(nèi)布置,DSM 堅持每半 年重新設(shè)計一次店面,各種各樣的文化概念都有機會被呈現(xiàn)。用店鋪的“改頭換面”使顧客時時感受不一樣的新鮮感應(yīng)該是最簡單的辦法了。

    5.3 多樣化服務(wù)體驗

    隨著國內(nèi)時尚消費者消費品質(zhì)的提高以及對個性化事物需求的增加,為了吸引和加強目標(biāo)客戶群的忠誠度,越來越多的買手店還會為顧客免費提供各類體驗服務(wù)。比如私人形象設(shè)計,節(jié)日定制禮品、新品發(fā)布會邀請以及各種藝術(shù)展、時尚沙龍等等。多樣化的服務(wù)體驗一方面是為了維系顧客與店鋪情感,另一方面則是為店鋪提供了多元化營銷渠道,通過社交媒體擴大店鋪影響力,增加目標(biāo)客戶群體數(shù)量。


    6

    結(jié)論與展望

    在全球買手市場普遍處于衰落的情況下,中國大陸買手市場卻呈現(xiàn)出上升趨勢,吸引全球知名買手店落戶中國,帶動中國買手市場水平的不斷攀升。中國本土買手店起步雖晚但發(fā)展迅猛,不僅買手店的數(shù)量急劇增加,而且范圍也從一線城市擴展到二三線城市。特別是一些優(yōu)質(zhì)的本土買手店在創(chuàng)造出自己品牌特色的同時,更挖掘出了一大批優(yōu) 秀的中國本土設(shè)計師品牌,帶動整個中國時尚設(shè)計產(chǎn)業(yè)前進一大步。但是不能否認(rèn)的是, 中國買手市場在往前進步的同時也面臨著許多困難。買手店受眾群體較小,專業(yè)買手人員的極度缺乏以及市場規(guī)范不健全,買手店整體水平參差不齊等問題都亟待解決。中國擁有世界上最大的消費群體,中國消費者的消費能力與品質(zhì)也在逐年上升,因此中國買手店具有巨大的發(fā)展?jié)摿?未來可期。買手店的成熟需要較長的時間與經(jīng)驗,因此中國買手市場也需要一個充足的生長的空間和時間去不斷完善自己。


    參考文獻

    [1] 云娟娟.買手春天[J].中國服飾,2018(8):38-39

    [2] 姜姝宇.零售業(yè)集合店業(yè)態(tài)發(fā)展的現(xiàn)狀、阻礙及趨勢[J].商業(yè)經(jīng)濟研究, 2018(6):24-26

    [3] 王乙婷.市場被 Farfetch 擠壓,Yoox Net-a-porter 第一季度收入增速放緩 時尚頭條網(wǎng),2018 http://news.ladymax.cn/201805/16-33508.html

    [4] 張倩,唐強. 網(wǎng)絡(luò)購物時代精品店的情感主題[J].電子商務(wù),2015(11):101-103 [5] 王晶晶,楊義雄. 設(shè)計師品牌買手店 O2O 營銷模式[J].紡織學(xué)報,2018(39):146-151 [6] 董笑妍,張彥山. 揭開買手店面紗中國式買手店運營模式初探[J].紡織服裝周刊, 2014(15)

    [7] Frank Lavin. How To Win And Retain Loyal Customers In China. FORBES,2018https://www.forbes.com/sites/franklavin/2018/05/21/how-to-win-and-retain-lo yal-customers-in-china/#52beeece2806

    [8] 宋璇,王書利. 潮流配飾——國內(nèi)時尚買手店發(fā)展新探究[J]. 時尚設(shè)計與工 程 ,2017(6):10-12

    [9] Brands Turn to China’s Digital Influencers to Fuel Sales. Financial Time, 2017 https: www.ft.com

    [10] 趙洪珊.時尚買手機制與我國服裝產(chǎn)業(yè)鏈的融合[J]防織導(dǎo)報 2010(3):20-22 [11] 董笑妍. 買手店離盈利還有多遠(yuǎn)[J]. 紡織服裝周刊,2015(1):59-59

    [12] 朱斌.電商背景下都市商圈發(fā)展模式分析[J].商業(yè)經(jīng)濟研究,2018(1):150-152


    作者簡介


    徐鏑雅 碩士研究生

    北京服裝學(xué)院中國生活方式設(shè)計研究院奢侈品設(shè)計與管理專業(yè) 研究方向:時尚品牌管理與營銷

    郵箱:landyxdy@hotmail.com


    李喆 博士 副教授

    北京服裝學(xué)院中國生活方式設(shè)計研究院 院長助理 奢侈品設(shè)計與管理專業(yè)碩士研究生導(dǎo)師 研究方向:奢侈品牌設(shè)計與管理

    郵箱:361597823@qq.com


    排版|王小果

網(wǎng)站首頁   |    關(guān)于我們   |    公司新聞   |    產(chǎn)品方案   |    用戶案例   |    售后服務(wù)   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區(qū)    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權(quán)所有