微服务实践之路--rpc

Leo_wlCnBlogs Leo_wlCnBlogs     2022-08-15     732

关键词:

微服务实践之路--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服务吧,一定要有一个完美的合适的解决办法,那会是什么呢。。。

springcloud微服务实践之路-eurekaserver中的第一个异常

EMERGENCY!EUREKAMAYBEINCORRECTLYCLAIMINGINSTANCESAREUPWHENTHEY‘RENOT.RENEWALSARELESSERTHANTHRESHOLDANDHENCETHEINSTANCESARENOTBEINGEXPIREDJUSTTOBESAFE 查看详情

微服务实践:微服务的事件驱动数据管理

 微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动... 查看详情

微服务实践:从单体式架构迁移到微服务架构

 微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动... 查看详情

微服务实战:选择微服务部署策略

 微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动... 查看详情

微服务实践

...了一年多,从16年初到现在。在使用SpringCloud之前我们对微服务实践是没有太多的体会和经验的。从最初的开源软件云收藏来熟悉SpringBoot,到项目中的慢慢使用,再到最后全面拥抱SpringCloud。这篇文章就给大家介绍 查看详情

chrisrichardson微服务实战系列

微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动数据... 查看详情

java微服务实践—springboot系列

Java微服务实践—SpringBoot系列网盘地址:https://pan.baidu.com/s/1aMqPO4pXLeXDHvRuze-JWw密码:j62z备用地址(腾讯微云):https://share.weiyun.com/c74335d7e383158ee3c4aaf193d471ed密码:r5gqsk 查看详情

java微服务实践—springboot系列

Java微服务实践—SpringBoot系列网盘地址:https://pan.baidu.com/s/1aMqPO4pXLeXDHvRuze-JWw密码:j62z备用地址(腾讯微云):https://share.weiyun.com/c74335d7e383158ee3c4aaf193d471ed密码:r5gqsk 查看详情

京东宅急送的微服务实践分享(下)|架构师小组交流会

...点评关于高可用架构的实践分享第三期:京东、宅急送的微服务实践分享(上)第三期小组交流会邀请到了国内电商领头羊京东、宅急送技术负责人,在上篇京东、宅急送的微服务实践分享(上)中他们介绍了各自产 查看详情

京东宅急送的微服务实践分享(下)|架构师小组交流会

...点评关于高可用架构的实践分享第三期:京东、宅急送的微服务实践分享(上)第三期小组交流会邀请到了国内电商领头羊京东、宅急送技术负责人,在上篇京东、宅急送的微服务实践分享(上)中他们介绍了各自产 查看详情

微服务实践之网关(springcloudgateway)详解-springcloud(2021.0.x)-3(代码片段)

...业目的注明出处可自由转载出自:shusheng007系列文章微服务实践之服务注册与发现(Nacos)-SpringCloud(2020.0.x)-1微服务实践之负载均衡(SpringCloudLoadBalancer)-SpringCloud(2020.0.x)-2概述本文将介绍微服务架构中的SpringCloudGateway这... 查看详情

微服务实践(总)-原文

...tp://dockone.io/people/hokingyang 希望读者通过本系列文章对微服务优缺点有一个比较好的理解,以及何时使用这种架构。也许微服务架构比较适合你的应用。也许你正在开发一个大型、复杂单体式应用,日常开发和部署经验非常缓... 查看详情

中小型互联网公司微服务实践-经验和教训

...了一年多,从16年初到现在。在使用SpringCloud之前我们对微服务实践是没有太多的体会和经验的。从最初的开源软件云收藏来熟悉SpringBoot,到项目中的慢慢使用,再到最后全面拥抱SpringCloud。这篇文章就给大家介绍 查看详情

免费领取|微服务实践沙龙-上海站大咖演讲资料分享

...云社区。9月1日,网易云联合谐云在上海InnoSpace举办了“微服务实践沙龙”,邀请到了携程和蚂蚁金服等微服务的先行者,共同分享了落地实践过程中总结的干货经验。感谢讲师和上海小伙伴们的支持,这里将演讲资料分享给大... 查看详情

serverless下的微服务实践

...Server,只需要去专注业务逻辑即可,本文将详解微服务体系与平台化的Serverless架构融合的过程。微服务架构介绍微服务架构诞生背景在互联网早期即Web1.0的时代,当时流行的是单体应用,研发团队比较小,主... 查看详情

3.16go微服务实战(微服务理论)---go语言基于es-cqrs的微服务实践

第18章 Go语言基于ES-CQRS的微服务实践18.1 理论介绍 18.1.1 事件溯源 指的是将每次的事件都记录下来,而不是去记录对象的状态。只能追加,不能对已经添加的事件做修改。 18.1.2 命令查询职责分离 1.命令 不返回任何结果... 查看详情

3.16go微服务实战(微服务理论)---go语言基于es-cqrs的微服务实践

第18章 Go语言基于ES-CQRS的微服务实践18.1 理论介绍 18.1.1 事件溯源 指的是将每次的事件都记录下来,而不是去记录对象的状态。只能追加,不能对已经添加的事件做修改。 18.1.2 命令查询职责分离 1.命令 不返回任何结果... 查看详情

微服务实践:从单体式架构迁移到微服务架构

迁移到微服务综述迁移单体式应用到微服务架构意味着一系列现代化过程,有点像这几代开发者一直在做的事情,实时上,当迁移时,我们可以重用一些想法。一个策略是:不要大规模(bigbang)重写代码(只有当你承担重建一... 查看详情