background
I believe the source of the students read Dubbo in Dubbo should see many of the interface has a @SPI
comment, the author is no exception, but did not know specifically what this annotation, in order to solve a problem, how to use? A simple search online, the Chinese name: service delivery interfaces, see the following figure (from Baidu Encyclopedia).
Perhaps because dubbo
the function itself is powerful, so I only know that can dubbo
be customized to achieve certain policies, such as load balancing, serialization, thread pool type, etc., but has not yet officially on-line environments. Take time to study at taking advantage of the holidays, the next record, want to be useful.
Sample Code
The following codes are validated through local, purely hand to knock, the specific implementation see Glory's Github
Note: The test project to build spring-boot
+ the Spring-the Boot-Dubbo
Verify ideas
As the figure belongs, said that @SPI
to achieve a particular service, it would be simple to achieve, the most familiar than load balancing ( LoadBalance
) strategy, the local start two provider
different ports, through consumer
to determine access parameters specified in the a provider
.
Start provider
Extremely simple codes, the code follows the frame
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 指定版本和分组
@Service(version = "1.0.0",group = "glory")
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(Integer port) {
logger.info("Hello " + port + " request from consumer: " + RpcContext.getContext().getRemoteAddress());
return "Hello ,"+port+" response from provider: " + RpcContext.getContext().getLocalAddress();
}
}
The following is the application.yml
configuration file
server:
port: 8083
dubbo:
application:
name: dubbo-common-provider
scan:
base-packages: com.redsun.rpc.dubbo
protocol:
name: dubbo
port: 12345
registry:
address: zookeeper://127.0.0.1:2181
Also note that you need to specify a different port at startup, whether in not start.
At this point you can start the normal two local application, start effect is as follows:
Start consumer
consumer
The code is also very simple framework
surely we can guess guessed, GloryLoadBalance
is a kind of load balancing strategy to achieve their own, to choose the parameters passed through the frontinvoker
public class GloryLoadBalance implements LoadBalance {
private static final Logger logger = LoggerFactory.getLogger(GloryLoadBalance.class);
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
Integer port = (Integer) invocation.getArguments()[0];// 前端传入的参数
logger.info("前端传入端口为:" + port);
// java8的流式编程,有兴趣的同学可以研究下,后续会再专门写一篇
Invoker<T> invokerRet = invokers.stream().filter(invoker -> invoker.getUrl().getPort() == port).findFirst().get();
return invokerRet == null ? invokers.get(0) : invokerRet;
}
}
Here also wrote a simple controller
, easy to change the dynamic parameters
@RestController
@RequestMapping("/admin")
public class AdminController {
private static final Logger logger = LoggerFactory.getLogger(AdminController.class);
@Reference(version = "1.0.0",group = "glory", loadbalance = "glory")
private DemoService demoService;
@RequestMapping("/invoke")
public String invoke(@RequestParam(name = "port") Integer port) {
logger.info("invoke method be invoked!port = " + port);
return demoService.sayHello(port);
}
}
Of course, the final step will be, and most importantly, the META-INF.dubbo.internal
added profile directory, following the equal sign in front of the glory
fact that you have configured the loadbalance
key, and if the path is wrong or not configured, will still get to the default implementationrandom
glory=com.redsun.rpc.dubbo.loadbalance.GloryLoadBalance
dubbo
Will load META-INF.dubbo.internal
all the configuration information in the directory, the dubbo
directory will be a lot of default implementation
postman calls
The following two graphs tested to meet expectations.
Source
Here it's time to turn the source code, and I am here to talk about simple look at the source code of that experience.
- Do not tangle in the implementation of each method, usually debug with, it is easy with the halo, and finally lost interest
- Learn to read notes, in particular, method names, class names, variable names, source code is different from the usual I have written, since open source, that is accessible for all, it notes, named often written in very great detail specification, the English do not know ? Translation, more often met
- Remember when debug with several core classes, after reading the entire call chain, the structure of the code you have to have a rough sort of cognitive next (really do not want to see, Baidu also, I often do it, then and then look at myself, under certification)
load
Extension Loader # getExtensionClasses
// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
/**
* put clazz in extensionClasses
*/
private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) {
Class<?> c = extensionClasses.get(name);
if (c == null) {
// 将扩展类GloryLoadBalance存入map中,最终会将默认提供的几种都存入map中
extensionClasses.put(name, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName());
}
}
Load class will eventually achieve good ReferenceConfig
load balancing parameter is set toglory
run
AbstractClusterInvoker#invoke
In the consumer
launch invoke
will be to create an instance of selective according to config key time
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
List<Invoker<T>> invokers = list(invocation);
// 初始化加载负载均衡类
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
Extension Loader # getExtensionLoader
Each of the META-INF.dubbo.internal
files in the directory are ExtensionLoader
objects stored in a static class variable EXTENSION_LOADERS
in
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
// 此处判断是否以SPI注解修饰
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
Extension Loader # getExtension
If this time is empty, an example of the use of information by loading the above-Class newInstance, the code is relatively simple, interested can talk about.
to sum up
The above should be seen, if you want to expand the use of dubbo follow-up will be very simple, to increase an implementation class (implement the corresponding interface), and then in META-INF
adding a configuration file directory, key corresponds to the good can be done. Serialization can also be in accordance with their wishes.
When the code can be seen with some other extension classes are not used, dubbo are loaded into a Class object, it can be considered a little bit of small flaws it.