zoukankan      html  css  js  c++  java
  • Openstack+Kubernetes+Docker微服务实践之路--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服务吧,一定要有一个完美的合适的解决办法,那会是什么呢。。。

  • 相关阅读:
    Junit单元测试
    win7的6个网络命令
    WOJ1024 (POJ1985+POJ2631) Exploration 树/BFS
    WOJ1022 Competition of Programming 贪心 WOJ1023 Division dp
    woj1019 Curriculum Schedule 输入输出 woj1020 Adjacent Difference 排序
    woj1018(HDU4384)KING KONG 循环群
    woj1016 cherry blossom woj1017 Billiard ball 几何
    woj1013 Barcelet 字符串 woj1014 Doraemon's Flashlight 几何
    woj1012 Thingk and Count DP好题
    woj1010 alternate sum 数学 woj1011 Finding Teamates 数学
  • 原文地址:https://www.cnblogs.com/xguo/p/6090088.html
Copyright © 2011-2022 走看看