1 Star 1 Fork 0

minagamiyuki / my-scala-dispatcher

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
README.md 4.52 KB
一键复制 编辑 原始数据 按行查看 历史
minagamiyuki 提交于 2020-01-22 02:11 . add:readme

my-scala-dispatcher

介绍

用netty + scala 写的一个简易web框架.支持路由(相当于@RequestMapping),支持返回值: string,json对象,byte[].自用,轻量级,启动只需要2秒,占用20m的堆.

做这个小框架的初衷是,自家云服务器的内存只有256M.实在是承受不起spring-web的占用.

况且我只是想要个简单的rest-api而已.web容器那些强大的功能我根本用不上.所以就打算自己写一个web服务器.

╮( ̄▽ ̄)╭ 算是练手吧.对java8特性了解比较深的话上手scala还是很快的.好多scala的特性在新的jdk里面也有啦.

总共两百多行代码.用到了很多scala的特性.比如traint,lambda,模式匹配,caseClass 换成jdk7实现估计怎么也得1000行以上吧.

依赖

使用了Google-Guice作为依赖注入,

Reflections扫描指定路径下的Mappings,

JSON转换用了FastJSON(自己写着玩,用着爽就完事了)

code

启动类: com.mina.spider.Application.main

核心类:Dispatcher.scala

@Singleton
class Dispatcher {


  private[this] val log = LoggerFactory.getLogger(this.getClass)

  private[this] val allocator = UnpooledByteBufAllocator.DEFAULT

  private[this] var mapping: mutable.Set[MappingCase] = _

  @Inject
  def this(scanner: MappingScanner) {
    this()
    mapping = scanner.routerMapping(Constant.mappingPackage)
  }


  def dispatch(req: FullHttpRequest): Option[DefaultFullHttpResponse] = {
    log.debug("dispatch uri :{}", req.uri())
    mapping
      .find(x => x.pattern.matches(req.uri()) && x.method == req.method.toString.toUpperCase)
      .map(_.mappings)
      .flatMap(routingTo(req, _))
      .orElse(strResponse(str = Constant.NOT_FOUND, status = HttpResponseStatus.NOT_FOUND))
  }

  private def routingTo(req: FullHttpRequest, mapping: Mappings): Option[DefaultFullHttpResponse] = {
     try {
       mapping.execute(req).flatMap {
         case str: String => strResponse(str)
         case bytes: Array[Byte] => response(allocator.heapBuffer(bytes.length).writeBytes(bytes), contentType = "application/octet-stream")
         case ref: AnyRef => Option(ref).map(JSON.toJSONString(_)).flatMap(strResponse(_, contentType = "application/json"))
         case _ => strResponse("null")
       }
     } catch {
       case e: Exception =>
         log.error("execute mapping error:\r\n", e)
         strResponse(str = Option(e.getMessage).getOrElse("exception with 417"), status = HttpResponseStatus.EXPECTATION_FAILED)
     }
   }
...

http服务启动类:HttpServer.scala

@Singleton
class HttpServer {

  private[this] val log = LoggerFactory.getLogger(this.getClass)

  private[this] val bossGroup = new NioEventLoopGroup()

  private[this] val workerGroup = new NioEventLoopGroup()

  @Inject
  private[this] var channelIn: HttpChannelInitializer = _


  @throws[InterruptedException]
  def start(): Unit = {
    try {
      log.info("------ http server starting..")
      val serverBootstrap = new ServerBootstrap
      serverBootstrap
        .group(bossGroup, workerGroup)
        .channel(classOf[NioServerSocketChannel])
        .childHandler(channelIn)
        .option(ChannelOption.SO_BACKLOG.asInstanceOf[ChannelOption[Any]], 128)
        .childOption(ChannelOption.SO_KEEPALIVE.asInstanceOf[ChannelOption[Any]], true)
        .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(Constant.MIN_BUFFER_SIZE, Constant.INITIAL_BUFFER_SIZE, Constant.MAXIMUM_BUFFER_SIZE));
      val channelFuture = serverBootstrap.bind(Constant.iNetPort).sync
      log.info("------http server started,port:" + Constant.iNetPort)
      channelFuture.channel.closeFuture.sync
    } finally {
      workerGroup.shutdownGracefully()
      bossGroup.shutdownGracefully()
    }
  }
...

mapping映射类扫描器:MappingScanner.scala

@Singleton
class MappingScanner {

  private val log = LoggerFactory.getLogger(this.getClass)

  val injector: Injector = Application.injector

  def routerMapping(packageName: String = "*"): mutable.Set[MappingCase] = {
    val reflections: Reflections = new Reflections(packageName)
    log.info("scan mappings,package:{}", packageName)
    CollectionConverters.SetHasAsScala(reflections.getTypesAnnotatedWith(classOf[HandlerMapping]))
      .asScala
      .map(k => {
        val mapping = k.getAnnotation(classOf[HandlerMapping])
        MappingCase(mapping.httpMethod(), injector.getInstance(k).asInstanceOf[Mappings], methodMatcher(mapping))
      })
  }

  private def methodMatcher(mapping: HandlerMapping): Regex = ("^" + mapping.value() + "$").r
}
Scala
1
https://gitee.com/minagamiyuki/my-scala-dispatcher.git
git@gitee.com:minagamiyuki/my-scala-dispatcher.git
minagamiyuki
my-scala-dispatcher
my-scala-dispatcher
master

搜索帮助