Coder Social home page Coder Social logo

togetheros / cicada Goto Github PK

View Code? Open in Web Editor NEW
954.0 61.0 212.0 289 KB

🚀 Fast lightweight HTTP service framework.

Home Page: https://crossoverjie.top/categories/cicada/

License: Apache License 2.0

Java 99.74% Shell 0.26%
http netty4 high-performance json lightweight

cicada's Introduction



Introduction

Fast, lightweight Web framework based on Netty; without too much dependency, and the core jar package is only 30KB.

If you are interested, please click Star.

Features

Quick Start

Create a project with Maven, import core dependency.

<dependency>
    <groupId>top.crossoverjie.opensource</groupId>
    <artifactId>cicada-core</artifactId>
    <version>x.y.z</version>
</dependency>

Of course, it is recommended to introduce an additional IOC container plugin:

<dependency>
    <groupId>top.crossoverjie.opensource</groupId>
    <artifactId>cicada-ioc</artifactId>
    <version>2.0.4</version>
</dependency>

start class:

public class MainStart {

    public static void main(String[] args) throws InterruptedException {
        CicadaServer.start(MainStart.class,"/cicada-example") ;
    }
}

Configuring Business Action

@CicadaAction("routeAction")
public class RouteAction {

    private static final Logger LOGGER = LoggerBuilder.getLogger(RouteAction.class);


    @CicadaRoute("getUser")
    public void getUser(DemoReq req){

        LOGGER.info(req.toString());
        WorkRes<DemoReq> reqWorkRes = new WorkRes<>() ;
        reqWorkRes.setMessage("hello =" + req.getName());
        CicadaContext.getContext().json(reqWorkRes) ;
    }

    @CicadaRoute("getInfo")
    public void getInfo(DemoReq req){

        WorkRes<DemoReq> reqWorkRes = new WorkRes<>() ;
        reqWorkRes.setMessage("getInfo =" + req.toString());
        CicadaContext.getContext().json(reqWorkRes) ;
    }

    @CicadaRoute("getReq")
    public void getReq(CicadaContext context,DemoReq req){

        WorkRes<DemoReq> reqWorkRes = new WorkRes<>() ;
        reqWorkRes.setMessage("getReq =" + req.toString());
        context.json(reqWorkRes) ;
    }



}

Launch and apply access: http://127.0.0.1:5688/cicada-example/routeAction/getUser?id=1234&name=zhangsan

{"message":"hello =zhangsan"}

Cicada Context

Through context.json(), context.text(), you can choose different response ways.

@CicadaAction("routeAction")
public class RouteAction {

    private static final Logger LOGGER = LoggerBuilder.getLogger(RouteAction.class);

    @CicadaRoute("getUser")
    public void getUser(DemoReq req){

        LOGGER.info(req.toString());
        WorkRes<DemoReq> reqWorkRes = new WorkRes<>() ;
        reqWorkRes.setMessage("hello =" + req.getName());
        CicadaContext.getContext().json(reqWorkRes) ;
    }
    
    @CicadaRoute("hello")
    public void hello() throws Exception {
        CicadaContext context = CicadaContext.getContext();

        String url = context.request().getUrl();
        String method = context.request().getMethod();
        context.text("hello world url=" + url + " method=" + method);
    }    


}

Cookie Support

Set Cookie

Cookie cookie = new Cookie() ;
cookie.setName("cookie");
cookie.setValue("value");
CicadaContext.getResponse().setCookie(cookie);

Get Cookie

Cookie cookie = CicadaContext.getRequest().getCookie("cookie");
logger.info("cookie = " + cookie.toString());

Custom configuration

By default, the configuration file under the classpath is read.

You can also customize the configuration file.

Just need to extends top.crossoverjie.cicada.server.configuration.AbstractCicadaConfiguration class.

Write the name of the configuration file at the same time.

Like this:

public class RedisConfiguration extends AbstractCicadaConfiguration {


    public RedisConfiguration() {
        super.setPropertiesName("redis.properties");
    }

}

public class KafkaConfiguration extends AbstractCicadaConfiguration {

    public KafkaConfiguration() {
        super.setPropertiesName("kafka.properties");
    }


}

Get configuration information

Get the configuration infomation, follow this:

KafkaConfiguration configuration = (KafkaConfiguration) getConfiguration(KafkaConfiguration.class);
RedisConfiguration redisConfiguration = (RedisConfiguration) ConfigurationHolder.getConfiguration(RedisConfiguration.class);
ApplicationConfiguration applicationConfiguration = (ApplicationConfiguration) ConfigurationHolder.getConfiguration(ApplicationConfiguration.class);

String brokerList = configuration.get("kafka.broker.list");
String redisHost = redisConfiguration.get("redis.host");
String port = applicationConfiguration.get("cicada.port");

LOGGER.info("Configuration brokerList=[{}],redisHost=[{}] port=[{}]",brokerList,redisHost,port);

External configuration file

Configuration files can also be read in multiple environments, just add VM parameters, also ensure that the parameter name and file name are consistent.

-Dapplication.properties=/xx/application.properties
-Dkafka.properties=/xx/kakfa.properties
-Dredis.properties=/xx/redis.properties

Custom interceptor

Implement top.crossoverjie.cicada.example.intercept.CicadaInterceptor interface.

@Interceptor(value = "executeTimeInterceptor")
public class ExecuteTimeInterceptor implements CicadaInterceptor {

    private static final Logger LOGGER = LoggerBuilder.getLogger(ExecuteTimeInterceptor.class);

    private Long start;

    private Long end;

    @Override
    public boolean before(Param param) {
        start = System.currentTimeMillis();
        return true;
    }

    @Override
    public void after(Param param) {
        end = System.currentTimeMillis();

        LOGGER.info("cast [{}] times", end - start);
    }
}

Custom exception handle

You can define global exception handle,like this:

@CicadaBean
public class ExceptionHandle implements GlobalHandelException {
    private final static Logger LOGGER = LoggerBuilder.getLogger(ExceptionHandle.class);

    @Override
    public void resolveException(CicadaContext context, Exception e) {
        LOGGER.error("Exception", e);
        WorkRes workRes = new WorkRes();
        workRes.setCode("500");
        workRes.setMessage(e.getClass().getName());
        context.json(workRes);
    }
}

Performance Test

Test Conditions: 100 threads and 100 connections ;1G RAM/4 CPU

Nearly 10W requests per second.

ChangeLog

v2.0.2

  • fix #40
  • add global handle exception interface.
  • get bean by class type.

v2.0.1

  • Logo.
  • Cookie Support.
  • Beautify the log.

v2.0.0

  • Fixed #12 #22 #28
  • Flexible routing ways.
  • Pluggable IOC beanFactory.

v1.0.3

  • Fixed #9
  • Fixed #8,Multiple response ways.
  • Refactoring core code and add Cicada Context.
  • Elegant closing service.

v1.0.2

  • Fixed #6
  • Customize the configuration file.
  • Using flexible.
  • Refactor the code.

Contact author

crossoverJie#gmail.com

qrcode_for_gh_3a954a025f10_258.jpg

Special thanks

cicada's People

Contributors

crossoverjie avatar dependabot[bot] avatar windbelike avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cicada's Issues

并发问题

不打开keepalive的情况下,像http这种短连接协议,很大程度上是没有用到nio的特性的。也就是说,io线程数决定并发数,在netty中也就是work线程,NioEventLoopGroup默认的线程是cpu*2,对于HTTP服务器来说是远远不够的。将这个值做成用户可配置的是否会更好?

Got a null NullPointerException when APP starting!

Describe the bug
if i set my project structure like this:

   ├── src/main/java
   ├── ├──MainStart.java               //app start

i didn't set the package path, then,got this error:

ThreadLocalHolder.setLocalTime(System.currentTimeMillis());
 AppConfig.getInstance().setRootPackageName(clazz.getPackage().getName());

throw java.lang.NullPointerException.

Expected behavior
I think there should be a default value here instead of throwing an exception.

Boss线程数好像设置有误

Boss线程数好像设置有误
private static final int BOSS_SIZE = Runtime.getRuntime().availableProcessors() * 2; private static EventLoopGroup boss = new NioEventLoopGroup(BOSS_SIZE);

Netty 的服务器端的 acceptor 阶段, 没有使用到多线程,
服务器端的 ServerSocketChannel 只绑定到了 bossGroup 中的一个线程, 在调用 Java NIO 的 Selector.select 处理客户端的连接请求时, 实际上是在一个线程中的, 所以对只有一个服务的应用来说, bossGroup 设置多个线程是没有什么作用的

Custom configuration

Describe the solution you'd like

  • Customize the configuration file.
  • Using flexible.
  • Refactor the code.

一个Action类只能实现一个路由?

Is your feature request related to a problem? Please describe.
Action类是通过实现WorkAction接口的execute()方法来实现路由的,这样一个类只能实现一个路由,实际使用起来未必会有点麻烦吧

Describe the solution you'd like
如果能够像spring mvc那样一个controller下可以有多个RequestMapping就比较好吧。

Describe alternatives you've considered
Action类不用实现WorkAction接口,直接扫描所有被@CicadaAction注解的类,然后再反射获取这个类中被@CicadaExcute注解的方法(我看到有@CicadaExcute这个注解,但没有被使用,是否是准备用于这方面的?),再反射调用这个方法。

不过这样的话就是比较难约束用户对于被@CicadaExcute注解的方法传入的参数,我想到有两种方式约束:

  • 约束必须传入且只能传入CicadaContextParam,否则就抛出异常
  • 可以任意传入参数,如果检查到有CicadaContextParam就在invoke的时候传进去,如果没有就不传,如果有其他参数就传null值。

Interceptor加载优化

Describe the bug
在加载Interceptor时候的优化;

To Reproduce
ClassScanner类中的getCicadaInterceptor方法,原实现如下:

for (Class<?> cls : clsList) {

    if (cls.getAnnotation(Interceptor.class) == null) {
        continue;
    }

    Annotation[] annotations = cls.getAnnotations();
    for (Annotation annotation : annotations) {
        if (!(annotation instanceof Interceptor)) {
            continue;
        }
        Interceptor interceptor = (Interceptor) annotation;
        interceptorMap.put(interceptor.order(), cls);
    }
}

建议改为:

for (Class<?> cls : clsList) {
    Annotation annotation = cls.getAnnotation(Interceptor.class);
    if (annotation == null) {
        continue;
    }

    Interceptor interceptor = (Interceptor) annotation;
    interceptorMap.put(interceptor.order(), cls);
}

Expected behavior
只需要执行一次cls.getAnnotation,没有必要取第二次遍历判断。

异步任务

HttpDispatcher是阻塞式的,WorkAction中如果发起异步任务的话会有大麻烦。

关于netty的一些建议

1.建议使用SimpleChannelInboundHandler取代ChannelInboundHandlerAdapter,避免请求消息体类型判断,方便后续扩展自定义的第三方的http请求
2.因为请求是无状态的,建议使用单例的模式,避免请求量大的时候创建过多的handler去处理请求消息
例子:
`public class RequestHandlerHandler extends SimpleChannelInboundHandler {
public static final IMHandler INSTANCE = new IMHandler();

private Map<Byte, SimpleChannelInboundHandler<? extends Packet>> handlerMap;

private IMHandler() {
    handlerMap = new HashMap<>();
    handlerMap.put(MESSAGE_REQUEST, MessageRequestHandler.INSTANCE);
    handlerMap.put(LOGIN_REQUEST, LoginRequestHandler.INSTANCE);
}

@Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
    handlerMap.get(packet.getCommand()).channelRead(ctx, packet);
}

}`

ClassScanner$getCustomRouteBean获取IOC容器过程复杂

这个获取IOC容器是不是有点复杂了

  /**
     * get custom route bean
     * @return
     * @throws Exception
     */
    public static Class<?> getCustomRouteBean() throws Exception {
        List<Class<?>> classList = new ArrayList<>();

        
        Set<Class<?>> classes = getCustomRouteBeanClasses(BASE_PACKAGE) ;
        for (Class<?> aClass : classes) {

            if (aClass.getInterfaces().length == 0){
                continue;
            }
            if (aClass.getInterfaces()[0] != CicadaBeanFactory.class){
                continue;
            }
            classList.add(aClass) ;
        }

        if (classList.size() > 2){
            throw new CicadaException(StatusEnum.DUPLICATE_IOC);
        }

        if (classList.size() == 2){
            Iterator<Class<?>> iterator = classList.iterator();
            while (iterator.hasNext()){
                if (iterator.next()== CicadaDefaultBean.class){
                    iterator.remove();
                }
            }
        }

        return classList.get(0);
    }

使用时也仅仅:

public void init(String packageName) throws Exception {
        Map<String, Class<?>> cicadaAction = ClassScanner.getCicadaAction(packageName);

        Class<?> bean = ClassScanner.getCustomRouteBean();
        cicadaBeanFactory = (CicadaBeanFactory) bean.newInstance() ;

        for (Map.Entry<String, Class<?>> classEntry : cicadaAction.entrySet()) {
            Object instance = classEntry.getValue().newInstance();
            cicadaBeanFactory.register(instance) ;
        }
    }

建议:

  • getCustomRouteBean 逻辑不用这么复杂,直接返回CicadaIoc.class

  • 命名也不应该是getCustomRouteBean ,而应该是getIocFactory

读取配置文件的操作可否从action的execute里面抽离

读取配置文件的操作能不能从action里面抽离,action里面只写业务逻辑,读取配置文件的操作在整个框架启动的时候实现。

搞个框架的生命周期啥的,可能实现起来相当困难,大佬可以酌情忽略

拦截器问题

以下拦截器接口:

public interface CicadaInterceptor {
    /**
     * before
     * @param param
     */
    void before(Param param) ;
    /**
     * after
     * @param param
     */
    void after(Param param) ;
}

如果要在拦截器中驳回请求,
这个接口如何支持,或者有相关的适配器?
或者将接口改成:

public interface CicadaInterceptor {
    /**
     * before
     * @param param
     */
    boolean before(Param param) ;
    /**
     * after
     * @param param
     */
    void after(Param param) ;
}

会不会更好?

而且感觉拦截器只接受Param参数,应该是不够用的。

初始化方法好像有点问题...

CicadaSetting.java类中setting方法中
CicadaBeanManager.getInstance().initBean(rootPath);
这里的rootPath应该是packageName吧?(通过clazz对象获取)

Chain of responsibility

Is your feature request related to a problem? Please describe.
目前的拦截器只能处理一个拦截任务。

Describe the solution you'd like
利用责任链模式支持灵活拓展

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.