AI 年度盘点与2025发展趋势展望,50+案例解析亮相AICon 了解详情
写点什么

Micronaut 教程(二):分布式跟踪、JWT 安全和 AWS Lambda 部署

  • 2018-12-15
  • 本文字数:10489 字

    阅读完需:约 34 分钟

Micronaut教程(二):分布式跟踪、JWT安全和AWS Lambda部署

关键要点

  • Micronaut 提供了与 Zipkin 和 Jaeger 等多种分布式跟踪解决方案的无缝集成。

  • 框架提供了几种“开箱即用”的安全解决方案,例如基于 JWT 的认证。

  • Micronaut 提供了“令牌传播”之类的功能,用以简化微服务之间的安全通信。

  • 因为内存占用少,Micronaut 能够运行在功能即服务(FaaS)无服务器环境中。


在本系列的第一篇文章中,我们使用基于 JVM 的Micronaut框架开发并部署了三个微服务。在第二篇文章中,我们将为应用程序添加几个功能:分布式跟踪、JWT 安全性和无服务器功能。此外,我们也将介绍 Micronaut 提供的用户输入验证功能。

分布式跟踪

将系统分解为更小、更细粒度的微服务可以带来多种好处,但也会给生产环境的监控系统增加复杂性。


你应该假设你的网络将会受到恶意实体的骚扰,它们时刻准备着随心所欲地释放它们的愤怒。

——Sam Newman,《构建微服务》


Micronaut 与 Jaeger 和 Zipkin 原生集成——它们都是顶级的开源分布式跟踪解决方案。


Zipkin 是一种分布式跟踪系统,用于收集时序数据,这些数据可用于解决微服务架构中的延迟问题。它负责收集和查找这些数据。


启动 Zipkin 的简单方法是通过 Docker:


$ docker run -d -p 9411:9411 openzipkin/zipkin
复制代码


这个应用程序由三个微服务组成,也就是我们在第一篇文章中开发的三个微服务(gateway、inventory、books)。


我们需要对这三个微服务做出修改。


修改 build.gradle,加入跟踪依赖项:


build.gradle
compile "io.micronaut:micronaut-tracing"
复制代码


将以下依赖项添加到 build.gradle 中,这样就可以将跟踪数据发送到 Zipkin。


build.gradle
runtime 'io.zipkin.brave:brave-instrumentation-http' runtime 'io.zipkin.reporter2:zipkin-reporter' compile 'io.opentracing.brave:brave-opentracing'
复制代码


配置跟踪选项:


src/main/resources/application.yml
tracing: zipkin: http: url: http://localhost:9411 enabled: true sampler: probability: 1
复制代码


设置 tracing.zipkin.sample.probability = 1,意思是我们要跟踪所有的请求。在生产环境中,你可能希望设置较低的百分比。


在测试时禁用跟踪:


src/test/resources/application-test.yml
tracing: zipkin: enabled: false
复制代码


只需要很少的配置更改,就可以将分布式跟踪集成到 Micronaut 中。

运行应用程序

现在让我们运行应用程序,看看分布式跟踪集成是否能够正常运行。在第一篇文章中,我们集成了 Consul,用于实现服务发现。因此,在启动微服务之前需要先启动 Zipkin 和 Consul。在微服务启动好以后,它们将在 Consul 服务发现中进行注册。当我们发出请求时,它们会向 Zipkin 发送数据。


Gradle 提供了一个 flag(-parallel)用来启动微服务:


./gradlew -parallel run
复制代码


你可以通过 cURL 命令向三个微服务发起请求:


$ curl http://localhost:8080/api/books[{"isbn":"1680502395","name":"Release It!","stock":3},{"isbn":"1491950358","name":"Building Microservices","stock":2}]
复制代码


然后,你可以通过http://localhost:9411来访问Zipkin UI。

JWT 安全性

Micronaut 提供了多种开箱即用的安全选项,你可以使用基本的身份验证、基于会话的身份验证、JWT 身份验证、Ldap 身份验证,等等。JSON Web Token(JWT)是一种开放的行业标准(RFC 7519)用于在参与方之间声明安全。


Micronaut 提供了开箱即用的用于生成、签名、加密和验证 JWT 令牌的功能。


我们将把 JWT 身份验证集成到我们的应用程序中。

修改 gateway 微服务,让它支持 JWT

gateway 微服务将负责生成和传播 JWT 令牌。


修改 build.gradle,为每个微服务(gateway、inventory 和 books)添加 micronaut-security-jwt 依赖项:


gateway/build.gradle
compile "io.micronaut:micronaut-security-jwt" annotationProcessor "io.micronaut:micronaut-security"
复制代码


修改 application.yml:


gateway/src/main/resources/application.ymlmicronaut:    application:        name: gateway    server:        port: 8080    security:        enabled: true        endpoints:            login:                enabled: true            oauth:                enabled: true        token:            jwt:                enabled: true               signatures:                   secret:                       generator:                           secret: pleaseChangeThisSecretForANewOne            writer:                header:                   enabled: true            propagation:                enabled: true                service-id-regex: "books|inventory"
复制代码


我们做了几个重要的配置变更:


  • micronaut.security.enable = true 启用了安全,并默认为每个端点提供安全保护。

  • micronaut.security.endpoints.login.enable = true 启用了/login 端点,我们将用它进行身份验证。

  • micronaut.security.endpoints.oauth.enable = true 启用了/oauth/access_tokenendpoint 端点,在令牌过期时,我们可以使用它来获取新的 JWT 访问令牌。

  • micronaut.security.jwt.enable = true 启用了 JWT 功能。

  • 我们让应用程序启用签名的 JWT。更多的签名和加密选项,请参阅 JWT 令牌生成文档。

  • micronaut.security.token.propagation.enabled = true 表示启用了令牌传播。这是一种在微服务架构中简化 JWT 或其他令牌安全机制的功能。

  • micronaut.security.writer.header.enabled = ture 启用了一个令牌写入器,它将为开发人员在 HTTP 标头中写入 JWT 令牌。

  • micronaut.security.token.propagation.service-id-regex 设置了一个正则表达式,用于匹配需要进行令牌传播的服务。我们匹配了应用程序中的其他两个服务。


你可以使用 @Secured 注解来配置 Controller 或 Controller Action 级别的访问。


使用 @Secured(“isAuthenticated()”)注解 BookController.java,只允许经过身份验证的用户访问。同时记得使用 @Secured(“isAuthenticated()”)注解 inventory 和 books 微服务的 BookController 类。


/login 端点被调用时,会尝试通过任何可用的 AuthenticationProvider 对用户进行身份验证。为了简单起见,我们将允许两个用户访问,他们是福尔摩斯和华生。创建 SampleAuthenticationProvider:


gateway/src/main/java/example/micronaut/SampleAuthenticationProvider.java
package example.micronaut;
import io.micronaut.context.annotation.Requires; import io.micronaut.context.env.Environment; import io.micronaut.security.authentication.AuthenticationFailed; import io.micronaut.security.authentication.AuthenticationProvider; import io.micronaut.security.authentication.AuthenticationRequest; import io.micronaut.security.authentication.AuthenticationResponse; import io.micronaut.security.authentication.UserDetails; import io.reactivex.Flowable; import org.reactivestreams.Publisher;
import javax.inject.Singleton; import java.util.ArrayList; import java.util.Arrays;
@Requires(notEnv = Environment.TEST) @Singleton public class SampleAuthenticationProvider implements AuthenticationProvider {
@Override public Publisher<AuthenticationResponse> authenticate(AuthenticationRequest authenticationRequest) { if (authenticationRequest.getIdentity() == null) { return Flowable.just(new AuthenticationFailed()); } if (authenticationRequest.getSecret() == null) { return Flowable.just(new AuthenticationFailed()); } if (Arrays.asList("sherlock", "watson").contains(authenticationRequest.getIdentity().toString()) && authenticationRequest.getSecret().equals("elementary")) { return Flowable.just(new UserDetails(authenticationRequest.getIdentity().toString(), new ArrayList<>())); } return Flowable.just(new AuthenticationFailed()); } }
复制代码

修改 inventory 和 books,让它们支持 JWT

对于 inventory 和 books,除了添加 micronaut-security-jwt 依赖项并使用 @Secured 注解控制器之外,我们还需要修改 application.yml,以便能够验证在 gateway 中生成和签名的 JWT 令牌。


修改 application.yml:


inventory/src/main/resources/application.yml
micronaut: application: name: inventory server: port: 8081 security: enabled: true token: jwt: enabled: true signatures: secret: validation: secret: pleaseChangeThisSecretForANewOne
复制代码


请注意,我们使用与 gateway 配置中相同的秘钥,这样就可以验证由 gateway 微服务签名的 JWT 令牌。

运行安全的应用程序

在启动了 Zipkin 和 Consul 之后,你就可以同时启动这三个微服务。Gradle 提供了一个方便的 flag(-parallel):


./gradlew -parallel run
复制代码


你可以运行 cURL 命令,然后会收到 401 错误,表示未授权!


$ curl -I http://localhost:8080/api/books HTTP/1.1 401 UnauthorizedDate: Mon, 1 Oct 2018 18:44:54 GMT transfer-encoding: chunked connection: close
复制代码


我们需要先登录,并获得一个有效的 JWT 访问令牌:


$ curl -X "POST" "http://localhost:8080/login" \-H 'Content-Type: application/json; charset=utf-8' \-d $'{ "username": "sherlock", "password": "password" }' {"username":"sherlock","access_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWI iOiJzaGVybG9jayIsIm5iZiI6MTUzODQxMjQwOSwicm9sZXMiOltdLCJpc3MiOiJnYX Rld2F5IiwiZXhwIjoxNTM4NDE2MDA5LCJpYXQiOjE1Mzg0MTI0MDl9.1W4CXbN1bJgM CQlCDKJtm7zHWzyZeIr1rHpTuDy6h0","refresh_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ zaGVybG9jayIsIm5iZiI6MTUzODQxMjQwOSwicm9sZXMiOltdLCJpc3MiOiJnYXRld2 F5IiwiaWF0IjoxNTM4NDEyNDA5fQ.l72msZKwHmYeLs7T0vKtRxu7_DZr62rPCILNmC 7UEZ4","expires_in":3600,"token_type":"Bearer"}
复制代码


Micronaut 提供了开箱即用的 RFC 6750 Bearer Token 规范支持。我们可以使用从/login 响应标头中获得的 JWT 来调用/api/books 端点。


curl "http://localhost:8080/api/books" \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzaGVybG9jayIsIm5iZiI6MTUzODQxMjQwOS wicm9sZXMiOltdLCJpc3MiOiJnYXRld2F5IiwiZXhwIjoxNTM4NDE2MDA5LCJpYXQiO jE1Mzg0MTI0MDl9.1W4CXbN1bJgMCQlCDKJtm7zHWz-yZeIr1rHpTuDy6h0'[{"isbn":"1680502395","name":"Release It!","stock":3}, {"isbn":"1491950358","name":"Building Microservices","stock":2}]
复制代码

Serverless

我们将添加一个部署到 AWS Lambda 的功能来验证 books 的 ISBN。


mn create-function example.micronaut.isbn-validator
复制代码


注意:我们使用了 Micronaut CLI 提供的 create-function 命令。

验证

我们将创建一个单例来处理 ISBN 10 验证。


创建一个封装操作的接口:


package example.micronaut;
import javax.validation.constraints.Pattern;
public interface IsbnValidator { boolean isValid(@Pattern(regexp = "\\d{10}") String isbn);}
复制代码


Micronaut 的验证基于标准框架JSR 380,也称为 Bean Validation 2.0。


Hibernate Validator是这个标准的参考实现。


将以下代码段添加到 build.gradle 中:


isbn-validator/build.gradle
compile "io.micronaut.configuration:micronaut-hibernatevalidator"
复制代码


创建一个实现了 IsbnValidator 的单例。


isbn-validator/src/main/java/example/micronaut/DefaultIsbnValidator.java
package example.micronaut;
import io.micronaut.validation.Validated; import javax.inject.Singleton; import javax.validation.constraints.Pattern;
@Singleton @Validated public class DefaultIsbnValidator implements IsbnValidator {
/** * must range from 0 to 10 (the symbol X is used for 10), and must be such that the sum of all the ten digits, each multiplied by its (integer) weight, descending from 10 to 1, is a multiple of 11. * @param isbn 10 Digit ISBN * @return whether the ISBN is valid or not. */ @Override public boolean isValid(@Pattern(regexp = "\\d{10}") String isbn) { char[] digits = isbn.toCharArray(); int accumulator = 0; int multiplier = 10; for (int i = 0; i < digits.length; i++) { char c = digits[i]; accumulator += Character.getNumericValue(c) * multiplier; multiplier--; } return (accumulator % 11 == 0); }}
复制代码


与之前的代码清单一样,你要为需要验证的类添加 @Validated 注解。


创建单元测试:


isbn-validator/src/test/java/example/micronaut/IsbnValidatorTest.java
package example.micronaut;
import io.micronaut.context.ApplicationContext; import io.micronaut.context.DefaultApplicationContext; import io.micronaut.context.env.Environment; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException;import javax.validation.ConstraintViolationException;import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue;
public class IsbnValidatorTest {
private static ApplicationContext applicationContext;
@BeforeClass public static void setupContext() { applicationContext = new DefaultApplicationContext(Environment.TEST).start(); } @AfterClass public static void stopContext() { if (applicationContext!=null) { applicationContext.stop(); } }
@Rule public ExpectedException thrown = ExpectedException.none();
@Test public void testTenDigitValidation() { thrown.expect(ConstraintViolationException.class); IsbnValidator isbnValidator = applicationContext.getBean(IsbnValidator.class); isbnValidator.isValid("01234567891"); }
@Test public void testControlDigitValidationWorks() { IsbnValidator isbnValidator = applicationContext.getBean(IsbnValidator.class); assertTrue(isbnValidator.isValid("1491950358")); assertTrue(isbnValidator.isValid("1680502395")); assertFalse(isbnValidator.isValid("0000502395")); }}
复制代码


如果我们尝试使用十一位数字字符串调用该方法,就会抛出 javax.validation.ConstraintViolationException。

函数的输入和输出

这个函数将接受单个参数(ValidationRequest,它是一个封装了 ISBN 的 POJO)。


isbn-validator/src/main/java/example/micronaut/IsbnValidationRequest.java
package example.micronaut;
public class IsbnValidationRequest { private String isbn; public IsbnValidationRequest() { } public IsbnValidationRequest(String isbn) { this.isbn = isbn; } public String getIsbn() { return isbn; }
public void setIsbn(String isbn) { this.isbn = isbn; }}
复制代码


并返回单个结果(ValidationResponse,一个封装了 ISBN 和一个指示 ISBN 是否有效的布尔值的 POJO)。


isbn-validator/src/main/java/example/micronaut/IsbnValidationResponse.java
package example.micronaut;
public class IsbnValidationResponse { private String isbn; private Boolean valid; public IsbnValidationResponse() { }
public IsbnValidationResponse(String isbn, boolean valid) { this.isbn = isbn; this.valid = valid; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public Boolean getValid() { return valid; } public void setValid(Boolean valid) { this.valid = valid; }}
复制代码

函数测试

当我们运行 create-function 命令时,Micronaut 会在 src/main/java/example/micronaut 目录创建一个 IsbnValidatorFunction 类。修改它,让它实现 java.util.Function 接口。


isbn-validator/src/main/java/example/micronaut/IsbnValidatorFunction.java
package example.micronaut;
import io.micronaut.function.FunctionBean;import java.util.function.Function; import javax.validation.ConstraintViolationException;
@FunctionBean("isbn-validator") public class IsbnValidatorFunction implements Function<IsbnValidationRequest, IsbnValidationResponse> {
private final IsbnValidator isbnValidator;
public IsbnValidatorFunction(IsbnValidator isbnValidator) { this.isbnValidator = isbnValidator; }
@Override public IsbnValidationResponse apply(IsbnValidationRequest req) { try { return new IsbnValidationResponse(req.getIsbn(), isbnValidator.isValid(req.getIsbn())); } catch(ConstraintViolationException e) { return new IsbnValidationResponse(req.getIsbn(),false); } }}
复制代码


上面的代码做了几件事:


  • 使用 @FunctionBean 注解了一个返回函数的方法。

  • 你可以在函数中使用 Micronaut 的编译时依赖注入。我们通过构造函数注入了 IsbnValidator。


函数也可以作为 Micronaut 应用程序上下文的一部分运行,这样方便进行测试。应用程序已经在类路径中包含了用于测试的 function-web 和 HTTP 服务器依赖项:


isbn-validator/build.gradle
testRuntime "io.micronaut:micronaut-http-server-netty" testRuntime "io.micronaut:micronaut-function-web"
复制代码


要在测试中调用函数,需要修改 IsbnValidatorClient.java


isbn-validator/src/test/java/example/micronaut/IsbnValidatorClient.java
package example.micronaut;
import io.micronaut.function.client.FunctionClient; import io.micronaut.http.annotation.Body; import io.reactivex.Single;import javax.inject.Named;
@FunctionClient public interface IsbnValidatorClient { @Named("isbn-validator") Single<IsbnValidationResponse> isValid(@Body IsbnValidationRequest isbn);}
复制代码


同时修改 IsbnValidatorFunctionTest.java。我们需要测试不同的场景(有效的 ISBN、无效的 ISBN、超过 10 位的 ISBN 和少于 10 位的 ISBN)。


isbn-validator/src/test/java/example/micronaut/IsbnValidatorFunctionTest.java
package example.micronaut;
import io.micronaut.context.ApplicationContext; import io.micronaut.runtime.server.EmbeddedServer; import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue;
public class IsbnValidatorFunctionTest {
@Test public void testFunction() { EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
IsbnValidatorClient client = server.getApplicationContext().getBean(IsbnValidatorClient.class);
assertTrue(client.isValid(new IsbnValidationRequest("1491950358")).blockingGet().getValid()); assertTrue(client.isValid(new IsbnValidationRequest("1680502395")).blockingGet().getValid()); assertFalse(client.isValid(new IsbnValidationRequest("0000502395")).blockingGet().getValid()); assertFalse(client.isValid(new IsbnValidationRequest("01234567891")).blockingGet().getValid()); assertFalse(client.isValid(new IsbnValidationRequest("012345678")).blockingGet().getValid()); server.close();}
}
复制代码

部署到 AWS Lambda

假设你拥有 Amazon Web Services(AWS)帐户,那么就可以转到 AWS Lambda 并创建一个新功能。


选择 Java 8 运行时。名称为 isbn-validator,并创建一个新的角色表单模板。角色名称为 lambda_basic_execution。



运行./gradlew shadowJar 生成一个 Jar 包。


shadowJar 是 Gradle ShadowJar 插件提供的一个任务。


$ du -h isbn-validator/build/libs/isbn-validator-0.1-all.jar 11M isbn-validator/build/libs/isbn-validator-0.1-all.jar
复制代码


上传 JAR,并指定 Handler。


io.micronaut.function.aws.MicronautRequestStreamHandler 
复制代码


我只分配了 256Mb 内存,超时时间为 25 秒。


从另一个微服务中调用函数

我们将在 gateway 微服务中使用这个 lambda。修改 gateway 微服务中的 build.gradle,添加 micronaut-function-client:


com.amazonaws:aws-java-sdk-lambda dependencies:
build.gradle
compile "io.micronaut:micronaut-function-client" runtime 'com.amazonaws:aws-java-sdk-lambda:1.11.285'
复制代码


修改 src/main/resources/application.yml:


src/main/resources/application.yml
aws: lambda: functions: vat: functionName: isbn-validator qualifer: isbn region: eu-west-3 # Paris Region
复制代码


创建一个接口:


src/main/java/example/micronaut/IsbnValidator.java
package example.micronaut;
import io.micronaut.http.annotation.Body;import io.reactivex.Single;
public interface IsbnValidator { Single<IsbnValidationResponse> validateIsbn(@Body IsbnValidationRequest req); }
复制代码


创建一个 @FunctionClient:


src/main/java/example/micronaut/FunctionIsbnValidator.java
package example.micronaut;
import io.micronaut.context.annotation.Requires; import io.micronaut.context.env.Environment; import io.micronaut.function.client.FunctionClient; import io.micronaut.http.annotation.Body; import io.reactivex.Single;import javax.inject.Named;
@FunctionClient @Requires(notEnv = Environment.TEST) public interface FunctionIsbnValidator extends IsbnValidator { @Override @Named("isbn-validator") Single<IsbnValidationResponse> validateIsbn(@Body IsbnValidationRequest req);}
复制代码


关于上面这些代码有几点值得注意:


  • FunctionClient 注解可以在接口上应用引入通知(introduction advice),这样接口定义的方法就会成为远程函数的调用者。

  • 使用函数名 isbn-validator,与 application.yml 定义的一样。


最后一步是修改 gateway 的 BookController,让它调用函数。


src/main/java/example/micronaut/BooksController.java
package example.micronaut;
import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.security.annotation.Secured; import io.reactivex.Flowable;
import java.util.List;
@Secured("isAuthenticated()")@Controller("/api") public class BooksController { private final BooksFetcher booksFetcher; private final InventoryFetcher inventoryFetcher; private final IsbnValidator isbnValidator; public BooksController(BooksFetcher booksFetcher, InventoryFetcher inventoryFetcher, IsbnValidator isbnValidator) { this.booksFetcher = booksFetcher; this.inventoryFetcher = inventoryFetcher; this.isbnValidator = isbnValidator; }
@Get("/books") Flowable<Book> findAll() { return booksFetcher.fetchBooks() .flatMapMaybe(b -> isbnValidator.validateIsbn(new IsbnValidationRequest(b.getIsbn())) .filter(IsbnValidationResponse::getValid) .map(isbnValidationResponse -> b) ) .flatMapMaybe(b -> inventoryFetcher.inventory(b.getIsbn()) .filter(stock -> stock > 0) .map(stock -> { b.setStock(stock); return b; }) ); }}
复制代码


我们通过构造函数注入了 IsbnValidator。调用远程函数对程序员来说是透明的。

结论

下面的图片说明了我们在这一系列文章中开发的应用程序:


  • 我们有三个微服务(一个 Java 服务、一个 Groovy 服务和一个 Kotlin 服务)。

  • 这些微服务使用 Consul 进行服务发现。

  • 这些微服务使用 Zipkin 作为分布式跟踪服务。

  • 我们添加了第四个微服务,一个部署到 AWS Lambda 的功能。

  • 微服务之间的通信是安全的。每个请求在 Authorization Http 标头中包含一个 JWT 令牌就可以通过网络。JWT 令牌通过内部请求自动传播。


关于 Micronaut 的更多内容,请访问官方网站

关于作者


Sergio del AmoCaballero 是一名手机应用程序(iOS、Android,后端由 Grails/Micronaut 驱动)开发者。自 2015 年起,Sergio del Amo 为 Groovy 生态系统和微服务维护着一个新闻源Groovy Calamari


查看英文原文Micronaut Tutorial: Part 2: Easy Distributed Tracing, JWT Security and AWS Lambda Deployment


2018-12-15 09:002067
用户头像

发布了 731 篇内容, 共 450.5 次阅读, 收获喜欢 2002 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

这份笔记太牛了!手把手教你从零开始搭建Spring Cloud Alibaba!

Java 架构 面试 程序人生 编程语言

不愧是阿里P8架构师总结的Java面试笔记,上线仅七天,Github标星55K

Java 程序员 架构 面试 后端

太牛了!同事凭借这份Java面试题1000道手册(pdf)入职了阿里,定级P7

Java spring 程序员 架构 编程语言

(实战篇)漫游语音识别技术—带你走进语音识别技术的世界

攻城先森

深度学习 音视频 nlp 语音识别

手把手带你做好团队管理|引航计划|管理

石云升

团队管理 管理 引航计划 技术专题合集

靠这份GitHub star过万的1121页图解算法成功杀进字节跳动

Java 架构 面试 程序人生 编程语言

模块六作业

Geek_fc100d

「架构实战营」

架构实战营 毕业总结和毕业设计项目

李东旭

「架构实战营」

面试进阶必备:“阿里爸爸”高并发秒杀架构设计笔记(全彩版)

Java 架构 面试 程序人生 编程语言

华为大佬的“百万级”MySQL笔记,基础+优化+架构一键搞定

Java 架构 面试 程序人生 编程语言

《Go 开发指南》-管理 Go 环境

看山

Effective-go 10月月更

这份阿里P8撰写的面试笔记就是逊了!才帮助十几个人圆了大厂梦

Java 架构 面试 程序人生 编程语言

毕业设计项目

燕燕 yen yen

架构是训练营

引航计划|AI|优质合集手把手带你玩转AI

Nydia

AI 引航计划

Spark 系列教程(2)运行模式介绍

Se7en

Apache ShenYu源码阅读系列-基于ZooKeeper的数据同步

子夜2104

Java 网关 shenyu

2021年常见面试真题汇总,含了14个技术栈,已助我成功拿到腾讯offer!

Java 架构 架构师 java面试

架构1期模块九作业

五只羊

架构实战营

后端的另一种打开方式-路由还能这么玩~

Bob

微服务 后端 网络 服务 引航计划

7. 简单生成器函数,Pool 实现多进程程序,异常管理,浏览器版本帮助手册

梦想橡皮擦

10月月更

【LeetCode】字符串中的单词数Java题解

Albert

算法 LeetCode 10月月更

(mode4)千万级学生管理系统考试试卷存储方案

消失的子弹

架构 云原生

安全逆向分析实战

网络安全学海

Linux 网络安全 信息安全 WEB安全 漏洞分析

模块五作业

Geek_fc100d

「架构实战营」

金九银十一线大厂Java面试题大全(整理版)1000+面试题附答案详解,最全面详细!

Java 程序员 架构 面试 后端

微博评论系统架构设计

guangbao

让我薪资提升了个新高度!这份阿里核心教程:理解JVM垃圾回收器手册帮了我大忙!

Java 架构 面试 程序人生 编程语言

40K成功入职:六年开发终获小米Offer(附面经+面试题+答案详解)

Java spring 程序员

12道Zookeeper高频面试题,你顶得住吗?

Java 架构 面试 分布式 后端

如何评估一个软件的项目费用?

石云升

项目管理 管理 引航计划 内容合集 10月月更

(深入篇)漫游语音识别技术—带你走进语音识别技术的世界

攻城先森

深度学习 音视频 nlp 语音识别

Micronaut教程(二):分布式跟踪、JWT安全和AWS Lambda部署_语言 & 开发_Sergio del Amo Caballero_InfoQ精选文章