一、dubbogo相关介绍链接
1、Dubbo基本使用与原理详解
2、dubbogo 3.0:牵手 gRPC 走向云原生时代
3、快速上手 dubbo-go
4、dubbogo github page
5、dubbo-go examples
6、dubbbo 3.0 快速开始
7、hesson协议序列化example
二、安装zookeeper
这里我们使用zookeeper
作为注册中心
1、安装docker-compose
apt install docker-compose
2、编写docker-compose.yaml
mkdir zookeeper
cd zookeeper
vim docker-compose.yaml
输入如下内容并保存
version: '3'
services:
zookeeper:
image: zookeeper
ports:
- 2181:2181
admin:
image: apache/dubbo-admin:latest
depends_on:
- zookeeper
ports:
- 8081:8080
environment:
- admin.registry.address=zookeeper://zookeeper:2181
- admin.config-center=zookeeper://zookeeper:2181
- admin.metadata-report.address=zookeeper://zookeeper:2181
运行
docker-compose -f /home/jun/docker/zookeeper/docker-compose.yaml up -d
查看容器是否启动
主机浏览器打开地址查看dubbo-admin:http://192.168.96.129:8081
登录用户名密码均为
root
192.168.96.129
换为自己虚拟机的ip
三、JAVA
端
此文介绍一个示例,使用spring boot dubbo
的java
客户端调用dubbo-go
的go
语言服务
3.1、初始化springboot dubbo
3.1.1、spring.io
按照图示新建一个项目,文件夹如下图:
3.1.2、转换为多module
项目
将当前目录所有文件移动到名为dubbo-consumer
的子目录下,然后在当前目录新增pom
文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.5.5
com.example
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
pom
1.8
dubbo-client
dubbo-consumer
org.projectlombok
lombok
true
修改dubbo-consumer
的pom
文件内容为:
4.0.0
com.example
demo
0.0.1-SNAPSHOT
dubbo-consumer
0.0.1-SNAPSHOT
dubbo-demo-consumer
false
2.7.14
2.12.0
3.4.14
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.apache.dubbo
dubbo-spring-boot-starter
${dubbo.version}
org.apache.dubbo
dubbo-dependencies-zookeeper
${dubbo.version}
pom
org.slf4j
slf4j-log4j12
com.example
dubbo-client
0.0.1-SNAPSHOT
compile
org.springframework.boot
spring-boot-maven-plugin
新建子项目dubbo-client
,用于存放dubbogo
适配的接口类,pom
文件内容为
4.0.0
com.example
demo
0.0.1-SNAPSHOT
dubbo-client
3.1.3、最后项目的目录情况:
- --> 1:
dubbogo
互通协议的interface
接口 - --> 2:
dubbogo
互通协议的dto
请求响应类型 - --> 3:
dubbogo
消费端的请求接口类 - --> 4:
dubbogo
消费端的配置文件
3.2、代码编写
3.2.1、dubbo-client
QueryUserParam
package com.demo.exp.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class QueryUserParam implements Serializable {
private String userName;
private String userId;
}
QueryUserResponse
package com.demo.exp.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class QueryUserResponse implements Serializable {
private String userId;
private String userName;
private String birthDate;
private long age;
private boolean isDead;
}
UserService
package com.demo.exp.service;
import com.demo.exp.dto.QueryUserParam;
import com.demo.exp.dto.QueryUserResponse;
public interface UserService {
QueryUserResponse QueryUser(QueryUserParam req);
}
3.2.2、dubbo-consumer
dto.Result
package com.example.demo.dto;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class Result implements Serializable {
private static final long serialVersionUID = 1L;
// 业务码code
private int resultCode;
// 返回信息
private String message;
// 返回的数据
private T data;
public Result(){}
public Result(int code, String message){
this.resultCode = code;
this.message = message;
}
@Override
public String toString(){
return "Result{" +
"resultCode=" + resultCode +
", message='" + message + "'" +
", data=" + data +
"}";
}
}
dto.ResultGenerator
package com.example.demo.dto;
import org.springframework.util.StringUtils;
public class ResultGenerator {
private static final String DEFAULT_SUCCESS_MESSAGE = "success";
private static final String DEFAULT_FAIL_MESSAGE = "fail";
private static final int RESULT_CODE_SUCCESS = 200;
private static final int RESULT_CODE_SERVER_ERROR = 500;
public static Result genSuccessResult(){
Result res = new Result();
res.setResultCode(RESULT_CODE_SUCCESS);
res.setMessage(DEFAULT_SUCCESS_MESSAGE);
return res;
}
public static Result genSuccessResult(String msg){
Result res = new Result();
res.setResultCode(RESULT_CODE_SUCCESS);
res.setMessage(msg);
return res;
}
public static Result genSuccessResult(Object data){
Result res = new Result();
res.setResultCode(RESULT_CODE_SUCCESS);
res.setMessage(DEFAULT_SUCCESS_MESSAGE);
res.setData(data);
return res;
}
public static Result genFailResult(String msg){
Result res = new Result();
res.setResultCode(RESULT_CODE_SERVER_ERROR);
if(!StringUtils.hasLength(msg)){
res.setMessage(DEFAULT_FAIL_MESSAGE);
}else{
res.setMessage(msg);
}
return res;
}
public static Result genErrorResult(int code, String msg){
Result res = new Result();
res.setResultCode(code);
res.setMessage(msg);
return res;
}
}
UserController
package com.example.demo.controllers;
import com.demo.exp.dto.QueryUserParam;
import com.demo.exp.dto.QueryUserResponse;
import com.demo.exp.service.UserService;
import com.example.demo.dto.Result;
import com.example.demo.dto.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {
@DubboReference(retries = 0, timeout = 1000 * 60, application = "dubbogo-demo", group = "dubbo-service", version = "1.0.0")
UserService userService;
@GetMapping("/query")
@ResponseBody
public Result QueryUser(){
QueryUserParam req = new QueryUserParam();
req.setUserName("test");
RpcContext rpcContext = RpcContext.getContext();
rpcContext.setAttachment("app", "dubbo-consumer");
QueryUserResponse resp = null;
try{
resp = userService.QueryUser(req);
}catch (Throwable e){
log.error("QueryUser error", e);
return ResultGenerator.genFailResult(e.getMessage());
}
return ResultGenerator.genSuccessResult(resp);
}
}
-
application.yml
配置文件
server:
port: 7000
spring:
application:
name: dubbo-consumer
logging:
level:
root: info
#dubbo
dubbo:
application:
name: dubbo-consumer
registry:
address: zookeeper://192.168.96.129:2181
timeout: 60000
protocal:
name: dubbo
consumer:
retries: 1
check: false
四、go
端
4.1、初始化go
项目
mkdir dubbogodemo
cd dubbogodemo
go mod init dubbo-demo
go get github.com/pkg/errors
go get dubbo.apache.org/dubbo-go/[email protected]
4.2、 编写go代码
4.2.1、项目结构
- --> 1:
dubbogo
互通协议的dto
请求响应类型 - --> 2:
dubbogo
互通协议的interface
实现 - --> 3: 工具类
- --> 4:
dubbogo
服务提供方的配置文件 - --> 5:
dubbogo
服务启动工具
4.2.2、dto
UserRequest
package dto
type UserRequest struct {
UserName string // 用户名
UserId string // 用户id
}
func(req UserRequest) JavaClassName() string{
return "com.demo.exp.dto.QueryUserParam"
}
UserResponse
package dto
type UserResponse struct {
UserName string // 用户名
UserId string // 用户id
BirthDate string // 出生日期
Age int64 // 年龄
IsDead bool // 是否已去世
}
func(req UserResponse) JavaClassName() string{
return "com.demo.exp.dto.QueryUserResponse"
}
4.2.3、service
UserService
package service
import (
"context"
"dubbo-demo/pkg/dto"
"dubbo-demo/pkg/util"
"github.com/pkg/errors"
"time"
)
type UserService struct {
}
func (u *UserService) Reference() string {
return "UserService"
}
func (u *UserService) QueryUser(ctx context.Context, in *dto.UserRequest)(*dto.UserResponse, error){
appName := util.GetDubboContextAppName(ctx)
if appName == ""{
return nil, errors.Errorf("auth error, no appname")
}
if appName != "dubbo-consumer"{
return nil, errors.Errorf("auth error, not allowed")
}
if in.UserId != ""{
if in.UserId != "123456789"{
return nil, errors.Errorf("no user found with id[%s]", in.UserId)
}
return &dto.UserResponse{
UserName: "dubbo-user1",
UserId: "123456789",
BirthDate: util.GetCommonTimeStr(time.Now().Add(-56 * 360 * 24 * time.Hour)),
Age: 56,
IsDead: true,
}, nil
}
if in.UserName == ""{
return nil, errors.Errorf("please input query condition")
}
return &dto.UserResponse{
UserName: in.UserName,
UserId: "666666666",
BirthDate: util.GetCommonTimeStr(time.Now().Add(-30 * 360 * 24 * time.Hour)),
Age: 30,
IsDead: false,
}, nil
}
4.2.2、util
func GetDubboContextAppName(ctx context.Context)(appName string){
ctxMap, ok := ctx.Value(constant.DubboCtxKey("attachment")).(map[string]interface{})
if !ok{
return
}
if str, ok := ctxMap["app"]; ok{
appName, _ = str.(string)
}
return
}
func GetCommonTimeStr(t time.Time) string{
return t.Format("2006-01-02 15:04:05")
}
4.2.4、dubbo.yml
# dubbo server yaml configure file
dubbo:
application:
name: dubbogo-demo
module: dubbogo
version: 1.0.0
owner: demo
registries:
demoZK:
protocol: zookeeper
timeout: 3s
address: 192.168.96.129:2181
protocols:
dubbo:
name: dubbo
port: 20000
provider:
register: true
registryIDs:
- demoZK
services:
UserService:
protocol: dubbo
interface: com.demo.exp.service.UserService
group: dubbo-service
version: 1.0.0
loadbalance: random
warmup: 100
cluster: failover
methods:
- name: QueryUser
retries: 1
loadbalance: random
logger:
zap-config:
level: info
4.2.5、cmd
cmd.go
package cmd
import (
"dubbo-demo/pkg/dto"
"dubbo-demo/pkg/service"
"dubbo.apache.org/dubbo-go/v3/config"
_ "dubbo.apache.org/dubbo-go/v3/imports"
hessian "github.com/apache/dubbo-go-hessian2"
)
func StartDubboServer(){
config.SetProviderService(&service.UserService{})
hessian.RegisterPOJO(&dto.UserRequest{})
hessian.RegisterPOJO(&dto.UserResponse{})
err := config.Load(config.WithPath("./conf/dubbo.yml"))
if err != nil{
panic(err)
}
select {
}
}
main.go
package main
import (
"dubbo-demo/cmd"
)
func main(){
cmd.StartDubboServer()
}
4.3、go mod
如果import
包有问题或者报错,按照如下操作试试
go mod tidy
go get dubbo.apache.org/dubbo-go/[email protected]
go mod tidy
五、运行测试
5.1、启动go
项目
goland
按照如下配置运行:
启动后,在
dubbo admin
可以看到服务已经注册:
详情页服务提供者可以看到运行种服务的ip和端口:
此时,消费者没有信息。
5.2、启动java
项目
idea
按照如下配置运行:
启动后,在
dubbo admin
服务详情页可以看到消费者信息:
5.3、调用测试
调用java
项目的api
接口,api
会通过dubbo
协议调用到go
项目的服务。
浏览器打开地址: 127.0.0.1:7000/api/user/query
得到如下返回:
{
"resultCode": 200,
"message": "success",
"data":
{
"userId": "666666666",
"userName": "test",
"birthDate": "1992-03-23 20:50:05",
"age": 30,
"dead": false
}
}
修改代码,当有错误error
时,返回如下:
{
"resultCode": 500,
"message": "no user found with id[666]"
}
六、项目代码
dubbogo example in github