本文承接RDS for MySQL,前面我们已经部署好了数据库服务,并测试了连通性,这里则开始部署我们的代码,并连通rds服务.
可以以官方demo为例,或者引入aws提供的aws-serverless-java-container-spring组件,进行部分改造即可。
官方github:GitHub - awslabs/aws-serverless-java-container: A Java wrapper to run Spring, Jersey, Spark, and other apps inside AWS Lambda.
这里介绍如何在我们的工程里添加lambda支持,我们的已有工程为Spring框架,首先在pom.xml中引入serverless依赖.
接着需要添加StreamLambdaHandler.class,SpringApiConfig.class.
两者功能在这里简单介绍一下,RequestStreamHandler是aws lambda 应用程序的入口,我们声明的StreamLambdaHandler继承了RequestStreamHandler,并加载了初始化时,需要扫描的方法类,这些类都在SpringApiConfig中注解引入。
RequestStreamHandler.class
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringLambdaContainerHandler handler;
static {
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(SpringApiConfig.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring framework", e);
}
}
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
}
在SpringApiConfig中,一次性引入多个需要调用的方法类,否则发布程序后,lambda将无法读取到相应的类。
SpringApiConfig.class:
@Configuration
// We use direct @Import instead of @ComponentScan to speed up cold starts
// @ComponentScan("my.service.controller")
@Import({ PingController.class, TestController.class, DataSyncController.class})
public class SpringApiConfig {
/*
* Create required HandlerMapping, to avoid several default HandlerMapping instances being created
*/
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/*
* Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
/*
* optimization - avoids creating default exception resolvers; not required as the serverless container handles
* all exceptions
*
* By default, an ExceptionHandlerExceptionResolver is created which creates many dependent object, including
* an expensive ObjectMapper instance.
*
* To enable custom @ControllerAdvice classes remove this bean.
*/
@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
return new HandlerExceptionResolver() {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return null;
}
};
}
}
配置完以后,开始配置我们真正的逻辑代码,上两个例子,程序员的好朋友hello word,与简单的jdbc链接查询。
PingController.class:
@Controller
@EnableWebMvc
public class PingController {
@Autowired
TestService testService;
@RequestMapping(path = "/ping", method = RequestMethod.GET)
public Map ping() {
System.out.println("controoler ping ");
Map pong = new HashMap<>();
pong.put("pong", "Hello, World!");
return pong;
}
@RequestMapping(path = "/getMySql", method = RequestMethod.GET)
public Map getMySql() {
Map results = new HashMap<>();
ResultSet resultSet = null;
try {
MysqlConnnect mysqlConnnect = new MysqlConnnect();
Connection con = mysqlConnnect.getRemoteConnection();
System.out.println("Remote connection successful.");
String sql = "SELECT * FROM test t where t.desc like ?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1, "%1%");
resultSet = preparedStatement.executeQuery();
List resultList = new ArrayList();
while (resultSet.next()) {
Map map = new ConcurrentHashMap();
map.put("id",resultSet.getInt("id"));
map.put("name",resultSet.getString("name"));
map.put("desc",resultSet.getString("desc"));
resultList.add(map);
}
// 释放资源
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (con != null) {
con.close();
}
results.put("msgCode",200);
results.put("msg","");
results.put("data",resultList);
}catch (Exception e){
System.out.println("SQLException:" + e.toString());
results.put("msgCode",500);
results.put("msg",e.toString());
results.put("data","");
}finally{
return results;
}
}
}
MysqlConnnect.class:
public class MysqlConnnect {
// static Logger logger = LogManager.getLogger(MysqlConnnect.class.getName());
@Autowired
private RdsMysqlInfo rdsMysqlInfo;
public Connection getRemoteConnection() {
System.out.println("getRemoteConnection:");
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String jdbcUrl = "jdbc:mysql://hostname:port/dbname?user=&password=";//数据库关键信息自己补充
System.out.println("jdbcUrl" + jdbcUrl);
System.out.println("Getting remote connection with connection string from environment variables.");
Connection con = DriverManager.getConnection(jdbcUrl);
System.out.println("Remote connection successful.");
return con;
} catch (ClassNotFoundException e) {
// logger.warn(e.toString());
System.out.println("ClassNotFoundException:" + e.toString());
} catch (SQLException e) {
// logger.warn(e.toString());
System.out.println("SQLException:" + e.toString());
}
return null;
}
}
编写完之后,执行打包命令 mvn compile package ,这里我们的包叫core,正常情况下,我们发布到其他服务例如tomcat,weblogic之类的都需要使用war包,但lambda上我们需要使用classes.jar,另外将需要使用的jdbc jar包打入classes.jar中。
尽管lambda有layers层功能可以加载到所依赖的所有jar包,但实际上对jdbc这个jar的引用并没有起作用╮(╯▽╰)╭
以上代码层已经准备完全。开始在lambda上面创建我们的function。
进入Lambda服务,选择创建函数,运行语言环境为java 8,角色选择现有角色,这个角色是我在IAM中已经创建好了,包含了lambda创建,apigateway创建,role创建等等一系列的权限,假如没有相应的权限,后面会有相应的提示,到时候加上即可。
在新的控制面板中,选择新创建的函数,在下方的函数代码中,上传我们的core-classes.jar,运行语言为java 8,处理程序填写规则为package.{ClassName}::{FunctionName},我这里package是service.controllers className是PingController 方法名是getMySql,这个很好理解吧,填完之后选择右上角保存。
由于新创建的lambda服务还没加入到与数据库的同一安全组内,所以此时是无法访问数据库服务的,在此我们把它加进去:
选择与rds for mysql同一vpc,并至少选择两项子网组,子网组跟地域有一定的关系,aws会默认生成,选择两项即可,安全组选择同一安全组。
接下来我们需要测试我们上传的api是否有效,选择配置测试事件,可以自定义传入的报文内容,配置完之后保存,回到主面板页面执行测试。
修改处理程序里的functionname为ping,保存,测试ping方法,hello world 它leile.
修改处理程序里的functionname为getMySql,保存,测试,毫无悬念。至此lambda发布java api配置完成,后面将阐述如何将api通过api-gateway发布至公网。