当前位置:   article > 正文

【云原生】整合K8s + SpringCloudK8s + gRpc + RocketMQ + Istio + Envoy

【云原生】整合K8s + SpringCloudK8s + gRpc + RocketMQ + Istio + Envoy

背景

本文把前面的代码整理一遍,不仅仅是demo层面,而是考虑到放进生产中使用,且尽可能用高版本,关于这块技术,网上的文章真是一言难尽,要么就是个概念,要么就是把官网的demo拿过来跑一遍,质量太差。

我本地有Istio,也安装了K8s和Docker,这些都可以根据官网来安装,我这里就忽略了。

我本地使用的版本情况

  1. jdk:17
  2. spring-boot-starter-parent:2.7.9,没有上3.0.0,是因为RocketMQ还不支持,上了之后启动RocketMQ报错,所以用了3.0.0之前的最后一个版本
  3. spring-cloud-starter-kubernetes-client-all:2.1.6

架构图

功能点

  1. 在K8s容器中安装Istio,默认也会安装Envoy代理/网关,和Kiali dashboard等

  1. 把Envoy作为南北向流量网关,负责请求转发,限流等

  1. 把Envoy作为东西向业务网关,负责服务之间负载均衡等

  1. service-provider 和 service-consumer 分别暴露gRpc接口,相互调用

  1. service-provider 发送RocketMQ消息,service-consumer 多个消费组同时消费

编码

service-provider

工程结构

最外层POM

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.7.9</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>service-provider</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <packaging>pom</packaging>
  15. <name>service-provider</name>
  16. <description>service-provider</description>
  17. <modules>
  18. <module>service-provider-proto</module>
  19. <module>service-provider-start</module>
  20. <module>service-provider-dto</module>
  21. </modules>
  22. <properties>
  23. <java.version>17</java.version>
  24. </properties>
  25. <dependencyManagement>
  26. <dependencies>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-dependencies</artifactId>
  30. <type>pom</type>
  31. <scope>import</scope>
  32. <version>2.7.9</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>com.example</groupId>
  36. <artifactId>service-consumer-proto</artifactId>
  37. <version>0.0.1-SNAPSHOT</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>com.example</groupId>
  41. <artifactId>service-provider-proto</artifactId>
  42. <version>0.0.1-SNAPSHOT</version>
  43. </dependency>
  44. <dependency>
  45. <groupId>com.example</groupId>
  46. <artifactId>service-provider-dto</artifactId>
  47. <version>0.0.1-SNAPSHOT</version>
  48. </dependency>
  49. </dependencies>
  50. </dependencyManagement>
  51. </project>

service-provider-dto

这个模块里面主要是dto类,方便把dto模块提供给其他服务pom引用,而不是引用整个服务,我这里只有一个Order类

service-provider-proto

这里是 service-provider 暴露出去的gRpc接口,我这里暴露了一个HelloService接口

同时这里需要配置下gRpc的一些依赖和插件,然后这个模块需要被本服务(接口的实现)或者其他服务(接口的调用)引用,我们看一下 service-provider-proto 模块的pom

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <parent>
  5. <artifactId>service-provider</artifactId>
  6. <groupId>com.example</groupId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. </parent>
  9. <groupId>com.example</groupId>
  10. <artifactId>service-provider-proto</artifactId>
  11. <version>0.0.1-SNAPSHOT</version>
  12. <packaging>jar</packaging>
  13. <name>service-provider-proto</name>
  14. <properties>
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. </properties>
  17. <dependencies>
  18. <!-- gRpc -->
  19. <dependency>
  20. <groupId>net.devh</groupId>
  21. <artifactId>grpc-spring-boot-starter</artifactId>
  22. <version>2.14.0.RELEASE</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>io.grpc</groupId>
  26. <artifactId>grpc-protobuf</artifactId>
  27. <version>1.52.1</version>
  28. </dependency>
  29. <dependency> <!-- necessary for Java 9+ -->
  30. <groupId>org.apache.tomcat</groupId>
  31. <artifactId>annotations-api</artifactId>
  32. <version>6.0.53</version>
  33. </dependency>
  34. </dependencies>
  35. <build>
  36. <extensions>
  37. <extension>
  38. <groupId>kr.motd.maven</groupId>
  39. <artifactId>os-maven-plugin</artifactId>
  40. <version>1.7.1</version>
  41. </extension>
  42. </extensions>
  43. <plugins>
  44. <plugin>
  45. <groupId>org.xolstice.maven.plugins</groupId>
  46. <artifactId>protobuf-maven-plugin</artifactId>
  47. <version>0.6.1</version>
  48. <configuration>
  49. <protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
  50. <pluginId>grpc-java</pluginId>
  51. <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.52.1:exe:${os.detected.classifier}</pluginArtifact>
  52. </configuration>
  53. <executions>
  54. <execution>
  55. <goals>
  56. <goal>compile</goal>
  57. <goal>compile-custom</goal>
  58. </goals>
  59. </execution>
  60. </executions>
  61. </plugin>
  62. </plugins>
  63. </build>
  64. </project>

service-provider-start

这里就是启动类了,我也把一些业务代码放进来了,按理说这不规范,但每个公司也有不同的分法。

我直接把代码都贴出来,不做过多的解释,方便有同学跟着写的。

ServiceProviderApplication
  1. package com.example.service.provider;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @SpringBootApplication
  6. @EnableDiscoveryClient
  7. public class ServiceProviderApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(ServiceProviderApplication.class, args);
  10. }
  11. }
HelloServiceImpl

这个是前面暴露HelloService的接口实现

  1. package com.example.service.provider.facade;
  2. import com.example.service.provider.DemoConfig;
  3. import com.example.service.provider.api.HelloServiceGrpc;
  4. import com.example.service.provider.api.SayHelloData;
  5. import com.example.service.provider.api.SayHelloRequest;
  6. import com.example.service.provider.api.SayHelloResponse;
  7. import lombok.extern.slf4j.Slf4j;
  8. import net.devh.boot.grpc.server.service.GrpcService;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. @Slf4j
  11. @GrpcService
  12. public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
  13. @Autowired
  14. private DemoConfig demoConfig;
  15. @Override
  16. public void sayHello(SayHelloRequest request, io.grpc.stub.StreamObserver<SayHelloResponse> responseObserver) {
  17. log.info("接收consumer的say hello grpc 请求");
  18. SayHelloData helloData = SayHelloData.newBuilder()
  19. .setName("maple")
  20. .setContent(demoConfig.getMessage())
  21. .build();
  22. SayHelloResponse.Builder builder = SayHelloResponse.newBuilder()
  23. .setCode(0)
  24. .setMessage("success")
  25. .setSuccess(true)
  26. .setData(helloData);
  27. responseObserver.onNext(builder.build());
  28. responseObserver.onCompleted();
  29. }
  30. }
DemoConfig

这个主要是测试动态配置是否生效的

  1. package com.example.service.provider;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @ConfigurationProperties(prefix = "bean")
  6. public class DemoConfig {
  7. private String message;
  8. public String getMessage() {
  9. return message;
  10. }
  11. public void setMessage(String message) {
  12. this.message = message;
  13. }
  14. }
MemberQueryGrpcClient

这个是 service-consumer 暴露出的gRpc接口,service-provider 去调用,因为生产中不同服务之间肯定是可以相互rpc调用的。

  1. package com.example.service.provider;
  2. import com.example.service.consumer.api.MemberQueryRequest;
  3. import com.example.service.consumer.api.MemberQueryResponse;
  4. import com.example.service.consumer.api.MemberQueryServiceGrpc;
  5. import com.google.protobuf.InvalidProtocolBufferException;
  6. import lombok.extern.slf4j.Slf4j;
  7. import net.devh.boot.grpc.client.inject.GrpcClient;
  8. import org.springframework.stereotype.Service;
  9. @Slf4j
  10. @Service
  11. public class MemberQueryGrpcClient {
  12. @GrpcClient("service-consumer")
  13. private MemberQueryServiceGrpc.MemberQueryServiceBlockingStub memberQueryBlockingStub;
  14. public String queryMember() {
  15. MemberQueryRequest request = MemberQueryRequest.newBuilder().setMemberId("111").setUsername("Wang Hong Bo").build();
  16. MemberQueryResponse response = memberQueryBlockingStub.queryMember(request);
  17. log.info("MemberQueryResponse.code:{}", response.getCode());
  18. log.info("MemberQueryResponse.message:{}", response.getMessage());
  19. log.info("MemberQueryResponse.success:{}", response.getSuccess());
  20. log.info("MemberQueryResponse.data:{}", response.getData());
  21. log.info("MemberQueryResponse.address1:{}", response.getData().getAddressList().get(0).getAddress());
  22. log.info("MemberQueryResponse.address2:{}", response.getData().getAddressList().get(1).getAddress());
  23. return response.toString();
  24. }
  25. }
RocketMQProducerService

这个是MQ生产者发送消息的服务,我这里通过这个代码

String sceneStr = SceneEnum.destination(scene) 把消息做了转换,topic中间以“|”分隔,比如说我的Topic为:TP_S_1100|EC_EVENT_0001

但是在阿里云买的RocketMQ产品,它不支持topic中间以“|”分隔,认为它是特殊字符,我是本地安装的RocketMQ,它是支持的。

  1. package com.example.service.provider.producer;
  2. import com.alibaba.fastjson.JSON;
  3. import com.example.service.provider.enums.SceneEnum;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.rocketmq.client.producer.SendCallback;
  6. import org.apache.rocketmq.client.producer.SendResult;
  7. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.messaging.support.MessageBuilder;
  10. import org.springframework.stereotype.Component;
  11. @Slf4j
  12. @Component
  13. public class RocketMQProducerService {
  14. @Autowired
  15. private RocketMQTemplate rocketMQTemplate;
  16. /**
  17. * 普通发送
  18. *
  19. * @param scene
  20. * @param payload
  21. */
  22. public void send(SceneEnum scene, Object payload) {
  23. String sceneStr = SceneEnum.destination(scene);
  24. log.info("producer.sendMessage: 【{}】,【{}】", sceneStr, JSON.toJSONString(payload));
  25. rocketMQTemplate.convertAndSend(sceneStr, payload);
  26. }
  27. /**
  28. * 同步发送
  29. *
  30. * @param scene
  31. * @param payload
  32. * @return
  33. */
  34. public SendResult sendSync(SceneEnum scene, Object payload) {
  35. String sceneStr = SceneEnum.destination(scene);
  36. SendResult sendResult = rocketMQTemplate.syncSend(sceneStr, payload);
  37. log.info("producer.sendMessage: 【{}】,【{}】, sendResult:{}", sceneStr, JSON.toJSONString(payload), sendResult);
  38. return sendResult;
  39. }
  40. /**
  41. * 发送异步消息
  42. *
  43. * @param scene
  44. * @param payload
  45. */
  46. public void sendASync(SceneEnum scene, Object payload) {
  47. rocketMQTemplate.asyncSend(SceneEnum.destination(scene), payload, new SendCallback() {
  48. @Override
  49. public void onSuccess(SendResult sendResult) {
  50. System.out.println("异步发送成功啦" + sendResult);
  51. }
  52. @Override
  53. public void onException(Throwable throwable) {
  54. System.out.println("异步发送出异常啦" + throwable.getMessage());
  55. }
  56. });
  57. }
  58. /**
  59. * 发送延时消息<br/>
  60. * 在start版本中 延时消息一共分为18个等级分别为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
  61. */
  62. public void sendDelay(SceneEnum scene, Object payload, int delayLevel) {
  63. rocketMQTemplate.syncSend(SceneEnum.destination(scene), MessageBuilder.withPayload(payload).build(), 2000, delayLevel);
  64. }
  65. /**
  66. * 发送单向消息(不关心发送结果,如日志)
  67. */
  68. public void sendOneWayMsg(SceneEnum scene, Object payload) {
  69. rocketMQTemplate.sendOneWay(SceneEnum.destination(scene), payload);
  70. }
  71. }
SceneEnum

这个是场景枚举类,生产中肯定有各种场景定义

  1. package com.example.service.provider.enums;
  2. public enum SceneEnum {
  3. ORDER_CREATE("11000001", "ORDER_CREATE"),
  4. ORDER_CONFIRM("11000002", "ORDER_CONFIRM"),
  5. ;
  6. private String sceneCode;
  7. private String desc;
  8. SceneEnum(String sceneCode, String desc) {
  9. this.sceneCode = sceneCode;
  10. this.desc = desc;
  11. }
  12. public String getSceneCode() {
  13. return sceneCode;
  14. }
  15. public String getDesc() {
  16. return desc;
  17. }
  18. public static String destination(SceneEnum scene) {
  19. String topic = "TP_S_" + scene.getSceneCode().substring(0, 4) + "|" + "EC_EVENT_" + scene.getSceneCode().substring(4, 8);
  20. String tag = scene.getDesc();
  21. return topic + ":" + tag;
  22. }
  23. }
ProviderController

这个类是我用来测试服务功能的

  1. package com.example.service.provider.controller;
  2. import com.example.Order;
  3. import com.example.service.provider.DemoConfig;
  4. import com.example.service.provider.MemberQueryGrpcClient;
  5. import com.example.service.provider.enums.SceneEnum;
  6. import com.example.service.provider.producer.RocketMQProducerService;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.cloud.context.config.annotation.RefreshScope;
  11. import org.springframework.web.bind.annotation.GetMapping;
  12. import org.springframework.web.bind.annotation.RequestMapping;
  13. import org.springframework.web.bind.annotation.RestController;
  14. @RefreshScope
  15. @Slf4j
  16. @RestController
  17. @RequestMapping("/provider")
  18. public class ProviderController {
  19. @Value("${maple-test.message}")
  20. private String mapleTestMessage;
  21. @Autowired
  22. private DemoConfig demoConfig;
  23. @Autowired
  24. private RocketMQProducerService producerService;
  25. @Autowired
  26. private MemberQueryGrpcClient memberQueryGrpcClient;
  27. @GetMapping("/provider-hello")
  28. public String sayHello() {
  29. log.info("hello world");
  30. return "hello world";
  31. }
  32. @GetMapping("/grpc/queryMember")
  33. public String queryMember() {
  34. log.info("消费服务:service-provider grpc 调用 service-consumer 的 query member 接口");
  35. return memberQueryGrpcClient.queryMember();
  36. }
  37. @GetMapping("/sendMq")
  38. public String sendMq() {
  39. Order order = new Order();
  40. order.setOrderName("Apple");
  41. for (int i = 0; i < 5; i++) {
  42. order.setPrice(i);
  43. producerService.sendSync(SceneEnum.ORDER_CREATE, order);
  44. }
  45. return "send message complete";
  46. }
  47. @GetMapping("/provider-value-config")
  48. public String valueConfig() {
  49. log.info("直接@Value获取配置:{}", mapleTestMessage);
  50. return mapleTestMessage;
  51. }
  52. @GetMapping("/demo-config")
  53. public String demoConfig() {
  54. log.info("通过ConfigurationProperties注解获取配置:{}", demoConfig.getMessage());
  55. return demoConfig.getMessage();
  56. }
  57. }
bootstrap.yaml

这个是我们的启动引导配置,所以里面只做了K8s的相关配置

  1. spring:
  2. application:
  3. name: service-provider
  4. cloud:
  5. kubernetes:
  6. reload:
  7. enabled: true #修改K8s的ConfigMap配置之后自动刷新,有默认的刷新策略和刷新时机
  8. config:
  9. name: ${spring.application.name} #定义配置文件的名称
  10. namespace: service-k8s-demo
  11. sources:
  12. - name: ${spring.application.name}#真正引用配置文件的名称,根据我们的profile自动找对应的配置文件,该服务专属的配置
  13. namespace: service-k8s-demo
  14. - name: service-common-config #一个公共的配置文件,微服务架构中每个服务都可以引用的
  15. namespace: service-k8s-demo
  16. management:
  17. endpoint:
  18. restart:
  19. enabled: true
  20. health:
  21. enabled: true
  22. probes:
  23. enabled: true
  24. show-details: always
  25. prometheus:
  26. enabled: true
  27. info:
  28. enabled: true
  29. endpoints:
  30. web:
  31. exposure:
  32. include: '*'
service-provider-start的POM
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <parent>
  5. <groupId>com.example</groupId>
  6. <artifactId>service-provider</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. </parent>
  9. <artifactId>service-provider-start</artifactId>
  10. <packaging>jar</packaging>
  11. <name>service-provider-start</name>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. </properties>
  15. <dependencies>
  16. <dependency>
  17. <groupId>com.example</groupId>
  18. <artifactId>service-consumer-proto</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>com.example</groupId>
  22. <artifactId>service-provider-proto</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>com.example</groupId>
  26. <artifactId>service-provider-dto</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-web</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-actuator</artifactId>
  39. </dependency>
  40. <!-- kubernetes -->
  41. <dependency>
  42. <groupId>org.springframework.cloud</groupId>
  43. <artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
  44. <version>2.1.6</version>
  45. <exclusions>
  46. <exclusion>
  47. <groupId>com.google.protobuf</groupId>
  48. <artifactId>protobuf-java</artifactId>
  49. </exclusion>
  50. </exclusions>
  51. </dependency>
  52. <!-- RocketMQ -->
  53. <dependency>
  54. <groupId>org.apache.rocketmq</groupId>
  55. <artifactId>rocketmq-spring-boot-starter</artifactId>
  56. <version>2.2.3</version>
  57. </dependency>
  58. <dependency>
  59. <groupId>org.projectlombok</groupId>
  60. <artifactId>lombok</artifactId>
  61. <optional>true</optional>
  62. <version>1.18.26</version>
  63. </dependency>
  64. </dependencies>
  65. <build>
  66. <plugins>
  67. <plugin>
  68. <groupId>org.springframework.boot</groupId>
  69. <artifactId>spring-boot-maven-plugin</artifactId>
  70. <configuration>
  71. <classifier>exec</classifier> <!-- 需要打可执行文件 -->
  72. </configuration>
  73. </plugin>
  74. </plugins>
  75. </build>
  76. </project>
Dockerfile

在 service-provider-start 模块下面,我们写了个Dockerfile文件,内容很简单。

  1. FROM arm64v8/openjdk:20-slim-buster
  2. ADD service-provider-start-0.0.1-SNAPSHOT-exec.jar service-provider.jar
  3. ENTRYPOINT java -jar service-provider.jar

config

service-provider-dev.yaml

开发环境的配置文件

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: service-provider-dev
  5. namespace: service-k8s-demo
  6. labels:
  7. spring.cloud.kubernetes.config: "true"
  8. data:
  9. application.yml: |-
  10. spring:
  11. profiles: dev
  12. server:
  13. port: 30000
  14. grpc:
  15. server:
  16. port: 9090
  17. rocketmq:
  18. producer:
  19. group: SERVICE_PRODUCER
  20. name-server: 124.222.91.116:9876
  21. bean:
  22. message: Hello World! --dev
  23. maple-test:
  24. message: maple for dev config map in k8s --dev
  25. redis:
  26. ip: k8s --dev
service-provider-prod.yaml

生产环境的配置文件

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: service-provider-prod
  5. namespace: service-k8s-demo
  6. labels:
  7. spring.cloud.kubernetes.config: "true"
  8. data:
  9. application.yml: |-
  10. spring:
  11. profiles: prod
  12. server:
  13. port: 30000
  14. grpc:
  15. server:
  16. port: 9090
  17. rocketmq:
  18. producer:
  19. group: SERVICE_PRODUCER
  20. name-server: 124.222.91.116:9876
  21. bean:
  22. message: Hello World! --prod
  23. maple-test:
  24. message: maple for dev config map in k8s --prod
  25. redis:
  26. ip: k8s --prod
service-common-config.yaml

公共的配置文件,我这里主要是定义了gRpc的服务地址与端口,因为生产中服务是要相互调用的,网上的很多文章把 address: 'dns:///service-consumer:9091' 都写为了 address: 'static://service-consumer:9091',但是这会导致服务上下线之后,调用方调不到服务的情况,所以采用DNS来动态的发现服务。

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: service-common-config
  5. namespace: service-k8s-demo
  6. labels:
  7. spring.cloud.kubernetes.config: "true"
  8. data:
  9. application.yml: |-
  10. grpc:
  11. client:
  12. GLOBAL:
  13. negotiation-type: plaintext
  14. enable-keep-alive: true
  15. keep-alive-without-calls: true
  16. service-consumer:
  17. address: 'dns:///service-consumer:9091'
  18. service-provider:
  19. address: 'dns:///service-provider:9090'

官网上也给出了下面这种写法来区分不同环境,但是把所有环境都放进一个文件太过臃肿,而且这个文件也不建议太大, 不能超过1M,如果太大就要考虑挂载到磁盘上的目录。

service-provider-deploy.yaml

这个是我们在K8s容器中部署服务的yaml文件

  1. apiVersion: v1
  2. kind: Namespace
  3. metadata:
  4. name: service-k8s-demo
  5. labels:
  6. name: service-k8s-demo
  7. ---
  8. apiVersion: v1
  9. kind: ServiceAccount
  10. metadata:
  11. name: service-k8s-demo
  12. namespace: service-k8s-demo
  13. ---
  14. kind: ClusterRole
  15. apiVersion: rbac.authorization.k8s.io/v1
  16. metadata:
  17. namespace: service-k8s-demo
  18. name: service-k8s-demo
  19. rules:
  20. - apiGroups:
  21. - ""
  22. resources:
  23. - services
  24. - configmaps
  25. - endpoints
  26. - nodes
  27. - pods
  28. - secrets
  29. - namespaces
  30. verbs:
  31. - get
  32. - list
  33. - watch
  34. ---
  35. apiVersion: rbac.authorization.k8s.io/v1
  36. kind: ClusterRoleBinding
  37. metadata:
  38. name: service-k8s-demo
  39. namespace: service-k8s-demo
  40. subjects:
  41. - kind: ServiceAccount
  42. name: service-k8s-demo
  43. namespace: service-k8s-demo
  44. roleRef:
  45. kind: ClusterRole
  46. name: service-k8s-demo
  47. apiGroup: rbac.authorization.k8s.io
  48. ---
  49. apiVersion: apps/v1
  50. kind: Deployment
  51. metadata:
  52. name: service-provider
  53. namespace: service-k8s-demo
  54. labels:
  55. app: service-provider
  56. spec:
  57. replicas: 3
  58. template:
  59. metadata:
  60. name: service-provider
  61. labels:
  62. app: service-provider
  63. spec:
  64. containers:
  65. - name: service-provider
  66. image: service-provider:1.0
  67. imagePullPolicy: IfNotPresent
  68. env:
  69. - name: SPRING_PROFILES_ACTIVE
  70. value: "dev"
  71. ports:
  72. - name: http
  73. protocol: TCP
  74. containerPort: 30000
  75. - name: grpc
  76. protocol: TCP
  77. containerPort: 9090
  78. serviceAccountName: service-k8s-demo
  79. restartPolicy: Always
  80. selector:
  81. matchLabels:
  82. app: service-provider
  83. ---
  84. apiVersion: v1
  85. kind: Service
  86. metadata:
  87. name: service-provider
  88. namespace: service-k8s-demo
  89. spec:
  90. selector:
  91. app: service-provider
  92. ports:
  93. - port: 80
  94. targetPort: 30000
  95. name: http
  96. - port: 9090
  97. targetPort: 9090
  98. name: grpc
  99. type: NodePort

你可以发现我在这里指定了个profiles,实际生产中可以有两份yaml文件,也可以一份yaml文件,把profile给动态传进来,这个我之后再研究过来更新吧。

service-gateway.yaml

这个是Envoy充当南北向网关的,负责请求的转发和限流等,我这里目前是测试了转发,限流后面测试完成再过来更新。

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: Gateway
  3. metadata:
  4. name: service-k8s-demo-gateway
  5. namespace: service-k8s-demo
  6. spec:
  7. selector:
  8. istio: ingressgateway # use istio default controller
  9. servers:
  10. - port:
  11. number: 31400
  12. name: http
  13. protocol: HTTP
  14. hosts:
  15. - "*"
  16. ---
  17. apiVersion: networking.istio.io/v1alpha3
  18. kind: VirtualService
  19. metadata:
  20. name: service-k8s-demo-virtual-service
  21. namespace: service-k8s-demo
  22. spec:
  23. hosts:
  24. - "*"
  25. gateways:
  26. - service-k8s-demo-gateway
  27. http:
  28. - match:
  29. - uri:
  30. prefix: /consumer
  31. route:
  32. - destination:
  33. host: service-consumer
  34. port:
  35. number: 80
  36. - match:
  37. - uri:
  38. prefix: /provider
  39. route:
  40. - destination:
  41. host: service-provider
  42. port:
  43. number: 80

至此为止,我们的 service-provider 模块的代码都已经全部完成了,不要妄想本地能跑起来哈,因为我们是在yaml文件里指定的profile,所以必须在K8s中执行yaml文件才能让他自动把帮我们去找config文件的,不过可以试着启动看看有没有什么报错,我本地启动是报这俩错,这是因为我们本地没有和K8s打交道,导致没有K8s的环境,所以也找不到配置类,报了RocketMQ的错,但是没有关系,等我们使用K8s时这些问题都没有了。

service-consumer

工程结构

最外层POM

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.7.9</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>service-consumer</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <packaging>pom</packaging>
  15. <name>service-consumer</name>
  16. <description>service-consumer</description>
  17. <modules>
  18. <module>service-consumer-proto</module>
  19. <module>service-consumer-start</module>
  20. </modules>
  21. <properties>
  22. <java.version>17</java.version>
  23. </properties>
  24. <dependencyManagement>
  25. <dependencies>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-dependencies</artifactId>
  29. <type>pom</type>
  30. <scope>import</scope>
  31. <version>2.7.9</version>
  32. </dependency>
  33. <dependency>
  34. <groupId>com.example</groupId>
  35. <artifactId>service-consumer-proto</artifactId>
  36. <version>0.0.1-SNAPSHOT</version>
  37. </dependency>
  38. <dependency>
  39. <groupId>com.example</groupId>
  40. <artifactId>service-provider-proto</artifactId>
  41. <version>0.0.1-SNAPSHOT</version>
  42. </dependency>
  43. <dependency>
  44. <groupId>com.example</groupId>
  45. <artifactId>service-provider-dto</artifactId>
  46. <version>0.0.1-SNAPSHOT</version>
  47. </dependency>
  48. </dependencies>
  49. </dependencyManagement>
  50. </project>

service-consumer-proto

  1. syntax = "proto3";
  2. option java_multiple_files = true;
  3. option java_package = "com.example.service.consumer.api";
  4. option java_outer_classname = "MemberQueryServiceProto";
  5. service MemberQueryService {
  6. rpc queryMember (MemberQueryRequest) returns (MemberQueryResponse) {
  7. }
  8. }
  9. message MemberQueryRequest {
  10. string memberId = 1;
  11. string username = 2;
  12. }
  13. message MemberQueryResponse {
  14. int32 code = 1;
  15. string message = 2;
  16. bool success = 3;
  17. MemberQueryData data = 4;
  18. }
  19. message MemberQueryData {
  20. string memberId = 1;
  21. string username = 2;
  22. int32 age = 3;
  23. repeated AddressData address = 4;
  24. map<string, string> extMap = 5;
  25. }
  26. message AddressData {
  27. string address = 1;
  28. string phone = 2;
  29. }
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <parent>
  5. <artifactId>service-consumer</artifactId>
  6. <groupId>com.example</groupId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. </parent>
  9. <groupId>com.example</groupId>
  10. <artifactId>service-consumer-proto</artifactId>
  11. <version>0.0.1-SNAPSHOT</version>
  12. <packaging>jar</packaging>
  13. <name>service-consumer-proto</name>
  14. <properties>
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. </properties>
  17. <dependencies>
  18. <!-- gRpc -->
  19. <dependency>
  20. <groupId>net.devh</groupId>
  21. <artifactId>grpc-spring-boot-starter</artifactId>
  22. <version>2.14.0.RELEASE</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>io.grpc</groupId>
  26. <artifactId>grpc-protobuf</artifactId>
  27. <version>1.52.1</version>
  28. </dependency>
  29. <dependency> <!-- necessary for Java 9+ -->
  30. <groupId>org.apache.tomcat</groupId>
  31. <artifactId>annotations-api</artifactId>
  32. <version>6.0.53</version>
  33. </dependency>
  34. </dependencies>
  35. <build>
  36. <extensions>
  37. <extension>
  38. <groupId>kr.motd.maven</groupId>
  39. <artifactId>os-maven-plugin</artifactId>
  40. <version>1.7.1</version>
  41. </extension>
  42. </extensions>
  43. <plugins>
  44. <plugin>
  45. <groupId>org.xolstice.maven.plugins</groupId>
  46. <artifactId>protobuf-maven-plugin</artifactId>
  47. <version>0.6.1</version>
  48. <configuration>
  49. <protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
  50. <pluginId>grpc-java</pluginId>
  51. <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.52.1:exe:${os.detected.classifier}</pluginArtifact>
  52. </configuration>
  53. <executions>
  54. <execution>
  55. <goals>
  56. <goal>compile</goal>
  57. <goal>compile-custom</goal>
  58. </goals>
  59. </execution>
  60. </executions>
  61. </plugin>
  62. </plugins>
  63. </build>
  64. </project>

service-consumer-start

POM
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <parent>
  7. <groupId>com.example</groupId>
  8. <artifactId>service-consumer</artifactId>
  9. <version>0.0.1-SNAPSHOT</version>
  10. </parent>
  11. <artifactId>service-consumer-start</artifactId>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. </properties>
  15. <dependencies>
  16. <dependency>
  17. <groupId>com.example</groupId>
  18. <artifactId>service-provider-proto</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>com.example</groupId>
  22. <artifactId>service-consumer-proto</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>com.example</groupId>
  26. <artifactId>service-provider-dto</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-web</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-actuator</artifactId>
  39. </dependency>
  40. <!-- kubernetes -->
  41. <dependency>
  42. <groupId>org.springframework.cloud</groupId>
  43. <artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
  44. <version>2.1.6</version>
  45. <exclusions>
  46. <exclusion>
  47. <groupId>com.google.protobuf</groupId>
  48. <artifactId>protobuf-java</artifactId>
  49. </exclusion>
  50. </exclusions>
  51. </dependency>
  52. <!-- RocketMQ -->
  53. <dependency>
  54. <groupId>org.apache.rocketmq</groupId>
  55. <artifactId>rocketmq-spring-boot-starter</artifactId>
  56. <version>2.2.3</version>
  57. </dependency>
  58. <dependency>
  59. <groupId>org.projectlombok</groupId>
  60. <artifactId>lombok</artifactId>
  61. <optional>true</optional>
  62. <version>1.18.26</version>
  63. </dependency>
  64. </dependencies>
  65. <build>
  66. <plugins>
  67. <plugin>
  68. <groupId>org.springframework.boot</groupId>
  69. <artifactId>spring-boot-maven-plugin</artifactId>
  70. <configuration>
  71. <classifier>exec</classifier>
  72. </configuration>
  73. </plugin>
  74. </plugins>
  75. </build>
  76. </project>
MemberQueryServiceImpl

这个是 service-consumer 暴露接口的实现类

  1. package com.example.service.consumer.facade;
  2. import com.example.service.consumer.api.*;
  3. import com.google.common.collect.Maps;
  4. import lombok.extern.slf4j.Slf4j;
  5. import net.devh.boot.grpc.server.service.GrpcService;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Map;
  9. @Slf4j
  10. @GrpcService
  11. public class MemberQueryServiceImpl extends MemberQueryServiceGrpc.MemberQueryServiceImplBase {
  12. @Override
  13. public void queryMember(MemberQueryRequest request, io.grpc.stub.StreamObserver<MemberQueryResponse> responseObserver) {
  14. log.info("接收其他服务的 query member 的 grpc 请求, 请求参数为:{}", request);
  15. List<AddressData> addressList = new ArrayList<>();
  16. addressList.add(AddressData.newBuilder().setAddress("杭州").setPhone("110").build());
  17. addressList.add(AddressData.newBuilder().setAddress("马来西亚").setPhone("911").build());
  18. Map<String, String> extMap = Maps.newHashMap();
  19. extMap.put("secondField", "2");
  20. extMap.put("lastField", "last");
  21. MemberQueryData queryData = MemberQueryData.newBuilder()
  22. .setMemberId(request.getMemberId())
  23. .setUsername(request.getUsername())
  24. .setAge(18)
  25. .addAllAddress(addressList)
  26. .putExtMap("firstField", "test-maple")
  27. .putAllExtMap(extMap)
  28. .build();
  29. MemberQueryResponse.Builder builder = MemberQueryResponse.newBuilder()
  30. .setCode(0)
  31. .setMessage("success")
  32. .setSuccess(true)
  33. .setData(queryData);
  34. responseObserver.onNext(builder.build());
  35. responseObserver.onCompleted();
  36. }
  37. }
ServiceConsumerApplication
  1. package com.example.service.consumer;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @EnableDiscoveryClient
  6. @SpringBootApplication
  7. public class ServiceConsumerApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(ServiceConsumerApplication.class, args);
  10. }
  11. }
CreateOrderGotoneConsumer

这个是消费订单创建的消息,然后给用户发送通知的

  1. package com.example.service.consumer.mq;
  2. import com.alibaba.fastjson.JSON;
  3. import com.example.Order;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
  6. import org.apache.rocketmq.spring.core.RocketMQListener;
  7. import org.springframework.stereotype.Component;
  8. /**
  9. * 创建订单成功,发送gotone通知
  10. */
  11. @Slf4j
  12. @Component
  13. @RocketMQMessageListener(topic = "TP_S_1100|EC_EVENT_0001", consumerGroup = "CREATE_ORDER_GOTONE_CONSUMER")
  14. public class CreateOrderGotoneConsumer implements RocketMQListener<Order> {
  15. @Override
  16. public void onMessage(Order order) {
  17. log.info("consumer message: 【CreateOrderGotoneConsumer】,【{}】", JSON.toJSONString(order));
  18. }
  19. }
DeductInventoryConsumer

这个也是消费订单创建的消息,扣减库存的,这两个是不同的consumerGroup,所以都能消费到消息,但是这个情况一定要做好幂等处理,访问其中一个consumerGroup消费失败触发重试一直投递消息,导致处理成功的consumerGroup也一直收到消息,所以要做好幂等。

  1. package com.example.service.consumer.mq;
  2. import com.alibaba.fastjson.JSON;
  3. import com.example.Order;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
  6. import org.apache.rocketmq.spring.core.RocketMQListener;
  7. import org.springframework.stereotype.Component;
  8. /**
  9. * 创建订单成功,扣减库存
  10. */
  11. @Slf4j
  12. @Component
  13. @RocketMQMessageListener(topic = "TP_S_1100|EC_EVENT_0001", consumerGroup = "DEDUCT_INVENTORY_CONSUMER")
  14. public class DeductInventoryConsumer implements RocketMQListener<Order> {
  15. @Override
  16. public void onMessage(Order order) {
  17. log.info("consumer message: 【DeductInventoryConsumer】,【{}】", JSON.toJSONString(order));
  18. }
  19. }
ProviderServiceGrpcClient

这个是 service-consumer 调用 service-provider 的gRpc 接口

  1. package com.example.service.consumer.grpc;
  2. import com.example.service.provider.api.HelloServiceGrpc;
  3. import com.example.service.provider.api.SayHelloRequest;
  4. import com.example.service.provider.api.SayHelloResponse;
  5. import net.devh.boot.grpc.client.inject.GrpcClient;
  6. import org.springframework.stereotype.Service;
  7. @Service
  8. public class ProviderServiceGrpcClient {
  9. @GrpcClient("service-provider")
  10. private HelloServiceGrpc.HelloServiceBlockingStub helloServiceBlockingStub;
  11. public String sayHello() {
  12. SayHelloRequest request = SayHelloRequest.newBuilder().setName("maple123").build();
  13. SayHelloResponse sayHelloResponse = helloServiceBlockingStub.sayHello(request);
  14. return sayHelloResponse.toString();
  15. }
  16. }
ConsumerController

这个是测试consumer服务的入口

  1. package com.example.service.consumer.controller;
  2. import com.example.service.consumer.grpc.ProviderServiceGrpcClient;
  3. import com.example.service.consumer.ServiceConsumerApplication;
  4. import lombok.RequiredArgsConstructor;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.boot.SpringApplication;
  8. import org.springframework.cloud.client.discovery.DiscoveryClient;
  9. import org.springframework.cloud.context.config.annotation.RefreshScope;
  10. import org.springframework.web.bind.annotation.GetMapping;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.RestController;
  13. import java.util.List;
  14. @Slf4j
  15. @RequiredArgsConstructor
  16. @RefreshScope
  17. @RestController
  18. @RequestMapping("/consumer")
  19. public class ConsumerController {
  20. private final DiscoveryClient discoveryClient;
  21. private final ProviderServiceGrpcClient providerServiceGrpcClient;
  22. public static void main(String[] args) {
  23. SpringApplication.run(ServiceConsumerApplication.class, args);
  24. }
  25. @Value("${consumer.body}")
  26. private String consumerBody;
  27. @GetMapping("/grpc/hello")
  28. public String sayHello() {
  29. log.info("消费服务:service-consumer grpc 调用 service-provider");
  30. return providerServiceGrpcClient.sayHello();
  31. }
  32. @GetMapping("/consumerBody")
  33. public String consumerBody() {
  34. log.info("获取配置consumerBody:{}", consumerBody);
  35. return consumerBody;
  36. }
  37. @GetMapping("/consumers/services")
  38. public List<String> findServices() {
  39. log.info("当前注册中心下所有服务");
  40. List<String> services = discoveryClient.getServices();
  41. services.stream().map(discoveryClient::getInstances).forEach(v ->
  42. v.forEach(s -> System.out.printf("%s:%s uri:%s%n", s.getHost(), s.getPort(), s.getUri())));
  43. return services;
  44. }
  45. }
bootstrap.yaml

这个是 service-consumer 的引导类配置,也放了rocketmq 的配置进来,因为对于消费者它启动的时候就会去初始化consumer。

  1. spring:
  2. application:
  3. name: service-consumer
  4. cloud:
  5. kubernetes:
  6. reload:
  7. enabled: true
  8. config:
  9. name: ${spring.application.name}
  10. namespace: service-k8s-demo
  11. sources:
  12. - name: ${spring.application.name}
  13. namespace: service-k8s-demo
  14. - name: service-common-config
  15. namespace: service-k8s-demo
  16. management:
  17. endpoint:
  18. restart:
  19. enabled: true
  20. health:
  21. enabled: true
  22. info:
  23. enabled: true
  24. endpoints:
  25. web:
  26. exposure:
  27. include: '*'
  28. rocketmq:
  29. name-server: 124.222.91.116:9876
Dockerfile
  1. FROM arm64v8/openjdk:20-slim-buster
  2. ADD service-consumer-start-0.0.1-SNAPSHOT-exec.jar service-consumer.jar
  3. ENTRYPOINT java -jar service-consumer.jar

config

service-consumer-dev.yaml
  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: service-consumer-dev
  5. namespace: service-k8s-demo
  6. labels:
  7. spring.cloud.kubernetes.config: "true"
  8. data:
  9. application.yml: |-
  10. spring:
  11. profiles: dev
  12. server:
  13. port: 30001
  14. grpc:
  15. server:
  16. port: 9091
  17. consumer:
  18. body: "1234567890--dev"
service-consumer-prod.yaml
  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: service-consumer-prod
  5. namespace: service-k8s-demo
  6. labels:
  7. spring.cloud.kubernetes.config: "true"
  8. data:
  9. application.yml: |-
  10. spring:
  11. profiles: prod
  12. server:
  13. port: 30001
  14. grpc:
  15. server:
  16. port: 9091
  17. consumer:
  18. body: "1234567890--prod"

service-consumer-deploy.yaml

  1. apiVersion: v1
  2. kind: Namespace
  3. metadata:
  4. name: service-k8s-demo
  5. labels:
  6. name: service-k8s-demo
  7. ---
  8. apiVersion: v1
  9. kind: ServiceAccount
  10. metadata:
  11. name: service-k8s-demo
  12. namespace: service-k8s-demo
  13. ---
  14. kind: ClusterRole
  15. apiVersion: rbac.authorization.k8s.io/v1
  16. metadata:
  17. namespace: service-k8s-demo
  18. name: service-k8s-demo
  19. rules:
  20. - apiGroups:
  21. - ""
  22. resources:
  23. - services
  24. - configmaps
  25. - endpoints
  26. - nodes
  27. - pods
  28. - secrets
  29. - namespaces
  30. verbs:
  31. - get
  32. - list
  33. - watch
  34. ---
  35. apiVersion: rbac.authorization.k8s.io/v1
  36. kind: ClusterRoleBinding
  37. metadata:
  38. name: service-k8s-demo
  39. namespace: service-k8s-demo
  40. subjects:
  41. - kind: ServiceAccount
  42. name: service-k8s-demo
  43. namespace: service-k8s-demo
  44. roleRef:
  45. kind: ClusterRole
  46. name: service-k8s-demo
  47. apiGroup: rbac.authorization.k8s.io
  48. ---
  49. apiVersion: apps/v1
  50. kind: Deployment
  51. metadata:
  52. name: service-consumer
  53. namespace: service-k8s-demo
  54. labels:
  55. app: service-consumer
  56. spec:
  57. replicas: 1
  58. template:
  59. metadata:
  60. name: service-consumer
  61. labels:
  62. app: service-consumer
  63. spec:
  64. containers:
  65. - name: service-consumer
  66. image: service-consumer:1.0
  67. imagePullPolicy: IfNotPresent
  68. env:
  69. - name: SPRING_PROFILES_ACTIVE
  70. value: "prod"
  71. ports:
  72. - name: http
  73. protocol: TCP
  74. containerPort: 30001
  75. - name: grpc
  76. protocol: TCP
  77. containerPort: 9091
  78. serviceAccountName: service-k8s-demo
  79. restartPolicy: Always
  80. selector:
  81. matchLabels:
  82. app: service-consumer
  83. ---
  84. apiVersion: v1
  85. kind: Service
  86. metadata:
  87. name: service-consumer
  88. namespace: service-k8s-demo
  89. spec:
  90. selector:
  91. app: service-consumer
  92. ports:
  93. - port: 80
  94. targetPort: 30001
  95. name: http
  96. - port: 9091
  97. targetPort: 9091
  98. name: grpc
  99. type: NodePort

至此为止,我们的 service-consumer 模块的代码也都已经全部完成了。

部署

0. 准备环境:docker、K8s、Istio

  1. service-provider 和 service-consumer 打镜像,我这里直接打到本地仓库,这里是docker的范畴(docker build -t service-provider:1.0 . 等)

  1. 把 service-provider 和 service-consumer 的 config 都在K8s容器中执行,这里是K8s的范畴(kubectl apply -f service-consumer-dev.yaml 等)

  1. 把 service-provider 和 service-consumer 部署到 K8s 容器中,这里是K8s的范畴(kubectl apply -f service-consumer-deploy.yaml 等),部署完成之后我们查看一下相关的pod,service,config等

  1. 这个时候两个服务的端口都有了,就可以使用controller里的rest接口相互进行测试了,可以测试下service-consumer调用service-provide的gRpc接口,可以发现 service-provide 虽然有3台但全打在了一个POD上,这个是因为gRpc是基于HTTP2.0多路复用,L4层基于连接级别的负载均衡,在K8s中负载均衡是失效的,需要借助L7应用层负载均衡来做,Envoy和Linkerd等可以实现,Linkerd实现起来很简单,可以点这里

  1. 把我们的namespace 注入到 Envoy 中

kubectl label namespace service-k8s-demo istio-injection=enabled
  1. 启动Istio dashboard Kiali

istioctl dashboard kiali &
  1. 做测试,我的Gateway里的端口是31400

测试gRpc相互调用,同时也关注下负载均衡的问题,这里解决了。

测试RocketMQ 生产消费

测试动态配置

至此,测试完成。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/181648
推荐阅读
  

闽ICP备14008679号