servlet 3.0非同期特性を使用してspring-cloud-zulを改造
7392 ワード
spring-cloud-zulはspringMVCに依存してルーティングを登録していることを知っていますが、springMVCはservletの上に構築されています(ここではマイクロサービス専門家の楊波先生がネットワークモデルを書いたことがありますが、参考にしてみてください).servlet 3.0の前にthread per connection方式でリクエストを処理していました.各リクエストはservletコンテナにスレッドを割り当てて処理する必要があり、ユーザーリクエストに応答するまでコンテナスレッドプールに戻されます.バックエンドのトラフィック処理に時間がかかると、このスレッドはブロックされ続け、他のことはできません.消費時間リクエストが多い場合、servletコンテナスレッドは消費され、新しいリクエストを処理できません.したがって、Netflixは、バックエンドの遅いサービスのためにリソースが消費され、サービスが利用できないことを防止するために、溶断されたコンポーネント
まずzulのmavenプロジェクトを作成しましょう.
主にスレッドの名前を印刷します.このfilterはzulのフロントフィルタです.zulがルーティングを実行するときにどのスレッドで実行されているかを見てみましょう.次のmainメソッドを起動しますが、バックエンドサービスが必要です.簡単です.springcloudプロジェクトの名前は
出力:
filterを実行するスレッドがservletコンテナスレッドであることはよくわかりますが、非同期に改造してから比較してみましょう.
文章spring-cloud-zul原理解析(一)ではspring-cloud-zulのルーティングマッピングがspringMVCの2大コンポーネント
この2つのクラスはspring-cloud-zulで拡張を提供していないことがわかり、servletの非同期論理を実現するためにそれらを置き換えることができません.どうすればいいのでしょうか.Spring-cloud-zulには
その後、私たちは2つの自分の配置を作成して、
ここで少し修正しました
このような目的は、主に注釈
ははは、filterを実行するスレッドは私たちのカスタムスレッド名になり、私たちのニーズに達し、servletは非同期になりました.
これは私がspring-cloud-zulに対して非同期servletを実現する考えで、記録して、最も良い実現方式ではないかもしれませんが、もしあなたがもっと良い方法があれば、伝言を残して私に一緒に検討してください!
Hystrix
を開発した.しかし、servlet 3.0が出てから非同期servletをサポートします.ビジネス操作を独立したスレッドプールに入れることができます.これにより、servletスレッドをできるだけ早く解放することができます.springMVC自体も非同期servletをサポートします.この文章では、servlet 3.0の非同期特性を使用してspring-cloud-zulのパフォーマンスを最適化する方法について説明します.まずzulのmavenプロジェクトを作成しましょう.
async-zuul
と言います.具体的なコードはgithubに置いてあります.プロジェクトはconsulに依存して登録センターを行い、起動時にローカルでconsulを起動し、効果を見るためにzulのfilterクラスを新規作成します.@Component
public class TestFilter extends ZuulFilter {
// , github
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("============== :" + Thread.currentThread().getName()
+ ", url:" + request.getRequestURI() + "================");
return null;
}
}
主にスレッドの名前を印刷します.このfilterはzulのフロントフィルタです.zulがルーティングを実行するときにどのスレッドで実行されているかを見てみましょう.次のmainメソッドを起動しますが、バックエンドサービスが必要です.簡単です.springcloudプロジェクトの名前は
book
です.url:/book/borrow
を提供します.起動後、consulにサービスを登録します.成功したら、zulのエージェントを通じてbookサービスに問い合わせます.http://localhost:8080/book/book/borrow
出力:
========== :http-nio-8080-exec-10, url:/book/book/borrow=======
filterを実行するスレッドがservletコンテナスレッドであることはよくわかりますが、非同期に改造してから比較してみましょう.
文章spring-cloud-zul原理解析(一)ではspring-cloud-zulのルーティングマッピングがspringMVCの2大コンポーネント
ZuulHandlerMapping
とZuulController
に使用されていることを分析したが,現在は非同期servletをサポートできないに違いない.では、この2つのクラスはどこでロードされていますか?答えはZuulServerAutoConfiguration
です.spring-cloud-zul自動構成クラスです.ソースコードは次のとおりです.@Configuration
@ConditionalOnBean(annotation=EnableZuulProxy.class)
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
// ..........
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
mapping.setErrorController(this.errorController);
return mapping;
}
// ..........
}
この2つのクラスはspring-cloud-zulで拡張を提供していないことがわかり、servletの非同期論理を実現するためにそれらを置き換えることができません.どうすればいいのでしょうか.Spring-cloud-zulには
ZuulProxyAutoConfiguration
からZuulServerAutoConfiguration
に続く自動構成があります.この2つの構成クラスをすべて置き換えて、私たち自身に変えればいいのではないでしょうか.はい、しかし、まずロードの2つの自動構成クラスを排除しなければなりません.springbootはこのような設定を提供します.@EnableZuulProxy
// ZuulProxyAutoConfiguration
@SpringBootApplication(exclude=ZuulProxyAutoConfiguration.class)
public class Startup {
public static void main(String[] args) {
SpringApplication.run(Startup.class, args);
}
}
その後、私たちは2つの自分の配置を作成して、
ZuulServerAutoConfiguration
とZuulProxyAutoConfiguration
の2つのクラスを完全にコピーしましたが、この2つのクラスだけではだめです.この2つのクラスはクラスRibbonCommandFactoryConfiguration
に使用されています.中の内部クラスはprotected
で、私たちは使用できません.自分で作成しなければなりません.RibbonCommandFactoryConfiguration
からコピーしなければなりません.それから、ZuulController
の論理を非同期方式に変更する必要があります.ZuulController ,
ZuulController`クラスを継承するクラスを追加します.以下のようにします.public class MyZuulController extends ZuulController{
private final AsyncTaskExecutor asyncTaskExecutor;
public MyZuulController(AsyncTaskExecutor asyncTaskExecutor) {
super();
this.asyncTaskExecutor = asyncTaskExecutor;
}
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//
final AsyncContext asyncCtx = request.startAsync();
this.asyncTaskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
MyZuulController.this.handleRequestInternal((HttpServletRequest)asyncCtx.getRequest(),
(HttpServletResponse)asyncCtx.getResponse());
}catch (Exception e) {
e.printStackTrace();
}finally {
asyncCtx.complete();
RequestContext.getCurrentContext().unset();
}
}
});
return null;
}
}
@Configuration
@ConditionalOnBean(annotation=EnableZuulProxy.class)
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@Import(ServerPropertiesAutoConfiguration.class)
public class MyZuulServerAutoConfiguration {
// , ZuulServerAutoConfiguration
/**
*
* @return
*/
@Bean
public AsyncTaskExecutor zuulAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("zuul-async-");
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
return executor;
}
// MyZuulController ,
@Bean
public ZuulController zuulController(AsyncTaskExecutor asyncTaskExecutor) {
return new MyZuulController(asyncTaskExecutor);
}
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,AsyncTaskExecutor asyncTaskExecutor) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController(asyncTaskExecutor));
mapping.setErrorController(this.errorController);
return mapping;
}
}
@Configuration
@Import({ MyRibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
MyRibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
MyRibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })
@ConditionalOnBean(annotation=EnableZuulProxy.class)
public class MyZuulProxyAutoConfiguration extends MyZuulServerAutoConfiguration {
// , ZuulProxyAutoConfiguration
}
public class MyRibbonCommandFactoryConfiguration {
// , RibbonCommandFactoryConfiguration
}
ここで少し修正しました
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
// :
@ConditionalOnBean(annotation=EnableZuulProxy.class)
このような目的は、主に注釈
@EnableZuulProxy
と組み合わせて使用され、この注釈がオンになってこそ構成クラスがロードされる.ZuulController
をカスタムMyZuulController
に置き換えました.ここでは非同期化の主な論理ですが、serv 3.0が提供してくれたapiを使用して非同期化をオンにします.準備が整いました.zulを再起動し、上のurlにアクセスし、出力します.========== :zuul-async-1, url:/book/book/borrow==========
ははは、filterを実行するスレッドは私たちのカスタムスレッド名になり、私たちのニーズに達し、servletは非同期になりました.
これは私がspring-cloud-zulに対して非同期servletを実現する考えで、記録して、最も良い実現方式ではないかもしれませんが、もしあなたがもっと良い方法があれば、伝言を残して私に一緒に検討してください!