{"id":15044424,"url":"https://github.com/twogoods/hellorpc","last_synced_at":"2025-04-10T00:42:34.492Z","repository":{"id":57723751,"uuid":"81814856","full_name":"twogoods/HelloRpc","owner":"twogoods","description":"rpc","archived":false,"fork":false,"pushed_at":"2018-09-12T08:08:40.000Z","size":177,"stargazers_count":6,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-10T00:42:29.451Z","etag":null,"topics":["breaker","consul-zookeeper","java8","rpc","spring-boot"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twogoods.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-13T10:50:47.000Z","updated_at":"2018-09-12T08:08:42.000Z","dependencies_parsed_at":"2022-08-28T08:22:36.399Z","dependency_job_id":null,"html_url":"https://github.com/twogoods/HelloRpc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FHelloRpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FHelloRpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FHelloRpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FHelloRpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twogoods","download_url":"https://codeload.github.com/twogoods/HelloRpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137998,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["breaker","consul-zookeeper","java8","rpc","spring-boot"],"created_at":"2024-09-24T20:50:34.290Z","updated_at":"2025-04-10T00:42:34.460Z","avatar_url":"https://github.com/twogoods.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HelloRPC\n算是自己**从零编写一个RPC**的一个实践。实践下来，RPC主要有这么几个部分:通信包括连接建立消息传输，编解码要传输的内容，序列化和反序列化，最后是客户端和服务端的实现，客户调用一个服务的代理实现，发起对服务端的请求。使用现有的一些类库可以快速的完成一个RPC框架，如`netty`完成tcp传输,`protostuff`、`kryo`提供高效的序列化，至于服务的代理我们可以用jdk自身的`Proxy`或`Cglib`来完成。\n# 现有功能\n- 基本的客户端、服务端请求调用\n- SpringBoot集成\n- 基于Consul和Zookeeper的服务注册发现\n- 断路器\n\n### 简单调用示例\n引入依赖：\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.twogoods\u003c/groupId\u003e\n  \u003cartifactId\u003ehellorpc-core\u003c/artifactId\u003e\n  \u003cversion\u003e0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n定义服务和服务实现\n\n```\npublic interface EchoService {\n    String hello(String s);\n}\n\npublic class EchoServiceImpl implements EchoService {\n    @Override\n    public String hello(String s) {\n        return s;\n    }\n}\n```\nServer:\n\n```\n//配置:端口号,最大的传输容量(单位M),服务响应的Handle\nServer server = new Server.Builder()\n             .port(9001)\n             .serviceName(\"test\")//服务名，全局唯一\n             .serviceId(\"dev\")//服务id，区分不同环境，建议 dev,test,prod\n             .maxCapacity(3)\n             .build();\n//发布服务\nserver.addService(EchoService.class, new EchoServiceImpl())\n       .addService(TestService.class, new TestServiceImpl());\nserver.start();\n```\nClient：\n\n```\nClientProperty client = new ClientProperty();\nclient.serviceName(\"test\")\n        .provider(\"127.0.0.1:9001\")\n        .interfaces(\"com.tg.rpc.example.service.EchoService\");\nClient client = new Client.Builder().maxCapacity(3)\n        .requestTimeoutMillis(3500)\n        .connectionMaxTotal(10)\n        .connectionMaxIdle(6)\n        .client(client)\n        .build();\nDefaultClientInterceptor interceptor = new DefaultClientInterceptor(client);\nClientProxy clientProxy = new JdkClientProxy(interceptor);\nEchoService echoService = clientProxy.getProxy(EchoService.class);\nSystem.out.println(echoService.echo(\"twogoods\"));\n```\n客户端可以接入多组服务\n\n```\nClientProperty clientA = new ClientProperty();\nclientA.serviceName(\"A\")\n        .provider(\"127.0.0.1:9001\")\n        .interfaces(\"com.tg.rpc.xxx.EchoService\");\n       \nClientProperty clientB = new ClientProperty();\nclientB.serviceName(\"B\")\n        .provider(\"127.0.0.1:8090\").provider(\"127.0.0.1:8080\")//同一服务的多个实例\n        .interfaces(\"com.tg.rpc.xxx.TestAService\")//一个服务下的多个接口\n        .interfaces(\"com.tg.rpc.xxx.TestBService\");\n        \nClient client = new Client.Builder().maxCapacity(3)\n        .requestTimeoutMillis(3500)\n        .connectionMaxTotal(10)\n        .connectionMaxIdle(6)\n        .enableBreaker()\n        .client(clientA)//加入两组服务\n        .client(clientB)\n        .build();\n```\n---\n\n### 服务注册与发现\n支持Consul和Zookeeper:\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.twogoods\u003c/groupId\u003e\n  \u003cartifactId\u003ehellorpc-consul\u003c/artifactId\u003e\n  \u003cversion\u003e0.1\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.twogoods\u003c/groupId\u003e\n  \u003cartifactId\u003ehellorpc-zookeeper\u003c/artifactId\u003e\n  \u003cversion\u003e0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n只需在server和client里增加相应的组件即可\n\n```\n//server端使用服务注册组件\nServiceRegistry serviceRegistry = ConsulCompentFactory.getRegistry(\"localhost\", 8500);\nServer server = new Server.Builder()\n        .port(9001)\n        .serviceName(\"testService\")\n        .serviceId(\"dev\")\n        .maxCapacity(3)\n        .serviceRegistry(serviceRegistry)\n        .build();\n        \n//client端使用服务发现组件     \nServiceDiscovery serviceDiscovery = ConsulCompentFactory.getDiscovery(\"localhost\", 8500);\nClient client = new Client.Builder()\n        .serviceDiscovery(serviceDiscovery)\n        .connectionMinIdle(1)\n        .maxCapacity(3)\n        .client(clientA)\n        .build();\n```\n以上是基于consul的配置，使用zookeeper只需更换使用不同的组件即可\n\n ```\nServiceDiscovery serviceDiscovery = ZookeeperCompentFactory.getDiscovery(\"localhost\",2181);\nServiceRegistry serviceRegistry = ZookeeperCompentFactory.getRegistry(\"localhost\", 2181);\n ```\n---\n### 整合SpringBoot\n#### 服务端配置\n引入对Spring的支持\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.twogoods\u003c/groupId\u003e\n  \u003cartifactId\u003ehellorpc-spring\u003c/artifactId\u003e\n  \u003cversion\u003e0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n使用`@RpcService`注解\n\n```\npublic interface EchoService {\n    String echo(String s);\n}\n\n@RpcService\npublic class EchoServiceImpl implements EchoService {\n    @Override\n    public String echo(String s) {\n        return s;\n    }\n}\n```\n`application.yml`配置\n\n``` \ntgrpc:\n    server:\n        port: 9001\n        serviceName: testService\n        serviceId: dev\n        registery: consul\n        consulHost: 127.0.0.1\n        consulPort: 8500\n```\n启动server:\n\n```\n@SpringBootApplication\n@EnableAutoConfiguration\n@EnableRpcServer //启用Server\n@ComponentScan()\npublic class ServerApplication {\n    public static void main(String[] args) {\n        ApplicationContext applicationContext = SpringApplication.run(ServerApplication.class);\n    }\n}\n```\n#### client配置\n调用方使用`@RpcReferer`注解.\n\n```\n@Component\npublic class ServiceCall {\n\n    @RpcReferer\n    private EchoService echoService;\n\n    public String echo(String s) {\n        return echoService.echo(s);\n    }\n}\n```\n`application.yml`配置\n\n```\ntgrpc:\n    client:\n        registery: consul\n        consulHost: 127.0.0.1\n        consulPort: 8500\n        maxCapacity: 3\n        maxTotal: 3\n        maxIdle: 3\n        minIdle: 0\n        borrowMaxWaitMillis: 5000\n        clients:\n            - serviceName: testService\n              requestTimeoutMillis: 2000\n              interfaces:\n                    - com.tg.rpc.example.service.EchoService\n                    - com.tg.rpc.example.service.TestService\n              providerList: 127.0.0.1:8080\n```\nClient发起调用\n\n```\n@SpringBootApplication\n@EnableAutoConfiguration\n@EnableRpcClient //启用Client\n@ComponentScan(basePackages = {\"com.tg.rpc.springsupport.bean.client\"})\npublic class ClientApplication {\n    public static void main(String[] args) {\n        ApplicationContext applicationContext = SpringApplication.run(ClientApplication.class);\n        ServiceCall serviceCall = (ServiceCall) applicationContext.getBean(\"serviceCall\");\n        System.out.println(\"echo return :\" + serviceCall.echo(\"TgRPC\"));\n    }\n}\n```\n\n### 断路器\n熔断发生在客户端，默认的熔断策略：30秒内请求数大于60并且错误率超过50%触发熔断，熔断后每20秒过一个请求测试后端服务是否正常，调用成功则关闭熔断。\n熔断期间的客户端不发送实际请求到服务端，如果你的服务接口使用了Java8接口里的默认方法，那么执行此默认方法，否则抛出`RequestRejectedException`异常，因此建议在定义接口的时候使用默认方法：\n\n```\npublic interface TestServiceIface {\n    default String echo(String str) {\n        return str;\n    }\n}\n```\n熔断组件真正执行方法的时候有两种方式\n#### 1、利用反射执行\n```\nBreakerProperty breakerProperty = new BreakerProperty().addClass(\"com.tg.rpc.breaker.TestServiceIface\");//要监控的类\nBreaker breaker = new Breaker(breakerProperty);\nMethod metricsMethod = TestServiceIface.class.getMethod(\"echo\", String.class);//熔断发生在方法级别\nObject obj = new TestServiceIfaceImpl();\nReflectTask task = new ReflectTask(metricsMethod, new Object[]{\"twogoods\"}, obj, 100l);\nObject res = breaker.execute(task);\nSystem.out.println(res);\n```\n#### 2、函数式思想传入具体行为\n```\nBreakerProperty breakerProperty = new BreakerProperty().addClass(\"com.tg.rpc.breaker.TestServiceIface\");\nBreaker breaker = new Breaker(breakerProperty);\nMethod metricsMethod = TestServiceIface.class.getMethod(\"echo\", String.class);\nTestServiceIface testServiceIface = new TestServiceIfaceImpl();\nTaskExecuteHook\u003cString, String\u003e taskExecuteHook = testServiceIface::echo;//真正的执行行为\nObject res = breaker.execute(new HookTask\u003c\u003e(taskExecuteHook, \"twogoods\", () -\u003e {\n    return new Object[]{\"twogoods\"};\n}, metricsMethod));\nSystem.out.println(res);\n```\n`Functional`的方式现在只支持只有一个参数的方法，它不好灵活的支持多参数，如JDK里的`Consumer`和`BiConsumer`，多一个参数就需要多定义一个接口。\nRPC框架提供了对熔断的支持，默认是关闭熔断的，开启只需修改配置\n\n```\nClient client = new Client.Builder()\n        .requestTimeoutMillis(3500)\n        .enableBreaker()//开启断路器\n        .client(client)\n        .build();\n```\n或者在SpringBoot的配置文件里增加\n\n```\ntgrpc:\n    client:\n        breakerable: true\n```\n---\n更多使用请参考[example](https://github.com/twogoods/HelloRpc/tree/master/example)模块\n### TODO\n- http调用的支持、异步编程、超时与重试、监控、限流(server)、降级开关(server)\n- netty优化\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwogoods%2Fhellorpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwogoods%2Fhellorpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwogoods%2Fhellorpc/lists"}