微服務實踐之路--RPC


微服務實踐之路--RPC

重點來了,本文全面闡述一下我們的RPC是怎么實現並如何使用的,跟Kubernetes和Openstack怎么結合。 
在選型一文中說到我們選定的RPC框架是Apache Thrift,它的用法是在Main方法中重啟服務,在Client端連接服務去調用,

而我的想法是要跟Dubblo、HSF的用法一樣,因為很多人都熟習這兩個框架的用法,特別是我們好幾個項目都是基於EDAS開發的,而且世面上用Dubbo的公司也很多。

順便再說一下我們對於RPC的幾點要求:

  • 1,兼容Dubbo和HSF的使用方法,支持版本和服務分組,支持項目隔離
  • 2,客戶端重試機制,可以配置次數和間隔時間
  • 3,客戶端線程池
  • 4,服務端可以平滑無縫升級而不影響客戶端的使用

兼容Dubbo就必然要使用Spring框架,那我們就直接上Spring Boot好了,號稱Spring Boot為微服務開發而生,一步到位,將Thrift跟Spring Boot整合。

版本和服務分組可以通過Kubernetes的Service的Label來實現,我們客戶端在查找服務的時候通過這兩個標簽再加上接口類的Label來定位Service的Cluster IP,這里不直接使用Service名稱來調用服務的原因是通過Label查詢Servcie更加靈活一些,Service的名稱不受限制,隨時可以啟動一個帶有相同Label的新Service來替換舊的Service.
項目隔離可以用Kubernetes的namespace來實現,一個namespace是一個項目,當然項目之間也可以互相調用,默認情況下是整個Kubernetes集群的服務都是可以被調用到的如果在沒有指定namespace的情況下。

客戶端重試機制用代理Thrift連接的方式來實現,在連接或接口方法調用異常時發起重新連接,參考:https://liveramp.com/engineering/reconnecting-thrift-client/

客戶端連接池是由於在WEB項目中每次用戶發起請求是在一個獨立的線程中,而Thrift的Client Socket連接不是線程安全的,因此要為每個用戶准備一個Socket連接,有點像數據庫的連接池,這個可以用apache的commons pool2來實現,這個有很多網友的文章可以參考,本文就不在贅述了。

服務端平滑升級可以使用Kubernetes的Kubectl rolling-update來實現,它的機制是先創建一個RC,然后新建一個新版本Pod,停掉一個舊版本Pod,逐步來完成整個RC的更新,最后刪除舊的RC,把新的RC名稱改為舊的RC名稱,升級過程如下圖:

 
這里會有一個問題,因為有一個時間段會新舊RC共存,由於Service是基於RC的Label建立的,那過來的請求是不是會得到兩種結果?


 如果是的話要防止這樣的情況發生就要像上面說的,將整個Service替換,先啟動一個新的Service跟舊的Service有相同Label,然后刪除舊的Service以及RC,在發生服務請求的時候Thrift Client在找不到舊的服務的時候根據Label重新查找Service就會切換到新的Service上。

 
下面展示一下大概的實現及使用方法,假設你熟習Kubernetes或者簡單了解,熟習Docker。

服務端

配置

     <bean class="io.masir.testcloud.thrift.HelloImpl" id="helloImpl"/>     <bean class="io.masir.testcloud.thrift.ThriftSpringProviderBean" init-method="init" id="providerBean">         <property name="serviceInterface" value="io.masir.testcloud.thrift.HelloService"/>         <property name="serviceVersion" value="1.0.0"/>         <property name="serviceGroup" value="testServiceGroup"/>         <property name="target" ref="helloImpl"/>     </bean>

ThriftSpringProviderBean核心代碼 這是Thrift和Spring整合的核心代碼,可以借鑒其它網友的Thrift Spring實例。

 public class ThriftSpringProviderBean  extends Thread {       private int port = 10809;     private String serviceInterface;     private String serviceVersion;     private String serviceGroup;     private Object target;       public void run() {         try {             TServerSocket serverTransport = new TServerSocket(getPort());             Class Processor = Class.forName(getServiceInterface() + "$Processor");             Class Iface = Class.forName(getServiceInterface() + "$Iface");             Constructor con = Processor.getConstructor(Iface);             TProcessor processor = (TProcessor) con.newInstance(getTarget());             TBinaryProtocol.Factory protFactory = new TBinaryProtocol.Factory(true, true);             TThreadPoolServer.Args args = new TThreadPoolServer.Args(serverTransport);             args.protocolFactory(protFactory);             args.processor(processor);             TServer server = new TThreadPoolServer(args);             logger.info("Starting server on port " + getPort() + " ...");             server.serve();         } catch (TTransportException e) {             e.printStackTrace();         } catch (Exception e) {             e.printStackTrace();         }     }       public void init() {         start();     }       public String getServiceInterface() {         if(serviceInterface.endsWith(".Iface")){             serviceInterface = serviceInterface.replace(".Iface","");         }         return serviceInterface;     } }

客戶端

考慮到Kubernetes是有負載和服務發現的功能,那我們如何跟Thrift整合在一起使用是我們要解決的問題

配置

     <bean class="io.masir.testcloud.thrift.ThriftClientBeanProxyFactory">         <property name="k8sAPIServer" value="http://100.0.1.5:8080/"/>         <property name="interfaceName" value="io.masir.testcloud.thrift.HelloService"/>         <property name="version" value="0.0.1"/>         <property name="group" value="thrifttest"/>     </bean>

k8sAPIServer 是Kubernetes的API地址,用來根據 group、version、interfaceName 三個參數查找服務的集群地址

ThriftClientBeanProxyFactory 的實現請參考 http://blog.csdn.net/muyuxuebao/article/details/51556066  ,包括重新機制也有了。

另外推薦一個Kubernetes Api訪問的Java組件,非常好用和靈活

         <dependency>             <groupId>io.fabric8</groupId>             <artifactId>kubernetes-client</artifactId>             <version>1.4.14</version>         </dependency>

生成Image

服務的Dockerfile

FROM registry2.io/public/java:7 Copy jn-boot-0.0.1.jar /jn-boot.jar EXPOSE 10809 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo Asia/Shanghai > /etc/timezone ENV TZ Asia/Shanghai ENTRYPOINT java -jar /jn-boot.jar

消費者Dockerfile

FROM registry2.io/public/java:7 COPY jn-boot-client-0.0.2.jar /jn-boot-client.jar EXPOSE 10809 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo Asia/Shanghai > /etc/timezone ENV TZ Asia/Shanghai ENTRYPOINT java -jar /jn-boot-client.jar

需要有兩個地方注意一下,使用Copy,每次都要覆蓋拷貝到Image中,另一個是日期應該放在基礎Image中,Build生成Image后Push到我們Registry中。

部署到Kubernetes

程序開發或者說開發思路基本實現了,下面就是部署上線測試,Kubernetes的Pods基於Docker運行,那就會用到Registry,一個Pod會是一個Docker容器,所以Kubernetes的流程是從Registry中拿到Image然后啟動一個Dokcer容器,由於我們配置的Registry是有權限的,所以要先生成Kubernetes的Secrets,

kubectl create secret docker-registry registry2key --docker-server=registry2.io --docker-username=admin --docker-password=1 --docker-email=xxxx@163.com --namespace=thrift-demo

然后在yaml中配置:

apiVersion: v1 kind: ReplicationController metadata:   name: thrift-c   namespace: thrift-demo spec:   replicas:1   selector:     app: thrift-c   template:     metadata:       name: thrift-c       labels:         app: thrift-c     spec:       containers:       - name: thrift-c         image: registry2.io/thrift/thrift-c:0.0.2         imagePullPolicy: Always         ports:         - containerPort: 9091       imagePullSecrets:         - name: registry2key

注意里面的 imagePullSecrets registry2key

{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "thrift-c-app", "namespace": "thrift-demo" }, "spec": { "selector": { "app": "thrift-c" }, "ports": [ { "protocol": "TCP", "port": 9091, "targetPort": 9091 } ] } }

Kubernetes的配置網上有很多,大家分頭去參考,這里不過多說明,這是一個Thrift客戶端的Kubenetes RC和Service配置,在Kubernetes Master雲主機上通過Kubectl運行並啟動這個RC

 
 
另外還需要部署Thrift服務端的RC、Service,如圖:

(請注意服務端的Service的Label)

下面是Replication Controllers

調用測試,查看服務的訪問地址,我們的客戶端服務使用的是Nodeport,查看Nodeport的方式,或者在Dashboard上查看

然后通過Kubernetes集群中的任意一台機器加上NodePort端口就可以訪問我們的Thrift客戶端服務了。

在本文中我們可以看到使用了大量的Kubernetes特性,服務發現、服務負載(基於Service)、滾動升級等等,其中服務發現是在我們添加了Pods數量后會被Service自動發現,包括后面要說的自動擴容,而負載就是Service會在所有Pods中通過某種機制選擇某個Pod來調用,事實上還有很多Kubernetes的特性等待我們去使用和發掘,Kubernetes真是一個得力的容器助手,希望我們能把它用好,也希望Kubernetes越來越完善。

在下文中我們將說一說服務的發布,總不能都通過IP+NodePort的方式來訪問所有WEB服務吧,一定要有一個完美的合適的解決辦法,那會是什么呢。。。


免責聲明!

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



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