Cross-language interoperability with Java applications
Preparation
environment
JDK 8, Golang >= 1.15, Dubbo 3.0.2, zookeeper enabled,
Go-Java Interoperability Prerequisites
The transfer structure defined by Go/Java is consistent
- PB serialization
proto for Go
// The response message containing the greetings message User { string name = 1; string id = 2; int32 age = 3; }
proto for Java
// The response message containing the greetings message User { string name = 1; string id = 2; int32 age = 3; }
- Hessian serialization
POJO for Go, please refer to Dubbogo Hessian serialization support document
type User struct { ID string name string Age int32 } func (u *User) JavaClassName() string { return "org.apache.dubbo.User" } func init(){ hessian.RegisterPOJO(&User{}) }
POJOs for Java
package org.apache.dubbo public class User { private String id; private String name; private int age; }
Java requires the same method signature as Go
E.g:
Java Interface
public interface IGreeter { /** * <pre> * Sends a greeting * </pre> */ User sayHello(HelloRequest request); }
Go client (automatically generated by protoc-gen-go-triple based on proto files)
type GreeterClientImpl struct { // Sends a greeting SayHello func(ctx context.Context, in *HelloRequest) (*User, error) }
Go server (defined by the developer)
type GreeterProvider struct { api. GreeterProviderBase } func (s *GreeterProvider) SayHello(ctx context.Context, in *api.HelloRequest) (*api.User, error) { logger.Infof("Dubbo3 GreeterProvider get user name = %s\n", in.Name) return &api.User{Name: "Hello " + in.Name, Id: "12345", Age: 21}, nil }
Go methods need to comply with Dubbogo 3.0 User Service Interface Definition Specification
Java’s triplet is consistent with the interface configured by Go service/reference
The triplet is configured at the interface level: interface, group, version. **It should be noted that the concept of group and version is the group and vesion of the dubbo interface, which are configured in the properties file of spring cloud when starting the dubbo-java service, not the version that mvn depends on in pom.xml. ** group and version are empty by default, in the dubbo-go framework, you can specify group and version in the corresponding position of service/reference.
E.g:
Java interface full name: com.apache.dubbo.sample.basic.IGreeter, interface version is v1.0.1, group is
Go-client:
references: GreeterClientImpl: protocol: tri interface: com.apache.dubbo.sample.basic.IGreeter # must be compatible with grpc or dubbo-java group: dubbogo # need to correspond to the server, the default is empty version: v1.0.1 # needs to correspond to the server, the default is empty
Go-server:
services: GreeterProvider: protocol-ids: tripleProtocol interface: com.apache.dubbo.sample.basic.IGreeter # must be compatible with grpc or dubbo-java group: dubbogo # need to correspond to the server, the default is empty version: v1.0.1 # needs to correspond to the server, the default is empty
1. Intercommunication based on Triple protocol (PB serialization)
Reference dubbo-go-samples/helloworld
1.1 Go-Client -> Java-Server
Java-Server start
- Define Java PB file, please refer to Dubbo Quick Start
syntax = "proto3";
option java_package = "org.apache.dubbo.sample.hello";
package helloworld;
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message User {
string name = 1;
string id = 2;
int32 age = 3;
}
The interface description file defines the generated Java class org.apache.dubbo.sample.hello.Helloworld, and the transmission structure HelloRequest and User class included in the class.
Define the service interface:
com.apache.dubbo.sample.basic.IGreeter
package com.apache.dubbo.sample.basic;
// Import the class generated according to PB
import org.apache.dubbo.sample.hello.Helloworld.User;
import org.apache.dubbo.sample.hello.Helloworld.HelloRequest;
public interface IGreeter {
/**
* <pre>
* Sends a greeting
* </pre>
*/
// define the interface
User sayHello(HelloRequest request);
}
Implement the service interface:
IGreeter1Impl.java
package com.apache.dubbo.sample.basic;
import org.apache.dubbo.sample.hello.Helloworld.User;
import org.apache.dubbo.sample.hello.Helloworld.HelloRequest;
public class IGreeter1Impl implements IGreeter {
@Override
public User sayHello(HelloRequest request) {
System.out.println("receiv: " + request);
User usr = User. newBuilder()
.setName("hello " + request.getName())
.setAge(18)
.setId("12345").build();
return usr;
}
}
Use the Dubbo3 framework to start the service
ApiProvider.java
package com.apache.dubbo.sample.basic;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import java.util.concurrent.CountDownLatch;
public class ApiProvider {
public static void main(String[] args) throws InterruptedException {
ServiceConfig<IGreeter> service = new ServiceConfig<>();
service.setInterface(IGreeter.class);
service.setRef(new IGreeter1Impl());
// Use the Triple protocol
service.setProtocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051));
service.setApplication(new ApplicationConfig("demo-provider"));
// Use ZK as the registration center
service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
service. export();
System.out.println("dubbo service started");
new CountDownLatch(1). await();
}
}
Start the service, you can see the following log output, which means that the Java Triple Server started successfully
main INFO bootstrap.DubboBootstrap: [DUBBO] DubboBootstrap has started., dubbo version: 3.0.2, current host: 192.168.0.108
dubbo service started
Go-Client start
For the Dubbo service that has been started, if you need to develop its corresponding Go-client, you need to perform the following steps:
Write a proto file adapted to Java
samples_api.proto
syntax = "proto3";
package api; // pacakge name is optional
//necessary
option go_package = "./;api";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (User) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message User {
string name = 1;
string id = 2;
int32 age = 3;
}
- Use protoc-gen-triple to generate interface files
protoc -I .samples_api.proto --triple_out=plugins=triple:.
- Write configuration file: dubbogo.yml
dubbo:
registries:
demoZK:
protocol: zookeeper
address: 127.0.0.1:2181
consumer:
references:
GreeterClientImpl:
protocol: tri
interface: com.apache.dubbo.sample.basic.IGreeter # must be compatible with grpc or dubbo-java
- Write the main.go file and initiate the call
// Import the generated interface structure
var grpcGreeterImpl = new(api. GreeterClientImpl)
// export DUBBO_GO_CONFIG_PATH=dubbogo.yml
func main() {
config. SetConsumerService(grpcGreeterImpl)
if err := config.Load(); err != nil {
panic(err)
}
time. Sleep(3 * time. Second)
logger.Info("start to test dubbo")
req := &api.HelloRequest{
Name: "Laurence",
}
reply, err := grpcGreeterImpl.SayHello(context.Background(), req)
if err != nil {
logger. Error(err)
}
logger.Infof("client response result: %v\n", reply)
}
- You can view the log of the successful call
- go-client
cmd/client.go:53 client response result: name:"hello laurence" id:"12345" age:18
-java-server
receiver: name: "laurence"
1.2 Java-Client -> Go-Server
Go-Server start
- Define the configuration file
dubbo:
registries:
demoZK:
protocol: zookeeper
address: 127.0.0.1:2181
protocols:
triple:
name: tri
port: 20000
provider:
services:
GreeterProvider:
interface: com.apache.dubbo.sample.basic.IGreeter # must be compatible with grpc or dubbo-java
- Introduce the transport structure and define the service
type GreeterProvider struct {
api. GreeterProviderBase
}
func (s *GreeterProvider) SayHello(ctx context.Context, in *api.HelloRequest) (*api.User, error) {
logger.Infof("Dubbo3 GreeterProvider get user name = %s\n", in.Name)
return &api.User{Name: "Hello " + in.Name, Id: "12345", Age: 21}, nil
}
- Start the service
// export DUBBO_GO_CONFIG_PATH=dubbogo.yml
func main() {
config. SetProviderService(&GreeterProvider{})
if err := config.Load(); err != nil {
panic(err)
}
select {}
}
Java-Client start
Proto file writing and interface generation refer to the introduction of the above java-server
Start Consumer
ApiCnosumer.java
public class ApiConsumer {
public static void main(String[] args) throws InterruptedException, IOException {
ReferenceConfig<IGreeter> ref = new ReferenceConfig<>();
ref. setInterface(IGreeter. class);
ref. setCheck(false);
ref.setProtocol(CommonConstants.TRIPLE);
ref. setLazy(true);
ref. setTimeout(100000);
ref. setApplication(new ApplicationConfig("demo-consumer"));
ref.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
final IGreeter iGreeter = ref. get();
System.out.println("dubbo ref started");
Helloworld.HelloRequest req = Helloworld.HelloRequest.newBuilder().setName("laurence").build();
try {
final Helloworld. User reply = iGreeter. sayHello(req);
TimeUnit. SECONDS. sleep(1);
System.out.println("Reply:" + reply);
} catch (Throwable t) {
t. printStackTrace();
}
System.in.read();
}
}
2. Intercommunication based on Dubbo protocol (Hessian2 serialization)
Reference dubbo-go-samples/rpc/dubbo
2.1 Go-Client -> Java-Server
Java-Server start
- Define the Java interface, parameters and return values, please refer to Dubbo Quick Start
package org.apache.dubbo;
// The service interface that needs to be exposed
public interface UserProvider {
User getUser(int usercode);
}
package org.apache.dubbo;
public class User implements Serializable {
private String id;
private String name;
private int age;
private Date time = new Date();
/* ... */
}
- Implement the service interface:
UserProviderImpl.java
package org.apache.dubbo;
public class UserProviderImpl implements UserProvider {
public User getUser(int userCode) {
return new User(String. valueOf(userCode), "userCode get", 48);
}
}
- Start with SpringBoot
Provider.java
package org.apache.dubbo;
// use when config by API
/*
import java.util.concurrent.CountDownLatch;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
*/
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Provider {
// main function, config from spring boot
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo.provider.xml"});
context. start();
System.in.read(); // press any key to exit
}
// config by API
// public static void startComplexService() throws InterruptedException {
// ServiceConfig<ComplexProvider> service = new ServiceConfig<>();
// service.setInterface(ComplexProvider.class);
// service. setRef(new ComplexProviderImpl());
// service.setProtocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, 20001));
// service.setApplication(new ApplicationConfig("demo-provider"));
// service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
// service. export();
// System.out.println("dubbo service started");
// new CountDownLatch(1). await();
// }
}
Configure Dubbo parameters through Spring
Resources/META-INF.spring/dubbo.provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- application name -->
<dubbo:application name="user-info-server"/>
<!-- which local registry to connect to -->
<dubbo:registry id="dubbogo" address="zookeeper://127.0.0.1:2181" />
<!-- Use dubbo protocol to expose services on port 20880 -->
<dubbo:protocol id="dubbo" name="dubbo" host="127.0.0.1" port="20010" />
<!-- Declare the service interface that needs to be exposed -->
<dubbo:service id="aaa" registry="dubbogo" timeout="3000" interface="org.apache.dubbo.UserProvider" ref="demoService"/>
<dubbo:service id="bbb" registry="dubbogo" timeout="3000" interface="org.apache.dubbo.UserProvider" ref="otherService" version="2.0"/>
<dubbo:service id="ccc" registry="dubbogo" timeout="3000" interface="org.apache.dubbo.UserProvider" ref="otherService" group="as" version="2.0"/>
<bean id="demoService" class="org.apache.dubbo.UserProviderImpl" />
<bean id="otherService" class="org.apache.dubbo.UserProviderAnotherImpl"/>
</beans>
Start the Provider class, and you can see the following log output, which means that the Dubbo Server has started successfully
[DUBBO] Dubbo Bootstrap is ready., dubbo version: 2.7.7, current host: 127.0.0.1
[DUBBO] Dubbo Bootstrap has started., dubbo version: 2.7.7, current host: 127.0.0.1
Go-Client start
For the Dubbo service that has been started, if you need to develop its corresponding Go-client, you need to perform the following steps:
- Write the POJO class User adapted to Java
import(
hessian "github.com/apache/dubbo-go-hessian2"
)
// The field needs to correspond to the Java side, with the first letter capitalized
type User struct {
ID string
name string
Age int32
Time time. Time
}
func (u *User) JavaClassName() string {
return "org.apache.dubbo.User" // needs to correspond to the User class name on the Java side
}
func init(){
hessian.RegisterPOJO(&pkg.User{}) // register POJO
}
Write a client stub class that is consistent with the Java side, and its interface method needs to correspond to the Java side
It is stipulated that the first parameter must be context.Context, and the last return value must be error
import(
"dubbo.apache.org/dubbo-go/v3/config"
)
var (
userProvider = &pkg. UserProvider{}
)
// UserProvider client stub class
type UserProvider struct {
// The dubbo label is used to adapt the uppercase method name of the go side client -> the lowercase method name of the java side, only the dubbo protocol client needs to use it
GetUser func(ctx context.Context, req int32) (*User, error) `dubbo:"getUser"`
}
func init(){
// Register the client stub class to the framework, and instantiate the client interface pointer userProvider
config. SetConsumerService(userProvider)
}
- Write configuration file: dubbogo.yml
dubbo:
registries:
demoZK: # Define the registration center ID
protocol: zookeeper
timeout: 3s
address: 127.0.0.1:2181
consumer:
references:
UserProvider: # stub class name
protocol: dubbo # dubbo protocol, default hessian2 serialization method
interface: org.apache.dubbo.UserProvider # The interface needs to correspond to the Java side
logger:
zap-config:
level: info # log level
Or use Triple + Hessian2 to serialize the request Server. If this example communicates with Java Server, Triple cannot be used.
dubbo:
registries:
demoZK:
protocol: zookeeper
timeout: 3s
address: 127.0.0.1:2181
consumer:
references:
UserProvider:
protocol: tri # triple protocol
serialization: hessian2 # serialization method hessian2, triple protocol defaults to pb serialization, if not configured, an error will be reported
interface: org.apache.dubbo.UserProvider
logger:
zap-config:
level: info
- Write the main.go file and initiate the call
func main(){
config. Load()
var i int32 = 1
user, err := userProvider. GetUser2(context. TODO(), i)
if err != nil {
panic(err)
}
logger.Infof("response result: %v", user)
}
- You can view the log of the successful call, which meets expectations
- go-client
response result: User{ID:1, Name:userCode get, Age:48, Time:2021-10-21 20:25:26.009 +0800 CST}
2.2 Java-Client -> Go-Server
Go-Server start
- Define the configuration file
dubbo:
registries:
demoZK:
protocol: zookeeper
address: 127.0.0.1:2181
protocols:
dubbo:
name: dubbo
port: 20000
provider:
services:
UserProvider:
interface: org.apache.dubbo.UserProvider
logger:
zap-config:
level: info
- Introduce transport structure, define service and method name mapping
type UserProvider struct {
}
func (u *UserProvider) GetUser(ctx context.Context, req int32) (*User, error) {
var err error
logger.Infof("req:%#v", req)
user := &User{}
user.ID = strconv.Itoa(int(req))
return user, err
}
// MethodMapper defines method name mapping, from Go method name to Java lowercase method name, only dubbo protocol service interface needs to use
func (s *UserProvider) MethodMapper() map[string]string {
return map[string]string{
"GetUser": "getUser",
}
}
func init(){
config.SetProviderService(&pkg.UserProvider{})
}
- Start the service
// export DUBBO_GO_CONFIG_PATH=dubbogo.yml
func main() {
if err := config.Load(); err != nil {
panic(err)
}
select {}
}
Java-Client start
Java client Spring configuration
resources/META-INF.spring/dubbo.consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- Consumer application name, used to calculate dependencies, not a matching condition, do not be the same as the provider --> <dubbo:application name="user-info-client" /> <!-- which local registry to connect to --> <dubbo:registry id="dubbogo" address="zookeeper://127.0.0.1:2181" /> <!-- dubbo.registry.address from dubbo.properties --> <!-- dubbo:registry address="${dubbo.registry.address}" / --> <!-- Use dubbo protocol to expose services on port 20880 --> <dubbo:protocol id="dubbo" name="dubbo" /> <!-- Declare the service interface that needs to be used --> <dubbo:reference registry="dubbogo" check="false" id="userProvider" protocol="dubbo" interface="org.apache.dubbo.UserProvider"> <!--<dubbo:parameter key="heartbeat" value="10000"/ --> </dubbo:reference> <dubbo:reference registry="dubbogo" check="false" id="userProvider1" protocol="dubbo" version="2.0" interface="org.apache.dubbo.UserProvider"> </dubbo:reference> <dubbo:reference registry="dubbogo" check="false" id="userProvider2" protocol="dubbo" version="2.0" group="as" interface="org.apache.dubbo.UserProvider"> </dubbo:reference> </beans>
Initiate the call
public class Consumer {
// Define a private variable (Required in Spring)
private static UserProvider userProvider;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo.consumer.xml"});
userProvider = (UserProvider)context. getBean("userProvider");
testGetUser();
}
private static void testGetUser() throws Exception {
User user = userProvider. getUser(1);
System.out.println(user.getId());
}
}