Generic Service Invocation
Traditionally, Dubbo services require a shared API jar containing interface definitions. However, generic invocation allows a consumer to call a remote method without having the interface class in its classpath. This is useful for gateway services or dynamic scripting scenarios where the caller only needs to know the interface name and method signature.
Provider Side
The provider exposes a standard service implementation:
@DubboService(protocol = "dubbo")
public class GreetingServiceImpl implements IGreetingService {
@Override
public String sayHello() {
return "Hello world!!!";
}
}
The service registers metadata in the registry (e.g., Nacos) indicating the interface and available methods:
side=provider
methods=sayHello
interface=com.example.dubbo.api.IGreetingService
generic=false
application=dubbo-provider-demo
Consumer Side
The consumer uses GenericService instead of a specific interface. Setting generic = true enables this mode.
@RestController
public class GatewayController {
@DubboReference(
interfaceName = "com.example.dubbo.api.IGreetingService",
generic = true,
check = false
)
private GenericService remoteService;
@GetMapping("/invoke")
public String invokeService(){
// $invoke(methodName, parameterTypes, args)
Object result = remoteService.$invoke("sayHello", new String[]{}, new Object[]{});
return result.toString();
}
}
Accessing the endpoint returns the expected result:
Hello world!!!
Service Degradation (Mock)
Dubbo supports service degradation via the mock parameter. When a call fails or times out, the framework can execute a local mock implemantation instead of throwing an exception. This ensures high availability.
@RestController
public class MessageController {
@DubboReference(
registry = {"shanghai","hunan"},
cluster = "failfast",
mock = "com.example.dubbo.consumer.LocalMockService",
timeout = 500,
check = false
)
private IMessageService messageService;
@GetMapping("/msg")
public String getMessage() {
return messageService.sayHello("hello");
}
}
In this setup, if the remote call fails, Dubbo will instantiate LocalMockService to handle the request.
Host Address Binding
When a Dubbo provider starts, it must determine which IP address to bind to and which IP to register with the registry. The logic is primarily located within the ServiceConfig export process.
The flow triggers at: DubboBootstrap.start() -> exportServices() -> ServiceConfig.doExportUrlsFor1Protocol().
String host = resolveHostAddress(protocolConfig, registryURLs, map);
Integer port = resolvePort(protocolConfig, name, map);
Resolving the Host Logic
The method resolveHostAddress follows a specific priority order to determine the IP:
private String resolveHostAddress(ProtocolConfig protocolConfig,
List<URL> registryURLs,
Map<String, String> map) {
boolean useAnyHost = false;
// 1. Check System Property for explicit bind IP
String bindHost = getSystemProperty(DUBBO_IP_TO_BIND);
if (bindHost != null && isInvalidLocalHost(bindHost)) {
throw new IllegalArgumentException("Invalid bind ip specified: " + bindHost);
}
// 2. Check Protocol/Provider configuration
if (StringUtils.isEmpty(bindHost)) {
bindHost = protocolConfig.getHost();
if (provider != null && StringUtils.isEmpty(bindHost)) {
bindHost = provider.getHost();
}
// 3. Try InetAddress.getLocalHost()
if (isInvalidLocalHost(bindHost)) {
useAnyHost = true;
try {
bindHost = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
// handle exception
}
}
// 4. Connect to Registry to detect local IP
if (isInvalidLocalHost(bindHost)) {
for (URL registryURL : registryURLs) {
if ("multicast".equalsIgnoreCase(registryURL.getParameter("registry"))) continue;
try (Socket socket = new Socket()) {
SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
socket.connect(addr, 1000);
bindHost = socket.getLocalAddress().getHostAddress();
break;
} catch (Exception e) {
// fallback logic
}
}
}
// 5. Iterate local network interfaces
if (isInvalidLocalHost(bindHost)) {
bindHost = getValidLocalAddress();
}
}
map.put(BIND_IP_KEY, bindHost);
// Determine Registry IP (can be different from Bind IP)
String registryHost = getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(registryHost)) {
registryHost = bindHost; // Default to bind host
}
map.put(ANYHOST_KEY, String.valueOf(useAnyHost));
return registryHost;
}
The resolution strategy is:
- Check environment variable
DUBBO_IP_TO_BIND. - Read
dubbo.protocols.dubbo.hostconfiguration. - Use
InetAddress.getLocalHost().getHostAddress(). - Open a Socket connection to the Registry to detect the outgoing IP.
- Iterate through local network interfaces (NICs) to find a valid addresss.
If you need the registry to see a different IP than the bind IP, set -DUBBO_IP_TO_REGISTRY=your_public_ip.
Configuration Priority
All configuration eventually merges into the URL object that Dubbo uses for the invocation.
dubbo://192.168.1.100:20880/com.example.dubbo.api.IMyService?application=provider-app&cluster=failover&...
In a non-clustered (or standard) scenario, the priority rules are:
- Method Level: Configuration defined on a specific method (e.g.,
@Method) overrides all. - Interface Level: Configuration defined on the reference/service annotation.
- Global Level: Configuration defined in properties or JVM arguments.
If the configuration level is the same, the Consumer's settings take precedence over the Provider's settings.