OSGi:进入微服务架构的门户

在构建可扩展,可靠的分布式系统的背景下,“模块化”和“微服务体系结构”这两个术语如今经常出现。 众所周知,Java平台本身在模块化方面很弱( Java 9将通过交付Jigsaw项目来解决这一问题),从而为OSGi和JBoss Modules等框架的出现提供了机会。

当我在2007年第一次听说OSGi时 ,我真的很高兴Java应用程序可以基于OSGi的所有优点而受益。 但是很快就产生了挫败感,而不是兴奋感:没有工具支持,兼容的库和框架非常有限,非常不稳定,很难对运行时进行故障排除。 显然,普通的Java开发人员还没有准备好使用它,因此,我不得不把它放在架子上。 多年来, OSGi已经成熟很多,并得到了广泛的社区支持。

好奇的读者可能会问:特别是使用模块和OSGi有什么好处? 仅举几个问题,它有助于解决:

  • 显式(和版本控制)依赖关系管理:模块声明所需的内容(以及版本范围)
  • 占用空间小:模块未包装所有依赖项
  • 易于发布:模块可以独立开发和发布
  • 热重新部署:可以重新部署各个模块,而不会影响其他模块

在今天的帖子中,我们将对使用OSGi构建模块化Java应用程序的最新技术水平进行10000英尺的考察。 撇开OSGi的好坏进行讨论,我们将构建一个包含以下模块的示例应用程序:

  • 数据访问模块
  • 商业服务模块
  • REST服务模块

用于数据访问的Apache OpenJPA 2.3.0 / JPA 2.0 (不幸的是,我们选择的OSGi实现尚未支持JPA 2.1 ),用于REST层的Apache CXF 3.0.1 / JAX-RS 2.0是应用程序的两个主要构建块。 我发现Christian Schneider的博客Liquid Liquidity是有关OSGi (以及许多其他主题)的宝贵信息来源。

在OSGi世界中,模块称为bundles 。 捆绑包显示了它们的依赖关系(导入包)和它们公开的包(导出包),因此其他捆绑包可以使用它们。 Apache Maven也支持此打包模型。 捆绑包由OSGi运行时或容器管理,在我们的情况下将是Apache Karaf 3.0.1 (实际上,这是我们需要下载 和解压缩的内容)。

让我停止说话,更好地显示一些代码。 我们将从顶部( REST )开始,一直到底部(数据访问),因为它更容易遵循。 我们的PeopleRestService是JAX-RS 2.0服务实现的典型示例:

package com.example.jaxrs;import java.util.Collection;import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;import com.example.data.model.Person;
import com.example.services.PeopleService;@Path( "/people" )
public class PeopleRestService {private PeopleService peopleService;@Produces( { MediaType.APPLICATION_JSON } )@GETpublic Collection< Person > getPeople( @QueryParam( "page") @DefaultValue( "1" ) final int page ) {return peopleService.getPeople( page, 5 );}@Produces( { MediaType.APPLICATION_JSON } )@Path( "/{email}" )@GETpublic Person getPerson( @PathParam( "email" ) final String email ) {return peopleService.getByEmail( email );}@Produces( { MediaType.APPLICATION_JSON  } )@POSTpublic Response addPerson( @Context final UriInfo uriInfo,@FormParam( "email" ) final String email, @FormParam( "firstName" ) final String firstName, @FormParam( "lastName" ) final String lastName ) {peopleService.addPerson( email, firstName, lastName );return Response.created( uriInfo.getRequestUriBuilder().path( email ).build() ).build();}@Produces( { MediaType.APPLICATION_JSON  } )@Path( "/{email}" )@PUTpublic Person updatePerson( @PathParam( "email" ) final String email, @FormParam( "firstName" ) final String firstName, @FormParam( "lastName" )  final String lastName ) {final Person person = peopleService.getByEmail( email );if( firstName != null ) {person.setFirstName( firstName );}if( lastName != null ) {person.setLastName( lastName );}return person;              }@Path( "/{email}" )@DELETEpublic Response deletePerson( @PathParam( "email" ) final String email ) {peopleService.removePerson( email );return Response.ok().build();}public void setPeopleService( final PeopleService peopleService ) {this.peopleService = peopleService;}
}

我们可以看到,这里没有任何关于OSGi的信息 。 唯一的依赖是在某种程度上应该被注入PeopleRestServicePeopleService。 怎么样? 通常, OSGi应用程序使用蓝图作为依赖项注入框架,这与旧伙伴基于XML的Spring配置非常相似。 它应该与应用程序一起打包在OSGI-INF / blueprint文件夹中。 这是基于Apache CXF 3.0.1构建的REST模块的蓝图示例:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"xmlns:cxf="http://cxf.apache.org/blueprint/core"xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsdhttp://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsdhttp://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsdhttp://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd"><cxf:bus id="bus"><cxf:features><cxf:logging/></cxf:features>       </cxf:bus><jaxrs:server address="/api" id="api"><jaxrs:serviceBeans><ref component-id="peopleRestService"/></jaxrs:serviceBeans><jaxrs:providers><bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" /></jaxrs:providers></jaxrs:server><!-- Implementation of the rest service --><bean id="peopleRestService" class="com.example.jaxrs.PeopleRestService"><property name="peopleService" ref="peopleService"/></bean>         <reference id="peopleService" interface="com.example.services.PeopleService" />
</blueprint>

非常小而简单:基本上,配置仅指出为了使模块正常工作,应该提供对com.example.services.PeopleService的引用(有效地由OSGi容器提供)。 为了了解它将如何发生,让我们看一下另一个公开服务的模块。 它仅包含一个接口PeopleService

package com.example.services;import java.util.Collection;import com.example.data.model.Person;public interface PeopleService {Collection< Person > getPeople( int page, int pageSize );Person getByEmail( final String email );Person addPerson( final String email, final String firstName, final String lastName );void removePerson( final String email );
}

并提供其实现作为PeopleServiceImpl类:

package com.example.services.impl;import java.util.Collection;import org.osgi.service.log.LogService;import com.example.data.PeopleDao;
import com.example.data.model.Person;
import com.example.services.PeopleService;public class PeopleServiceImpl implements PeopleService {private PeopleDao peopleDao;private LogService logService;@Overridepublic Collection< Person > getPeople( final int page, final int pageSize ) {        logService.log( LogService.LOG_INFO, "Getting all people" );return peopleDao.findAll( page, pageSize );}@Overridepublic Person getByEmail( final String email ) {logService.log( LogService.LOG_INFO, "Looking for a person with e-mail: " + email );return peopleDao.find( email );        }@Overridepublic Person addPerson( final String email, final String firstName, final String lastName ) {logService.log( LogService.LOG_INFO, "Adding new person with e-mail: " + email );return peopleDao.save( new Person( email, firstName, lastName ) );}@Overridepublic void removePerson( final String email ) {logService.log( LogService.LOG_INFO, "Removing a person with e-mail: " + email );peopleDao.delete( email );}public void setPeopleDao( final PeopleDao peopleDao ) {this.peopleDao = peopleDao;}public void setLogService( final LogService logService ) {this.logService = logService;}
}

这次又一次是非常小的,干净的实现,它具有两个可注入的依赖项org.osgi.service.log.LogServicecom.example.data.PeopleDao 。 它的蓝图配置位于OSGI-INF / blueprint文件夹中,看起来也很紧凑:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"><service ref="peopleService" interface="com.example.services.PeopleService" />        <bean id="peopleService" class="com.example.services.impl.PeopleServiceImpl"><property name="peopleDao" ref="peopleDao" />    <property name="logService" ref="logService" /></bean><reference id="peopleDao" interface="com.example.data.PeopleDao" /><reference id="logService" interface="org.osgi.service.log.LogService" />
</blueprint>

预期由OSGi容器在运行时提供对PeopleDaoLogService的引用。 但是, PeopleService的实现是作为服务公开的,一旦其捆绑包被激活, OSGi容器将能够将其注入PeopleRestService

难题的最后一部分是数据访问模块,它稍微复杂一些:它包含持久性配置( META-INF / persistence.xml ),并且基本上依赖于OSGi容器的JPA 2.0功能。 persistence.xml非常基本:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"version="2.0"><persistence-unit name="peopleDb" transaction-type="JTA"><jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=peopleDb)</jta-data-source>       <class>com.example.data.model.Person</class><properties><property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>         </properties>        </persistence-unit>
</persistence>

与服务模块类似,还有一个PeopleDao公开的接口:

package com.example.data;import java.util.Collection;import com.example.data.model.Person;public interface PeopleDao {Person save( final Person person );Person find( final String email );Collection< Person > findAll( final int page, final int pageSize );void delete( final String email ); 
}

通过其实现PeopleDaoImpl

package com.example.data.impl;import java.util.Collection;import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;import com.example.data.PeopleDao;
import com.example.data.model.Person;public class PeopleDaoImpl implements PeopleDao {private EntityManager entityManager;@Overridepublic Person save( final Person person ) {entityManager.persist( person );return person;}@Overridepublic Person find( final String email ) {return entityManager.find( Person.class, email );}public void setEntityManager( final EntityManager entityManager ) {this.entityManager = entityManager;}@Overridepublic Collection< Person > findAll( final int page, final int pageSize ) {final CriteriaBuilder cb = entityManager.getCriteriaBuilder();final CriteriaQuery< Person > query = cb.createQuery( Person.class );query.from( Person.class );return entityManager.createQuery( query ).setFirstResult(( page - 1 ) * pageSize ).setMaxResults( pageSize ) .getResultList();}@Overridepublic void delete( final String email ) {entityManager.remove( find( email ) );}
}

请注意,尽管我们正在执行数据操作,但是没有提及事务,也没有对实体管理器的事务API的显式调用。 我们将使用声明式方法进行事务处理,因为蓝图配置支持(位置不变, OSGI-INF / blueprint文件夹):

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"  xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.1.0"xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"><service ref="peopleDao" interface="com.example.data.PeopleDao" /><bean id="peopleDao" class="com.example.data.impl.PeopleDaoImpl"><jpa:context unitname="peopleDb" property="entityManager" /><tx:transaction method="*" value="Required"/></bean><bean id="dataSource" class="org.hsqldb.jdbc.JDBCDataSource"><property name="url" value="jdbc:hsqldb:mem:peopleDb"/></bean><service ref="dataSource" interface="javax.sql.DataSource"> <service-properties> <entry key="osgi.jndi.service.name" value="peopleDb" /> </service-properties> </service>     
</blueprint>

要记住的一件事:应用程序不需要创建JPA 2.1的实体管理器: OSGi运行时能够做到这一点,并将其注入到需要的地方,由jpa:context声明驱动。 因此, tx:transaction指示运行时将选定的服务方法包装在事务内。

现在,当提供最后一个服务PeopleDao时 ,我们准备使用Apache Karaf 3.0.1部署我们的模块。 三个步骤很容易做到:

  • 运行Apache Karaf 3.0.1容器
    bin/karaf (or bin\karaf.bat on Windows)
  • 从Apache Karaf 3.0.1 shell执行以下命令:
    feature:repo-add cxf 3.0.1
    feature:install http cxf jpa openjpa transaction jndi jdbc
    install -s mvn:org.hsqldb/hsqldb/2.3.2
    install -s mvn:com.fasterxml.jackson.core/jackson-core/2.4.0
    install -s mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.0
    install -s mvn:com.fasterxml.jackson.core/jackson-databind/2.4.0
    install -s mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/2.4.0
    install -s mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/2.4.0
  • 构建我们的模块并将其复制到Apache Karaf 3.0.1的deploy文件夹中(容器仍在运行时):
    mvn clean package
    cp module*/target/*jar apache-karaf-3.0.1/deploy/

在Apache Karaf 3.0.1 Shell中运行list命令时,应该看到所有激活的捆绑软件(模块)的列表,类似于此列表:

apache-karaf列表
其中module-servicemodule-jax-rsmodule-data对应于我们正在开发的模块。 默认情况下,我们所有的Apache CXF 3.0.1服务将在基本URL http:// :8181 / cxf / api /上提供。 通过在Apache Karaf 3.0.1 shell中执行cxf:list-endpoints -f命令很容易检查。

apache-karaf-cxf-list-endpoints

让我们通过发送几个HTTP请求来确保REST层按预期工作。 让我们创建一个新人:

curl http://localhost:8181/cxf/api/people -iX POST -d "firstName=Tom&lastName=Knocker&email=a@b.com"HTTP/1.1 201 Created
Content-Length: 0
Date: Sat, 09 Aug 2014 15:26:17 GMT
Location: http://localhost:8181/cxf/api/people/a@b.com
Server: Jetty(8.1.14.v20131031)

并确认该人已成功创建:

curl -i http://localhost:8181/cxf/api/peopleHTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 09 Aug 2014 15:28:20 GMT
Transfer-Encoding: chunked
Server: Jetty(8.1.14.v20131031)[{"email":"a@b.com","firstName":"Tom","lastName":"Knocker"}]

还要很好地检查数据库是否有人填充。 使用Apache Karaf 3.0.1 shell,只需执行两个命令即可轻松完成: jdbc:datasourcesjdbc:query peopleDb“ select * from people”

apache-karaf-list-datasources

太棒了! 我希望这篇介绍性很强的博客文章能打开您可以用来开发健壮,可扩展,模块化和可管理的软件的另一项有趣的技术。 我们没有涉及很多东西,但是您可以在这里找到它们。 完整的源代码可在GitHub上获得 。

Hibernate 4.2.x / 4.3.x用户注意:不幸的是,在当前版本的Apache Karaf 3.0.1中 , Hibernate 4.3.x完全可以正常工作(因为尚不支持JPA 2.1 ),但是我设法做到了与Hibernate 4.2.x一起运行时,容器通常拒绝解析与JPA相关的依赖关系。

翻译自: https://www.javacodegeeks.com/2014/08/osgi-the-gateway-into-micro-services-architecture.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html笔记(五)2D、3D、3D动画

大标题小节一、2D1. css3 渐变的语法及应用2. 动画过渡&#xff1a;transition3. 2D转换属性&#xff1a;transform二、3D1. 3D转换2. 3D视距3. 3D翻转方法4. 3D位置移动5. 3D缩放三、3D动画1. keyframes2. 动画属性animation一、2D 1. css3 渐变的语法及应用&#xff1b; &a…

基于上下文的访问控制

拓扑图 配置步骤 1配置端口ip地址&#xff0c;并检测连通性 服务器 ping pc端 服务器 telnet R3 2配置命令 R3(config)# ip access-list extended go R3(config-ext-nacl)# deny ip any any //此ACL目的是隔绝外网流量 R3(config-ext-nacl)# exit R3(config)# interface s0…

使用Gradle将JAR工件发布到Artifactory

因此&#xff0c;我浪费了一两天&#xff08;投资&#xff09;只是为了了解如何使用Gradle将JAR发布到本地运行的Artifactory服务器。 我使用Gradle Artifactory插件进行发布。 我迷失了无穷循环&#xff0c;包括各种版本的各种插件和执行各种任务。 是的&#xff0c;我之前阅读…

Combox使用的一些技巧

这些天做一个小型的CMS&#xff0c;也就几张表&#xff0c;用WCFLINQ2SQLSilverlight这种方式开发的&#xff0c;对最常用的控件如DataGrid,DataForm以及Toolkit里其它一些控件的用法有了一定的了解&#xff0c;同时参照JV9的教程&#xff0c;把Silverlight里的验证机制仔细的学…

HTML+css实现的效果

一、鼠标划过效果 1. 类似电子书的图书效果2. 绝对定位实用案例 二、锚点的应用 三、css精灵&#xff08;图片整合&#xff09; 用一张图片写出一串电话号码用一张图片写出导航栏的滑动效果 四、后台管理布局 单飞布局双飞布局后台页面管理布局 五、css3部分 在一个div…

[LeetCode] Longest Substring Without Repeating Characters 最长无重复字符的子串 C++实现java实现...

最长无重复字符的子串 Given a string, find the length of the longest substring without repeating characters. Example 1: Input: "abcabcbb" Output: 3 Explanation: The answer is "abc", with the length of 3. Example 2: Input: "bbbbb&qu…

使用自动机的Lucene新的邻近查询

最简单的Apache Lucene查询TermQuery匹配包含指定术语的任何文档&#xff0c;无论该术语出现在每个文档中的何处 。 使用BooleanQuery可以将多个TermQuery组合在一起&#xff0c;并完全控制哪些术语是可选的&#xff08; SHOULD &#xff09;和哪些是必需的&#xff08; MUST &…

关于HTML的面试题-html4/css2篇

1. 什么是HTML&#xff1f;2. 用过什么调试器&#xff08;浏览器&#xff09;&#xff0c;编辑器&#xff1f;3. HTML4.0 和 HTML5.0 的区别&#xff1f;4. 手写 HTML 代码5. 元素类型有哪些&#xff08;display有哪些属性&#xff09;&#xff1f;块元素、行元素和行内块元素的…

Java EE:异步构造和功能

介绍 Java EE具有许多API和构造以支持异步执行。 从可伸缩性和性能的角度来看&#xff0c;这是至关重要的。 让我们假设2个模块相互交互。 当模块A &#xff08;发送方&#xff09;以同步方式向模块B &#xff08;接收方&#xff09;发送消息时&#xff0c;通信将在单个线程的…

用 JA Transmenu 模块做多级弹出菜单

转自http://www.joomlagate.com “弹出菜单”这个说法本来不规范&#xff0c;尽管你我都明白这是什么意思&#xff0c;而实际上我们所理解的那个菜单样式英文说法是“Slide Menu”&#xff08;滑动菜单&#xff09;&#xff0c;如果要用“弹出菜单”就成了“Popup Menu”。当然…

关于HTML的面试题-html5/css3篇

1. html5 新增标签有哪些&#xff08;或者新增的语义标签&#xff09;&#xff1f;2. HTML5 中有哪些新特性&#xff1f;3. 视频有哪几种格式&#xff1f;这几种格式有什么区别&#xff1f;4. 写出你知道的层级选择符&#xff08;结构性伪类选择器&#xff09;5. 什么是渐进增强…

Elasticsearch的用例:灵活的查询缓存

在前两篇有关Elasticsearch用例的文章中&#xff0c;我们已经看到Elasticsearch 可用于存储甚至大量文档 &#xff0c;并且我们可以通过Query DSL使用Lucene的全文功能访问那些 文档 。 在这篇较短的文章中&#xff0c;我们将把两个用例放在一起&#xff0c;以了解读取繁重的应…

Vue底层架构及其应用(上)转

https://mp.weixin.qq.com/s?__bizMzIzNjcwNzA2Mw&mid2247486427&idx1&sn61f9579bbe1dfe26da4b53eb538fee13&chksme8d28643dfa50f557c56ce8b5bc9b0597a157a20791e21b1812fe2a30ff4cf2c813608473b43&mpshare1&scene23&srcid#rd 一、前言 市面上有很…

jquery笔记一:下载安装、语法、选择器、遍历选择元素的方法、jQuery动画

目前 jQuery 兼容于所有主流浏览器, 包括 IE 6&#xff01;开发时常用 jquery.js&#xff0c;上线用 jquery.min.js。 jq插件 目前jQuery有三个大版本&#xff1a; &#xff08;1&#xff09;1.x.x: 兼容ie6,7,8&#xff0c;使用最为广泛&#xff0c;官网只做BUG维护&#xff…

jquery简介 each遍历 prop attr

一、JQ简介 jQuery是一个快速、简洁的JavaScript框架&#xff0c;它封装了JavaScript常用的功能代码&#xff0c;提供一种简便的JavaScript设计模式&#xff0c;优化HTML文档操作、事件处理、动画设计和Ajax交互。 装载的先后次序&#xff1a;  jQuery封装库在上&#xff0…

如何让Visitor变得可爱1

本文转自&#xff1a;http://www.cnblogs.com/idior/archive/2005/01/19/94280.html 在wayfarer的文章中提到了如何利用visitor模式实现添加新的功能。如他所说&#xff0c;在实际过程中显的不是那么可爱。不过他为我们提供了一个可行的解决方案&#xff0c;本文将在此基础上使…

EJB 3.x:生命周期和并发模型(第1部分)

Java EE组件生命周期和与并发相关的详细信息对于经验丰富的专业人员而言可能不是新知识&#xff0c;但是对于初学者而言&#xff0c;这可能需要花费一些时间。 就EJB而言&#xff0c;了解其生命周期 &#xff08;以及相关的并发场景&#xff09;对于确保使用EJB的正确用法和解…

单独使用 laydate 日期时间组件

layui 日期和时间组件官方文档 需要注意几点&#xff1a; 下载压缩包后&#xff0c;复制整个 laydate 文件夹放到项目中&#xff0c;里面的文件不要改动位置&#xff0c;否则会报错&#xff1b;在移动端使用时&#xff0c;要给 input 添加 readonly 属性&#xff0c;否则点击…

从零开始学习SVG

1 什么是SVG&#xff1f; MDN中的定义是&#xff1a;SVG即可缩放矢量图形&#xff08;Scalable Vector Graphics&#xff0c;SVG)&#xff0c;是一种用来描述二维矢量图形的 XML 标记语言。 简单地说&#xff0c;SVG 面向图形&#xff0c;HTML 面向文本。SVG 与 Flash 类似&am…

layui 关于layDate设置时间限制问题

前面是尝试结果&#xff0c;有兴趣的可以康康&#xff0c;赶时间的可以直接翻到底下 ----->直达车 因为只有一个页面&#xff0c;没什么标准&#xff0c;所以自己就使用了layui的时间控件&#xff0c;并且作为独立组件。 第一次尝试 - 使用 min 一开始只想让结束时间有个…