grpc基于envoy治理 java实现 control panel

2024-01-01 10:38

本文主要是介绍grpc基于envoy治理 java实现 control panel,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

envoy的部署与架构:

见SRE空间:envoy

envoy基础(1.7版本的):

 PDF

业内厂商实现案例:

网易轻舟:

其中有几个关注点:

1.轻舟的这套服务网格技术的研发成员有C++的研发人员

2.轻舟的envoy部署模式是sidecar模式(要是按照这种模式部署,对雪球来说步子跨的是不是有些大?)

 PDF

从kong到envoy的演进:https://zhuanlan.zhihu.com/p/242260216?utm_source=wechat_session

其中有几个关注点:

1.从严选的架构可以看到在envoy之前,网易是有api网关规划的(当然雪球自研的openresty+warden有与之对应功能)

2.演进过程可以看出是兼容升级的(而我们目前旧的体系要实现哪些部分的梳理还欠缺)

 

Apisix Linkerd Envoy对比

envoy自身也有比较docs:https://www.servicemesher.com/envoy/intro/comparison.html

nginx

haproxy

AWS ELB

SmartStack

Finagle

proxygen 和 wangle

gRPC

linkerd

nghttp2

 

首先Linkerd与Envoy的对比,作者在issue里有介绍:

https://github.com/envoyproxy/envoy/issues/99

linkerd是一个独立的、开源的RPC路由代理,建立在Netty和Finagle上。

linkerd提供了许多Finagle的特性,包括感知延迟的负载平衡、连接池、断路、重试预算、截止日期、跟踪、细粒度检测,以及用于请求级路由的流量路由层。

linkerd提供了一个可插拔的服务发现接口(对Consul和ZooKeeper,以及Marathon和Kubernetes api提供标准支持)。

 

linkerd的内存和CPU要求明显高于envoy的。

然而,它的基础技术是广泛的生产测试和广泛部署。

与Envoy不同的是,linkerd提供了一种极简的配置语言,并且明确地不支持热加载,而是依赖于动态供应和服务抽象。

linkerd支持HTTP/1.1, Thrift, ThriftMux, HTTP/2(实验性)和gRPC(实验性)。

 

至于Apisix和Envoy

网上一大堆,找出来的基本上都是相互类似的

 APISIX 在响应延迟和 QPS 层面都略优于 Envoy, 由于 nginx 的多 worker 的协作方式在高并发场景下更有优势,得益于此, APISIX 在开启多个 worker 进程后性能提升较 Enovy 更为明显;

 

但是两者并不冲突, Envoy 的总线设计使它在处理东西向流量上有独特的优势, APISIX 在性能和延迟上的表现使它在处理南北向流量上具有海量的吞吐能力,根据自己的业务场景来选择合理的组件配合插件构建自己的服务才是正解。

grpc基于envoy治理实现

server端

这个使用envoy提供的ADS注册

路由规则和cluster、endpoint的注册则根据接口动态修改,这样整个设计模式就是按照service mesh概念里的控制面板抽离出来了

public static VirtualHost getVirtualHost(String name, String clusterName) {

    RouteAction routeAction = RouteAction.newBuilder()

            .setCluster(clusterName)

            .build();

 

    HeaderMatcher headerMatcher = HeaderMatcher.newBuilder()

            .setName("UID")

            .setExactMatch("5784024476")

            .build();

 

    RouteMatch routeMatch = RouteMatch.newBuilder()

            .setGrpc(GrpcRouteMatchOptions.newBuilder()) // 确定只匹配Grpc Match

            .setPrefix("/")

            .addHeaders(headerMatcher)

            .build();

    Route route = Route.newBuilder()

            .setMatch(routeMatch)

            .setRoute(routeAction)

            .build();

    VirtualHost virtualHost = VirtualHost.newBuilder()

            .setName(name)

            .addDomains("*")

            .addRoutes(route)

            .build();

 

    return virtualHost;

}

 

服务发现动态配置,示例代码:

Endpoint endpoint = ClusterSnapshot.toEndpoint(host + ":" + port);

ClusterLoadAssignment clusterLoadAssignment = simpleCache.getSnapshot(0).endpoints().resources().get(CLUSTER_NAME);

LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.newBuilder()

        .addLbEndpoints(LbEndpoint.newBuilder().setEndpoint(endpoint).build())

        .build();

 

List<ClusterLoadAssignment> assignments = ImmutableList.<ClusterLoadAssignment>builder()

        .add(clusterLoadAssignment.toBuilder().addEndpoints(localityLbEndpoints).build())

        .build();

 

Cluster.EdsClusterConfig edsClusterConfig = ClusterSnapshot.getEdsClusterConfig(CLUSTER_NAME, DISCOVERY_CLUSTER_NAME);

List<Cluster> clusters = ImmutableList.<Cluster>builder()

        .add(ClusterSnapshot.getCluster(CLUSTER_NAME, edsClusterConfig))

        .build();

Snapshot snapshot = Snapshot.create(

        clusters,

        assignments,

        ImmutableList.of(),

        ImmutableList.of(),

        ImmutableList.of(),

        version);

simpleCache.setSnapshot(0, snapshot);

 

client端

对于路由信息目前是在header(也就是GRPC的类Metadata)里面附加路由参数:

https://skyao.io/learning-grpc/basic/metadata/

提供对读取和写入元数据数值的访问,元数据数值在调用期间交换。

key 容许关联到多个值。

这个类不是线程安全,实现应该保证 header 的读取和写入不在多个线程中并发发生。

client代码实现:ClientHeaderInterceptor

public class ClientHeaderInterceptor implements ClientInterceptor {

 

    private static final Logger logger = Logger.getLogger(ClientHeaderInterceptor.class.getName());

    Metadata.Key<String> cookie = Metadata.Key.of("cookie", Metadata.ASCII_STRING_MARSHALLER);

 

    @Override

    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,

                                                               CallOptions callOptions, Channel next) {

        return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {

            @Override

            public void start(Listener<RespT> responseListener, Metadata headers) {

                //此处为你登录后获得的cookie的值

                headers.put(cookie, "U=6371181803");

                super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {

                    @Override

                    public void onHeaders(Metadata headers) {

                        /**

                         * if you don't need receive header from server, you can

                         * use {@link io.grpc.stub.MetadataUtils#attachHeaders}

                         * directly to send header

                         */

                        logger.info("header received from server:" + headers);

                        super.onHeaders(headers);

                    }

                }, headers);

            }

        };

    }

}

调用,这部分先写死,后续更改为动态配置,示例代码:

static final Metadata.Key<String> COOKIE = Metadata.Key.of("cookie", Metadata.ASCII_STRING_MARSHALLER);

private final ManagedChannel originalChannel;

private final XUserDeviceServiceGrpc.XUserDeviceServiceBlockingStub blockingStub;

private final XUserDeviceServiceGrpc.XUserDeviceServiceBlockingStub headerBlockingStub;

 

public XueqiuPushUserClientTest(String host, int port) {

    originalChannel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();

    //将ClientHeaderInterceptor和原有的channel结合,生成包含拦截器的channel

    Channel channel = ClientInterceptors.intercept(originalChannel, new ClientHeaderInterceptor());

    blockingStub = XUserDeviceServiceGrpc.newBlockingStub(channel);

 

    //如果只需要在客户端传送header,而不需要接受服务端的header可以简单调用MetadataUtils.attachHeaders注册meta数据而不用定义Interceptor

    XUserDeviceServiceGrpc.XUserDeviceServiceBlockingStub stub = XUserDeviceServiceGrpc.newBlockingStub(originalChannel);

    Metadata meta = new Metadata();

    meta.put(COOKIE, "U=6371181803");

    headerBlockingStub = MetadataUtils.attachHeaders(stub, meta);

}

这篇关于grpc基于envoy治理 java实现 control panel的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/558998

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2