前言
学习Dubbo的过程中发现官网文章太过简单,而且没有提供完整的项目整合,导致入门门槛比较高,初学者不知从何下手。本文将在SpringBoot的基础上整合Dubbo,注册中心使用当下流行的Nacos,还将使用Dubbo-Admin来管理服务。本文未提到Dubbo的基础知识与基本概念,这方面内容可以直接查阅【Dubbo官网-总体架构】。
步骤
- SpringBoot多模块基础项目创建
- Dubbo服务提供方实现
- Dubbo服务消费方实现
- 自定义Filter拦截所有消费请求
- 自定义LoadBalance完成特殊负载均衡
- DubboAdmin的使用
SpringBoot多模块基础项目创建
项目结构
└─dubbo-test
│ pom.xml
├─dubbo-test-consumer(服务消费方)
│ │ pom.xml
│ └─src
├─dubbo-test-interface(消费方和提供方共享的接口定义)
│ │ pom.xml
│ └─src
└─dubbo-test-provider(服务提供方)
│ pom.xml
└─src
多模块的创建这里不再赘述,主要分为三个子模块。消费方和提供方共享接口定义模块,避免代码重复。服务提供方实现接口定义,消费方根据接口定义去请求提供方的具体实现,Dubbo完成了整个请求的过程。
Dubbo服务提供方实现
配置文件
dubbo:application:name: dubbo-test-providerprotocol:name: dubboport: -1registry:address: nacos://192.168.0.129:8848username: nacospassword: nacosconfig-center:address: nacos://192.168.0.129:8848username: nacospassword: nacosgroup: dubbometadata-report:address: nacos://192.168.0.129:8848username: nacospassword: nacosgroup: dubbo
关键代码
//用户服务
@DubboService
public class UserServiceImpl implements IUserService {private static final Random random = new Random();@Overridepublic User login(String username, String password) {if(!"123456".equals(password)){return null;}User user = new User();user.setId(random.nextInt(1000));user.setUsername(username + "_" + user.getId());user.setSex(user.getId()%2 == 0 ? "男" : "女");return user;}
}//群组服务
@DubboService
@Component
public class GroupServiceImpl implements IGroupService {@Value("${dubbo.application.name}")private String appName;@Overridepublic String join(Integer userId, String groupId) {return userId+"_"+groupId+"_"+appName;}
}
这里实现了两个服务,均是来自dubbo-test-interface中的接口定义。用户服务做了一个简单的随机用户信息的创建,群组服务返回了当前属于哪一台主机(为后文自定义负载均衡做准备)。
Dubbo服务消费方实现
配置文件
server:port: 8888dubbo:application:name: dubbo-test-consumerprotocol:name: dubboport: -1registry:address: nacos://192.168.0.129:8848username: nacospassword: nacosconfig-center:address: nacos://192.168.0.129:8848group: dubbousername: nacospassword: nacosmetadata-report:address: nacos://192.168.0.129:8848group: dubbousername: nacospassword: nacosconsumer:filter: logFilter
关键代码
@RestController
public class UserController {@DubboReferenceprivate IUserService userService;@DubboReference(loadbalance = "groupLoadBalance")private IGroupService groupService;@GetMapping("/login")public User login(String username, String password){return userService.login(username, password);}@GetMapping("/join")public String join(Integer userId, String groupId){return groupService.join(userId, groupId);}
}
代码引用了两个服务,其中IGroupService服务使用了自定义的负载均衡策略。
自定义Filter拦截所有消费请求
关键代码
@Slf4j
@Activate(group = CommonConstants.CONSUMER)
public class LogFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {log.info("开始调用,接口:{},参数:{}", invocation.getMethodName(), JSON.toJSONString(invocation.getArguments()));Result result = invoker.invoke(invocation);log.info("调用完成,结果:{}", JSON.toJSONString(result.getValue()));return result;}
}
配置
配置filter需要在yml增加【dubbo.consumer.filter: logFilter】,其中logFilter是自定义的过滤器名称,这个需要在resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter中配置,默认项目中是没有这个文件的,需要按文件夹路径逐一手动创建,具体参考【Dubbo官网-调用拦截扩展】。
自定义LoadBalance完成特殊负载均衡
在现实业务中默认负载均衡策略可能不能满足需求,我们可以自定义负载均衡策略。这里通过继承Dubbo默认的RandomLoadBalance来进行自定义拓展,实现了一个通过群组id获取指定服务器的策略,达到了同一个群的用户在同一台服务器的业务效果。当然这里只是一个功能演示,有许多未考虑的情况,具体需求还需根据场景使用其它手段实现。
关键代码
@Slf4j
public class GroupLoadBalance extends RandomLoadBalance {private static final Map<String, Integer> groupMapping = new HashMap<>();@Overridepublic <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {log.info("进入自定义负载均衡,{}", url.getAbsolutePath());//获取调用方法名称String methodName = invocation.getMethodName();if(!"join".equals(methodName) || invocation.getArguments() == null|| invocation.getArguments().length != 2){//直接采用默认策略log.info("不满足条件,直接采用默认策略");return super.select(invokers, url, invocation);}//获取群组参数String groupId = (String) invocation.getArguments()[1];Integer selected = groupMapping.get(groupId);if(selected != null){//查找已有的服务器for(Invoker<T> invoker : invokers){if(selected.equals(invoker.hashCode())){log.info("查找到群组对应的服务器,直接返回");return invoker;}}}//未找到已有的服务器则使用默认策略选择服务器log.info("当前群组没有对应服务器,使用默认策略选择服务器");Invoker<T> randomSelected = super.select(invokers, url, invocation);//保存群组对应的服务器groupMapping.put(groupId, randomSelected.hashCode());return randomSelected;}
}
配置
自定义负载均衡不用再yml中增加配置,只需要在引用时使用名称指定特定负载均衡策略即可,如上文提到的@DubboReference(loadbalance = "groupLoadBalance")。同filter一样,也需要在resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance中配置名称和对应的Class地址,具体参考【Dubbo官网-负载均衡扩展】。
DubboAdmin的使用
dubbo-admin是官方推出的一个后台管理软件,可以查看当前dubbo的运行情况并做动态配置调整。可以从官方仓库【apache/dubbo-admin】拉取代码部署,也可以直接使用官方准备好的docker运行,还可以下打好包的jar文件运行。这里使用官方Releases的0.5.0版本运行,下载地址【apache-dubbo-admin-0.5.0-bin-release.zip】。
下载后打开bin/config/application.properties文件,将原本的zookeeper配置注释,打开下面的nacos注释并修改链接信息。如下图所示:
然后使用 bin/startup.cmd脚本启动即可,停止则使用shutdown.cmd。
最后打开浏览器输入http://localhost:8080/即可访问,默认账号root,密码root。
完整代码
printlin/dubbo-test