Golang微服务对接Spring-Cloud Gateway

Golang
73
0
0
2024-06-28
标签   微服务

最近接了一个需求,要把一些现有的golang微服务对接到老平台的spring cloud微服务框架中。这些golang微服务提供了一些proto+grpc接口,对接老平台目的就是,想要快速复用这些后端的业务能力,这样一来,只需要涉及交互的设计和前端编码的工作,后端不需要用java重写一遍造轮子,并且这些golang微服务是之前上过线的,所以稳定性和准确性都有保证。

老平台的架构和这个极精简版类似:

​所以,我就把重心放在如何将golang微服务接入到spring cloud gateway里,然后要满足以下几个需求:

  1. 复用spring cloud gateway路由和负载均衡能力,能将url正确路由到golang微服务内,当然前提是golang微服务需要暴露出restful api
  2. 复用spring cloud gateway的身份认证能力
  3. java和golang微服务可以互相进行rpc调用,比如golang微服务可以通过rpc获取某个租户的信息

通过在网上的一番调研,最终锁定了spring-cloud-alibaba-sidecar和grpc-gateway这两个组件,整体的对接思路如下,下面开始搞个demo验证一下:

1. Spring Cloud Gateway环境搭建

1.1 mvn安装

wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
tar -zxvf apache-maven-3.9.6-bin.tar.gz
vim /etc/profile
/*
    尾部追加
    export MAVEN_HOME=/usr/local/apache-maven-3.9.6
    export PATH=$MAVEN_HOME/bin:$PATH
*/

source /etc/profile 
mvn -v
/*
    Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
    Maven home: /usr/download/apache-maven-3.9.6
    Java version: 21.0.2, vendor: Oracle Corporation, runtime: /usr/lib/jvm/jdk-21-oracle-x64
    Default locale: en_US, platform encoding: UTF-8
    OS name: "linux", version: "3.10.0-1160.el7.x86_64", arch: "amd64", family: "unix"
*/

vim conf/settings.xml
/* <!-- mirror标签页下新增
    <mirror>
        <id>huaweicloud</id>
        <mirrorOf>central</mirrorOf>
        <name>huaweicloud</name>
        <url>https://mirrors.huaweicloud.com/repository/maven/</url>
    </mirror>
*/

1.2 jdk安装

wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.rpm
rpm -ivh jdk-21_linux-x64_bin.rpm
javac -version
/*
javac 21.0.2
*/

java --version
/*
java 21.0.2 2024-01-16 LTS
Java(TM) SE Runtime Environment (build 21.0.2+13-LTS-58)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.2+13-LTS-58, mixed mode, sharing)
*/

1.4 spring cloud gateway

git clone git@github.com:spring-guides/gs-gateway.git
cd gs-gateway/initial
vim pom.xml
/*
pom修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>3.2.0</version>
                <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>gs-gateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>gs-gateway</name>
        <description>Demo project for Spring Boot</description>
        <properties>
                <java.version>17</java.version>
                <spring-cloud.version>2023.0.0-RC1</spring-cloud.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-starter-gateway</artifactId>
                </dependency>

                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
                        <scope>test</scope>
                </dependency>

                <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
                </dependency>
                <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                </dependency>

                <dependency>
                   <groupId>org.springframework.cloud</groupId>
                   <artifactId>spring-cloud-starter-loadbalancer</artifactId>
                </dependency>

        </dependencies>
        <dependencyManagement>
                <dependencies>
                        <dependency>
                                <groupId>org.springframework.cloud</groupId>
                                <artifactId>spring-cloud-dependencies</artifactId>
                                <version>${spring-cloud.version}</version>
                                <type>pom</type>
                                <scope>import</scope>
                        </dependency>

                        <dependency>
                          <groupId>com.alibaba.cloud</groupId>
                          <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                          <version>2022.0.0.0-RC1</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>

                </dependencies>
        </dependencyManagement>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <version>3.9.0</version>
                                        <configuration>
                                                <source>2.1</source>
                                                <target>2.1</target>
                                                <verbose>true</verbose>
                                        </configuration>
                        </plugin>
                </plugins>
        </build>
        <repositories>
                <repository>
                        <id>spring-milestones</id>
                        <name>Spring Milestones</name>
                        <url>https://repo.spring.io/milestone</url>
                        <snapshots>
                                <enabled>false</enabled>
                        </snapshots>
                </repository>
        </repositories>

</project>
*/

mvn install
java -jar gs-gateway-0.0.1-SNAPSHOT.jar --debug
curl http://localhost:8080/get
/*
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Connection": "close",
    "Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:56207\"",
    "Hello": "World",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.54.0",
    "X-Forwarded-Host": "localhost:8080"
  },
  "origin": "0:0:0:0:0:0:0:1, 73.68.251.70",
  "url": "http://localhost:8080/get"
}
*/

2. Nacos安装

wget https://github.com/alibaba/nacos/releases/download/2.3.0/nacos-server-2.3.0.tar.gz
tar -zxvf nacos-server-2.3.0.tar.gz
bin/startup.sh -m standalone

3. Golang和Java启动web服务

mvn archetype:generate
vim pom.xml
/*
修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>3.2.0</version>
                <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>javaweb</groupId>
        <artifactId>javaweb</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>javaweb</name>
        <description>Demo project for Spring Boot</description>
        <properties>
                <java.version>17</java.version>
                <spring-cloud.version>2023.0.0-RC1</spring-cloud.version>
        </properties>
        <dependencies>
                <dependency>
                       <groupId>junit</groupId>
                       <artifactId>junit</artifactId>
                       <version>4.12</version>
                       <scope>test</scope>
               </dependency>

               <dependency>
                 <groupId>com.alibaba.cloud</groupId>
                 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
               </dependency>
               <dependency>
                 <groupId>com.alibaba.cloud</groupId>
                 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                 </dependency>

               <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <version>2.6.3</version>
                </dependency>


                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
                        <scope>test</scope>
                </dependency>

        </dependencies>
        <dependencyManagement>
                <dependencies>
                        <dependency>
                                <groupId>org.springframework.cloud</groupId>
                                <artifactId>spring-cloud-dependencies</artifactId>
                                <version>${spring-cloud.version}</version>
                                <type>pom</type>
                                <scope>import</scope>
                        </dependency>

                        <dependency>
                          <groupId>com.alibaba.cloud</groupId>
                          <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                          <version>2022.0.0.0-RC1</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>

                </dependencies>
        </dependencyManagement>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                                <version>2.6.3</version>
                        </plugin>
                </plugins>
        </build>
        <repositories>
                <repository>
                        <id>spring-milestones</id>
                        <name>Spring Milestones</name>
                        <url>https://repo.spring.io/milestone</url>
                        <snapshots>
                                <enabled>false</enabled>
                        </snapshots>
                </repository>
        </repositories>

</project>
*/ 

vim bootstrap.yml
/*
新增如下:
server:
  port: 10001

spring:
  profiles:
    active: dev
  application:
    name: javaweb
  cloud:
    nacos:
      config:
        file-extension: yaml
        group: dev
        server-addr: 127.0.0.1:8848
      discovery:
        auto-register: true
        server-addr: 127.0.0.1:8848
        locator:
          enabled: true
          lower-case-service-id: true
*/

vim App.java
/*
修改java文件如下:
package javaweb;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;

@EnableDiscoveryClient
@RestController
@RequestMapping("/java-service-1")
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "[java]Hello, World!we server in port 10001";
    }

    @Bean
    public TomcatServletWebServerFactory servletContainer(){
         return new TomcatServletWebServerFactory(8022) ;
    }
}
*/

java -jar target/javaweb-0.0.1-SNAPSHOT.jar --spring.config.location=/usr/code/javaweb/bootstrap.yml --spring.cloud.nacos.config.import-check.enabled=false
curl http://localhost:10001/java-service-1/hello

wget https://golang.google.cn/dl/go1.21.6.darwin-amd64.tar.gz
tar -zxvf go1.21.6.linux-amd64.tar.gz -C /usr/local/
vim /etc/profile
/*
尾部追加:
export GOROOT=/usr/local/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
*/

source /etc/profile 

go version
/*
go version go1.21.6 linux/amd64
*/

vi main.go
/*
新建main.go如下:
package main

import (
        "encoding/json"
        "fmt"
        "log"
        "net/http"
)

func main() {
        http.HandleFunc("/golang-service/hello", helloHandler)
        http.HandleFunc("/golang-service/health", healthHandler)
        log.Println("Go 微服务已启动,监听端口 10002...")
        log.Fatal(http.ListenAndServe(":10002", nil))
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "[golang] hello world!")
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        actuator := make(map[string]string)
        actuator["status"] = "UP"
        _ = json.NewEncoder(w).Encode(actuator)

        log.Println("[golang] i'm alive!...")
}

*/
go run main.go
//2024/02/03 10:43:21 Go 微服务已启动,监听端口 10002...
curl http://localhost:10002/golang-service/hello

3. Alibaba Sidecar

git clone git@github.com:alibaba/spring-cloud-alibaba.git

cd spring-cloud-alibaba/spring-cloud-alibaba-examples/spring-cloud-alibaba-sidecar-examples/spring-cloud-alibaba-sidecar-nacos-example/

vi src/main/resources/application.yml
/*
server:
  port: 8070
spring:
  cloud:
    nacos:
      username: 'nacos'
      password: 'nacos'
      discovery:
        server-addr: 127.0.0.1:8848
        group: DEFAULT_GROUP
    gateway:
      discovery:
        locator:
          enabled: true
    loadbalancer:
      nacos:
        enabled: true
      ribbon:
        enabled: false

  application:
    name: golangweb
sidecar:
  # 异构微服务的IP
  ip: 127.0.0.1
  # 异构微服务的端口
  port: 10002

  # 异构微服务的健康检查URL
  health-check-url: http://localhost:10002/golang-service/health
management:
  endpoint:
    health:
      show-details: always
*/

mvn install

java -jar target/spring-cloud-alibaba-sidecar-nacos-example-2022.0.0.0.jar

大家注意看这个10002是golang微服务的端口,心跳检查也持续进行,灰常完美。

4. Gateway 路由配置修改

# vim bootstrap.yml
server:
  port: 10003

spring:
  profiles:
    active: dev
  application:
    name: gy-gateway

  cloud:
    gateway:
      discovery:
        auto-register: true
        server-addr: 127.0.0.1:8848
        locator:
          enabled: true
          # 是否使用service-id的小写,默认是大写
          lower-case-service-id: true
      routes:
        - id: javawebsvr
          uri: lb://javaweb
          predicates:
            - Path=/java-service-1/** # 断言,路径相匹配的进行路由(注意**为通配符)
          filters:
            - StripPrefix=1
        - id: golangsvr
          # uri: http://127.0.0.1:10002/ 都行
          uri: lb://golangweb
          predicates:
            - Path=/golang-service/** # 断言,路径相匹配的进行路由(注意**为通配符)
          filters:
            - StripPrefix=1
curl http://localhost:10003/golang-service/golang-service/hello
curl http://localhost:10003/java-service-1/java-service-1/hello

5. Java调用Golang微服务

在之前的javaweb工程的基础上改了一些东西:
vi bootstrap.yml
/*
server:
  port: 10011
*/

vim App.java
/*
package javaweb;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.client.RestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import java.util.List;

@EnableDiscoveryClient
@RestController
@RequestMapping("/java-service-1")
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "[java]Hello, World!we server in port 10001";
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/hellogolang")
    public String hellogolang() {
        List<String> services = discoveryClient.getServices();

        for (String service : services) {
            System.out.println("**service**:"+service);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances("golangweb");

        for (ServiceInstance instance : instances) {

            System.out.println(instance.getInstanceId()+"\t"+instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
            String url = "http://"+instance.getHost() + ":" + instance.getPort() + "/golang-service/hello";
            return restTemplate.getForObject(url, String.class);
        }
        return "ha";

    }
}
*/

java -jar target/javaweb-0.0.1-SNAPSHOT.jar --spring.config.location=/usr/code/javaweb/bootstrap.yml --spring.cloud.nacos.config.import-check.enabled=false --debug --spring.main.allow-circular-references=true --spring.main.web-application-type=reactive

Reference

https://sca.aliyun.com/zh-cn/blog/spring-boot-to-spring-cloud-best-practice

https://blog.csdn.net/lucky_ykcul/article/details/105219891

https://blog.csdn.net/it_sunwz/article/details/115271506

https://blog.csdn.net/u011019141/article/details/78458566

https://blog.csdn.net/zsf_join/article/details/99819845

https://github.com/spring-guides/gs-gateway

https://spring.io/guides/gs/gateway/

https://www.jianshu.com/p/c4952ef14660

https://blog.csdn.net/llm_hao/article/details/122856895

https://blog.csdn.net/weixin_44102521/article/details/117689297

https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html

https://javahungry.blogspot.com/2019/05/solved-no-main-manifest-attribute-in-jar.html

https://blog.csdn.net/zsf_join/article/details/99819845

https://blog.csdn.net/hkl_Forever/article/details/129353742

https://blog.csdn.net/m4330187/article/details/122182282

https://juejin.cn/post/6844903999439634446

https://blog.csdn.net/m0_507173