Dubbo泛化
我们每次去发布一个服务,必然会先定义一个接口,并且把这个接口放在一个api的jar包中,给到服务调用方来使用。本质上,对于开发者来说仍然是面向接口编程,而且对于使用者来说,可以不需要关心甚至不需要知道这个接口到底是怎么触发调用的。简而言之,泛化调用,最最直接的表现就是服务消费者不需要有任何接口的实现,就能完成服务的调用。
代码实例:
- provider
在服务端定义了一个接口
@DubboService(protocol = {"dubbo"})
public class DemoService implements IDemoService{
@Override
public String getTxt() {
return "Hello world!!!";
}
}
注册到nacos的元数据
side=provider
methods=getTxt
release=2.7.7
deprecated=false
dubbo=2.0.2
pid=10884
interface=com.springboot.dubbo.provider.services.IDemoService
generic=false
path=com.springboot.dubbo.provider.services.IDemoService
protocol=dubbo
application=spring-boot-dubbo-sample-provider
dynamic=true
category=providers
anyhost=true
timestamp=1608312655153
- consumer
@RestController
public class DemoController {
// 设置generic=true代表开启泛化功能
@DubboReference(interfaceName = "com.springboot.dubbo.provider.services.IDemoService",generic = true,check = false)
GenericService genericService;
@GetMapping("/demo")
public String demo(){
//对象的传值
Map<String,Object> user=new HashMap<>();
user.put("",""); //key表达user对象中的属性,value表达属性的值
return genericService.$invoke("getTxt",new String[0],null).toString();
}
}
- 结果(调用http://localhost:port/demo)
Hello world!!!
服务降级
@RestController
public class SayController {
@DubboReference(registry = {"shanghai","hunan"},
protocol = "dubbo",
loadbalance = "consistenthash",
mock = "com.springboot.dubbo.consumer.SayHelloService",
timeout = 500,
cluster = "failfast",check = false,
methods = {
@Method(loadbalance = "",name ="sayHello" )
},
retries = 5)
ISayHelloService sayHelloService;
@GetMapping("/say")
public String say(){
return sayHelloService.sayHello("hello");
}
}
主机绑定
代码位置
DubboBootstrap.start() -> exportServices() -> ServiceConfigBase.export() -> ServiceConfig.doExport() -> doExportUrls() -> doExportUrlsFor1Protocol()
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
findConfigedHosts
private String findConfigedHosts(ProtocolConfig protocolConfig,
List<URL> registryURLs,
Map<String, String> map) {
boolean anyhost = false;
String hostToBind = getValueFromConfig(protocolConfig, DUBBO_IP_TO_BIND);
if (hostToBind != null && hostToBind.length() > 0 && isInvalidLocalHost(hostToBind)) {
throw new IllegalArgumentException("Specified invalid bind ip from property:" + DUBBO_IP_TO_BIND + ", value:" + hostToBind);
}
// if bind ip is not found in environment, keep looking up
if (StringUtils.isEmpty(hostToBind)) {
hostToBind = protocolConfig.getHost();
if (provider != null && StringUtils.isEmpty(hostToBind)) {
hostToBind = provider.getHost();
}
if (isInvalidLocalHost(hostToBind)) {
anyhost = true;
try {
logger.info("No valid ip found from environment, try to find valid host from DNS.");
hostToBind = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.warn(e.getMessage(), e);
}
if (isInvalidLocalHost(hostToBind)) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
if (MULTICAST.equalsIgnoreCase(registryURL.getParameter("registry"))) {
// skip multicast registry since we cannot connect to it via Socket
continue;
}
try (Socket socket = new Socket()) {
SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
socket.connect(addr, 1000);
hostToBind = socket.getLocalAddress().getHostAddress();
break;
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
}
if (isInvalidLocalHost(hostToBind)) {
hostToBind = getLocalHost();
}
}
}
}
map.put(BIND_IP_KEY, hostToBind);
// registry ip is not used for bind ip by default
String hostToRegistry = getValueFromConfig(protocolConfig, DUBBO_IP_TO_REGISTRY);
if (hostToRegistry != null && hostToRegistry.length() > 0 && isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
} else if (StringUtils.isEmpty(hostToRegistry)) {
// bind ip is used as registry ip by default
hostToRegistry = hostToBind;
}
map.put(ANYHOST_KEY, String.valueOf(anyhost));
return hostToRegistry;
}
-
查找环境变量中是否存在启动参数 [DUBBO_IP_TO_BIND] =服务注册的ip
-
读取配置文件, dubbo.protocols.dubbo.host= 服务注册的ip
-
InetAddress.getLocalHost().getHostAddress() 获得本机ip地址
-
通过Socket去连接注册中心,从而获取本机IP
-
会轮询本机的网卡,直到找到合适的IP
-
地址上面获取到的ip地址是bindip,如果需要作为服务注册中心的ip, DUBBO_IP_TO_REGISTRY -dDUBBO_IP_TO_REGISTRY=ip
配置的优先级问题
服务端配置的信息,都会装载到url上
dubbo%3A%2F%2F169.254.99.173%3A20880%2Fcom.springboot.dubbo.ISayHelloService%3Fanyhost%3Dtrue%26application%3Dspring-boot-dubbo-sample-provider%26cluster%3Dfailover%26deprecated%3D
false%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dcom.springboot.dubbo.ISayHelloService%26methods%3DsayHello%26pid%3D10884%26release%3D2.7.7%26side%3Dprovider
%26timestamp%3D1608312656576
非集群(cluster=failver)
-
客户端没有配置
- 以客户端优先
-
客户端有配置
- 方法层面的配置要优先于接口层面的配置, 接口层面的配置要优先于全局配置
- 如果级别一样,以客户端的配置优先,服务端次之