最近接了一个需求,要把一些现有的golang微服务对接到老平台的spring cloud微服务框架中。这些golang微服务提供了一些proto+grpc接口,对接老平台目的就是,想要快速复用这些后端的业务能力,这样一来,只需要涉及交互的设计和前端编码的工作,后端不需要用java重写一遍造轮子,并且这些golang微服务是之前上过线的,所以稳定性和准确性都有保证。
老平台的架构和这个极精简版类似:
所以,我就把重心放在如何将golang微服务接入到spring cloud gateway里,然后要满足以下几个需求:
- 复用spring cloud gateway路由和负载均衡能力,能将url正确路由到golang微服务内,当然前提是golang微服务需要暴露出restful api
- 复用spring cloud gateway的身份认证能力
- 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