Sidecar
Sidecar 可以让非 JVM 语言的应用程序使用 Eureka、Ribbon、和 Config Server,它包含一个简单的 http api,用于获取给定服务的所有实例(即主机和端口),也可以通过嵌入式 Zuul 服务网关来代理服务调用,该代理从Eureka 获取路由条目,可以通过主机查找或 Zuul 代理直接访问 Spring Config Server。非JVM 应用程序应实施运行状况检查,以便 Sidecar 可以在应用程序启动或关闭时向 Eeureka 报告。
简而言之、它做的事情更加像一个适配器。能把一个普通的 PHP 或者 Golang 等其他语言开发的服务伪装成一个正常的 SpringCloud 服务,通过简单的配置,我们就可以让一些其他语言开发的 Web 应用,加入到 SpringCloud 体系中来。
使用 Gin 搭建邮件服务
在使用 Sidecar 整合非 JVM 服务前,需要先准备一个所谓的第三方服务
下载安装 Gin
go get -u github.com/gin-gonic/gin
初始化项目
go mod init project-name
将 Gin 导入项目中
import "github.com/gin-gonic/gin"
go mod tidy
go mod download
go mod vendor
编写邮件路由
package main
import (
"email-service/controller"
"email-service/middleware"
"github.com/gin-gonic/gin"
)
var router = gin.Default()
func main() {
router.Use(middleware.Cors())
router.POST("/ping", controller.Ping)
router.GET("/health.json", controller.Health)
router.POST("/email", controller.EmailHandler)
router.Run("127.0.0.1:9000")
}
各个路由对应的处理器函数
package controller
import (
"email-service/model"
"email-service/service"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func Ping(ctx *gin.Context) {
log.Printf("参数:%v", ctx.Params)
//ctx.JSON(http.StatusOK, gin.H{
// "message": "pong",
//})
ctx.String(200, "message:%v", "pong")
}
// 健康检查
func Health(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"status": "UP",
})
}
// 发送邮件
func EmailHandler(context *gin.Context) {
req := &model.EmailReq{}
if err := context.BindJSON(req); err != nil {
log.Printf("参数错误, req: %v", req)
context.JSON(400, model.Result{
Code: 400,
Message: "参数错误",
})
return
}
log.Printf("req:%v\n", req)
err := service.SendEmail(req.ToUser, req.UserName)
if err != nil {
log.Printf("SendEmail fail, err=%v", err)
context.JSON(500, model.Result{
Code: 500,
Message: "服务器内部错误",
})
return
}
context.JSON(200, model.Result{
Code: 200,
Message: "邮件发送成功",
})
}
核心邮件服务
package service
import (
"bytes"
"email-service/dao"
"github.com/jordan-wright/email"
"html/template"
"log"
"math/rand"
"net/smtp"
"time"
)
var (
r *rand.Rand
fromUser = "" // 填写自己的邮箱
password = "" // 填写授权码
host = "smtp.qq.com"
activeCode = ""
)
// 使用第三方库发送邮件
func SendEmail(toUser, userName string) error {
activeCode = RandString(6)
e := email.NewEmail()
e.From = fromUser
e.To = []string{toUser}
e.Subject = "购票成功"
t, err := template.ParseFiles("templates/email.html")
if err != nil {
log.Fatalf("ParseFiles error: %v", err)
return err
}
body := new(bytes.Buffer)
//作为变量传递给html模板
t.Execute(body, struct {
Name string
ActiveCode string
}{
Name: userName,
ActiveCode: activeCode,
})
// html形式的消息
e.HTML = body.Bytes()
// 从缓冲中将内容作为附件到邮件中
//e.Attach(body, "email.html", "text/html")
// 以路径将文件作为附件添加到邮件中
//e.AttachFile("$GOPATH/src/email/main.go")
// 发送邮件(如果使用QQ邮箱发送邮件的话,password不是邮箱密码而是授权码)
err = e.Send("smtp.qq.com:587", smtp.PlainAuth("", fromUser, password, host))
if err == nil {
dao.InsertEmail(fromUser, toUser)
}
return err
}
func RandString(len int) string {
r = rand.New(rand.NewSource(time.Now().Unix()))
bytes := make([]byte, len)
for i := 0; i < len; i++ {
b := r.Intn(26) + 65
bytes[i] = byte(b)
}
return string(bytes)
}
**完整代码地址:**gitee.com/movie-ticke…
使用 Sidecar 调用非 JVM 服务的步骤
1、首先要确保第三方服务中已经提供了 health 接口,此接口用于 Sidecar 对第三方服务做健康检查并上报 Eureka 服务器,且应该返回如下的 Json 字符串
{
"status": "UP"
}
2、在父工程下创建一 个 SpringBoot 子工程
3、导入Sidecar依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-sidecar</artifactId>
</dependency>
</dependencies>
**4、在启动类加入 @SpringBootApplication 和 @EnableSidecar 注解 **
@EnableSidecar注解整合了@EnableCircuitBreaker,@EnableDiscoveryClient和@EnableZuulProxy三个注解
@SpringBootApplication
@EnableSidecar
public class SidecarApplication {
public static void main(String[] args) {
SpringApplication.run(SidecarApplication.class, args);
}
}
5、修改 application.properties 配置文件
server.port=6666
spring.application.name=rpc-sidecar
eureka.client.serviceUrl.defaultZone=http://user:password@139.9.187.243:8888/eureka/
eureka.instance.prefer-ip-address=true
eureka.instance.ip-address=127.0.0.1
eureka.client.enabled=true
# 监听要使用得第三方服务
sidecar.port=9000
sidecar.ip-address=127.0.0.1
# 做健康检查的接口地址
sidecar.health-uri=http://${sidecar.ip-address}:${sidecar.port}/health.json
sidecar.home-page-uri=http://${sidecar.ip-address}:${sidecar.port}
# 配置传播敏感头部到下游的黑名单
zuul.sensitive-headers=Cookie,Set-Cookie
6、依次启动 go 邮件服务、Eureka 服务、Sidecar服务
7、接下来就可以在需要使用邮件服务的地方正常调用邮件了,与调用 Spring 服务无异
e.g.:此场景是用户成功购买调用票之后向用户发送含有取票码的邮件
EmailCTO body = new EmailD router.POST("/email", controller.EmailHandler)
TO(tmpUser.getNickName(), tmpUser.getUsername());
Result result = restTemplate.postForObject("http://rpc-sidecar/email", body, Result.class);
利用 Sidecar 在非 JVM 服务中调用 SpringCloud 服务
在之前的 rpc-sidecar服务中,其实已经默认整合了 Zuul 和 Eureka以及 Hystrix,所以我们只要在非 JVM服务中去调用 rpc-sidecar服务,它自带的网关会帮我们转发请求,Zuul 默认使用服务名称,我们也可以自己去配置自定义的转发路径,如果需要传递 Header 要记得在rpc-sidecar服务中 配置传播敏感头部的黑名单
e.g.
# 配置传播敏感头部到下游的黑名单
zuul.sensitive-headers=Cookie,Set-Cookie
router.GET("/user/:instance/:id", controller.UserHandler)
func UserHandler(ctx *gin.Context) {
instance := ctx.Param("instance")
id := ctx.Param("id")
if id == "" {
ctx.JSON(http.StatusBadRequest, gin.H{
"msg": "参数错误",
})
return
}
client := http.DefaultClient
url := fmt.Sprintf("http://127.0.0.1:6666/microservice-user/%s?id=%s", instance, id)
request, err := http.NewRequest(http.MethodGet, url, nil)
if request == nil || err != nil {
log.Printf("new request fail, err:%v", err)
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": "服务器内部错误",
})
return
}
authorization := ctx.GetHeader("Authorization")
log.Printf("Authorization:%v", authorization)
request.Header.Add("Authorization", authorization)
resp, err := client.Do(request)
if err != nil {
log.Printf("err: %v, resp: %v\n", err, resp)
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": "服务器内部错误",
})
return
}
log.Printf("resp:%v\n\n", resp)
var result = model.Result{}
if 200 == resp.StatusCode {
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil{
log.Printf("Unmarshal err: %v\n", err)
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": "服务器内部错误",
})
return
}
}
ctx.JSON(http.StatusOK, result)
测试结果