springcloud微服務總結二 注冊中心


一:netflix和springcloud關系

netflix公司開源了很多組件,包括服務注冊與發現(Netflix Eureka)、斷路器(Netflix Hystrix)、負載均衡(Netflix Ribbon)、網關(Netflix Zuul)、配置管理(Netflix Archaius)、事件總線(spring cloud bus)等等。

springcloud對這些組件實現了封裝,形成spring-cloud-netflix核心模塊。雖然Spring Cloud到現在為止不只有Netflix提供的方案可以集成,還有很多方案,但Netflix是最成熟的。

但是netfilx公司在宣布2.0以后就不在對源碼進行開源

二:Eureka簡介

 

為什么使用Eureka

微服務架構,我認為最核心的問題是就是要解決服務發現與負載均衡,而eureka就是為了解決服務的注冊與發現,ribbon解決負載均衡

 

Eureka采用的是典型的C-S架構,在Eureka架構中有3個重要角色

1:Eureka Server (注冊中心服務端)

2:Service Provider(服務提供者客戶端)

3:Service Consumer(服務消費者客戶端)

4 :服務監控界面

 

三 dubbo的原理對比

Provider

暴露服務方稱之為“服務提供者”。
 
Consumer
調用 遠程服務方稱之為“服務消費者”。
 
Registry
服務注冊與發現的中心目錄服務稱之為“服務注冊中心”。
 
Monitor
統計服務的調用次數和調用時間的日志服務稱之為“服務監控中心”。
 
(1) 連通性:
注冊中心負責服務地址的注冊與查找,相當於 目錄服務,服務提供者和消費者只在啟動時與注冊中心交互,注冊中心不轉發請求,壓力較小
監控中心負責統計各服務調用次數,調用時間等,統計先在內存匯總后每分鍾一次發送到監控中心服務器,並以報表展示
服務提供者向注冊中心注冊其提供的服務,並匯報調用時間到監控中心,此時間不包含網絡開銷
服務消費者向注冊中心獲取服務提供者地址列表,並根據負載算法直接調用提供者,同時匯報調用時間到監控中心,此時間包含網絡開銷
注冊中心,服務提供者,服務消費者三者之間均為長連接,監控中心除外
注冊中心通過 長連接感知服務提供者的存在,服務提供者宕機,注冊中心將立即推送事件通知消費者
注冊中心和監控中心全部宕機,不影響已運行的提供者和消費者,消費者在 本地緩存了提供者列表
注冊中心和監控中心都是可選的,服務消費者可以直連服務提供者
 
(2) 健壯性:
監控中心宕掉不影響使用,只是丟失部分 采樣數據
數據庫宕掉后,注冊中心仍能通過 緩存提供服務列表查詢,但不能注冊新服務
注冊中心對等 集群,任意一台宕掉后,將自動切換到另一台
注冊中心全部宕掉后,服務提供者和服務消費者仍能通過本地緩存通訊
服務提供者無狀態,任意一台宕掉后,不影響使用
服務提供者全部宕掉后,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復
 
(3) 伸縮性:
注冊中心為對等集群,可動態增加機器部署實例,所有客戶端將自動發現新的注冊中心
服務提供者無狀態,可動態增加機器部署實例,注冊中心將推送新的服務提供者信息給消費者

  • 面向接口代理的高性能RPC調用
    提供高性能的基於代理的遠程調用能力,服務以接口為粒度,為開發者屏蔽遠程調用底層細節。
  • 智能負載均衡
    內置多種負載均衡策略,智能感知下游節點健康狀況,顯著減少調用延遲,提高系統吞吐量。
  • 服務自動注冊與發現
    支持多種注冊中心服務,服務實例上下線實時感知。
  • 高度可擴展能力
    遵循微內核+插件的設計原則,所有核心能力如Protocol、Transport、Serialization被設計為擴展點,平等對待內置實現和第三方實現。
  • 運行期流量調度
    內置條件、腳本等路由策略,通過配置不同的路由規則,輕松實現灰度發布,同機房優先等功能。
  • 可視化的服務治理與運維
    提供豐富服務治理、運維工具:隨時查詢服務元數據、服務健康狀態及調用統計,實時下發路由策略、調整配置參數。

 

四 簡單的rpc服務治理架構的設計

 

  可以使用socket建立一個長連接,在服務提供者調用注冊中心的注冊方法,消費端調用服務的調用方法,底層需要用到反射加動態代理

代碼核心代碼:

package com.cxy;



import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * RpcFramework
 * 
 * @author william.liangf
 */
public class RpcFramework {

    /**
     * 暴露服務
     * 
     * @param service 服務實現
     * @param port 服務端口
     * @throws Exception
     */
    public static void export(final Object service, int port) throws Exception {
        if (service == null)
            throw new IllegalArgumentException("service instance == null");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Export service " + service.getClass().getName() + " on port " + port);
        ServerSocket server = new ServerSocket(port);
        for(;;) {
            try {
                final Socket socket = server.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            try {
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                try {
                                    String methodName = input.readUTF();
                                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                                    Object[] arguments = (Object[])input.readObject();
                                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                                    try {
                                        Method method = service.getClass().getMethod(methodName, parameterTypes);
                                        Object result = method.invoke(service, arguments);
                                        output.writeObject(result);
                                    } catch (Throwable t) {
                                        output.writeObject(t);
                                    } finally {
                                        output.close();
                                    }
                                } finally {
                                    input.close();
                                }
                            } finally {
                                socket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 引用服務
     * 
     * @param <T> 接口泛型
     * @param interfaceClass 接口類型
     * @param host 服務器主機名
     * @param port 服務器端口
     * @return 遠程服務
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if (interfaceClass == null)
            throw new IllegalArgumentException("Interface class == null");
        if (! interfaceClass.isInterface())
            throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
        if (host == null || host.length() == 0)
            throw new IllegalArgumentException("Host == null!");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
                Socket socket = new Socket(host, port);
                try {
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                    try {
                        output.writeUTF(method.getName());
                        output.writeObject(method.getParameterTypes());
                        output.writeObject(arguments);
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                        try {
                            Object result = input.readObject();
                            if (result instanceof Throwable) {
                                throw (Throwable) result;
                            }
                            return result;
                        } finally {
                            input.close();
                        }
                    } finally {
                        output.close();
                    }
                } finally {
                    socket.close();
                }
            }
        });
    }

}

消費者和提供者代碼:

package com.cxy;
/**
 * 
 * 
 * 服務暴露方法,需要調用服務的export方法
 * @author 15084
 *
 */
public class RpcProvider {
    public static void main(String[] args) throws Exception {
        HelloService service = new HelloServiceImpl();
        RpcFramework.export(service, 1234);
    }

}
package com.cxy;
/**
 * 
 * 服務消費方,需要調用rpcframework中的那個refer方法,來對服務進行調用
 * @author 15084
 *
 */
public class RpcConsumer {
    public static void main(String[] args) throws Exception {
        HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
        for (int i = 0; i < Integer.MAX_VALUE; i ++) {
            String hello = service.hello("World" + i);
            System.out.println(hello);
            Thread.sleep(1000);
        }
    }
}
package com.cxy;

public interface HelloService {
    String hello(String name);

}
package com.cxy;

public class HelloServiceImpl  implements HelloService{

     public String hello(String name) {
            return "Hello " + name;
        }

}

 

 

五 注冊中心eureka的服務模塊搭建:

  在搭建父工程的時候引入一個springcloud組件,這樣就可以進行版本控制,然后將相關組價刪除就好

然后在注冊中心模塊引入eureka的依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

然后加入配置文件:

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false #單機版建議設置為false,設置false的目的是防止自己注冊自己,集群版采用默認的true
    fetch-registry: false  #單機版建議設置為false,設置false的目的是防止自己發現自己,集群版采用默認的true
spring:
  application:
    name: eureka-server    # 應用名

在啟動服務的時候加上@EnableEurekaServer這個注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
 
@SpringBootApplication
@EnableEurekaServer  //開啟eureka服務
public class EurekaServerApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

這樣就可以進行啟動了

就可以通過訪問localhost:8761/,訪問到注冊中心的管理界面

 

六 zookeeper或者console作為注冊中心:

eureka作為注冊中心是需要自己新建工程,而console和zookeeper本事一種程序只需要啟動相關組件服務就可以並不需要進行代碼操作,只是在調用端的時候進行相關發現的修改

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM