基於Vert.x和SpringBoot實現響應式開發
Vert.x是作為一個事件匯流排的設計,以保證應用中不同部分以一種非堵塞的執行緒安全方式通訊,其原理來自於Erlang和Akka,它是能充分利用多核處理器效能並實現高併發程式設計的需求。
所有Vert.x 的VERTICLE預設是一個單執行緒,不像Node.js只有一個單執行緒,vert.x能在很多執行緒中執行很多VERTICLE,每個執行緒一個VERTICLE,這樣你可以指定幾個VERTICLE作為"worker",能夠以多執行緒方式執行你的任務。
Vert.x可以透過使用Hazelcast實現底層的事件匯流排的多節點叢集。
下面以一個案例說明如何基於 Spring Boot, Spring Data JPA, 和 Spring REST開發一個聊天室系統?
下面沒有引入Vert.x而是使用傳統Spting語法實現的系統:
上述程式碼中 @Bean是提供了訪問JPA的EntityManager, TransactionManager, 和 DataSource. 這是一個標準的Springboot案例原始碼。前端使用CustomerEnpoints作為RESTful的控制器,接受客戶端的請求。
CustomerVerticle 類是作為@Component,意味著在啟動時,Spring會初始化這個類,它也有@PostConstruct標註的start方法,這樣Verticle 在啟動被載入時會執行其方法內容:
在這個啟動方法中,引入了vertx-web庫包:Router,能夠讓使用者定義將請求過濾轉換為HTTP URLs, methods, 和頭部header 等資訊,BodyHandler 是能將POST/PUT提交的內容轉變為一個JSON物件,Vert.x能夠將其作為RoutingContext的一部分進行處理;RoutingContext包含Vert.x請求物件和響應物件和任何Http請求中資料,包括POST內容資料等等,blockingHandler是接受RoutingContext作為輸入引數。
注意到blockingHandler方法使用了Java 8的方法引用,如this::getCustomerById,這比使用lambda將邏輯插入blockingHandler 中更具有程式碼可讀性。getCustomerById的方法如下:
在getCustomerById方法中實現真正的業務處理,比如呼叫DAO訪問資料庫,序列化和反序列化物件,類似在MVC的控制器中實現一樣。
完整見Github
這段程式碼的主要問題是效能的擴充套件性有限,這段程式碼要麼可以在Tomcat中執行,要麼以微服務方式在嵌入式伺服器如Jetty或undertow中執行,總之,只能是一個請求捆綁一個執行緒,當在等待I/O等堵塞操作時,所有的資源都會消耗在等待上,這種區域性堵塞引發整體等待是一種資源浪費。
而如果我們引入了Vert.x,使用@Bean注入了Vertx例項:
訪問JPA的程式碼基本沒有變,主要是RESTful控制器更換了,使用CustomerVerticle替代了CustomerEnpoints。
完整程式碼見:Convert-To-Vert.x-Web
你可能覺得這比原來Spring程式碼更復雜了些,這裡只是介紹案例原始碼,真正實戰中可以使用Vert.x的元註解庫實現類似JAX-RS的REST端點方式,當然最主要是我們獲得了更好的可伸縮擴充套件性,在這段程式碼底層下面,Vertx使用了Netty作為非同步IO操作,這樣就可以處理更多併發請求,當然限制於資料庫連線池的大小。
上面展示了將前端RESTful的IO從傳統的同步模式如Jetty轉換到非同步IO入Netty方式,我們也可以將訪問後端資料庫的同步IO放入 Worker Verticles,這樣就會更有效率地處理來自客戶端的請求,程式碼可見:Convert-To-Worker-Verticles
原文參考:
所有Vert.x 的VERTICLE預設是一個單執行緒,不像Node.js只有一個單執行緒,vert.x能在很多執行緒中執行很多VERTICLE,每個執行緒一個VERTICLE,這樣你可以指定幾個VERTICLE作為"worker",能夠以多執行緒方式執行你的任務。
Vert.x可以透過使用Hazelcast實現底層的事件匯流排的多節點叢集。
下面以一個案例說明如何基於 Spring Boot, Spring Data JPA, 和 Spring REST開發一個聊天室系統?
下面沒有引入Vert.x而是使用傳統Spting語法實現的系統:
@SpringBootApplication @EnableJpaRepositories @EnableTransactionManagement @Slf4j public class Application { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(Application.class, args); System.out.println("Let's inspect the beans provided by Spring Boot:"); String[] beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); for (String beanName : beanNames) { System.out.println(beanName); } } @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); return builder.setType(EmbeddedDatabaseType.HSQL).build(); } @Bean public EntityManagerFactory entityManagerFactory() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setGenerateDdl(true); LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setJpaVendorAdapter(vendorAdapter); factory.setPackagesToScan("com.zanclus.data.entities"); factory.setDataSource(dataSource()); factory.afterPropertiesSet(); return factory.getObject(); } @Bean public PlatformTransactionManager transactionManager(final EntityManagerFactory emf) { final JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(emf); return txManager; } } <p class="indent"> |
上述程式碼中 @Bean是提供了訪問JPA的EntityManager, TransactionManager, 和 DataSource. 這是一個標準的Springboot案例原始碼。前端使用CustomerEnpoints作為RESTful的控制器,接受客戶端的請求。
CustomerVerticle 類是作為@Component,意味著在啟動時,Spring會初始化這個類,它也有@PostConstruct標註的start方法,這樣Verticle 在啟動被載入時會執行其方法內容:
@PostConstruct public void start() throws Exception { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.get("/v1/customer/:id") .produces("application/json") .blockingHandler(this::getCustomerById); router.put("/v1/customer") .consumes("application/json") .produces("application/json") .blockingHandler(this::addCustomer); router.get("/v1/customer") .produces("application/json") .blockingHandler(this::getAllCustomers); vertx.createHttpServer().requestHandler(router::accept).listen(8080); } <p class="indent"> |
在這個啟動方法中,引入了vertx-web庫包:Router,能夠讓使用者定義將請求過濾轉換為HTTP URLs, methods, 和頭部header 等資訊,BodyHandler 是能將POST/PUT提交的內容轉變為一個JSON物件,Vert.x能夠將其作為RoutingContext的一部分進行處理;RoutingContext包含Vert.x請求物件和響應物件和任何Http請求中資料,包括POST內容資料等等,blockingHandler是接受RoutingContext作為輸入引數。
注意到blockingHandler方法使用了Java 8的方法引用,如this::getCustomerById,這比使用lambda將邏輯插入blockingHandler 中更具有程式碼可讀性。getCustomerById的方法如下:
private void getCustomerById(RoutingContext rc) { log.info("Request for single customer"); Long id = Long.parseLong(rc.request().getParam("id")); try { Customer customer = dao.findOne(id); if (customer==null) { rc.response().setStatusMessage("Not Found").setStatusCode(404).end("Not Found"); } else { rc.response().setStatusMessage("OK").setStatusCode(200).end(mapper.writeValueAsString(dao.findOne(id))); } } catch (JsonProcessingException jpe) { rc.response().setStatusMessage("Server Error").setStatusCode(500).end("Server Error"); log.error("Server error", jpe); } } <p class="indent"> |
在getCustomerById方法中實現真正的業務處理,比如呼叫DAO訪問資料庫,序列化和反序列化物件,類似在MVC的控制器中實現一樣。
完整見Github
這段程式碼的主要問題是效能的擴充套件性有限,這段程式碼要麼可以在Tomcat中執行,要麼以微服務方式在嵌入式伺服器如Jetty或undertow中執行,總之,只能是一個請求捆綁一個執行緒,當在等待I/O等堵塞操作時,所有的資源都會消耗在等待上,這種區域性堵塞引發整體等待是一種資源浪費。
而如果我們引入了Vert.x,使用@Bean注入了Vertx例項:
public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } private Vertx vertx; /** * Create an {@link ObjectMapper} for use in (de)serializing objects to/from JSON * @return An instance of {@link ObjectMapper} */ @Bean public ObjectMapper objectMapper() { return new ObjectMapper(new JsonFactory()); } /** * A singleton instance of {@link Vertx} which is used throughout the application * @return An instance of {@link Vertx} */ @Bean public Vertx getVertxInstance() { if (this.vertx==null) { this.vertx = Vertx.vertx(); } return this.vertx; } .... <p class="indent"> |
訪問JPA的程式碼基本沒有變,主要是RESTful控制器更換了,使用CustomerVerticle替代了CustomerEnpoints。
完整程式碼見:Convert-To-Vert.x-Web
你可能覺得這比原來Spring程式碼更復雜了些,這裡只是介紹案例原始碼,真正實戰中可以使用Vert.x的元註解庫實現類似JAX-RS的REST端點方式,當然最主要是我們獲得了更好的可伸縮擴充套件性,在這段程式碼底層下面,Vertx使用了Netty作為非同步IO操作,這樣就可以處理更多併發請求,當然限制於資料庫連線池的大小。
上面展示了將前端RESTful的IO從傳統的同步模式如Jetty轉換到非同步IO入Netty方式,我們也可以將訪問後端資料庫的同步IO放入 Worker Verticles,這樣就會更有效率地處理來自客戶端的請求,程式碼可見:Convert-To-Worker-Verticles
原文參考:
相關文章
- 基於screen.width的偽響應式開發
- 響應式開發心得
- 基於媒體查詢和 rem 的響應式佈局實踐REM
- Vue 響應式實現原理Vue
- 自己實現一個VUE響應式--VUE響應式原理Vue
- 基於React和A-Frame開發虛擬現實React
- 基於 Redis 實現分散式應用限流Redis分散式
- 基於ThinkPHP框架開發的響應式學生資訊後臺管理系統PHP框架
- Dory:基於React的響應式部落格開源平臺React
- 基於ThinkPHP5.0+BootStrap框架開發的響應式介面部落格系統PHPboot框架
- 《SpringBoot實戰開發》——基於Gradle+Kotlin的企業級應用開發最佳實踐Spring BootGradleKotlin
- Vue 3 響應式原理及實現Vue
- 標籤實現響應式圖片
- 基於React和SpringBoot的快速開發模板QuickAdminReactSpring BootUI
- 基於"堆"的底層實現和應用
- SpringBoot中的響應式web應用Spring BootWeb
- Swagger基於SpringBoot實現SwaggerSpring Boot
- SpringBoot基於資料庫實現簡單的分散式鎖Spring Boot資料庫分散式
- elf,基於flexbox的響應式CSS框架FlexCSS框架
- 基於Vue的事件響應式進度條元件Vue事件元件
- 基於 Bootstrap 的響應式後臺儀表板boot
- 基於vite多頁面實現多端同構開發和部署Vite
- 『前端開發』- 相容IE8的響應式開發前端
- 基於redis和zookeeper的分散式鎖實現方式Redis分散式
- 如何實現 font-size 的響應式
- Vue響應式原理與模擬實現Vue
- Vue響應式資料: Observer模組實現VueServer
- js實現移動端字型響應式JS
- Vue響應式原理以及簡單實現Vue
- 開發響應式web頁面的經驗Web
- 7 個 Bootstrap 線上編輯器用於快速開發響應式網站boot網站
- 基於SpringBoot AOP面向切面程式設計實現Redis分散式鎖Spring Boot程式設計Redis分散式
- 基於redis實現分散式鎖Redis分散式
- 基於ZK實現分散式鎖分散式
- 深入淺出基於“依賴收集”的響應式原理
- 聊天室應用開發實踐(二):實現基於 Web 的聊天室Web
- 為何現在響應式程式設計在業務開發微服務開發不普及程式設計微服務
- XPages 開發實踐:開發基於 XPages 的複合應用程式