小小的SB磨合之路

2018-06-10 2018-06-17 2018-06-24 2018-07-01 2018-07-08 2018-07-15 2018-07-22 任务1 任务3 任务4 任务2 项目A 项目B 甘特图
一.开发具体问题
  1. 2021.09.26 SpringBoot自带的序列化工具是fasterxml的Jackson,今天有三个个问题?
    1) 如何搜索任意key
    (部分)JsonNodefindValue方法
    2) 什么时候使用ObjectMapperreadValue,什么时候使用readToTree
    readvalue-and-readtree-in-jackson-when-to-use-which

    readValue() can be used for any and all types, including JsonNode. readTree() only works for JsonNode (tree model); and is added for convenience.


    3) Json String中对应内部静态类字段不存在时反序列化后这个域是否存在
    不存在,为null,也就是说无参构造函数并没有执行,仔细想想,都没有识别到它的存在,为什么要执行构造函数

  2. 2021.09.23 谁引入了这个jar?
    某个类缺少一个方法,毫无疑问是版本不兼容,这个问题又来了(我在2019年8月遇到过一次);
    jar: servelet-api-2.3.jar
    class: javax.servlet.http.HttpServletResponse
    谁引入的这个jar?怎么找到pom依赖三元素?笨!jar文件前面Idea那里显示的就是三元素,输进maven helper分析即可

  3. 打包进jar的资源文件读取,ResourceUtils.getFile可以读取文件,那文件夹怎么读取?getUrl.toURI()来new一个File读取文件夹提示文件Path为空【补充具体异常】;原来打进jar的文件其实是不能用File读取,文件系统中只有jar这个文件,只能转成stream读出内容,文件夹怎么转成stream读出文件信息?这太难了,看这个方法:https://stackoverflow.com/a/68179822

  4. 2021.09.22 @Configuration类是个什么类,起什么作用?待答 Spring的配置可以可以认为是一个对象,比如一个prefix下映射到一个Bean,一部分属性在application.yaml一部分在active的配置中,最终都是可以生效的(经过测试验证了的)

  5. JPA定义的entity字段有NOT_NULL限制,也有defaultValue(数据库定义字段),为什么写不进去:not-null propertites reference a null or trasient value 因为写入数据库的语句肯定相应字段是null那么问题就变成了,JPA如何不更新某个字段但是读的时候要把该字段读出来?
    配置updatable = false即不更新这个字段
    或者
    联合字段注解@UpdatedDate 、entity上加注解@EntityListeners、任意类上注解@EnableJpaAuditing自动set日期字段 creation-timestamp-and-last-update-timestamp-with-hibernate-and-mysql注意字段要定义为
    解决updatetime字段问题后,新问题是用dao(CrudRepository)保存这个entity后立即查询出更新时间却为null,使用另一http请求访问却可以查出来:
    原因是open-in-view Spring Boot中建议关闭Open-EntityManager-in-view
    spring-open-session-in-view 建议细读
    what-is-this-spring-jpa-open-in-view-true-property-in-spring-boot 建议细读
    关于spring的事务管理,需要先理解AOP实现的原理,在一个被@Transaction注解的方法中(不设置额外的TxType),会随着方法调用默认在此线程传递,不需要特殊处理,在这个方法结束后则事务终止。
    那么,假如在service层不注解,如果在controller层看有事务吗?不会有
    事务什么时候开始的,和open session in view是什么关系?事务开始就是@Transaction注解的方法处开始,和open session in view 没有关系,open session in view只是会在请求进入的时候就申请MySQL连接,一些web接口是不需要访问db的

  6. DI 注入方式与@Autowired(因为Idea不建议Field反射注入: Field inject is not recommended)
    setter 注解在setter方法上
    constructor 注解在构造方法上
    Field反射 注解在Field
    配置注入 yaml先看-D jvm参数,再看系统环境变量

  7. Jackson序列化与反序列化相关注解 待答
    21.10.08补充 @JsonValue注解只会把这个域的值传给序列化器,其他域会被忽略;所以只能使用即带key又带body(另一个Json结构)的类来表示,用@JsonProperties来弥补,无法通过@JsonValue一键直达,一个类只能有一个JsonValue注解

  8. 3年前的疑问: Could not autowired: No beans of "kafkaProperties" type found----> @CompnentsScan

  9. 自定义的AspectControllerAdvice的顺序问题:自定义的Aspect用于监控Controller中请求处理时延和返回值日志输出,但当处理请求中出现异常时,则不会进行统计监测;尝试进行Order注解,并不管用,Order只能在各自同类之间起作用(数字越小优先级越高,ControllerAdvice注解的顺序:https://stackoverflow.com/a/19500823
    搜索得知有两个办法https://stackoverflow.com/a/59697304:1) 用Interceptor,但是这个对response输出时不会打印body; 2) 拦截ControllerAdvice中注解了@ExceptionHandlers的方法,但是这个会导致出现异常时会出现两个log
    其他补充:finally代码永远会被执行,在RET指令前; Actuator也是基于Interceptor: org.springframework.boot.actuate.metrics.web.servlet.LongTaskTimingHandlerInterceptor

  10. CrudRepository没有update方法,怎么update? how-do-i-update-an-entity-using-spring-data-jpa 中文CSDN博客:工作单元模式:Unit of Work 对某个业务操作,比如银行的进出账的业务对象(进账对象,出账对象)进行统一管理,使它们不需要在自己的repository中提交(commit),放在工作单元中做持久化工作

    In this case you have a set of objects managed by persistence library. All changes you make to these objects will be flushed to the database automatically at the end of Unit of Work (i.e. at the end of the current transaction in typical case). When you need to insert new record to the database, you make the corresponding object managed. Managed objects are identified by their primary keys, so that if you make an object with predefined primary key managed, it will be associated with the database record of the same id, and state of this object will be propagated to that record automatically.

  11. @Lazy注解解释 与 Spring AOP与 final class(比如String)

  12. postProcessAfterInitialization postConstruct afterPropertiesSet方法作用时机 适用场景分析:尽量使用afterPropertiesSet,这个时候@Autowire@Value注入已经完成; BPPA是用来factory创建后修改bean的,使用它的时候不能再使用autowire,并且应该独立起一个类编写。先PostConstruct 后执行afterPropertiesSet

  13. 如果Controller同时有返回值和传入HttpServletResponse,最终客户端收到的内容怎么定?测试结果显示:

  • 如果同时有return和HttpServletResponse则返回是个缝合怪:HttpServletResponse + 一部分return值
  • 如果return为null则以HttpServletResponse为准
  • 如果return为null但是HttpServletResponse的stream写是异步的, 则因为KeepAlive机制,会出现有的请求是空,后面的积攒了多个stream的回复
    是谁调用的controller里的方法? org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle Tomcat容器的调用org.apache.catalina.core.StandardWrapperValve
  1. 如何做将请求处理异步化,不占用htto-nio线程? 可以采用好多个方法 https://www.w3cschool.cn/article/55411104.html Servlet的AsyncContext,或者SpringBoot的@Async注解;那么问题来了,已有的一个Controller,怎么灵活地让它运行时同步或者异步?可以采用request.startAsync();AsyncContext context =request.getAsyncContext(); 在需要异步时,将结果写入HttpServletResponse,然后return null就行,实测能生效,不过执行线程还是http-nio线程们,且哪块逻辑哪个线程执行很乱
    图中的father thread是startAsync前,current thread是异步中的逻辑,complete 是AsyncListenerOncomplete中输出
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    最终实践我用@Async注解来完成,http-nio线程停在future.get, 实际上是因Object.wait函数阻塞,处于TIMED_WAITING状态,搜索一番这个状态下线程是让出了CPU资源的,Thread.sleep也是TIMED_WAITING不过线程栈里括号里写的是sleeping,所以又引到了线程状态
    所以实际上这种场景下要实现异步,就要全链路异步,像锁链一样(所以就有了react,js一样响应式),松耦合才有效果,发起源头处总是要同步等待,这是无法避免的,因为客户端是同步的,总有个线程去看守future,不过一两个线程看守最好,我现在还没有实现;我最初认为的异步是完全释放http-nio线程,让它去接收下个请求,不要阻塞在controller中;但是实验一番,即使使用AsyncContext,也是会占用其他http-nio线程

  2. AOP切类内函数调用,有时为了监控函数调用、类内多函数操作数据支持事务中,有调用自身函数也需要被切到的需要,可以: 1.proxy类 2.autowire自己 3.supplier函数封装 stackoverflow self-injection-with-spring

  3. Spring 配合yaml文件object格式并不能直接 @Value来匹配成一个普通的map,必须写成map的花括号String格式

  4. Bean中自己调自己方法不能AOP增强。因为调用的是原始对象自己的方法,并不是增强对象的方法。https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-understanding-aop-proxies 使用动态代理活着CGLIB对类进行扩展; https://stackoverflow.com/questions/56614354/why-does-self-invocation-not-work-for-spring-proxies-e-g-with-aop 当然,你也可以切换为AspectJ,加载类时增强(当然也可以使用if()来在运行时增强)

  5. 请求参数中有枚举?参数只能大写。用@JsonValue 轻松解决

  6. tomcat无法限制请求body是个潜在的隐患,我经过测试,发送巨大的json (59MB) Content-Type: application/json,请求在微服务间传递,会造成pod oom,所以限制入参是非常重要的。另外一个 server.tomcat.max-http-post-size 参数只对application/x-www-urlencoded 即url上的参数大小有限制,json body没有限制,StackOverflow & Github SpringBoot issue 进一步可以查看tomcat源码对这个参数的使用,和tomcat文档中对maxPostSize的描述;可以通过自己实现一个Filter进行过滤: StackOverflow

  7. 多module项目实践,在项目前期,部署资源等需要复用,但是在开发的时候不能不考虑将来的拆分,所以可以采用多module便于将来维护,同样对当前的功能实现进行清晰隔离也有好处

  • 怎么从一个项目建一个module?project名上右键即可
  • 主module一定要是parent吗?为什么通常主module要作为parent?
    因为子module之间避免不了互相引用,所以需要一个父pom,如果要单独起一个project的话,为什么不复用主module呢?利用主module还能保证版本同步,于是主module除了需要 <packaging>pom</packaging><modules> 这个主module需要的特征标签外,还有 <dependencyManagment>这个父pom的特征标签,这个就形成了一个循环依赖一样的东西:子module也是子project,需要依赖父pom,同时父pom中的modulesdependencyManagement中又有子module, 实际上并没有循环依赖,因为父project只是个空架子,dependencyManagement只管提供三元组特别是版本信息,并不提供这个jar,jar还是发布到仓库中,由仓库提供
    当然也可以把父pom管理依赖版本的功能独立出去成为一个bom(就是实际打包为pom),独立出去更清晰,原项目父pom再以这个为父。
  • 从安全性考量的内存隔离?
    JVM没有这个本事,这个是OS的事情,多classloader是为了解决特别是底层框架的jar版本隔离,并没有JVM层面的“内存”隔离
  1. 文件下载,零拷贝,需要注意的是,Spring中设置了Content-Length之后,Request就commit了,后续的增加header等就加不上了.
Jackson
  1. Jackson使用@JsonProperties序列化Enum时,得到的string会带引号
  2. 希望把map提升时,可以增加一个getMap的方法,并且注解 @JsonAnyGetter, 这是人家作者在2011年就写了SF链接
  3. 如果希望把一个对象提升铺平,就用@JsonUnwrapped 注解
  4. 一个类想同时表示create和update应该用什么单词save
  5. 请求参数 RequestParameter 参数是枚举类型的反序列化问题 需要自定义converter来解决,而由于本人的代码洁癖,枚举类全用的是@JsonProperties 序列化和反序列化,就没有string构造函数,就没有采用这种方式
参数校验或转化
  1. 需要对所有RequestParameter 统一集中校验,应该怎么做?怎么把集中定义的注解约束在method参数校验时转移过去呢?validate体系本身就是基于注解的,怎么把注解的元数据改成逻辑呢?看了下源码,深度适配比较麻烦,走在路上突然想到:我定义一个类,定义中包含所有的请求参数,并加上validator限制,拦截器中通过filed名字,propertiesDescriptor反射设置进去,一切都是自动的。
  2. 请求参数是string,限制参数范围应该怎么做?1.自定义注解@ValidateString 2.自定义EnumValidator更好
  3. 请求参数实际上会是Enum类型,或者需要解密什么的,应该怎么做? 1.org.springframework.format.Formatter并注册到webMvcCofigSF链接 2. 另一种方式,converter看起来简单点
二.单元测试问题
  1. mockitomockBean配合使用,mockBean可以mock任意bean的任意方法,即使非直接bean也能mock

  2. 那mockito的的when怎么指示为任意参数?答:ArgumentMatchers类 的any()方法

  3. mock静态方法用thenAnswer()来完成

  4. Spring的另一种单元测试方法,不是使用SpringRunner而是MockitoJunitRunner,用@InjectMocks标记受测对象,用ReflectUtils反射设置要mock的field;实际上当使用MockitoJUnitRunner配合InjectMocks注解将会自动完成构造

  5. 注意不管哪种runner,被标记为@Mock @Mockbean的对象它的真实方法都不会得到执行,默认返回都是空值,所以注意,我遇到用不同runner得到的测结果不一致,就是因为有个bean mock和没被mock调用其方法产生区别

  6. mock部分方法,其他方法希望调用真实方法:用spy而不是mock:https://stackoverflow.com/questions/14970516/use-mockito-to-mock-some-methods-but-not-others

  7. doAnswer()和thenReturn的区别 https://stackoverflow.com/questions/36615330/mockito-doanswer-vs-thenreturn 大多时候区别不大:很多场景是不需要感知调用者信息而无脑返回一个固定值的时候

  8. when-then 和 do-when 都可以用来mock,它们的区别

    • when-then更符合语义
    • 对于spy的class,当你when-then的时候,真实method会被执行,所以用do-then;但是method不出错的话,返回值当然还是你when-then中指定的返回值
    • mock void方法时只能用do-when
      若出现报错:Unfinished stubbing detected here:是因为使用错误了:doReturn(result).when(targetObject).someMethod(); not doReturn(result).when(targetObject.someMethod());
  9. 要遮蔽一个被spy的对象的一个方法,可以通过doNothing来进行,这是mock对象的默认行为,什么都不做,但是总是抛空指针,原来要遮蔽的方法参数是boolean,而我使用了any(), 应该使用anyBoolean(); 同时注意,和doReturn一样,遮蔽方法是在括号外。

  10. 在SpringBoot单测中是可以定义一个同名Bean标记为@Primary来覆盖正式代码中的bean,可以用来mock外部资源

  11. 强烈建议在测试application.yaml中spring下定义 main:  lazy-initialization: true 可以加快测试,但是这会导致一些bean,特别是无法加载ApplicationListener这种通过META-INF来初始化的类;参见如何获取Context ,setLazy的意思是,在启动引用树中,当这个Bean没有被其他Bean引用到时,它就不会被初始化,而去掉,就会初始化。如果因为代码限制不能通过yaml配置全局加lazy限制,那么可以使用BeanFactoryPostProcessor, 提前根据Bean名字指定Lazy属性

  12. 加快测试的第二个方法:在测试代码中写个Configueration类,关闭一些中间件的SpringBoot的自动装配,自行初始化并缓存(static))bean,比如Redisson;其中一个重要的自动配置类是 GrpcServerFactoryAutoConfiguration这个可以解决GrpcServer端口占用问题,可以在相关测试中去掉DirtyContext;也可考虑把自己代码@Bean生成的bean缓存;注意缓存实现后,就不能再用DirtyContext注解了,否则会出现资源被关闭,shutdown

  13. 单元测试可以参考 https://java-self-testing.github.io/java-self-testing-book/05-testing-with-spring/

  14. anyString()any()的区别:后者接受null值,这在校验函数调用次数时非常有用。

  15. 从头写一个单测时,应该使用哪个注解?SpringBootTest 和 SpringRunner 是什么关系?DirtiesContext又是什么意思?https://www.baeldung.com/springjunit4classrunner-parameterized
    1). 从头写时:可以有以下几种组合:

    • A.SpringRunner + @SpringBootTest 没有 @SpringBootTest就会找不到bean
    • B. SpringRunner + @ContextConfiguration(classes = SimpleBean.class) ContextConfiguration中指定测试中需要Autowire的bean
    • C. 普通Runner(默认或者参数化)+ SpringClassRule + SpringMethodRule + @ContextConfiguration(classes = SimpleBean.class)
      并且实测,同一个JVM进程中,使用A的类们共享一个context,使用方式B的类们共享一个Context,如果两个类一个使用A,一个使用B,Context不共享;
      同一个类里的用例始终共享一个Context

    2). SpringBootTest使用默认配置机制,SpringRunner最重要的是提供了TestContextManager, TestContextManagers是启动Spring上下文的核心 @SpringBootTest@ContextConfiguration的作用大约是配置Context怎么启动。具体可参见SpringBootTest注解的classes属性
    3). DirtiesContext注解帮你关闭上下文,经过测试,没有此注解时,当一个bean属性被修改,在另一个测试类中也能感知到。但需注意1)中描述的context共享关系

  16. 遇到有时 @SpyBean居然无法mock, MockBean却可以

  17. mock一个void方法的行为,比如抓取监测对象的某个方法入参,虽然doAnswer(..).when(T)Mockito.when(T) 参数都是T,但是实际上参数含义是不一样的
    前者是doAnswer(..).when(mockObject).someMetho(any())
    后者是Mockito.when(mockObject.someMethod(any())
    同时需要注意要在被测函数调用前进行doAnswer

三.运维部署问题
  1. actuator 要用不同的端口且只能集群内部访问
  2. 我遇到模块actuator 监控数据不能输出到prometheus是因为模块metric的tag丢失了,tag信息是直接输出在curl的结果中的;metric信息丢失说明是因为配置没起作用, actuator 的配置在哪里被读取然后起作用呢?PrometheusMetricRegistry 因为这个bean对应的BPP没有执行postProcessAfterInitialization方法,业务bean实现该方法导致一些链接到的bean都没有被执行这个方法,进而配置未生效; 注意启动日志中的关键字: 类似的问题还会有事务不生效、AOP不生效
  3. 部署相关,稍微扩展下部署。部署有滚动部署 蓝绿部署 金丝雀(灰度)发布。滚动部署即一个个替换新模块,服务器数量少的时候还能接受,但是要注意依赖关系;蓝绿部署就是另启一套新环境来部署新模块,然后识别请求导入不同环境,金丝雀也能采用这样的办法。先来扯扯流量识别,现在都是采用istio进行流量管理,它一直能从网络层到应用层,可以通过IP、HTTP header来区分流量,识别请求后,可以采用一定的策略来分配流量,从而实现蓝绿或者灰度发布。扯扯灰度:软件有两种,客户端和服务器,所以客户端也可灰度。客户端有:App、固件、前端页面等;客户端灰度就容易多了,因为客户端是用户向云请求得到的,是fetch,这样就可以进行更细粒度的灰度,完全可以是在业务级上,比如22岁的充值超500的LOL男性玩家。
    再来梳理下istio的入门过程:
    知乎的这篇文章非常不错,讲了微服务的前世今生:[什么是Service Mesh][https://zhuanlan.zhihu.com/p/61901608?]
istio是什么?https://www.ibm.com/cloud/learn/istio

Istio is a configurable, open source service-mesh layer that connects, monitors, and secures the containers in a Kubernetes cluster. At this writing, Istio works natively with Kubernetes only, but its open source nature makes it possible for anyone to write extensions enabling Istio to run on any cluster software. Today, we’ll focus on using Istio with Kubernetes, its most popular use case.
Kubernetes is a container orchestration tool, and one core unit of Kubernetes is a node. A node consists of one or more containers, along with file systems or other components. A microservices architecture might have a dozen different nodes, each representing different microservices. Kubernetes manages availability and resource consumption of nodes, adding pods as demand increases with the pod autoscaler. Istio injects additional containers into the pod to add security, management, and monitoring.
istio是一个可配置的、开源的service-mesh层的概念,连接、监控并且巩固Kubernates集群中的容器。编写此文章时,istio只适用于Kubernetes,但是其开源的特性使得任何人可以编写拓展以使其跑在任何集群软件上。今天,我们将主要关注在Kubernetes上使用Istio,那是最常用的。
Kubernetes是一个容器编排工具,其中一个核心单元就是node。一个node包含一个或多个容器,以及文件系统或者其他组件。一个微服务架构也许有十几个不同的node,每个代表不同的微服务。Kubernetes管理了node的可用性和资源消耗,pod autoscaler按需增加pods。Istio向pod注入了额外的容器,以便实现安全、管理和监控。

k8s的service https://kubernetes.io/docs/concepts/services-networking/service/

An abstract way to expose an application running on a set of Pods as a network service.
With Kubernetes you don’t need to modify your application to use an unfamiliar service discovery mechanism. Kubernetes gives Pods their own IP addresses and a single DNS name for a set of Pods, and can load-balance across them

一种抽象的方式去将暴露一个运行在一组Pods上的应用程序作为一个网络服务。
使用Kubernetes你不必修改你的应用程序去使用一个不熟悉的服务发现机制,Kubernetes给予每个Pod它们自己的IP地址,给一组Pods DNS name,并且在Pods间负载均衡。

Service a unit of application behavior bound to a unique name in a service registry. Services consist of multiple network endpoints implemented by workload instances running on pods, containers, VMs etc.

其Service的selector通过Pod的lable去匹配Pod,可以参见defining-a-service
k8s的lable 和selector https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/Labels are key/value pairs that are attached to objects, such as pods. Service可以通过Pod的lable选择Pod,Pod可以根据Node的Lable选择Node

Istio的virtualService

注意virtualService是Istio的,不是(隶属)k8s的概念:https://istio.io/latest/docs/reference/config/networking/virtual-service/

A VirtualService defines a set of traffic routing rules to apply when a host is addressed. Each routing rule defines matching criteria for traffic of a specific protocol. If the traffic is matched, then it is sent to a named destination service (or subset/version of it) defined in the registry.
一个VirtualService定义了主机寻址时的一组流量路由规则。每个路由规则为一个具体的协议流量定义匹配规则。如果流量匹配,则将会被发送到注册里的具有名称的目的Service。

SideCar https://www.nginx.com/resources/glossary/sidecar

A sidecar is a separate container that runs alongside an application container in a Kubernetes pod – a helper application of sorts. Typically, the sidecar is responsible for offloading functions required by all apps within a service mesh – SSL/mTLS, traffic routing, high availability, and so on – from the apps themselves, and implementing deployment testing patterns such as circuit breaker, canary, and blue‑green. Sidecars are sometimes used to aggregate and format log messages from multiple app instances into a single file.

As data‑plane components, sidecars are typically managed by some type of control plane within the service mesh. While the sidecar routes application traffic and provides other data‑plane services, the control plane injects sidecars into a pod when necessary and performs administrative tasks, for example renewing mTLS certificates and pushing them to the appropriate sidecars as needed.
sidercar是一个单独的容器,k8s中,其和应用容器一起运行-一种辅助应用程序。

K8s Deployment

污点和容忍度 给Node一种灵活的标签方式,且部署Pod时可选择地部署:

污点(Taint) 则相反——它使节点能够排斥一类特定的 Pod。容忍度(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。
NodeSelector 节点(或者Pod)亲和性(affinity)与反亲和性
nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型

Deployment replicas和HPA的关系

https://technologyconversations.com/2018/10/10/to-replicas-or-not-to-replicas-in-kubernetes-deployments-and-statefulsets/
一开始是deployment为准,最终会以HPA为准,HPA会修改Deployment,这个行为无法接受。同时此文章建议使用HPA时就不要在deployment中指定副本数了,但是文心一言不这么认为,我们实践时也是两个都设置了但是要记得最小值设置一样。

So far, we observed that HPA modifies our Deployments. No matter how many replicas we defined in a Deployment (or a StatefulSets), HPA will change it to fit its own thresholds and calculations. In other words, when we update a Deployment, the number of replicas will be temporarily changed to whatever we have defined, only to be modified again by HPA a few moments later. That behavior is unacceptable.

Service 和 VirtualService

Service是k8s里的概念,VirtualService是Istio概念,你可以有多个k8s集群,但是Istio mesh 可能只有一个?
我们每个环境的 多个区域的K8S集群 到底是多个还是1个?答:多个
如果是多个,主从是怎么搞的,K8S集群的主从是怎么搞的,像VirtualService都在主区保存,Service保存在各自区?但是也没见从区有复制这些配置啊?答:部署Istiod的时候可选模式,如下面对多cluster的DNS解析的问题分析,Service会复制到多区,实际上我们实践也是复制了,只是我没注意到。
如果是单个,那么为什么又会有某区自己的 service?这些是docker namespace层面的东西?

namespace是docker的概念还是K8s的概念,应该是docker,那么看起来K8s和docker的关系并不是清晰的分层关系,而是有些嵌入渗透的:k8s是编排管理docker的

但是根据我们的配置,不同区是不同的EKS集群,不同的EKS集群打通,通过的是Gateway不同hostname的 + VirtualService来指向某区Service,感觉是通过DNS解析互通实现了网络互通?不同集群只要网络互通+DNS能统一注册,那么就是互通的。这里猜对了一半,因为实际上是Istio模型的选择,参见文档:https://istio.io/latest/docs/ops/deployment/deployment-models/

For example, if two clusters reside on the same underlying network, you can enable cross-cluster communication by simply configuring firewall rules.

  1. 另一个问题是Gateway下面的格式是什么意思?
  2. VirtualService下面的格式是什么意思?

回答1. istio Gateway居然和K8s Gateway不是一回事
Gateway表示就是一个云LB或者集群内的proxy
主要看Istio的:ports 表示Gateway会暴露这么些个端口 hosts表示Gateway暴露的域名

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: my-gateway
  namespace: some-config-namespace
spec:
  selector:
    app: my-gateway-controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - uk.bookinfo.com
    - eu.bookinfo.com
    tls:
      httpsRedirect: true # sends 301 redirect for http requests
  - port:
      number: 443
      name: https-443
      protocol: HTTPS
    hosts:
    - uk.bookinfo.com
    - eu.bookinfo.com
    tls:
      mode: SIMPLE # enables HTTPS on this port
      serverCertificate: /etc/certs/servercert.pem
      privateKey: /etc/certs/privatekey.pem
  - port:
      number: 2379 # to expose internal service via external port 2379
      name: mongo
      protocol: MONGO
    hosts:
    - "*"

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80

然后VirtualService是个处于应用层的配置化交换机
http那里的格式:https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPRoute

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings-route
spec:
  hosts:
  - ratings.prod.svc.cluster.local
  http: 
  - match:
    - headers:
        end-user:
          exact: jason
      uri:
        prefix: "/ratings/v2/"
      port: externalPort # 比如80?
      ignoreUriCase: true
    route:
    - destination:
        host: ratings.prod.svc.cluster.local / serviceName
        port: 如果Service暴露一个端口,那就不用设置了


K8S namespace或者istio的namespa和docker的namespace是同一个吗?根据创建K8S集群的示例来看是同一个Istio / Set up a Kubernetes Cluster
Service 的name只能在同namespace下解析,不同namespace要通过这个域名 my-service.default.svc.cluster.local

多集群问题

1.不同区 2.不同域名 3.不同ELB 4.都对外监听443 5.对内采用不同端口 6.Gateway不同域名换不同端口() 7.VirtualService 绑定指定Gateway,根据不同端口 转发到不同区的Service
5 6怎么对接的呢?之前不是说Gateway表示的是监听的不同端口吗?

这个问题很好,istio的这篇文章deployment-models 清晰地说明了部分问题,首先概念要清楚:k8sCluster network controlPlane mesh 他们分别是多个还是一个
我们主要关注多个k8s cluster的情况,所以着重翻译下这块:
一个mesh可以包括多个cluster:可以故障转移,基于地理位置路由,多种控制面板模型:不同级别的可用性,团队、项目隔离
cluster间的通信可以由你的云服务提供商完成,比如简单的配置下防火墙。
Istio uses the virtual IP returned by the DNS lookup to load balance across the list of active endpoints for the requested service 这句话真难翻译。the DNS lookup to load balance
Istio使用DNS查找返回的虚拟IP在所请求服务端点列表间进行负载均衡。Istio使用k8s service/endpoint或者Istio ServiceEntry来配置内部映射:域名到负载IP地址。
在多个集群下,这个两层域名系统使情况变得复杂。Istio天生就是多集群感知的,而k8s不是,所以客户端集群就需要这个service的DNS入口来让DNS查询成功,为了让DNS查询成功,k8s service必须部署到每个消费这个service的集群(我们的确如此)。不管请求是哪里发起的,能确保正确查找并传递给istio来正确路由,这可以用Istio的ServiceEntry实现,而不是k8s Service,但是ServiceEntry并不配置K8S的DNS Server,这意味着DNS需要被手动配置或者Istio DNS proxying的自动分配特性。
有如下几个进行中的DNS方向的改进故事:
DNS sidecar proxy,Istio 1.8增加的预览版,通过sidecar拦截了工作负载的DNS查询,主要参考这篇文章,非常有用。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Istio团队一开始想用Envoy的DNS Proxy,但是它不可靠且其DNS库对C类地址实现不好,于是istio团队自己写了个DNS proxy放在sidecar里, 同时保持了 scale和稳定性。Istiod给其推送域名IP映射(包括要访问的K8s services和ServiceEntry),这个对pod或者VM透明。

If the query is for a service within the mesh, irrespective of the cluster that the service is in, the agent responds directly to the application. If not, it forwards the query to the upstream name servers defined in /etc/resolv.conf.

DNS proxy in the Istio sidecar agent, Your applications can resolve Kubernetes services on any cluster in any namespace, without the need to create stub Kubernetes services in every cluster.再回过头来看模型说明的那篇文章:
This can also be achieved with Istio ServiceEntry, rather than Kubernetes Service. However, a ServiceEntry does not configure the Kubernetes DNS server. This means that DNS will need to be configured either manually or with automated tooling such as the Address auto allocation feature of Istio DNS Proxying.
所以跨集群DNS解析要么复制Service(成为stub Service) + K8s的DNS服务器,要么利用Istio ServiceEntry + Istio DNS proxy

现在感觉在Istio治理下,主要的考虑点是:向谁寻址,谁来如何路由的问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值