BusinessWorks目前以二方包的形式提供给已有业务系统用来满足平台化建设,适应业务快速开发需要。在这边ATA中,我们会介绍我们代码的组织结构和基本实现, 然后会针对交易领域简化开发一个订单下单的例子,来阐述如何进行业务的模块化开发,实现业务隔离。
1. 代码结构
businessworks-platform 是业务基础集成的平台实现
- platform-common 提供平台的一些底层基础类库实现,包括对guice的扩展实现
- platform-core 提供平台的一些核心功能实现,比如配置,jndi注册等。
- platform-integration 提供apache camel的集成功能
- platform-runtime 平台的运行期容器,负责启动平台容器,对容器内被管理对象进行生命周期管理
- platform-web 提供平台的元数据已经运行监控的web功能。
businessworks-trade 是交易业务领域的demo实现
- trade-domain 提供交易对象的demo实现,显现订单对象。
- trade-toc 对订单提供超时功能扩展
- trade-ump 对订单提供优惠功能扩展
businessworks-extension是业务方的扩展实现
- trade-lifeservice-rest 提供生活服务业务的rest扩展
- trade-lifeservice-toc 提供生活服务业务的超时实现
- trade-lifeservice-ump 提供生活服务业务的优惠实现
目前businessworks-trade和businessworks-extension是业务开发的一个demo实现,用来阐述如果利用businessworks进行业务的扩展隔离开发。
2. 核心功能实现介绍
2.1 IAdaptable 接口扩展实现
这三个接口: IAdapabable, IAdapatableFactory, IAdaptableManager 《从Eclipse平台看交易平台化》设计到的接口扩展实现。
2.2 Guice Module的定制实现
在Guice module基础上增加了模块的元数据信息,以及模块组装的一些实现。 比如ModuleBuiler,提供了模块组织在一起构建的builder类。
package com.taobao.businessworks.common.inject;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.util.List;
public class ModulesBuilder implements Iterable<Module> {
private final List<Module> modules = Lists.newArrayList();
public ModulesBuilder add(Module... modules) {
for (Module module : modules) {
add(module);
}
return this;
}
public ModulesBuilder add(Module module) {
modules.add(module);
if (module instanceof SpawnModules) {
Iterable<? extends Module> spawned = ((SpawnModules) module).spawnModules();
for (Module spawn : spawned) {
add(spawn);
}
}
return this;
}
@Override
public Iterator<Module> iterator() {
return modules.iterator();
}
public Injector createInjector() {
Modules.processModules(modules);
Injector injector = Guice.createInjector(modules);
Injectors.cleanCaches(injector);
// in ES, we always create all instances as if they are eager singletons
// this allows for considerable memory savings (no need to store construction info) as well as cycles
((InjectorImpl) injector).readOnlyAllSingletons();
return injector;
}
public Injector createChildInjector(Injector injector) {
Modules.processModules(modules);
Injector childInjector = injector.createChildInjector(modules);
Injectors.cleanCaches(childInjector);
// in ES, we always create all instances as if they are eager singletons
// this allows for considerable memory savings (no need to store construction info) as well as cycles
((InjectorImpl) childInjector).readOnlyAllSingletons();
return childInjector;
}
}
2.3 Camel的guice集成
通过guice集成camel提供的流程编排功能,并且可以以guice模块的形式注册新的流程实现。
2.4 平台的启动
Bootstrap会启动一个Node作为运行时容器, 在这个node容器内,所有的模块会被组装运行起来,包括通过插件方式提供的模块实现。
ModulesBuilder modules = Platform.getSystemModulesBuilder();
modules.add(new Version.Module(version));
modules.add(new PageCacheRecyclerModule(settings));
modules.add(new PluginsModule(settings, pluginsService)); //插件管理模块
modules.add(new SettingsModule(settings)); //配置模块
modules.add(new NodeModule(this));//运行node模块
modules.add(new NetworkModule()); //网络模块
modules.add(new MonitorModule(settings));//监控模块
modules.add(new JolokiaServerModule(settings));//Jolokia JMX模块
modules.add(new EnvironmentModule(environment));//环境模块
modules.add(new NodeEnvironmentModule(nodeEnvironment));//node环境模块
modules.add(new ThreadPoolModule(settings));//线程池模块
modules.add(new RestModule(settings)); //REST模块
modules.add(new TransportModule(settings));//传输层实现模块
if (settings.getAsBoolean(HTTP_ENABLED, true)) {
modules.add(new HttpServerModule(settings)); //http服务模块
}
modules.add(new RegistryModule(settings)); //注册模块
modules.add(new CamelRoutesModule()); //camel route管理模块
modules.add(new RoutesModule());
modules.add(new ActionModule(false));
modules.add(new NodeClientModule());
modules.add(new ExtensionsModule(settings));
injector = modules.createInjector();
在 modules.add(new PluginsModule(settings, pluginsService)); //插件管理模块中, 这个模块会按照一定的规范把所有的第三方插件提供的模块加入到这个模块组里。从而达到系统功能,业务功能扩展的目的。 具体如何实现可以在后续实例。
3. Businessworks接入已有系统
对于目前存在的业务系统,如果想使用Businessworks平台提供的平台化功能,需要做一下步骤:
引入二方包依赖
<dependency> <groupId>com.taobao.businessworks</groupId> <artifactId>platform-runtime</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency>
如果是个web系统,可以增加一个ServletContextListener,用来在web启动时,启动Businessworks容器。
public class BusinessworksListener implements ServletContextListener{/**
- 配置Businessworks
*/
@Override
public void contextInitialized(ServletContextEvent sce) {String home = sce.getServletContext().getRealPath("/"); System.setProperty("user.dir", home); System.setProperty("bw.foreground", "yes"); Bootstrap.main(null);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {Bootstrap.close(null);
}
}- 配置Businessworks
Businessworks对guava,log4j有版本要求,需要注意升级到对应版本。
这样,你的业务系统就具有平台化的功能了。
4. 简单交易下单业务开发示例
首先描述下我们要开发的业务场景, 我们首先会在Businessworks的平台上开发一个简易下单功能。 然后我们会逐步扩展这个下单功能,增加订单超时,订单优惠功能,最后我们会以生活服务lifeservice业务方为例,提供lifeservice的特定实现。
4.1 交易领域开发
trade-domain 插件:
首先开发业务功能,我们需要定义业务领域对象, 这里我们会用IOrder接口来实现。
package com.taobao.businessworks.domain.order;
import java.util.List;
import com.taobao.businessworks.common.adaptable.IAdaptable;
public interface IOrder extends IAdaptable {
public void addItem(DrinkType drinkType, int shots, boolean iced);
public List<OrderItem> getItems();
public void putAttribute(String key, Object value);
public String getType();
public Object getAttribute(String key);
}
可以看到这个IOrder接口扩展了一个IAdaptable接口,后续我们可以看到这个接口的功能作用。 在看IOrder的具体实现类,我们可以发现他集成了PlatformObject这个抽象类,在这个类中有IAdaptable接口的实现。 这样Order对象就自动实现了IAdaptable。
package com.taobao.businessworks.domain.order;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Maps;
import com.taobao.businessworks.common.adaptable.PlatformObject;
public class Order extends PlatformObject implements IOrder{
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
private int number;
private String type;
private float price;
private final Map<String, Object> attributes = Maps.newHashMap();
public Order(String type, int number) {
this.type= type;
this.number = number;
}
public String getType() {
return type;
}
public void putAttribute(String key, Object value) {
this.attributes.put(key, value);
}
public Object getAttribute(String key) {
return this.attributes.get(key);
}
public void addItem(DrinkType drinkType, int shots, boolean iced) {
this.orderItems.add(new OrderItem(this, drinkType, shots, iced));
}
public int getNumber() {
return number;
}
public List<OrderItem> getItems() {
return this.orderItems;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
PlatformObject:
public abstract class PlatformObject implements IAdaptable {
/**
* Constructs a new platform object.
*/
public PlatformObject() {
super();
}
/**
* Returns an object which is an instance of the given class
* associated with this object. Returns <code>null</code> if
* no such object can be found.
* <p>
* This implementation of the method declared by <code>IAdaptable</code>
* passes the request along to the platform's adapter manager; roughly
* <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>.
* Subclasses may override this method (however, if they do so, they
* should invoke the method on their superclass to ensure that the
* Platform's adapter manager is consulted).
* </p>
*
* @param adapter the class to adapt to
* @return the adapted object or <code>null</code>
* @see IAdaptable#getAdapter(Class)
*/
public Object getAdapter(Class adapter) {
return AdapterManager.getDefault().getAdapter(this, adapter);
}
}
将这个简单的订单模型加入到平台很简单,在实现一个插件类:
package com.taobao.businessworks.domain;
import java.util.ArrayList;
import java.util.Collection;
import com.taobao.businessworks.common.inject.Module;
import com.taobao.businessworks.plugins.AbstractPlugin;
public class DomainPlugin extends AbstractPlugin {
public DomainPlugin() {
}
@Override
public String name() {
return "trade-domain";
}
@Override
public String description() {
return "trade domain Plugin";
}
@Override
public Collection<Class<? extends Module>> modules() {
Collection<Class<? extends Module>> modules = new ArrayList<Class<? extends Module>>();
modules.add(DomainModule.class);
return modules;
}
}
这个plugin会被平台调用,通过modules()方法,将整个jar和包括的模块引入businessworks平台。
这样平台就具有了IOrder等模型的实现。
4.2 如何扩展开发领域功能
建设我们的简易下单系统需要增加超时功能和优惠功能。 按照我们以前的做法,我们会去IOrder接口里增加和超时或者 优惠相关的接口方法。这样带来的问题是我们的IOrder接口会随着业务功能的增加而膨胀,而且接口作为协议,已经对外暴露了,对基础接口的修改会导致依赖这个接口的上层代码的修改。 我们都知道作为核心类,应该尽可能保持它的稳定,这对业务系统的稳定和健壮也有很大的好处。
IAdaptable提供了一个优雅的实现让我们可以不改变IOrder接口,却可以给IOrder接口增加扩展的功能。
我们会以trade-toc这个项目为示例实现,来演示如果给交易这个领域增加超时功能。
首先我们会定义一个Toc接口,定义超时相关的功能:
public interface Toc extends TocComponent {
void setOrderConsignmentTimeout(IOrder order);
void setOrderConfirmTimeout(IOrder order);
int getOrderConsignmentTimeout(IOrder order);
int getOrderConfirmTimeout(IOrder order);
}
很简单,就是定义对订单设置确认收货超时和发货超时。
然后我们实现一个TocAdapterFactory工厂类,用来向平台注册IOrder的TOC扩展
package com.taobao.businessworks.toc;
import com.taobao.businessworks.common.adaptable.IAdapterFactory;
import com.taobao.businessworks.common.adaptable.IAdapterManager;
import com.taobao.businessworks.common.inject.Inject;
import com.taobao.businessworks.core.config.Platform;
import com.taobao.businessworks.domain.order.IOrder;
public class TocAdapterFactory implements IAdapterFactory {
private final TocsService tocsService;
@Inject
public TocAdapterFactory(TocsService tocsService) {
this.tocsService = tocsService;
IAdapterManager manager = Platform.getAdapterManager();
manager.registerAdapters(this, IOrder.class);
}
@Override
public Object getAdapter(Object adaptableObject, Class adapterType) {
if (adapterType == Toc.class) {
IOrder order = (IOrder) adaptableObject;
Toc toc = tocsService.toc(order.getType());
if (toc== null) {
toc = tocsService.toc("general");
}
return toc;
}
return null;
}
@Override
public Class[] getAdapterList() {
return new Class[] { Toc.class };
}
}
这个类会将Toc接口注册到AdapterManger里,这样IOrder就可以通过getAdapter方法得到toc相关的功能。
接着我们将继续把toc相关的功能都在TOC模块中封装实现:
TocsModule:
public class TocsModule extends AbstractModule {
private final Settings settings;
private Map<String, Class<? extends Module>> tocTypes = Maps.newHashMap();
@Inject
public TocsModule(Settings settings) {
this.settings = settings;
registerToc("general", GeneralTocModule.class);
registerToc("virtual", VirtualTocModule.class);
}
/**
* Registers a custom river type name against a module.
*
* @param type The type
* @param module The module
*/
public void registerToc(String type, Class<? extends Module> module) {
tocTypes.put(type, module);
}
@Override
protected void configure() {
bind(TocsService.class).asEagerSingleton();
bind(TocAdapterFactory.class).asEagerSingleton();
bind(TocsTypesRegistry.class).toInstance(new TocsTypesRegistry(ImmutableMap.copyOf(tocTypes)));
}
}
这个类有如下功能:
- 绑定了TocAdapterFactory实现,注册toc对IOrder的扩展。
- 提供了一个toc超时业务子模块的注册机制。 从代码可以看到,我们默认提供了GeneralTocModule, VirtualTocModule两个缺省实现。
- 提供了一个TocsService辅助服务类,用来将toc模块的功能以service方式提供。
这种可扩展的模块机制,是我们业务扩展和隔离的基础,新的业务对应的接口实现,可以通过提供业务超时子模块的方式注册到超时富模块。
我们可以看一个GeneralTocModule的实现:
public class GeneralTocModule extends AbstractModule {
public GeneralTocModule() {
super();
}
@Override
protected void configure() {
bind(Toc.class).to(GeneralToc.class).asEagerSingleton();
}
}
在这里, 平台提供了超时的一个缺省实现GernalToc,
public class GeneralToc extends AbstractTocComponent implements Toc{
final TocSettings settings;
@Inject
public GeneralToc(TocName tocName, TocSettings settings) {
super(tocName, settings);
this.settings = settings;
}
@Override
public void setOrderConsignmentTimeout(IOrder order) {
order.putAttribute("consignment_timeout", 100);
}
@Override
public int getOrderConsignmentTimeout(IOrder order) {
return 100;
}
@Override
public void setOrderConfirmTimeout(IOrder order) {
order.putAttribute("confirm_timeout", 200);
}
@Override
public int getOrderConfirmTimeout(IOrder order) {
return 200;
}
最后通过TocPlugin这个类,将TocsModule注册到平台内,从而让订单具有了超时功能扩展。
public class TocPlugin extends AbstractPlugin {
public TocPlugin() {
}
@Override
public String name() {
return "trade-toc";
}
@Override
public String description() {
return "trade toc Plugin";
}
@Override
public Collection<Class<? extends Module>> modules() {
Collection<Class<? extends Module>> modules = new ArrayList<Class<? extends Module>>();
modules.add(TocsModule.class);
return modules;
}
}
在extension-route这个插件里,我们注册扩展了两个restful api用来模拟下单的入口和实现:
http://localhost:9200/buy_general 代表生成一个general的订单,
http://localhost:9200/buy_virtual 代表生成一个virtual的订单,
public class RestBuyNowAction extends BaseRestHandler {
private final Version version;
private final CamelContext camelContext;
@Inject
public RestBuyNowAction(Settings settings, Version version, RestController controller, Client client, CamelContext camelContext) {
super(settings, controller, client);
this.version = version;
this.camelContext = camelContext;
controller.registerHandler(GET, "/buy_general", this);
controller.registerHandler(HEAD, "/buy_general", this);
}
@Override
public void handleRequest(final RestRequest request, RestChannel channel, final Client client) throws Exception {
RestStatus status = RestStatus.OK;
if (request.method() == RestRequest.Method.HEAD) {
channel.sendResponse(new BytesRestResponse(status));
return;
}
XContentBuilder builder = channel.newBuilder();
// Default to pretty printing, but allow ?pretty=false to disable
if (!request.hasParam("pretty")) {
builder.prettyPrint().lfAtEnd();
}
Order order = new Order("general",1);
Toc toc = (Toc)order.getAdapter(Toc.class);
if (toc!=null) {
toc.setOrderConfirmTimeout(order);
toc.setOrderConsignmentTimeout(order);
}
Ump ump = (Ump)order.getAdapter(Ump.class);
if (ump!=null) {
ump.setOrderPromation(order);
}
int consignment_timeout = toc.getOrderConsignmentTimeout(order);
int confirm_timeout = toc.getOrderConfirmTimeout(order);
float promotion = ump.getOrderPromation(order);
builder.startObject();
if (settings.get("name") != null) {
builder.field("name", settings.get("name"));
}
builder.startObject("version")
.field("confignment_timeout", consignment_timeout)
.field("confirm_timeout",confirm_timeout)
.field("promotion",promotion)
.endObject();
builder.endObject();
channel.sendResponse(new BytesRestResponse(status, builder));
}
}
这个代码里:
Toc toc = (Toc)order.getAdapter(Toc.class);
toc接口功过order的getAdapter的方式返回,在上面toc模快里TocAdapterFactory里, 解释了会根据order的type类型,返回对应的实现,这里会返回GenrealToc的实现。
我们可以通过在浏览器里输入url得到对应业务的toc和ump实现。
另外一乐buy_virtual的实现在类RestBuyVirtualAction中, 通过这两个实现对比,我们可以根据业务类型驱动平台调用对应的业务的超时和ump实现。
4.3 业务开发
下面我们再假设有个生活服务业务,需要对下单进行定制扩展, 生活服务订单会有自己特定的超时和ump优惠实现。 我们如何支持这个行业务呢:
第一步,实现一个extension-lifeservice-toc插件,里面会提供一个新的超时模块,注册对应的业务类型是“lifeservice”。
生活服务业务方会实现一个自己的Toc超时接口实现, 然后通过模块的形式封装,最后提供一个plugin实现类,将这个模块注册成为TocsModule的业务子模块:
public class LifeserviceTocPlugin extends AbstractPlugin {
public LifeserviceTocPlugin() {
}
@Override
public String name() {
return "lifeservice toc";
}
@Override
public String description() {
return "lifeservice toc extension Plugin";
}
public void onModule(TocsModule module) {
module.registerToc("lifeservice", LifeserviceTocModule.class);
}
}
通过这个方式,生活服务对Toc超时的实现就已经注册到平台里,可以被使用了。
类似的方式我们可以开发生活服务的Ump模块。
最后我们开发一个restful API用来测试生活服务下单
http://localhost:9200/buy_lifeservice
public class RestLifeserviceBuyNowAction extends BaseRestHandler {
private final Version version;
private final CamelContext camelContext;
@Inject
public RestLifeserviceBuyNowAction(Settings settings, Version version, RestController controller, Client client, CamelContext camelContext) {
super(settings, controller, client);
this.version = version;
this.camelContext = camelContext;
controller.registerHandler(GET, "/buy_lifeservice", this);
controller.registerHandler(HEAD, "/buy_lifeservice", this);
}
@Override
public void handleRequest(final RestRequest request, RestChannel channel, final Client client) throws Exception {
RestStatus status = RestStatus.OK;
if (request.method() == RestRequest.Method.HEAD) {
channel.sendResponse(new BytesRestResponse(status));
return;
}
XContentBuilder builder = channel.newBuilder();
// Default to pretty printing, but allow ?pretty=false to disable
if (!request.hasParam("pretty")) {
builder.prettyPrint().lfAtEnd();
}
Order order = new Order("lifeservice",1);
Toc toc = (Toc)order.getAdapter(Toc.class);
toc.setOrderConfirmTimeout(order);
toc.setOrderConsignmentTimeout(order);
Ump ump = (Ump)order.getAdapter(Ump.class);
ump.setOrderPromation(order);
int consignment_timeout = toc.getOrderConsignmentTimeout(order);
int confirm_timeout = toc.getOrderConfirmTimeout(order);
float promotion = ump.getOrderPromation(order);
builder.startObject();
if (settings.get("name") != null) {
builder.field("name", settings.get("name"));
}
builder.startObject("version")
.field("confignment_timeout", consignment_timeout)
.field("confirm_timeout",confirm_timeout)
.field("promotion",promotion)
.endObject();
builder.endObject();
channel.sendResponse(new BytesRestResponse(status, builder));
}
}
5. 总结
这边主要是针对业务开发和接入的实例性文档,在下一篇,会继续介绍业务流程的注册开发。同时也会对平台的一些基础功能模块做进一步介绍。